-
Buttons
+
Buttons
@@ -332,7 +239,7 @@ export default function DemoPage() {
{/* Icon Buttons */}
-
Icon Buttons
+
Icon Buttons
@@ -547,7 +454,7 @@ export default function DemoPage() {
name="radio"
value="option1"
checked={radioValue === 'option1'}
- onChange={(e) => handleRadioChange(e.target.value, 'radio')}
+ onChange={(e) => handleRadioChange(e.target.value)}
className="mr-2"
/>
Option 1
@@ -558,7 +465,7 @@ export default function DemoPage() {
name="radio"
value="option2"
checked={radioValue === 'option2'}
- onChange={(e) => handleRadioChange(e.target.value, 'radio')}
+ onChange={(e) => handleRadioChange(e.target.value)}
className="mr-2"
/>
Option 2
@@ -569,7 +476,7 @@ export default function DemoPage() {
name="radio"
value="option3"
checked={radioValue === 'option3'}
- onChange={(e) => handleRadioChange(e.target.value, 'radio')}
+ onChange={(e) => handleRadioChange(e.target.value)}
className="mr-2"
/>
Option 3
@@ -642,7 +549,7 @@ export default function DemoPage() {
valueSelector={(d) => d.value}
labelSelector={(d) => d.name}
keySelector={(d) => d.name}
- colorSelector={(d) => '#dc2626'}
+ colorSelector={() => '#dc2626'}
showPercentageInLegend
/>
@@ -670,40 +577,7 @@ export default function DemoPage() {
- {/* Tables */}
-
-
Tables
-
-
-
-
- Name |
- Age |
- Country |
- Status |
-
-
-
- {dummyTableData.map((row) => (
-
- {row.name} |
- {row.age} |
- {row.country} |
-
-
- {row.status}
-
- |
-
- ))}
-
-
-
-
+
{/* Lists */}
diff --git a/frontend/src/pages/DevPage.tsx b/frontend/src/pages/DevPage.tsx
index f4d1a1eaedfbd94278cc2bd866984a92054acdd2..64b1a2040985d05c5a38b2bb630a1b198e4ea600 100644
--- a/frontend/src/pages/DevPage.tsx
+++ b/frontend/src/pages/DevPage.tsx
@@ -3,11 +3,9 @@ import {
PageContainer, Heading, Button, Container,
} from '@ifrc-go/ui';
-// Local storage key for selected model
const SELECTED_MODEL_KEY = 'selectedVlmModel';
export default function DevPage() {
- // Model selection state
const [availableModels, setAvailableModels] = useState
>([]);
const [selectedModel, setSelectedModel] = useState('');
- // Fetch models on component mount
useEffect(() => {
fetchModels();
}, []);
@@ -25,11 +22,8 @@ export default function DevPage() {
fetch('/api/models')
.then(r => r.json())
.then(modelsData => {
- console.log('Models data:', modelsData);
- console.log('Available models count:', modelsData.models?.length || 0);
setAvailableModels(modelsData.models || []);
- // Load persisted model or set default model (first available model)
const persistedModel = localStorage.getItem(SELECTED_MODEL_KEY);
if (modelsData.models && modelsData.models.length > 0) {
if (persistedModel && modelsData.models.find((m: any) => m.m_code === persistedModel && m.is_available)) {
@@ -42,7 +36,7 @@ export default function DevPage() {
}
})
.catch(err => {
- console.error('Failed to fetch models:', err);
+
});
};
@@ -59,7 +53,6 @@ export default function DevPage() {
});
if (response.ok) {
- // Update local state
setAvailableModels(prev =>
prev.map(model =>
model.m_code === modelCode
@@ -67,19 +60,15 @@ export default function DevPage() {
: model
)
);
- console.log(`Model ${modelCode} availability toggled to ${!currentStatus}`);
} else {
const errorData = await response.json();
- console.error('Failed to toggle model availability:', errorData);
alert(`Failed to toggle model availability: ${errorData.error || 'Unknown error'}`);
}
} catch (error) {
- console.error('Error toggling model availability:', error);
alert('Error toggling model availability');
}
};
- // Handle model selection change
const handleModelChange = (modelCode: string) => {
setSelectedModel(modelCode);
localStorage.setItem(SELECTED_MODEL_KEY, modelCode);
@@ -195,12 +184,10 @@ export default function DevPage() {
fetch('/api/models')
.then(r => r.json())
.then(data => {
- console.log('Models API response:', data);
- alert('Check console for models API response');
+ alert('Models API response received successfully');
})
.catch(err => {
- console.error('Models API error:', err);
- alert('Models API error - check console');
+ alert('Models API error occurred');
});
}}
>
@@ -216,12 +203,10 @@ export default function DevPage() {
fetch(`/api/models/${selectedModel}/test`)
.then(r => r.json())
.then(data => {
- console.log('Model test response:', data);
- alert('Check console for model test response');
+ alert('Model test completed successfully');
})
.catch(err => {
- console.error('Model test error:', err);
- alert('Model test error - check console');
+ alert('Model test failed');
});
}}
>
diff --git a/frontend/src/pages/ExplorePage.tsx b/frontend/src/pages/ExplorePage.tsx
deleted file mode 100644
index 419c71ae7354acaf331a36d5dc3b595f7ac41f03..0000000000000000000000000000000000000000
--- a/frontend/src/pages/ExplorePage.tsx
+++ /dev/null
@@ -1,365 +0,0 @@
-import { PageContainer, Heading, TextInput, SelectInput, MultiSelectInput, Button } from '@ifrc-go/ui';
-import { useState, useEffect, useMemo } from 'react';
-import { useNavigate } from 'react-router-dom';
-import { StarLineIcon } from '@ifrc-go/icons';
-
-interface MapOut {
- image_id: string;
- file_key: string;
- image_url: string;
- source: string;
- type: string;
- epsg: string;
- image_type: string;
- countries?: {c_code: string, label: string, r_code: string}[];
- caption?: {
- title: string;
- generated: string;
- edited?: string;
- starred?: boolean;
- };
-}
-
-export default function ExplorePage() {
- const navigate = useNavigate();
- const [maps, setMaps] = useState([]);
- const [search, setSearch] = useState('');
- const [srcFilter, setSrcFilter] = useState('');
- const [catFilter, setCatFilter] = useState('');
- const [regionFilter, setRegionFilter] = useState('');
- const [countryFilter, setCountryFilter] = useState('');
- const [showStarredOnly, setShowStarredOnly] = useState(false);
- const [sources, setSources] = useState<{s_code: string, label: string}[]>([]);
- const [types, setTypes] = useState<{t_code: string, label: string}[]>([]);
- const [regions, setRegions] = useState<{r_code: string, label: string}[]>([]);
- const [countries, setCountries] = useState<{c_code: string, label: string, r_code: string}[]>([]);
- const [isLoadingFilters, setIsLoadingFilters] = useState(true);
-
- const fetchMaps = () => {
- setIsLoadingFilters(true);
- // Fetch maps
- fetch('/api/images/')
- .then(r => {
- if (!r.ok) {
- throw new Error(`HTTP ${r.status}: ${r.statusText}`);
- }
- return r.json();
- })
- .then(data => {
- // Ensure data is an array
- if (Array.isArray(data)) {
- setMaps(data);
- console.log(`Loaded ${data.length} maps`);
- if (data.length > 0) {
- console.log('Sample map data:', {
- image_id: data[0].image_id,
- source: data[0].source,
- type: data[0].type,
- countries: data[0].countries?.length || 0,
- caption: data[0].caption ? 'has caption' : 'no caption'
- });
- }
- } else {
- console.error('Expected array from /api/images/, got:', data);
- setMaps([]);
- }
- })
- .catch(err => {
- console.error('Failed to fetch maps:', err);
- setMaps([]);
- })
- .finally(() => {
- setIsLoadingFilters(false);
- });
- };
-
- useEffect(() => {
- fetchMaps();
- }, []);
-
- // Auto-refresh when component becomes visible (user navigates back)
- useEffect(() => {
- const handleVisibilityChange = () => {
- if (!document.hidden) {
- fetchMaps();
- }
- };
-
- document.addEventListener('visibilitychange', handleVisibilityChange);
- return () => {
- document.removeEventListener('visibilitychange', handleVisibilityChange);
- };
- }, []);
-
- useEffect(() => {
- // Fetch lookup data
- console.log('Fetching filter data...');
- setIsLoadingFilters(true);
-
- Promise.all([
- fetch('/api/sources').then(r => {
- console.log('Sources response:', r.status, r.statusText);
- if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
- return r.json();
- }),
- fetch('/api/types').then(r => {
- console.log('Types response:', r.status, r.statusText);
- if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
- return r.json();
- }),
- fetch('/api/regions').then(r => {
- console.log('Regions response:', r.status, r.statusText);
- if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
- return r.json();
- }),
- fetch('/api/countries').then(r => {
- console.log('Countries response:', r.status, r.statusText);
- if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
- return r.json();
- })
- ]).then(([sourcesData, typesData, regionsData, countriesData]) => {
- console.log('Fetched filter data:', {
- sources: sourcesData.length,
- types: typesData.length,
- regions: regionsData.length,
- countries: countriesData.length
- });
-
- if (Array.isArray(sourcesData)) {
- setSources(sourcesData);
- } else {
- console.error('Expected array from /api/sources, got:', sourcesData);
- setSources([]);
- }
-
- if (Array.isArray(typesData)) {
- setTypes(typesData);
- } else {
- console.error('Expected array from /api/types, got:', typesData);
- setTypes([]);
- }
-
- if (Array.isArray(regionsData)) {
- setRegions(regionsData);
- } else {
- console.error('Expected array from /api/regions, got:', regionsData);
- setRegions([]);
- }
-
- if (Array.isArray(countriesData)) {
- setCountries(countriesData);
- } else {
- console.error('Expected array from /api/countries, got:', countriesData);
- setCountries([]);
- }
-
- setIsLoadingFilters(false);
- }).catch(err => {
- console.error('Failed to fetch filter data:', err);
- // Set empty arrays on error to prevent undefined issues
- setSources([]);
- setTypes([]);
- setRegions([]);
- setCountries([]);
- setIsLoadingFilters(false);
- });
- }, []);
-
- const filtered = useMemo(() => {
- // Ensure maps is an array before filtering
- if (!Array.isArray(maps)) {
- console.warn('maps is not an array:', maps);
- return [];
- }
-
- return maps.filter(m => {
- // Search in filename, source, type, title, and caption
- const searchLower = search.toLowerCase();
- const searchMatch = !search ||
- m.file_key.toLowerCase().includes(searchLower) ||
- m.source.toLowerCase().includes(searchLower) ||
- m.type.toLowerCase().includes(searchLower) ||
- (m.caption?.title && m.caption.title.toLowerCase().includes(searchLower)) ||
- (m.caption?.edited && m.caption.edited.toLowerCase().includes(searchLower)) ||
- (m.caption?.generated && m.caption.generated.toLowerCase().includes(searchLower));
-
- // Filter by source
- const sourceMatch = !srcFilter || m.source === srcFilter;
-
- // Filter by type
- const typeMatch = !catFilter || m.type === catFilter;
-
- // Filter by region (check if any country in the image belongs to the selected region)
- const regionMatch = !regionFilter || (m.countries && m.countries.some(c => c.r_code === regionFilter));
-
- // Filter by country (check if any country in the image matches the selected country)
- const countryMatch = !countryFilter || (m.countries && m.countries.some(c => c.c_code === countryFilter));
-
- // Filter by starred status
- const starredMatch = !showStarredOnly || (m.caption && m.caption.starred === true);
-
- return searchMatch && sourceMatch && typeMatch && regionMatch && countryMatch && starredMatch;
- });
- }, [maps, search, srcFilter, catFilter, regionFilter, countryFilter]);
-
- return (
-
-
- {/* Header Section */}
-
-
-
Explore Examples
-
Browse and search through uploaded crisis maps
-
-
-
-
-
-
-
- {/* Filters Bar */}
-
-
- setSearch(e || '')}
- className="flex-1 min-w-[12rem]"
- />
-
- setSrcFilter(v as string || '')}
- keySelector={(o) => o.s_code}
- labelSelector={(o) => o.label}
- required={false}
- disabled={isLoadingFilters}
- />
-
- setCatFilter(v as string || '')}
- keySelector={(o) => o.t_code}
- labelSelector={(o) => o.label}
- required={false}
- disabled={isLoadingFilters}
- />
-
- setRegionFilter(v as string || '')}
- keySelector={(o) => o.r_code}
- labelSelector={(o) => o.label}
- required={false}
- disabled={isLoadingFilters}
- />
-
- setCountryFilter((v as string[])[0] || '')}
- keySelector={(o) => o.c_code}
- labelSelector={(o) => o.label}
- disabled={isLoadingFilters}
- />
-
-
-
- {/* Results Section */}
-
-
-
- {filtered.length} of {maps.length} examples
-
-
-
- {/* List */}
-
- {filtered.map(m => (
-
navigate(`/map/${m.image_id}`)}>
-
- {m.image_url ? (
-

{
- // Fallback to placeholder if image fails to load
- const target = e.target as HTMLImageElement;
- target.style.display = 'none';
- target.parentElement!.innerHTML = 'Img';
- }}
- />
- ) : (
- 'Img'
- )}
-
-
-
- {m.caption?.title || 'No title'}
-
-
- {m.source}
- {m.type}
-
-
-
- ))}
-
- {!filtered.length && (
-
- )}
-
-
-
-
- );
-}
diff --git a/frontend/src/pages/ExplorePage/ExplorePage.module.css b/frontend/src/pages/ExplorePage/ExplorePage.module.css
new file mode 100644
index 0000000000000000000000000000000000000000..8a15a666bda61711c1cf825ce18cfb09bf597740
--- /dev/null
+++ b/frontend/src/pages/ExplorePage/ExplorePage.module.css
@@ -0,0 +1,125 @@
+.metadataTags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--go-ui-spacing-sm);
+ align-items: center;
+}
+
+.metadataTag {
+ padding: var(--go-ui-spacing-xs) var(--go-ui-spacing-sm);
+ background-color: var(--go-ui-color-red-10);
+ color: var(--go-ui-color-red-90);
+ font-size: var(--go-ui-font-size-xs);
+ border-radius: var(--go-ui-border-radius-md);
+ font-weight: var(--go-ui-font-weight-medium);
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-red-20);
+ transition: all var(--go-ui-duration-transition-fast) ease;
+ white-space: nowrap;
+}
+
+.metadataTag:hover {
+ background-color: var(--go-ui-color-red-20);
+ transform: translateY(-1px);
+ box-shadow: var(--go-ui-box-shadow-xs);
+}
+
+.metadataTagSource {
+ padding: var(--go-ui-spacing-xs) var(--go-ui-spacing-sm);
+ background-color: var(--go-ui-color-blue-10);
+ color: var(--go-ui-color-blue-90);
+ font-size: var(--go-ui-font-size-xs);
+ border-radius: var(--go-ui-border-radius-md);
+ font-weight: var(--go-ui-font-weight-medium);
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-blue-20);
+ white-space: nowrap;
+}
+
+.metadataTagType {
+ padding: var(--go-ui-spacing-xs) var(--go-ui-spacing-sm);
+ background-color: var(--go-ui-color-red-90);
+ color: var(--go-ui-color-white);
+ font-size: var(--go-ui-font-size-xs);
+ border-radius: var(--go-ui-border-radius-md);
+ font-weight: var(--go-ui-font-weight-medium);
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-red-90);
+ white-space: nowrap;
+}
+
+.mapItem {
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+ border-radius: var(--go-ui-border-radius-lg);
+ padding: var(--go-ui-spacing-lg);
+ display: flex;
+ gap: var(--go-ui-spacing-lg);
+ cursor: pointer;
+ transition: all var(--go-ui-duration-transition-medium) ease;
+ background-color: var(--go-ui-color-white);
+}
+
+.mapItem:hover {
+ background-color: var(--go-ui-color-gray-10);
+ border-color: var(--go-ui-color-gray-30);
+ box-shadow: var(--go-ui-box-shadow-sm);
+ transform: translateY(-1px);
+}
+
+.mapItemImage {
+ background-color: var(--go-ui-color-gray-20);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--go-ui-color-gray-60);
+ font-size: var(--go-ui-font-size-xs);
+ overflow: hidden;
+ border-radius: var(--go-ui-border-radius-md);
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+ flex-shrink: 0;
+}
+
+.mapItemImage img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ image-rendering: pixelated;
+}
+
+.mapItemContent {
+ flex: 1;
+ min-width: 0;
+}
+
+.mapItemTitle {
+ font-weight: var(--go-ui-font-weight-medium);
+ color: var(--go-ui-color-text);
+ margin-bottom: var(--go-ui-spacing-sm);
+ font-size: var(--go-ui-font-size-md);
+ line-height: var(--go-ui-line-height-md);
+}
+
+.mapItemMetadata {
+ margin-bottom: var(--go-ui-spacing-sm);
+}
+
+/* Responsive adjustments */
+@media (max-width: 768px) {
+ .mapItem {
+ flex-direction: column;
+ gap: var(--go-ui-spacing-md);
+ }
+
+ .mapItemImage {
+ width: 100%;
+ height: 120px;
+ }
+
+ .metadataTags {
+ gap: var(--go-ui-spacing-xs);
+ }
+
+ .metadataTag,
+ .metadataTagSource,
+ .metadataTagType {
+ font-size: var(--go-ui-font-size-xs);
+ padding: var(--go-ui-spacing-2xs) var(--go-ui-spacing-xs);
+ }
+}
diff --git a/frontend/src/pages/ExplorePage/ExplorePage.tsx b/frontend/src/pages/ExplorePage/ExplorePage.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d965f4ed297ca67e4620535f0a700d31586e65fe
--- /dev/null
+++ b/frontend/src/pages/ExplorePage/ExplorePage.tsx
@@ -0,0 +1,357 @@
+import { PageContainer, TextInput, SelectInput, MultiSelectInput, Button, Container } from '@ifrc-go/ui';
+import { useState, useEffect, useMemo } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { StarLineIcon } from '@ifrc-go/icons';
+import styles from './ExplorePage.module.css';
+
+interface CaptionWithImageOut {
+ cap_id: string;
+ image_id: string;
+ title: string;
+ prompt: string;
+ model: string;
+ schema_id: string;
+ raw_json: any;
+ generated: string;
+ edited?: string;
+ accuracy?: number;
+ context?: number;
+ usability?: number;
+ starred: boolean;
+ created_at?: string;
+ updated_at?: string;
+ file_key: string;
+ image_url: string;
+ source: string;
+ event_type: string;
+ epsg: string;
+ image_type: string;
+ countries: {c_code: string, label: string, r_code: string}[];
+}
+
+export default function ExplorePage() {
+ const navigate = useNavigate();
+ const [captions, setCaptions] = useState([]);
+ const [search, setSearch] = useState('');
+ const [srcFilter, setSrcFilter] = useState('');
+ const [catFilter, setCatFilter] = useState('');
+ const [regionFilter, setRegionFilter] = useState('');
+ const [countryFilter, setCountryFilter] = useState('');
+ const [showStarredOnly, setShowStarredOnly] = useState(false);
+ const [sources, setSources] = useState<{s_code: string, label: string}[]>([]);
+ const [types, setTypes] = useState<{t_code: string, label: string}[]>([]);
+ const [regions, setRegions] = useState<{r_code: string, label: string}[]>([]);
+ const [countries, setCountries] = useState<{c_code: string, label: string, r_code: string}[]>([]);
+ const [isLoadingFilters, setIsLoadingFilters] = useState(true);
+
+ const fetchCaptions = () => {
+ setIsLoadingFilters(true);
+ fetch('/api/captions')
+ .then(r => {
+ if (!r.ok) {
+ throw new Error(`HTTP ${r.status}: ${r.statusText}`);
+ }
+ return r.json();
+ })
+ .then(data => {
+ if (Array.isArray(data)) {
+ setCaptions(data);
+
+ } else {
+
+ setCaptions([]);
+ }
+ })
+ .catch(() => {
+ setCaptions([]);
+ })
+ .finally(() => {
+ setIsLoadingFilters(false);
+ });
+ };
+
+ useEffect(() => {
+ fetchCaptions();
+ }, []);
+
+ useEffect(() => {
+ const handleVisibilityChange = () => {
+ if (!document.hidden) {
+ fetchCaptions();
+ }
+ };
+
+ document.addEventListener('visibilitychange', handleVisibilityChange);
+ return () => {
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
+ };
+ }, []);
+
+ useEffect(() => {
+
+ setIsLoadingFilters(true);
+
+ Promise.all([
+ fetch('/api/sources').then(r => {
+ if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
+ return r.json();
+ }),
+ fetch('/api/types').then(r => {
+ if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
+ return r.json();
+ }),
+ fetch('/api/regions').then(r => {
+ if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
+ return r.json();
+ }),
+ fetch('/api/countries').then(r => {
+ if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
+ return r.json();
+ })
+ ]).then(([sourcesData, typesData, regionsData, countriesData]) => {
+
+
+ if (Array.isArray(sourcesData)) {
+ setSources(sourcesData);
+ } else {
+
+ setSources([]);
+ }
+
+ if (Array.isArray(typesData)) {
+ setTypes(typesData);
+ } else {
+
+ setTypes([]);
+ }
+
+ if (Array.isArray(regionsData)) {
+ setRegions(regionsData);
+ } else {
+
+ setRegions([]);
+ }
+
+ if (Array.isArray(countriesData)) {
+ setCountries(countriesData);
+ } else {
+
+ setCountries([]);
+ }
+
+ setIsLoadingFilters(false);
+ }).catch(() => {
+
+ setSources([]);
+ setTypes([]);
+ setRegions([]);
+ setCountries([]);
+ setIsLoadingFilters(false);
+ });
+ }, []);
+
+ const filtered = useMemo(() => {
+ if (!Array.isArray(captions)) {
+
+ return [];
+ }
+
+ return captions.filter(c => {
+ const searchLower = search.toLowerCase();
+ const searchMatch = !search ||
+ c.file_key.toLowerCase().includes(searchLower) ||
+ c.source.toLowerCase().includes(searchLower) ||
+ c.event_type.toLowerCase().includes(searchLower) ||
+ c.title.toLowerCase().includes(searchLower) ||
+ (c.edited && c.edited.toLowerCase().includes(searchLower)) ||
+ (c.generated && c.generated.toLowerCase().includes(searchLower));
+
+ const sourceMatch = !srcFilter || c.source === srcFilter;
+ const typeMatch = !catFilter || c.event_type === catFilter;
+ const regionMatch = !regionFilter || (c.countries && c.countries.some(c => c.r_code === regionFilter));
+ const countryMatch = !countryFilter || (c.countries && c.countries.some(c => c.c_code === countryFilter));
+ const starredMatch = !showStarredOnly || c.starred === true;
+
+ return searchMatch && sourceMatch && typeMatch && regionMatch && countryMatch && starredMatch;
+ });
+ }, [captions, search, srcFilter, catFilter, regionFilter, countryFilter]);
+
+ return (
+
+
+
+ {/* Header Section */}
+
+
+
Browse and search through uploaded crisis maps
+
+
+
+
+
+
+
+ {/* Filters Bar */}
+
+
+ setSearch(e || '')}
+ className="flex-1 min-w-[12rem]"
+ />
+
+ setSrcFilter(v as string || '')}
+ keySelector={(o) => o.s_code}
+ labelSelector={(o) => o.label}
+ required={false}
+ disabled={isLoadingFilters}
+ />
+
+ setCatFilter(v as string || '')}
+ keySelector={(o) => o.t_code}
+ labelSelector={(o) => o.label}
+ required={false}
+ disabled={isLoadingFilters}
+ />
+
+ setRegionFilter(v as string || '')}
+ keySelector={(o) => o.r_code}
+ labelSelector={(o) => o.label}
+ required={false}
+ disabled={isLoadingFilters}
+ />
+
+ setCountryFilter((v as string[])[0] || '')}
+ keySelector={(o) => o.c_code}
+ labelSelector={(o) => o.label}
+ disabled={isLoadingFilters}
+ />
+
+
+
+ {/* Results Section */}
+
+
+
+
+ {filtered.length} of {captions.length} examples
+
+
+
+ {/* List */}
+
+ {filtered.map(c => (
+
navigate(`/map/${c.image_id}?captionId=${c.cap_id}`)}>
+
+ {c.image_url ? (
+

{
+ const target = e.target as HTMLImageElement;
+ target.style.display = 'none';
+ target.parentElement!.innerHTML = 'Img';
+ }}
+ />
+ ) : (
+ 'Img'
+ )}
+
+
+
+ {c.title}
+
+
+
+
+ {c.source}
+
+
+ {c.event_type}
+
+
+ {c.epsg}
+
+
+ {c.image_type}
+
+
+
+
+
+ ))}
+
+ {!filtered.length && (
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/pages/ExplorePage/index.ts b/frontend/src/pages/ExplorePage/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8f1bf9312a3e42ded6dccade29424485e5fcce6d
--- /dev/null
+++ b/frontend/src/pages/ExplorePage/index.ts
@@ -0,0 +1 @@
+export { default } from './ExplorePage';
diff --git a/frontend/src/pages/MapDetailPage.tsx b/frontend/src/pages/MapDetailPage.tsx
deleted file mode 100644
index 3ec4b864a47e3702f7a839f14c580fce956a8ac2..0000000000000000000000000000000000000000
--- a/frontend/src/pages/MapDetailPage.tsx
+++ /dev/null
@@ -1,197 +0,0 @@
-import { PageContainer, Button } from '@ifrc-go/ui';
-import { useState, useEffect } from 'react';
-import { useParams, useNavigate } from 'react-router-dom';
-
-interface MapOut {
- image_id: string;
- file_key: string;
- image_url: string;
- source: string;
- type: string;
- epsg: string;
- image_type: string;
- caption?: {
- title: string;
- generated: string;
- edited?: string;
- };
-}
-
-export default function MapDetailPage() {
- const { mapId } = useParams<{ mapId: string }>();
- const navigate = useNavigate();
- const [map, setMap] = useState(null);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState(null);
- const [contributing, setContributing] = useState(false);
-
- useEffect(() => {
- if (!mapId) {
- setError('Map ID is required');
- setLoading(false);
- return;
- }
-
- // Fetch the specific map
- fetch(`/api/images/${mapId}`)
- .then(response => {
- if (!response.ok) {
- throw new Error('Map not found');
- }
- return response.json();
- })
- .then(data => {
- setMap(data);
- setLoading(false);
- })
- .catch(err => {
- setError(err.message);
- setLoading(false);
- });
- }, [mapId]);
-
- const handleContribute = async () => {
- if (!map) return;
-
- setContributing(true);
- try {
- // Simulate uploading the current image by creating a new map entry
- const formData = new FormData();
- formData.append('source', map.source);
- formData.append('type', map.type);
- formData.append('epsg', map.epsg);
- formData.append('image_type', map.image_type);
-
- // We'll need to fetch the image and create a file from it
- const imageResponse = await fetch(map.image_url);
- const imageBlob = await imageResponse.blob();
- const file = new File([imageBlob], map.file_key, { type: 'image/jpeg' });
- formData.append('file', file);
-
- const response = await fetch('/api/images/', {
- method: 'POST',
- body: formData,
- });
-
- if (!response.ok) {
- throw new Error('Failed to contribute image');
- }
-
- const result = await response.json();
-
- // Navigate to the upload page with the new map ID and step 2
- navigate(`/upload?mapId=${result.image_id}&step=2`);
- } catch (err) {
- console.error('Contribution failed:', err);
- alert('Failed to contribute image. Please try again.');
- } finally {
- setContributing(false);
- }
- };
-
- if (loading) {
- return (
-
-
-
- );
- }
-
- if (error || !map) {
- return (
-
-
-
{error || 'Map not found'}
-
-
- );
- }
-
- return (
-
-
-
-
-
-
- {/* Image Section */}
-
-
- {map.image_url ? (
-

- ) : (
-
- No image available
-
- )}
-
-
-
- {/* Details Section */}
-
-
-
Title
-
-
- {map.caption?.title || '— no title —'}
-
-
-
-
-
-
Metadata
-
-
- {map.source}
-
-
- {map.type}
-
-
- {map.epsg}
-
-
- {map.image_type}
-
-
-
-
-
-
Generated Caption
-
-
- {map.caption?.edited || map.caption?.generated || '— no caption yet —'}
-
-
-
-
-
-
- {/* Contribute Section */}
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/frontend/src/pages/MapDetailsPage/MapDetailPage.module.css b/frontend/src/pages/MapDetailsPage/MapDetailPage.module.css
new file mode 100644
index 0000000000000000000000000000000000000000..ea1d736fcaab465fb1b9af2b3aa2835bcc4cc070
--- /dev/null
+++ b/frontend/src/pages/MapDetailsPage/MapDetailPage.module.css
@@ -0,0 +1,184 @@
+.backButton {
+ margin-bottom: var(--go-ui-spacing-lg);
+}
+
+.imageContainer {
+ background-color: var(--go-ui-color-gray-20);
+ border-radius: var(--go-ui-border-radius-lg);
+ overflow: hidden;
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+ box-shadow: var(--go-ui-box-shadow-sm);
+ transition: box-shadow var(--go-ui-duration-transition-medium) ease;
+}
+
+.imageContainer:hover {
+ box-shadow: var(--go-ui-box-shadow-md);
+}
+
+.imageContainer img {
+ width: 100%;
+ height: auto;
+ object-fit: contain;
+ image-rendering: pixelated;
+ display: block;
+}
+
+.imagePlaceholder {
+ width: 100%;
+ height: 16rem;
+ background-color: var(--go-ui-color-gray-30);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--go-ui-color-gray-60);
+ font-size: var(--go-ui-font-size-sm);
+ font-weight: var(--go-ui-font-weight-medium);
+}
+
+.metadataTags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--go-ui-spacing-sm);
+}
+
+.metadataTag {
+ padding: var(--go-ui-spacing-xs) var(--go-ui-spacing-sm);
+ background-color: var(--go-ui-color-red-10);
+ color: var(--go-ui-color-red-90);
+ font-size: var(--go-ui-font-size-sm);
+ border-radius: var(--go-ui-border-radius-md);
+ font-weight: var(--go-ui-font-weight-medium);
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-red-20);
+ transition: all var(--go-ui-duration-transition-fast) ease;
+}
+
+.metadataTag:hover {
+ background-color: var(--go-ui-color-red-20);
+ transform: translateY(-1px);
+ box-shadow: var(--go-ui-box-shadow-xs);
+}
+
+.captionContainer {
+ padding: var(--go-ui-spacing-md);
+ background-color: var(--go-ui-color-gray-10);
+ border-radius: var(--go-ui-border-radius-md);
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+}
+
+.captionText {
+ margin-bottom: var(--go-ui-spacing-md);
+ line-height: 1.6;
+ color: var(--go-ui-color-gray-900);
+}
+
+.captionText:last-child {
+ margin-bottom: 0;
+}
+
+.highlightedCaption {
+ background-color: var(--go-ui-color-blue-10);
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-blue-30);
+ border-radius: var(--go-ui-border-radius-md);
+ padding: var(--go-ui-spacing-md);
+ margin: var(--go-ui-spacing-md) 0;
+}
+
+.captionHighlight {
+ margin-top: var(--go-ui-spacing-sm);
+ font-size: var(--go-ui-font-size-sm);
+ color: var(--go-ui-color-blue-70);
+ font-style: italic;
+}
+
+.contributeSection {
+ margin-top: var(--go-ui-spacing-2xl);
+ padding-top: var(--go-ui-spacing-lg);
+ border-top: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+ display: flex;
+ justify-content: center;
+}
+
+.contributeButton {
+ background-color: var(--go-ui-color-red-90);
+ color: var(--go-ui-color-white);
+ padding: var(--go-ui-spacing-sm) var(--go-ui-spacing-xl);
+ border-radius: var(--go-ui-border-radius-lg);
+ font-weight: var(--go-ui-font-weight-medium);
+ transition: all var(--go-ui-duration-transition-medium) ease;
+ box-shadow: var(--go-ui-box-shadow-sm);
+ border: none;
+ cursor: pointer;
+ font-size: var(--go-ui-font-size-md);
+}
+
+.contributeButton:hover {
+ background-color: var(--go-ui-color-red-hover);
+ transform: translateY(-2px);
+ box-shadow: var(--go-ui-box-shadow-md);
+}
+
+.contributeButton:active {
+ transform: translateY(0);
+ box-shadow: var(--go-ui-box-shadow-sm);
+}
+
+.gridLayout {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: var(--go-ui-spacing-2xl);
+}
+
+@media (min-width: 1024px) {
+ .gridLayout {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+
+.detailsSection {
+ display: flex;
+ flex-direction: column;
+ gap: var(--go-ui-spacing-lg);
+}
+
+.loadingContainer {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 400px;
+ color: var(--go-ui-color-gray-60);
+ font-size: var(--go-ui-font-size-lg);
+ font-weight: var(--go-ui-font-weight-medium);
+}
+
+.errorContainer {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 400px;
+ color: var(--go-ui-color-negative);
+ font-size: var(--go-ui-font-size-lg);
+ font-weight: var(--go-ui-font-weight-medium);
+}
+
+
+
+/* Responsive adjustments */
+@media (max-width: 768px) {
+ .gridLayout {
+ gap: var(--go-ui-spacing-lg);
+ }
+
+ .metadataTags {
+ gap: var(--go-ui-spacing-xs);
+ }
+
+ .metadataTag {
+ font-size: var(--go-ui-font-size-xs);
+ padding: var(--go-ui-spacing-2xs) var(--go-ui-spacing-xs);
+ }
+
+ .contributeButton {
+ padding: var(--go-ui-spacing-sm) var(--go-ui-spacing-lg);
+ font-size: var(--go-ui-font-size-sm);
+ }
+}
diff --git a/frontend/src/pages/MapDetailsPage/MapDetailPage.tsx b/frontend/src/pages/MapDetailsPage/MapDetailPage.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..751cf05aa787e599e9a8e66619a995064d8beb1c
--- /dev/null
+++ b/frontend/src/pages/MapDetailsPage/MapDetailPage.tsx
@@ -0,0 +1,225 @@
+import { PageContainer, Button, Container, Spinner } from '@ifrc-go/ui';
+import { useState, useEffect } from 'react';
+import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
+import styles from './MapDetailPage.module.css';
+
+interface MapOut {
+ image_id: string;
+ file_key: string;
+ image_url: string;
+ source: string;
+ event_type: string;
+ epsg: string;
+ image_type: string;
+ countries?: Array<{
+ c_code: string;
+ label: string;
+ r_code: string;
+ }>;
+ captions?: Array<{
+ title: string;
+ generated: string;
+ edited?: string;
+ cap_id?: string;
+ }>;
+}
+
+export default function MapDetailPage() {
+ const { mapId } = useParams<{ mapId: string }>();
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+ const captionId = searchParams.get('captionId');
+ const [map, setMap] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+
+ useEffect(() => {
+ if (!mapId) {
+ setError('Map ID is required');
+ setLoading(false);
+ return;
+ }
+
+ fetch(`/api/images/${mapId}`)
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('Map not found');
+ }
+ return response.json();
+ })
+ .then(data => {
+ setMap(data);
+ setLoading(false);
+ })
+ .catch(err => {
+ setError(err.message);
+ setLoading(false);
+ });
+ }, [mapId]);
+
+ const handleContribute = () => {
+ if (!map) return;
+
+ const url = captionId ?
+ `/upload?mapId=${map.image_id}&step=2&captionId=${captionId}` :
+ `/upload?mapId=${map.image_id}&step=2`;
+ navigate(url);
+ };
+
+ if (loading) {
+ return (
+
+
+
+
+
Loading map details...
+
+
+
+ );
+ }
+
+ if (error || !map) {
+ return (
+
+
+
+
⚠️
+
Unable to load map
+
{error || 'Map not found'}
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+ {/* Image Section */}
+
+
+ {map.image_url ? (
+

+ ) : (
+
+ No image available
+
+ )}
+
+
+
+ {/* Details Section */}
+
+
+
+ {map.captions && map.captions.length > 0 ? map.captions[0].title : '— no title —'}
+
+
+
+
+
+
+ {map.source}
+
+
+ {map.event_type}
+
+
+ {map.epsg}
+
+
+ {map.image_type}
+
+
+
+
+
+
+ {map.captions && map.captions.length > 0 ? (
+ map.captions.map((caption, index) => (
+
+
{caption.edited || caption.generated}
+ {captionId && map.captions && map.captions[index] &&
+ 'cap_id' in map.captions[index] &&
+ map.captions[index].cap_id === captionId && (
+
+ ← This is the caption you selected
+
+ )}
+
+ ))
+ ) : (
+
— no caption yet —
+ )}
+
+
+
+
+
+ {/* Contribute Section */}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/MapDetailsPage/index.ts b/frontend/src/pages/MapDetailsPage/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..385792c83914c5860cdb267d1a07026e7ca6c351
--- /dev/null
+++ b/frontend/src/pages/MapDetailsPage/index.ts
@@ -0,0 +1 @@
+export { default } from './MapDetailPage';
diff --git a/frontend/src/pages/UploadPage.tsx b/frontend/src/pages/UploadPage.tsx
deleted file mode 100644
index 65c537a198401308869e1ad512c006e571d9f377..0000000000000000000000000000000000000000
--- a/frontend/src/pages/UploadPage.tsx
+++ /dev/null
@@ -1,620 +0,0 @@
-import { useCallback, useState, useEffect, useRef } from 'react';
-import type { DragEvent } from 'react';
-import {
- PageContainer, Heading, Button,
- SelectInput, MultiSelectInput, Container, IconButton, TextInput, TextArea, Spinner,
-} from '@ifrc-go/ui';
-import {
- UploadCloudLineIcon,
- ArrowRightLineIcon,
- DeleteBinLineIcon,
-} from '@ifrc-go/icons';
-import { Link, useSearchParams } from 'react-router-dom';
-
-const SELECTED_MODEL_KEY = 'selectedVlmModel';
-
-export default function UploadPage() {
- const [searchParams] = useSearchParams();
- const [step, setStep] = useState<1 | 2 | 3>(1);
- const [isLoading, setIsLoading] = useState(false);
- const stepRef = useRef(step);
- const uploadedImageIdRef = useRef(null);
- const [preview, setPreview] = useState(null);
- /* ---------------- local state ----------------- */
-
- const [file, setFile] = useState(null);
- const [source, setSource] = useState('');
- const [type, setType] = useState('');
- const [epsg, setEpsg] = useState('');
- const [imageType, setImageType] = useState('');
- const [countries, setCountries] = useState([]);
- const [title, setTitle] = useState('');
-
- // Metadata options from database
- const [sources, setSources] = useState<{s_code: string, label: string}[]>([]);
- const [types, setTypes] = useState<{t_code: string, label: string}[]>([]);
- const [spatialReferences, setSpatialReferences] = useState<{epsg: string, srid: string, proj4: string, wkt: string}[]>([]);
- const [imageTypes, setImageTypes] = useState<{image_type: string, label: string}[]>([]);
- const [countriesOptions, setCountriesOptions] = useState<{c_code: string, label: string, r_code: string}[]>([]);
-
- // Track uploaded image data for potential deletion
- const [uploadedImageId, setUploadedImageId] = useState(null);
-
- // Keep refs updated with current values
- stepRef.current = step;
- uploadedImageIdRef.current = uploadedImageId;
-
- // Wrapper functions to handle OptionKey to string conversion
- const handleSourceChange = (value: any) => setSource(String(value));
- const handleTypeChange = (value: any) => setType(String(value));
- const handleEpsgChange = (value: any) => setEpsg(String(value));
- const handleImageTypeChange = (value: any) => setImageType(String(value));
- const handleCountriesChange = (value: any) => setCountries(Array.isArray(value) ? value.map(String) : []);
-
- // Fetch metadata options on component mount
- useEffect(() => {
- Promise.all([
- fetch('/api/sources').then(r => r.json()),
- fetch('/api/types').then(r => r.json()),
- fetch('/api/spatial-references').then(r => r.json()),
- fetch('/api/image-types').then(r => r.json()),
- fetch('/api/countries').then(r => r.json()),
- fetch('/api/models').then(r => r.json())
- ]).then(([sourcesData, typesData, spatialData, imageTypesData, countriesData]) => {
- setSources(sourcesData);
- setTypes(typesData);
- setSpatialReferences(spatialData);
- setImageTypes(imageTypesData);
- setCountriesOptions(countriesData);
-
- // Set default values
- if (sourcesData.length > 0) setSource(sourcesData[0].s_code);
- if (typesData.length > 0) setType(typesData[0].t_code);
- if (spatialData.length > 0) setEpsg(spatialData[0].epsg);
- if (imageTypesData.length > 0) setImageType(imageTypesData[0].image_type);
- });
- }, []);
-
- useEffect(() => {
- const handleBeforeUnload = () => {
- if (uploadedImageIdRef.current && stepRef.current !== 3) {
- fetch(`/api/images/${uploadedImageIdRef.current}`, { method: "DELETE" }).catch(console.error);
- }
- };
-
- window.addEventListener('beforeunload', handleBeforeUnload);
- return () => {
- window.removeEventListener('beforeunload', handleBeforeUnload);
- if (uploadedImageIdRef.current && stepRef.current !== 3) {
- fetch(`/api/images/${uploadedImageIdRef.current}`, { method: "DELETE" }).catch(console.error);
- }
- };
- }, []);
-
- const [captionId, setCaptionId] = useState(null);
- const [imageUrl, setImageUrl] = useState(null);
- const [draft, setDraft] = useState('');
-
- // Handle URL parameters for direct step 2 navigation
- useEffect(() => {
- const mapId = searchParams.get('mapId');
- const stepParam = searchParams.get('step');
-
- if (mapId && stepParam === '2') {
- // Load the map data and start at step 2
- fetch(`/api/images/${mapId}`)
- .then(response => response.json())
- .then(mapData => {
- setImageUrl(mapData.image_url);
- setSource(mapData.source);
- setType(mapData.type);
- setEpsg(mapData.epsg);
- setImageType(mapData.image_type);
-
- return fetch(`/api/images/${mapId}/caption`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: new URLSearchParams({
- title: 'Generated Caption',
- prompt: 'Describe this crisis map in detail',
- ...(localStorage.getItem(SELECTED_MODEL_KEY) && {
- model_name: localStorage.getItem(SELECTED_MODEL_KEY)!
- })
- })
- });
- })
- .then(capResponse => capResponse.json())
- .then(capData => {
- setCaptionId(capData.cap_id);
- setDraft(capData.edited || capData.generated);
- setStep(2);
- })
- .catch(err => {
- console.error('Failed to load map data:', err);
- alert('Failed to load map data. Please try again.');
- });
- }
- }, [searchParams]);
-
- const resetToStep1 = () => {
- setStep(1);
- setFile(null);
- setPreview(null);
- setImageUrl(null);
- setCaptionId(null);
- setDraft('');
- setTitle('');
- setScores({ accuracy: 50, context: 50, usability: 50 });
- setUploadedImageId(null);
- };
- const [scores, setScores] = useState({
- accuracy: 50,
- context: 50,
- usability: 50,
- });
-
-
-
- /* ---- drag-and-drop + file-picker handlers -------------------------- */
- const onDrop = useCallback((e: DragEvent) => {
- e.preventDefault();
- const dropped = e.dataTransfer.files?.[0];
- if (dropped) setFile(dropped);
- }, []);
-
- const onFileChange = useCallback((file: File | undefined, _name: string) => {
- if (file) setFile(file);
- }, []);
-
- // blob URL / preview
- useEffect(() => {
- if (!file) {
- setPreview(null);
- return;
- }
- const url = URL.createObjectURL(file);
- setPreview(url);
- return () => URL.revokeObjectURL(url);
- }, [file]);
-
-
- async function readJsonSafely(res: Response): Promise {
- const text = await res.text(); // get raw body
- try {
- return text ? JSON.parse(text) : {}; // valid JSON or empty object
- } catch {
- return { error: text }; // plain text fallback
- }
- }
-
- function handleApiError(err: any, operation: string) {
- console.error(`${operation} failed:`, err);
- const message = err.message || `Failed to ${operation.toLowerCase()}`;
- alert(message);
- }
-
- /* ---- generate handler --------------------------------------------- */
- async function handleGenerate() {
- if (!file) return;
-
- setIsLoading(true);
-
- const fd = new FormData();
- fd.append('file', file);
- fd.append('source', source);
- fd.append('type', type);
- fd.append('epsg', epsg);
- fd.append('image_type', imageType);
- countries.forEach((c) => fd.append('countries', c));
-
- const modelName = localStorage.getItem(SELECTED_MODEL_KEY);
- if (modelName) {
- fd.append('model_name', modelName);
- }
-
- try {
- /* 1) upload */
- const mapRes = await fetch('/api/images/', { method: 'POST', body: fd });
- const mapJson = await readJsonSafely(mapRes);
- if (!mapRes.ok) throw new Error(mapJson.error || 'Upload failed');
- setImageUrl(mapJson.image_url);
-
- const mapIdVal = mapJson.image_id;
- if (!mapIdVal) throw new Error('Upload failed: image_id not found');
- setUploadedImageId(mapIdVal);
-
- /* 2) caption */
- const capRes = await fetch(
- `/api/images/${mapIdVal}/caption`,
- {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: new URLSearchParams({
- title: title || 'Generated Caption',
- prompt: 'Analyze this crisis map and provide a detailed description of the emergency situation, affected areas, and key information shown in the map.',
- ...(modelName && { model_name: modelName })
- })
- },
- );
- const capJson = await readJsonSafely(capRes);
- if (!capRes.ok) throw new Error(capJson.error || 'Caption failed');
- setCaptionId(capJson.cap_id);
- console.log(capJson);
-
- /* 3) Extract and apply metadata from AI response */
- const extractedMetadata = capJson.raw_json?.extracted_metadata;
- if (extractedMetadata) {
- console.log('Extracted metadata:', extractedMetadata);
-
- // Apply AI-extracted metadata to form fields
- if (extractedMetadata.title) setTitle(extractedMetadata.title);
- if (extractedMetadata.source) setSource(extractedMetadata.source);
- if (extractedMetadata.type) setType(extractedMetadata.type);
- if (extractedMetadata.epsg) setEpsg(extractedMetadata.epsg);
- if (extractedMetadata.countries && Array.isArray(extractedMetadata.countries)) {
- setCountries(extractedMetadata.countries);
- }
- }
-
- /* 4) continue workflow */
- setDraft(capJson.generated);
- setStep(2);
- } catch (err) {
- handleApiError(err, 'Upload');
- } finally {
- setIsLoading(false);
- }
- }
-
- /* ---- submit handler --------------------------------------------- */
- async function handleSubmit() {
- if (!captionId) return alert("No caption to submit");
-
- try {
- // 1. Update image metadata
- const metadataBody = {
- source: source,
- type: type,
- epsg: epsg,
- image_type: imageType,
- countries: countries,
- };
- const metadataRes = await fetch(`/api/images/${uploadedImageId}`, {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(metadataBody),
- });
- const metadataJson = await readJsonSafely(metadataRes);
- if (!metadataRes.ok) throw new Error(metadataJson.error || "Metadata update failed");
-
- // 2. Update caption
- const captionBody = {
- title: title,
- edited: draft || '', // Use draft if available, otherwise empty string
- accuracy: scores.accuracy,
- context: scores.context,
- usability: scores.usability,
- };
- const captionRes = await fetch(`/api/captions/${captionId}`, {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(captionBody),
- });
- const captionJson = await readJsonSafely(captionRes);
- if (!captionRes.ok) throw new Error(captionJson.error || "Caption update failed");
-
- // Clear uploaded IDs since submission was successful
- setUploadedImageId(null);
- setStep(3);
- } catch (err) {
- handleApiError(err, 'Submit');
- }
- }
-
- /* ---- delete handler --------------------------------------------- */
- async function handleDelete() {
- if (!uploadedImageId) return;
-
- if (confirm("Are you sure you want to delete this uploaded image? This action cannot be undone.")) {
- try {
- // Delete the image (this will cascade delete the caption)
- const res = await fetch(`/api/images/${uploadedImageId}`, {
- method: "DELETE",
- });
-
- if (!res.ok) {
- const json = await readJsonSafely(res);
- throw new Error(json.error || "Delete failed");
- }
-
- // Reset to step 1
- resetToStep1();
- } catch (err) {
- handleApiError(err, 'Delete');
- }
- }
- }
-
- /* ------------------------------------------------------------------- */
- return (
-
-
- {/* Drop-zone */}
- {step === 1 && (
-
-
-
- This app evaluates how well multimodal AI models turn emergency maps
- into meaningful text. Upload your map, let the AI generate a
- description, then review and rate the result based on your expertise.
-
-
- {/* "More »" link */}
-
-
-
e.preventDefault()}
- onDrop={onDrop}
- >
- {file && preview ? (
-
-
-

-
-
- {file.name}
-
-
- ) : (
- <>
-
-
Drag & Drop a file here
-
or
- >
- )}
-
- {/* File-picker button - always visible */}
-
-
-
-
- )}
-
- {/* Loading state */}
- {isLoading && (
-
-
-
Generating caption...
-
- )}
-
- {/* Generate button */}
- {step === 1 && !isLoading && (
-
-
-
- )}
-
- {step === 2 && imageUrl && (
-
-
-
-

-
-
-
- )}
-
- {step === 2 && (
-
- {/* ────── METADATA FORM ────── */}
-
-
-
- setTitle(value || '')}
- placeholder="Enter a title for this map..."
- required
- />
-
-
o.s_code}
- labelSelector={(o) => o.label}
- required
- />
- o.t_code}
- labelSelector={(o) => o.label}
- required
- />
- o.epsg}
- labelSelector={(o) => `${o.srid} (EPSG:${o.epsg})`}
- required
- />
- o.image_type}
- labelSelector={(o) => o.label}
- required
- />
- o.c_code}
- labelSelector={(o) => o.label}
- placeholder="Select one or more"
- />
-
-
-
- {/* ────── RATING SLIDERS ────── */}
-
-
-
How well did the AI perform on the task?
- {(['accuracy', 'context', 'usability'] as const).map((k) => (
-
-
-
- setScores((s) => ({ ...s, [k]: Number(e.target.value) }))
- }
- className="w-full accent-ifrcRed"
- />
- {scores[k]}
-
- ))}
-
-
-
- {/* ────── AI‑GENERATED CAPTION ────── */}
-
-
-
-
-
- {/* ────── SUBMIT BUTTON ────── */}
-
-
-
-
-
-
-
- )}
-
- {/* Success page */}
- {step === 3 && (
-
-
Saved!
-
Your caption has been successfully saved.
-
-
-
-
- )}
-
-
- );
-}
diff --git a/frontend/src/pages/UploadPage/UploadPage.module.css b/frontend/src/pages/UploadPage/UploadPage.module.css
new file mode 100644
index 0000000000000000000000000000000000000000..191fa3c7350a2f6bf17f4fa95c283db8106ede1e
--- /dev/null
+++ b/frontend/src/pages/UploadPage/UploadPage.module.css
@@ -0,0 +1,495 @@
+.uploadContainer {
+ margin: 0 auto;
+ max-width: var(--go-ui-width-screen-lg);
+ text-align: center;
+ padding: var(--go-ui-spacing-lg) var(--go-ui-spacing-md) var(--go-ui-spacing-2xl) var(--go-ui-spacing-md);
+ overflow-x: hidden;
+}
+
+.dropZone {
+ border: var(--go-ui-width-separator-thick) dashed var(--go-ui-color-gray-40);
+ background-color: var(--go-ui-color-gray-20);
+ border-radius: var(--go-ui-border-radius-xl);
+ padding: var(--go-ui-spacing-2xl) var(--go-ui-spacing-lg);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--go-ui-spacing-lg);
+ transition: all var(--go-ui-duration-transition-medium) ease;
+ max-width: var(--go-ui-width-screen-md);
+ margin: 0 auto;
+ min-height: 250px;
+ justify-content: center;
+}
+
+.dropZone:hover {
+ background-color: var(--go-ui-color-gray-30);
+ border-color: var(--go-ui-color-gray-50);
+}
+
+.dropZone.hasFile {
+ background-color: var(--go-ui-color-white);
+ border-color: var(--go-ui-color-gray-30);
+ min-height: 300px;
+ padding: var(--go-ui-spacing-lg);
+}
+
+.dropZoneIcon {
+ width: 2.5rem;
+ height: 2.5rem;
+ color: var(--go-ui-color-red-90);
+}
+
+.dropZoneText {
+ font-size: var(--go-ui-font-size-sm);
+ color: var(--go-ui-color-gray-70);
+ text-align: center;
+}
+
+.dropZoneSubtext {
+ font-size: var(--go-ui-font-size-sm);
+ color: var(--go-ui-color-gray-50);
+ margin: var(--go-ui-spacing-md) 0;
+}
+
+.filePreview {
+ width: 100%;
+ max-width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ animation: fadeIn 0.3s ease-in-out;
+}
+
+.filePreviewImage {
+ position: relative;
+ max-width: 100%;
+ max-height: 20rem;
+ overflow: visible;
+ border-radius: var(--go-ui-border-radius-lg);
+ background-color: var(--go-ui-color-gray-20);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: var(--go-ui-spacing-sm);
+ transition: all var(--go-ui-duration-transition-medium) ease;
+}
+
+.filePreviewImage:hover {
+ background-color: var(--go-ui-color-gray-30);
+ transform: translateY(-2px);
+ box-shadow: var(--go-ui-box-shadow-md);
+}
+
+.filePreviewImage img {
+ max-width: 100%;
+ max-height: 18rem;
+ width: auto;
+ height: auto;
+ object-fit: contain;
+ border-radius: var(--go-ui-border-radius-md);
+ box-shadow: var(--go-ui-box-shadow-sm);
+ transition: transform var(--go-ui-duration-transition-medium) ease;
+}
+
+.filePreviewImage img:hover {
+ transform: scale(1.02);
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.fileName {
+ font-size: var(--go-ui-font-size-sm);
+ font-weight: var(--go-ui-font-weight-medium);
+ color: var(--go-ui-color-gray-80);
+ margin-top: var(--go-ui-spacing-sm);
+ text-align: center;
+}
+
+.fileInfo {
+ font-size: var(--go-ui-font-size-xs);
+ color: var(--go-ui-color-gray-60);
+ margin-top: var(--go-ui-spacing-xs);
+ text-align: center;
+}
+
+.helpLink {
+ display: flex;
+ justify-content: center;
+ margin-top: var(--go-ui-spacing-md);
+}
+
+.helpLink a {
+ color: var(--go-ui-color-red-90);
+ font-size: var(--go-ui-font-size-xs);
+ transition: color var(--go-ui-duration-transition-fast) ease;
+ display: flex;
+ align-items: center;
+ gap: var(--go-ui-spacing-2xs);
+}
+
+.helpLink a:hover {
+ color: var(--go-ui-color-red-hover);
+ text-decoration: underline;
+}
+
+.loadingContainer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--go-ui-spacing-lg);
+ margin-top: var(--go-ui-spacing-2xl);
+}
+
+.loadingText {
+ color: var(--go-ui-color-gray-60);
+}
+
+.generateButtonContainer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--go-ui-spacing-lg);
+ margin-top: var(--go-ui-spacing-2xl);
+}
+
+.uploadedMapContainer {
+ margin-bottom: var(--go-ui-spacing-lg);
+}
+
+.uploadedMapImage {
+ width: 100%;
+ max-width: var(--go-ui-width-screen-lg);
+ max-height: 20rem;
+ overflow: visible;
+ background-color: var(--go-ui-color-gray-20);
+ border-radius: var(--go-ui-border-radius-lg);
+ box-shadow: var(--go-ui-box-shadow-sm);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: var(--go-ui-spacing-sm);
+ transition: all var(--go-ui-duration-transition-medium) ease;
+}
+
+.uploadedMapImage:hover {
+ background-color: var(--go-ui-color-gray-30);
+ transform: translateY(-2px);
+ box-shadow: var(--go-ui-box-shadow-md);
+}
+
+.uploadedMapImage img {
+ max-width: 100%;
+ max-height: 18rem;
+ width: auto;
+ height: auto;
+ object-fit: contain;
+ border-radius: var(--go-ui-border-radius-md);
+ box-shadow: var(--go-ui-box-shadow-sm);
+ transition: transform var(--go-ui-duration-transition-medium) ease;
+}
+
+.uploadedMapImage img:hover {
+ transform: scale(1.02);
+}
+
+.formSection {
+ margin-bottom: var(--go-ui-spacing-lg);
+}
+
+.formGrid {
+ display: grid;
+ gap: var(--go-ui-spacing-lg);
+ grid-template-columns: 1fr;
+ text-align: left;
+}
+
+@media (min-width: 1024px) {
+ .formGrid {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+
+.titleField {
+ grid-column: 1 / -1;
+}
+
+.ratingSection {
+ text-align: left;
+}
+
+.ratingDescription {
+ color: var(--go-ui-color-gray-70);
+ margin-bottom: var(--go-ui-spacing-lg);
+}
+
+.ratingSlider {
+ margin-top: var(--go-ui-spacing-lg);
+ display: flex;
+ align-items: center;
+ gap: var(--go-ui-spacing-sm);
+}
+
+.ratingLabel {
+ display: block;
+ font-size: var(--go-ui-font-size-sm);
+ font-weight: var(--go-ui-font-weight-medium);
+ text-transform: capitalize;
+ width: 5rem;
+ flex-shrink: 0;
+}
+
+.ratingInput {
+ width: 100%;
+ accent-color: var(--go-ui-color-red-90);
+}
+
+.ratingValue {
+ margin-left: var(--go-ui-spacing-sm);
+ width: 2.5rem;
+ text-align: right;
+ tabular-nums: true;
+ flex-shrink: 0;
+ font-size: var(--go-ui-font-size-sm);
+ color: var(--go-ui-color-gray-70);
+}
+
+.submitSection {
+ display: flex;
+ justify-content: center;
+ gap: var(--go-ui-spacing-md);
+ margin-top: var(--go-ui-spacing-2xl);
+ flex-wrap: wrap;
+}
+
+/* Success page styles */
+.successContainer {
+ text-align: center;
+ padding: var(--go-ui-spacing-2xl);
+}
+
+.successHeading {
+ color: var(--go-ui-color-green-90);
+ margin-bottom: var(--go-ui-spacing-lg);
+}
+
+.successText {
+ color: var(--go-ui-color-gray-700);
+ margin-bottom: var(--go-ui-spacing-xl);
+ font-size: var(--go-ui-font-size-lg);
+}
+
+.successButton {
+ display: flex;
+ justify-content: center;
+}
+
+/* View Full Size Button */
+.viewFullSizeButton {
+ display: flex;
+ justify-content: center;
+ margin-top: var(--go-ui-spacing-md);
+ padding-top: var(--go-ui-spacing-md);
+ border-top: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+}
+
+/* Full Size Modal */
+.fullSizeModalOverlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.8);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+ padding: var(--go-ui-spacing-lg);
+}
+
+.fullSizeModalContent {
+ background-color: var(--go-ui-color-white);
+ border-radius: var(--go-ui-border-radius-lg);
+ max-width: 95vw;
+ max-height: 95vh;
+ overflow: hidden;
+ box-shadow: var(--go-ui-box-shadow-xl);
+ display: flex;
+ flex-direction: column;
+}
+
+.fullSizeModalHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: var(--go-ui-spacing-lg);
+ border-bottom: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+ background-color: var(--go-ui-color-gray-10);
+}
+
+.fullSizeModalTitle {
+ margin: 0;
+ font-size: var(--go-ui-font-size-lg);
+ font-weight: var(--go-ui-font-weight-semibold);
+ color: var(--go-ui-color-gray-900);
+}
+
+.fullSizeModalImage {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: var(--go-ui-spacing-lg);
+ overflow: auto;
+}
+
+.fullSizeModalImage img {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+ border-radius: var(--go-ui-border-radius-md);
+ box-shadow: var(--go-ui-box-shadow-md);
+}
+
+/* Responsive adjustments for modal */
+@media (max-width: 768px) {
+ .fullSizeModalOverlay {
+ padding: var(--go-ui-spacing-sm);
+ }
+
+ .fullSizeModalContent {
+ max-width: 100vw;
+ max-height: 100vh;
+ }
+
+ .fullSizeModalHeader {
+ padding: var(--go-ui-spacing-md);
+ }
+
+ .fullSizeModalImage {
+ padding: var(--go-ui-spacing-md);
+ }
+}
+
+.confirmSection {
+ display: flex;
+ justify-content: center;
+ gap: var(--go-ui-spacing-md);
+ margin-top: var(--go-ui-spacing-xl);
+ padding-top: var(--go-ui-spacing-lg);
+ border-top: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+}
+
+.step2Layout {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--go-ui-spacing-2xl);
+ align-items: start;
+}
+
+.mapColumn {
+ position: sticky;
+ top: var(--go-ui-spacing-lg);
+}
+
+.contentColumn {
+ display: flex;
+ flex-direction: column;
+ gap: var(--go-ui-spacing-lg);
+}
+
+.contentColumn .formGrid {
+ display: grid;
+ gap: var(--go-ui-spacing-lg);
+ grid-template-columns: 1fr;
+ text-align: left;
+}
+
+/* Responsive adjustments */
+@media (max-width: 1024px) {
+ .step2Layout {
+ grid-template-columns: 1fr;
+ gap: var(--go-ui-spacing-lg);
+ }
+
+ .mapColumn {
+ position: static;
+ }
+}
+
+@media (max-width: 768px) {
+ .uploadContainer {
+ padding: var(--go-ui-spacing-md) var(--go-ui-spacing-sm) var(--go-ui-spacing-xl) var(--go-ui-spacing-sm);
+ }
+
+ .dropZone {
+ padding: var(--go-ui-spacing-lg) var(--go-ui-spacing-md);
+ min-height: 200px;
+ }
+
+ .dropZone.hasFile {
+ min-height: 250px;
+ padding: var(--go-ui-spacing-md);
+ }
+
+ .filePreviewImage {
+ max-width: 100%;
+ max-height: 15rem;
+ padding: var(--go-ui-spacing-xs);
+ }
+
+ .filePreviewImage img {
+ max-height: 13rem;
+ }
+
+ .ratingSlider {
+ gap: var(--go-ui-spacing-xs);
+ }
+
+ .ratingLabel {
+ width: 4rem;
+ font-size: var(--go-ui-font-size-xs);
+ }
+
+ .ratingValue {
+ width: 2rem;
+ font-size: var(--go-ui-font-size-xs);
+ }
+}
+
+@media (max-width: 480px) {
+ .dropZone {
+ padding: var(--go-ui-spacing-md) var(--go-ui-spacing-sm);
+ min-height: 180px;
+ }
+
+ .dropZone.hasFile {
+ min-height: 220px;
+ }
+
+ .filePreviewImage {
+ max-height: 12rem;
+ }
+
+ .filePreviewImage img {
+ max-height: 10rem;
+ }
+}
+
+.metadataSectionCard {
+ background-color: var(--go-ui-color-white);
+ border: var(--go-ui-width-separator-thin) solid var(--go-ui-color-separator);
+ border-radius: var(--go-ui-border-radius-lg);
+ padding: var(--go-ui-spacing-lg);
+ box-shadow: var(--go-ui-box-shadow-xs);
+}
diff --git a/frontend/src/pages/UploadPage/UploadPage.tsx b/frontend/src/pages/UploadPage/UploadPage.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6dbfddab507d14b26ac97f38f2f38ae1b2ad85b3
--- /dev/null
+++ b/frontend/src/pages/UploadPage/UploadPage.tsx
@@ -0,0 +1,784 @@
+import { useCallback, useState, useEffect, useRef } from 'react';
+import type { DragEvent } from 'react';
+import {
+ PageContainer, Heading, Button,
+ SelectInput, MultiSelectInput, Container, IconButton, TextInput, TextArea, Spinner,
+} from '@ifrc-go/ui';
+import {
+ UploadCloudLineIcon,
+ ArrowRightLineIcon,
+ DeleteBinLineIcon,
+} from '@ifrc-go/icons';
+import { Link, useSearchParams } from 'react-router-dom';
+import styles from './UploadPage.module.css';
+
+const SELECTED_MODEL_KEY = 'selectedVlmModel';
+
+export default function UploadPage() {
+ const [searchParams] = useSearchParams();
+ const [step, setStep] = useState<1 | '2a' | '2b' | 3>(1);
+ const [isLoading, setIsLoading] = useState(false);
+ const stepRef = useRef(step);
+ const uploadedImageIdRef = useRef(null);
+ const [preview, setPreview] = useState(null);
+
+ const [file, setFile] = useState(null);
+ const [source, setSource] = useState('');
+ const [eventType, setEventType] = useState('');
+ const [epsg, setEpsg] = useState('');
+ const [imageType, setImageType] = useState('');
+ const [countries, setCountries] = useState([]);
+ const [title, setTitle] = useState('');
+
+ const [sources, setSources] = useState<{s_code: string, label: string}[]>([]);
+ const [types, setTypes] = useState<{t_code: string, label: string}[]>([]);
+ const [spatialReferences, setSpatialReferences] = useState<{epsg: string, srid: string, proj4: string, wkt: string}[]>([]);
+ const [imageTypes, setImageTypes] = useState<{image_type: string, label: string}[]>([]);
+ const [countriesOptions, setCountriesOptions] = useState<{c_code: string, label: string, r_code: string}[]>([]);
+
+ const [uploadedImageId, setUploadedImageId] = useState(null);
+
+ stepRef.current = step;
+ uploadedImageIdRef.current = uploadedImageId;
+
+ const handleSourceChange = (value: any) => setSource(String(value));
+ const handleEventTypeChange = (value: any) => setEventType(String(value));
+ const handleEpsgChange = (value: any) => setEpsg(String(value));
+ const handleImageTypeChange = (value: any) => setImageType(String(value));
+ const handleCountriesChange = (value: any) => setCountries(Array.isArray(value) ? value.map(String) : []);
+
+ const handleStepChange = (newStep: 1 | '2a' | '2b' | 3) => {
+ setStep(newStep);
+ };
+
+ useEffect(() => {
+ Promise.all([
+ fetch('/api/sources').then(r => r.json()),
+ fetch('/api/types').then(r => r.json()),
+ fetch('/api/spatial-references').then(r => r.json()),
+ fetch('/api/image-types').then(r => r.json()),
+ fetch('/api/countries').then(r => r.json()),
+ fetch('/api/models').then(r => r.json())
+ ]).then(([sourcesData, typesData, spatialData, imageTypesData, countriesData]) => {
+ setSources(sourcesData);
+ setTypes(typesData);
+ setSpatialReferences(spatialData);
+ setImageTypes(imageTypesData);
+ setCountriesOptions(countriesData);
+
+ if (sourcesData.length > 0) setSource(sourcesData[0].s_code);
+ if (typesData.length > 0) setEventType(typesData[0].t_code);
+ if (spatialData.length > 0) setEpsg(spatialData[0].epsg);
+ if (imageTypesData.length > 0) setImageType(imageTypesData[0].image_type);
+ });
+ }, []);
+
+ useEffect(() => {
+ const handleBeforeUnload = () => {
+ if (uploadedImageIdRef.current && stepRef.current !== 3) {
+ fetch(`/api/images/${uploadedImageIdRef.current}`, { method: "DELETE" }).catch(console.error);
+ }
+ };
+
+ window.addEventListener('beforeunload', handleBeforeUnload);
+ return () => {
+ window.removeEventListener('beforeunload', handleBeforeUnload);
+ if (uploadedImageIdRef.current && stepRef.current !== 3) {
+ fetch(`/api/images/${uploadedImageIdRef.current}`, { method: "DELETE" }).catch(console.error);
+ }
+ };
+ }, []);
+
+ const [captionId, setCaptionId] = useState(null);
+ const [imageUrl, setImageUrl] = useState(null);
+ const [draft, setDraft] = useState('');
+
+ useEffect(() => {
+ const mapId = searchParams.get('mapId');
+ const stepParam = searchParams.get('step');
+ const captionIdParam = searchParams.get('captionId');
+
+ if (mapId && stepParam === '2') {
+ fetch(`/api/images/${mapId}`)
+ .then(response => response.json())
+ .then(mapData => {
+ setImageUrl(mapData.image_url);
+ setSource(mapData.source);
+ setEventType(mapData.event_type);
+ setEpsg(mapData.epsg);
+ setImageType(mapData.image_type);
+
+ setUploadedImageId(mapId);
+
+ if (captionIdParam) {
+ setCaptionId(captionIdParam);
+ const existingCaption = mapData.captions?.find((c: any) => c.cap_id === captionIdParam);
+ if (existingCaption) {
+ setDraft(existingCaption.edited || existingCaption.generated);
+ setTitle(existingCaption.title || 'Generated Caption');
+ }
+
+ if (mapData.countries && Array.isArray(mapData.countries)) {
+ setCountries(mapData.countries.map((c: any) => c.c_code));
+ }
+
+ handleStepChange('2a');
+ } else {
+ setCaptionId(null);
+ setDraft('');
+ setTitle('');
+
+
+ if (mapData.countries && Array.isArray(mapData.countries)) {
+ setCountries(mapData.countries.map((c: any) => c.c_code));
+ }
+
+ handleStepChange('2a');
+ }
+ })
+ .catch(err => {
+ alert('Failed to load map data. Please try again.');
+ });
+ }
+ }, [searchParams]);
+
+ const resetToStep1 = () => {
+ setStep(1);
+ setFile(null);
+ setPreview(null);
+ setImageUrl(null);
+ setCaptionId(null);
+ setDraft('');
+ setTitle('');
+ setScores({ accuracy: 50, context: 50, usability: 50 });
+ setUploadedImageId(null);
+ };
+ const [scores, setScores] = useState({
+ accuracy: 50,
+ context: 50,
+ usability: 50,
+ });
+
+ const [isFullSizeModalOpen, setIsFullSizeModalOpen] = useState(false);
+
+
+ const onDrop = useCallback((e: DragEvent) => {
+ e.preventDefault();
+ const dropped = e.dataTransfer.files?.[0];
+ if (dropped) setFile(dropped);
+ }, []);
+
+ const onFileChange = useCallback((file: File | undefined, _name: string) => {
+ if (file) setFile(file);
+ }, []);
+
+ useEffect(() => {
+ if (!file) {
+ setPreview(null);
+ return;
+ }
+ const url = URL.createObjectURL(file);
+ setPreview(url);
+ return () => URL.revokeObjectURL(url);
+ }, [file]);
+
+
+ async function readJsonSafely(res: Response): Promise {
+ const text = await res.text();
+ try {
+ return text ? JSON.parse(text) : {};
+ } catch {
+ return { error: text };
+ }
+ }
+
+ function handleApiError(err: any, operation: string) {
+
+ const message = err.message || `Failed to ${operation.toLowerCase()}`;
+ alert(message);
+ }
+
+ async function handleGenerate() {
+ if (!file) return;
+
+ setIsLoading(true);
+
+ const fd = new FormData();
+ fd.append('file', file);
+ fd.append('source', source);
+ fd.append('event_type', eventType);
+ fd.append('epsg', epsg);
+ fd.append('image_type', imageType);
+ countries.forEach((c) => fd.append('countries', c));
+
+ const modelName = localStorage.getItem(SELECTED_MODEL_KEY);
+ if (modelName) {
+ fd.append('model_name', modelName);
+ }
+
+ try {
+ const mapRes = await fetch('/api/images/', { method: 'POST', body: fd });
+ const mapJson = await readJsonSafely(mapRes);
+ if (!mapRes.ok) throw new Error(mapJson.error || 'Upload failed');
+ setImageUrl(mapJson.image_url);
+
+ const mapIdVal = mapJson.image_id;
+ if (!mapIdVal) throw new Error('Upload failed: image_id not found');
+ setUploadedImageId(mapIdVal);
+
+ const capRes = await fetch(
+ `/api/images/${mapIdVal}/caption`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: new URLSearchParams({
+ title: title || 'Generated Caption',
+ prompt: 'Analyze this crisis map and provide a detailed description of the emergency situation, affected areas, and key information shown in the map.',
+ ...(modelName && { model_name: modelName })
+ })
+ },
+ );
+ const capJson = await readJsonSafely(capRes);
+ if (!capRes.ok) throw new Error(capJson.error || 'Caption failed');
+ setCaptionId(capJson.cap_id);
+
+ const extractedMetadata = capJson.raw_json?.extracted_metadata;
+ if (extractedMetadata) {
+ if (extractedMetadata.title) setTitle(extractedMetadata.title);
+ if (extractedMetadata.source) setSource(extractedMetadata.source);
+ if (extractedMetadata.type) setEventType(extractedMetadata.type);
+ if (extractedMetadata.epsg) setEpsg(extractedMetadata.epsg);
+ if (extractedMetadata.countries && Array.isArray(extractedMetadata.countries)) {
+ setCountries(extractedMetadata.countries);
+ }
+ }
+
+ setDraft(capJson.generated);
+ handleStepChange('2a');
+ } catch (err) {
+ handleApiError(err, 'Upload');
+ } finally {
+ setIsLoading(false);
+ }
+ }
+
+ async function handleSubmit() {
+ if (!captionId) return alert("No caption to submit");
+
+ try {
+ const metadataBody = {
+ source: source,
+ event_type: eventType,
+ epsg: epsg,
+ image_type: imageType,
+ countries: countries,
+ };
+ const metadataRes = await fetch(`/api/images/${uploadedImageId}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(metadataBody),
+ });
+ const metadataJson = await readJsonSafely(metadataRes);
+ if (!metadataRes.ok) throw new Error(metadataJson.error || "Metadata update failed");
+
+ const captionBody = {
+ title: title,
+ edited: draft || '',
+ accuracy: scores.accuracy,
+ context: scores.context,
+ usability: scores.usability,
+ };
+ const captionRes = await fetch(`/api/captions/${captionId}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(captionBody),
+ });
+ const captionJson = await readJsonSafely(captionRes);
+ if (!captionRes.ok) throw new Error(captionJson.error || "Caption update failed");
+
+ setUploadedImageId(null);
+ handleStepChange(3);
+ } catch (err) {
+ handleApiError(err, 'Submit');
+ }
+ }
+
+ async function handleDelete() {
+ if (!uploadedImageId) {
+
+ alert('No caption to delete. Please try refreshing the page.');
+ return;
+ }
+
+ if (confirm("Are you sure you want to delete this caption? This action cannot be undone.")) {
+ try {
+ const captionsResponse = await fetch(`/api/images/${uploadedImageId}/captions`);
+ let hasOtherCaptions = false;
+
+ if (captionsResponse.ok) {
+ const captions = await captionsResponse.json();
+ hasOtherCaptions = captions.some((cap: any) => cap.cap_id !== captionId);
+ }
+
+ if (hasOtherCaptions) {
+ if (captionId) {
+ const capRes = await fetch(`/api/captions/${captionId}`, {
+ method: "DELETE",
+ });
+ if (!capRes.ok) {
+ throw new Error('Failed to delete caption');
+ }
+ }
+ } else {
+ const res = await fetch(`/api/images/${uploadedImageId}`, {
+ method: "DELETE",
+ });
+
+ if (!res.ok) {
+ const json = await readJsonSafely(res);
+
+ throw new Error(json.error || `Delete failed with status ${res.status}`);
+ }
+ }
+
+ resetToStep1();
+ } catch (err) {
+
+ handleApiError(err, 'Delete');
+ }
+ }
+ }
+
+ const handleProcessCaption = useCallback(async () => {
+ if (!uploadedImageId) {
+ alert('No image ID available to create a new caption.');
+ return;
+ }
+
+ setIsLoading(true);
+
+ try {
+ if (captionId) {
+ const captionBody = {
+ title: title,
+ edited: draft || '',
+ };
+ const captionRes = await fetch(`/api/captions/${captionId}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(captionBody),
+ });
+ if (!captionRes.ok) throw new Error('Failed to update caption');
+ } else {
+ const capRes = await fetch(
+ `/api/images/${uploadedImageId}/caption`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: new URLSearchParams({
+ title: 'New Contribution Caption',
+ prompt: 'Describe this crisis map in detail',
+ ...(localStorage.getItem(SELECTED_MODEL_KEY) && {
+ model_name: localStorage.getItem(SELECTED_MODEL_KEY)!
+ })
+ })
+ }
+ );
+ const capJson = await readJsonSafely(capRes);
+ if (!capRes.ok) throw new Error(capJson.error || 'Caption failed');
+
+ setCaptionId(capJson.cap_id);
+ setDraft(capJson.generated);
+ }
+
+ handleStepChange('2b');
+ } catch (err) {
+ handleApiError(err, 'Create New Caption');
+ } finally {
+ setIsLoading(false);
+ }
+ }, [uploadedImageId, title, captionId, draft]);
+
+ return (
+
+
+ {/* Drop-zone */}
+ {step === 1 && (
+
+
+
+ This app evaluates how well multimodal AI models turn emergency maps
+ into meaningful text. Upload your map, let the AI generate a
+ description, then review and rate the result based on your expertise.
+
+
+ {/* "More »" link */}
+
+
+
e.preventDefault()}
+ onDrop={onDrop}
+ >
+ {file && preview ? (
+
+
+

+
+
+ {file.name}
+
+
+ {(file.size / 1024 / 1024).toFixed(2)} MB
+
+
+ ) : (
+ <>
+
+
Drag & Drop a file here
+
or
+ >
+ )}
+
+ {/* File-picker button - always visible */}
+
+
+
+
+ )}
+
+ {/* Loading state */}
+ {isLoading && (
+
+
+
Generating caption...
+
+ )}
+
+ {/* Generate button */}
+ {step === 1 && !isLoading && (
+
+
+
+ )}
+
+ {step === '2a' && (
+
+ {/* Left Column - Map */}
+
+
+
+
+

+
+
+
+
+
+
+
+
+ {/* Right Column - Metadata Form */}
+
+
+
+
+
+ setTitle(value || '')}
+ placeholder="Enter a title for this map..."
+ required
+ />
+
+
o.s_code}
+ labelSelector={(o) => o.label}
+ required
+ />
+ o.t_code}
+ labelSelector={(o) => o.label}
+ required
+ />
+ o.epsg}
+ labelSelector={(o) => `${o.srid} (EPSG:${o.epsg})`}
+ required
+ />
+ o.image_type}
+ labelSelector={(o) => o.label}
+ required
+ />
+ o.c_code}
+ labelSelector={(o) => o.label}
+ placeholder="Select one or more"
+ />
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {step === '2b' && (
+
+ {/* Left Column - Map */}
+
+
+
+
+

+
+
+
+
+
+
+
+
+ {/* Right Column - Content */}
+
+ {/* ────── RATING SLIDERS ────── */}
+
+
+
+
How well did the AI perform on the task?
+ {(['accuracy', 'context', 'usability'] as const).map((k) => (
+
+
+
+ setScores((s) => ({ ...s, [k]: Number(e.target.value) }))
+ }
+ className={styles.ratingInput}
+ />
+ {scores[k]}
+
+ ))}
+
+
+
+
+ {/* ────── AI‑GENERATED CAPTION ────── */}
+
+
+ {/* ────── SUBMIT BUTTON ────── */}
+
+
+
+
+
+
+
+
+
+ )}
+
+ {/* Success page */}
+ {step === 3 && (
+
+
Saved!
+
Your caption has been successfully saved.
+
+
+
+
+ )}
+
+ {/* Full Size Image Modal */}
+ {isFullSizeModalOpen && (
+
setIsFullSizeModalOpen(false)}>
+
e.stopPropagation()}>
+
+
+
+
+

+
+
+
+ )}
+
+
+ );
+}
diff --git a/frontend/src/pages/UploadPage/index.ts b/frontend/src/pages/UploadPage/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0e1ce3f391ceb474884ebc693ad067c6b59f9476
--- /dev/null
+++ b/frontend/src/pages/UploadPage/index.ts
@@ -0,0 +1 @@
+export { default } from './UploadPage';
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index 245963f4581a7c0f08df9ad143edfa3cb1c687bc..e92b2c09f7c6e32118d2a01d27fcdc684d1a77b3 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -1,13 +1,13 @@
export interface MapOut {
- map_id: string; // UUID as string
+ map_id: string;
file_key: string;
sha256: string;
source: string;
region: string;
category: string;
caption?: {
- cap_id: string; // UUID as string
- map_id: string; // UUID as string
+ cap_id: string;
+ map_id: string;
generated: string;
edited?: string;
accuracy?: number;
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 83cfea835ed797cf1ed2ad0c8b960d2c4ff34d38..f292b3119037bd270b4b48e8823dcab5d6ece167 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -1,4 +1,3 @@
-/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json
index 227a6c6723b6136d17cdcd42288a01a5e5abcd25..b382d79f5d813f575264bcce32e2125391f7c294 100644
--- a/frontend/tsconfig.app.json
+++ b/frontend/tsconfig.app.json
@@ -6,16 +6,12 @@
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
-
- /* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
-
- /* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json
index f85a39906e5571aa351e61e43fff98bc0bedaa27..953116cc58eac4ad32f57cd13ef373f60163ab51 100644
--- a/frontend/tsconfig.node.json
+++ b/frontend/tsconfig.node.json
@@ -5,15 +5,11 @@
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
-
- /* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
-
- /* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 9f48c24e3d45d213a9c8ca50ecd39131fa891e91..e04b6857e8ef308bd6f7773e1e2cddcb83880f0d 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,4 +1,3 @@
-// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
@@ -6,12 +5,10 @@ export default defineConfig({
plugins: [react()],
server: {
proxy: {
- // proxy any /api/* request to localhost:8080
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
secure: false,
- // rewrite: (path) => path.replace(/^\/api/, '/api'), // not needed if same prefix
},
},
},
diff --git a/go-web-app-develop/.changeset/README.md b/go-web-app-develop/.changeset/README.md
deleted file mode 100644
index e5b6d8d6a67ad0dca8f20117fbfc72e076882d00..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.changeset/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Changesets
-
-Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
-with multi-package repos, or single-package repos to help you version and publish your code. You can
-find the full documentation for it [in our repository](https://github.com/changesets/changesets)
-
-We have a quick list of common questions to get you started engaging with this project in
-[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
diff --git a/go-web-app-develop/.changeset/config.json b/go-web-app-develop/.changeset/config.json
deleted file mode 100644
index 78e2547c0a77bd1108fc97b2ee04fca048e780dc..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.changeset/config.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
- "changelog": "@changesets/cli/changelog",
- "commit": false,
- "fixed": [],
- "linked": [],
- "access": "public",
- "baseBranch": "develop",
- "updateInternalDependencies": "patch",
- "ignore": [],
- "privatePackages": {
- "version": true,
- "tag": true
- }
-}
diff --git a/go-web-app-develop/.changeset/lovely-kids-boil.md b/go-web-app-develop/.changeset/lovely-kids-boil.md
deleted file mode 100644
index fbb5e507c03ea4314965d3f8fe7227baaf04131b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.changeset/lovely-kids-boil.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"go-web-app": patch
----
-
-Fix use of operational timeframe date in imminent final report form
diff --git a/go-web-app-develop/.changeset/pre.json b/go-web-app-develop/.changeset/pre.json
deleted file mode 100644
index c547ab77a110f9927419ee2de404109a6aba307b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.changeset/pre.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "mode": "pre",
- "tag": "beta",
- "initialVersions": {
- "go-web-app": "7.20.2",
- "go-ui-storybook": "1.0.7",
- "@ifrc-go/ui": "1.5.1"
- },
- "changesets": [
- "lovely-kids-boil",
- "solid-clubs-care",
- "sweet-gifts-cheer",
- "whole-lions-guess"
- ]
-}
diff --git a/go-web-app-develop/.changeset/solid-clubs-care.md b/go-web-app-develop/.changeset/solid-clubs-care.md
deleted file mode 100644
index 1ba2c262bebd10d30fd668efb4757d5e5caa1ec2..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.changeset/solid-clubs-care.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-"go-web-app": minor
----
-
-Add Crisis categorization update date
-
-- Add updated date for crisis categorization in emergency page.
-- Add consent checkbox over situational overview in field report form.
diff --git a/go-web-app-develop/.changeset/sweet-gifts-cheer.md b/go-web-app-develop/.changeset/sweet-gifts-cheer.md
deleted file mode 100644
index 7ead9c065d23406e1b6dfab6b7005ee81597125d..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.changeset/sweet-gifts-cheer.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-"go-web-app": minor
----
-
-Add support for DREF imminent v2 in final report
-
-- Add a separate route for the old dref final report form
-- Update dref final report to accomodate imminent v2 changes
-
diff --git a/go-web-app-develop/.changeset/whole-lions-guess.md b/go-web-app-develop/.changeset/whole-lions-guess.md
deleted file mode 100644
index e7f61facce276c868c920e75e99b59bbcfe16efd..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.changeset/whole-lions-guess.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-"go-web-app": patch
----
-
-- Fix calculation of Operation End date in Final report form
-- Fix icon position issue in the implementation table of DREF PDF export
-- Update the label for last update date in the crisis categorization pop-up
diff --git a/go-web-app-develop/.dockerignore b/go-web-app-develop/.dockerignore
deleted file mode 100644
index cf4994588663a234b28d9e5016c7aa5468008dce..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.dockerignore
+++ /dev/null
@@ -1,148 +0,0 @@
-# Swap files
-*.swp
-
-# Byte-compiled / optimized / DLL files
-__pycache__
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-env
-build
-develop-eggs
-dist
-downloads
-eggs
-.eggs
-lib
-lib64
-parts
-sdist
-var
-*.egg-info
-.installed.cfg
-*.egg
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov
-.tox
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*,cover
-.hypothesis
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-
-# Sphinx documentation
-docs/_build
-
-# PyBuilder
-target
-
-#Ipython Notebook
-.ipynb_checkpoints
-
-# SASS cache
-.sass-cache
-media_test
-
-# Rope project settings
-.ropeproject
-
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-# Runtime data
-pids
-*.pid
-*.seed
-*.pid.lock
-
-# Directory for instrumented libs generated by jscoverage/JSCover
-lib-cov
-
-# Coverage directory used by tools like istanbul
-coverage
-
-# nyc test coverage
-.nyc_output
-
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# Bower dependency directory (https://bower.io/)
-bower_components
-
-# node-waf configuration
-.lock-wscript
-
-# Compiled binary addons (http://nodejs.org/api/addons.html)
-build/Release
-
-# Dependency directories
-node_modules
-jspm_packages
-
-# Typescript v1 declaration files
-typings
-
-# Optional npm cache directory
-.npm
-
-# Optional eslint cache
-.eslintcache
-
-# Optional REPL history
-.node_repl_history
-
-# Output of 'npm pack'
-*.tgz
-
-# Yarn Integrity file
-.yarn-integrity
-
-# dotenv environment variables file
-.env
-.env*
-
-# Sensitive Deploy Files
-deploy/eb/
-
-# tox
-./.tox
-
-# Helm
-.helm-charts/
-
-# Docker
-Dockerfile
-.dockerignore
-
-# git
-.gitignore
\ No newline at end of file
diff --git a/go-web-app-develop/.github/ISSUE_TEMPLATE/01_bug_report.yml b/go-web-app-develop/.github/ISSUE_TEMPLATE/01_bug_report.yml
deleted file mode 100644
index f4abf01393ec0c3ee9e09833ec08f73ef8ea28a9..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/ISSUE_TEMPLATE/01_bug_report.yml
+++ /dev/null
@@ -1,92 +0,0 @@
-name: "Bug Report"
-description: "Report a technical or visual issue."
-labels: ["type: bug"]
-type: "Bug"
-body:
- - type: markdown
- attributes:
- value: |
- **Bug Report**
- Please fill out the form below with as much detail as possible.
- If the issue is visual, screenshots or videos are greatly appreciated.
- **Please review [our guide on reporting bugs](https://github.com/IFRCGo/go-web-app/blob/develop/CONTRIBUTING.md#reporting-bugs) before opening a new issue.**
-
- - type: input
- attributes:
- label: "Page URL"
- description: "The URL of the page where you encountered the issue."
- placeholder: "https://go.ifrc.org/"
- validations:
- required: true
-
- - type: dropdown
- attributes:
- label: "Environment"
- description: "Please select the environment where the bug occurred."
- options:
- - "Alpha"
- - "Staging"
- - "Production"
- validations:
- required: true
-
- - type: input
- attributes:
- label: "Browser"
- description: "Which browser are you using? (e.g., Chrome, Firefox, Safari)"
- placeholder: "Chrome"
- validations:
- required: true
-
- - type: textarea
- attributes:
- label: "Steps to Reproduce the Issue"
- description: |
- Please describe the issue in detail, including:
- 1. What actions led to the issue?
- 2. If possible, attach screenshots or videos demonstrating the problem.
- placeholder: |
- 1. I clicked on...
- 2. [Attach screenshots/videos if available]
- validations:
- required: true
-
- - type: textarea
- attributes:
- label: "Expected Behavior"
- description: "Describe what you expected to happen."
- placeholder: "I expected the page to..."
- validations:
- required: true
-
- - type: textarea
- attributes:
- label: "Actual Behavior"
- description: "Describe what actually happened, including any error messages."
- placeholder: "Instead, I saw..."
- validations:
- required: true
-
- - type: dropdown
- attributes:
- label: "Priority"
- description: "How urgent is this issue?"
- options:
- - "Low (Minor inconvenience)"
- - "Medium (Affects functionality, but there is a workaround)"
- - "High (Major functionality is broken)"
- - "Critical (Site is unusable)"
- validations:
- required: false
-
- - type: textarea
- attributes:
- label: "Additional Context (Optional)"
- description: |
- Provide any extra details, such as:
- - Related links.
- - Previous occurrences of this issue.
- - Workarounds you have tried.
- placeholder: "This issue also happened on [link]."
- validations:
- required: false
diff --git a/go-web-app-develop/.github/ISSUE_TEMPLATE/02_feature_request.yml b/go-web-app-develop/.github/ISSUE_TEMPLATE/02_feature_request.yml
deleted file mode 100644
index 163c573d859af4dafb2e2e4ccc12cfc03e93b571..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/ISSUE_TEMPLATE/02_feature_request.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: "Feature Request"
-description: "Suggest a new idea or enhancement."
-labels: ["type: feature-request"]
-type: "Feature"
-body:
- - type: markdown
- attributes:
- value: |
- **Feature Request**
- Thank you for suggesting a new feature!
- Please provide as much detail as possible to help us understand and evaluate your idea.
- **Please review [our guide on suggesting enhancements](https://github.com/IFRCGo/go-web-app/blob/develop/CONTRIBUTING.md#suggesting-enhancements).**
-
- - type: textarea
- attributes:
- label: "Feature Description"
- description: |
- Describe your feature request in detail, including:
- - What the feature is.
- - Why it is needed and how it will improve the project.
- - How it will benefit users (e.g., As a user, I want to [do something] so that [desired outcome].).
- placeholder: "As a user, I want to filter search results by date so that I can quickly find recent information."
- validations:
- required: true
-
- - type: textarea
- attributes:
- label: "Additional Context"
- description: |
- Provide any extra details or supporting information, such as:
- - Links to references or related resources.
- - Examples from other projects or systems.
- - Screenshots, mockups, or diagrams.
- *Tip: You can attach files by clicking here and dragging them in.*
- placeholder: |
- Here's a link to a similar feature in another project: [link].
- I've also attached a mockup of what this could look like.
- validations:
- required: false
diff --git a/go-web-app-develop/.github/ISSUE_TEMPLATE/03_epic_request.yml b/go-web-app-develop/.github/ISSUE_TEMPLATE/03_epic_request.yml
deleted file mode 100644
index f15ad133073a3b398d059b692a4e6768a6766b1f..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/ISSUE_TEMPLATE/03_epic_request.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: "Epic"
-description: "Track a larger initiative with multiple related tasks and deliverables."
-labels: ["type: epic"]
-type: "Feature"
-body:
- - type: markdown
- attributes:
- value: |
- **Epic**
- Use this to define a large, overarching initiative.
- **Please review [our guide on suggesting enhancements](https://github.com/IFRCGo/go-web-app/blob/develop/CONTRIBUTING.md#suggesting-enhancements).**
-
- - type: textarea
- attributes:
- label: "Epic Summary"
- description: |
- Provide a clear and concise summary of the epic.
- - What is this epic about?
- - What problem does it solve or what goal does it achieve?
- - How does it align with the project’s objectives?
- placeholder: |
- Example:
- This epic focuses on implementing a new feature.
- validations:
- required: true
-
- - type: textarea
- attributes:
- label: "Additional Context or Resources"
- description: "Provide any additional information, links, or resources that will help the team understand and execute this epic."
- placeholder: |
- Examples:
- - Link to design mockups: [link]
- - Technical specs document: [link]
- - Reference to similar features: [link]
- validations:
- required: false
diff --git a/go-web-app-develop/.github/ISSUE_TEMPLATE/config.yml b/go-web-app-develop/.github/ISSUE_TEMPLATE/config.yml
deleted file mode 100644
index 6e0e864658c3567d911593daf3fc122b0bb825a1..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/ISSUE_TEMPLATE/config.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-blank_issues_enabled: true
-contact_links:
- - name: Documentation
- url: https://go-wiki.ifrc.org/en/home
- about: Please consult the wiki to know more about IFRC GO.
diff --git a/go-web-app-develop/.github/dependabot.yml b/go-web-app-develop/.github/dependabot.yml
deleted file mode 100644
index 5af73a621cfd1ee6a9d876326a6177a53679c516..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/dependabot.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-version: 2
-updates:
- - package-ecosystem: npm
- directory: /
- schedule:
- interval: weekly
- groups:
- eslint:
- patterns:
- - "*eslint*"
- vite:
- patterns:
- - "*vite*"
- postcss:
- patterns:
- - "*postcss*"
- stylelint:
- patterns:
- - "*stylelint*"
- all-other-dependencies:
- patterns:
- - "*"
- exclude-patterns:
- - "*eslint*"
- - "*vite*"
- - "*postcss*"
- - "*stylelint*"
diff --git a/go-web-app-develop/.github/pull_request_template.md b/go-web-app-develop/.github/pull_request_template.md
deleted file mode 100644
index 0ff480c6e6965c575f1bae9f32185ed54e80225d..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/pull_request_template.md
+++ /dev/null
@@ -1,30 +0,0 @@
-## Summary
-
-Provide a brief description of what this PR addresses and its purpose.
-
-## Addresses
-
-* Issue(s): *List related issues or tickets.*
-
-## Depends On
-
-* Other PRs or Dependencies: *List PRs or dependencies this PR relies on.*
-
-## Changes
-
-* Detailed list or prose of changes
-* Breaking changes
-* Changes to configurations
-
-## This PR Ensures:
-
-* \[ ] No typos or grammatical errors
-* \[ ] No conflict markers left in the code
-* \[ ] No unwanted comments, temporary files, or auto-generated files
-* \[ ] No inclusion of secret keys or sensitive data
-* \[ ] No `console.log` statements meant for debugging
-* \[ ] All CI checks have passed
-
-## Additional Notes
-
-*Optional: Add any other relevant context, screenshots, or details here.*
diff --git a/go-web-app-develop/.github/workflows/add-issue-to-backlog.yml b/go-web-app-develop/.github/workflows/add-issue-to-backlog.yml
deleted file mode 100644
index 0a7a4ea7fc5c5d253d3baaa0ed03eed88f515013..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/workflows/add-issue-to-backlog.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: Add issues to Backlog
-
-on:
- issues:
- types:
- - opened
-
-jobs:
- add-to-project:
- name: Add issue to project
- runs-on: ubuntu-latest
- steps:
- - uses: actions/add-to-project@v0.4.0
- with:
- project-url: https://github.com/orgs/IFRCGo/projects/12
- github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
diff --git a/go-web-app-develop/.github/workflows/chromatic.yml b/go-web-app-develop/.github/workflows/chromatic.yml
deleted file mode 100644
index 2bc901b6f5ccea680d8ab29375ee52f4e3ab5ad0..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/workflows/chromatic.yml
+++ /dev/null
@@ -1,127 +0,0 @@
-name: 'Chromatic'
-
-on:
- pull_request:
- push:
- branches:
- - develop
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}-chromatic
- cancel-in-progress: true
-
-permissions:
- actions: write
- contents: read
- pages: write
- id-token: write
-
-jobs:
- changed-files:
- name: Check for changed files
- runs-on: ubuntu-latest
- outputs:
- all_changed_files: ${{ steps.changed-files.outputs.all_changed_files }}
- any_changed: ${{ steps.changed-files.outputs.any_changed }}
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- - name: Get changed files
- id: changed-files
- uses: tj-actions/changed-files@v44
- with:
- files: |
- packages/ui/**
- packages/go-ui-storybook/**
- ui:
- name: Build UI Library
- environment: 'test'
- runs-on: ubuntu-latest
- needs: [changed-files]
- if: ${{ needs.changed-files.outputs.any_changed == 'true' }}
- defaults:
- run:
- working-directory: packages/ui
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
- - name: Typecheck
- run: pnpm typecheck
- - name: Lint CSS
- run: pnpm lint:css
- - name: Lint JS
- run: pnpm lint:js
- - name: build UI library
- run: pnpm build
- - uses: actions/upload-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
- chromatic:
- name: Chromatic Deploy
- runs-on: ubuntu-latest
- needs: [ui]
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
- - uses: actions/download-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
- - name: Run Chromatic
- uses: chromaui/action@v1
- with:
- exitZeroOnChanges: true
- exitOnceUploaded: true
- onlyChanged: true
- skip: "@(renovate/**|dependabot/**)"
- projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- token: ${{ secrets.GITHUB_TOKEN }}
- autoAcceptChanges: "develop"
- workingDir: packages/go-ui-storybook
- github-pages:
- name: Deploy to Github Pages
- runs-on: ubuntu-latest
- needs: [ui]
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - uses: actions/download-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
- - uses: bitovi/github-actions-storybook-to-github-pages@v1.0.3
- with:
- install_command: pnpm install
- build_command: pnpm build-storybook
- path: packages/go-ui-storybook/storybook-static
- checkout: false
diff --git a/go-web-app-develop/.github/workflows/ci.yml b/go-web-app-develop/.github/workflows/ci.yml
deleted file mode 100644
index ebdd425e6e7243c4be81500e123eeb46c16319ca..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/workflows/ci.yml
+++ /dev/null
@@ -1,304 +0,0 @@
-name: CI
-
-on:
- pull_request:
- push:
- branches:
- - 'develop'
-
-env:
- APP_ADMIN_URL: ${{ vars.APP_ADMIN_URL }}
- APP_API_ENDPOINT: ${{ vars.APP_API_ENDPOINT }}
- APP_ENVIRONMENT: ${{ vars.APP_ENVIRONMENT }}
- APP_MAPBOX_ACCESS_TOKEN: ${{ vars.APP_MAPBOX_ACCESS_TOKEN }}
- APP_RISK_ADMIN_URL: ${{ vars.APP_RISK_ADMIN_URL }}
- APP_RISK_API_ENDPOINT: ${{ vars.APP_RISK_API_ENDPOINT }}
- APP_SENTRY_DSN: ${{ vars.APP_SENTRY_DSN }}
- APP_SENTRY_NORMALIZE_DEPTH: ${{ vars.APP_SENTRY_NORMALIZE_DEPTH }}
- APP_SENTRY_TRACES_SAMPLE_RATE: ${{ vars.APP_SENTRY_TRACES_SAMPLE_RATE }}
- APP_SHOW_ENV_BANNER: ${{ vars.APP_SHOW_ENV_BANNER }}
- APP_TINY_API_KEY: ${{ vars.APP_TINY_API_KEY }}
- APP_TITLE: ${{ vars.APP_TITLE }}
- GITHUB_WORKFLOW: true
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- ui:
- name: Build UI Library
- environment: 'test'
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: packages/ui
- steps:
- - uses: actions/checkout@v4
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
-
- - name: Typecheck
- run: pnpm typecheck
-
- - name: Lint CSS
- run: pnpm lint:css
-
- - name: Lint JS
- run: pnpm lint:js
-
- - name: Build
- run: pnpm build
-
- - uses: actions/upload-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
-
- test:
- name: Run tests
- environment: 'test'
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: app
- needs: [ui]
- steps:
- - uses: actions/checkout@v4
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
-
- - uses: actions/download-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
-
- - name: Run test
- run: pnpm test
-
- translation:
- continue-on-error: true
- name: Identify error with translation files
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: app
- needs: [ui]
- steps:
- - uses: actions/checkout@v4
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
-
- - uses: actions/download-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
-
- - name: Identify error with translation files
- run: pnpm lint:translation
-
- translation-migrations:
- if: |
- (github.event_name == 'pull_request' && github.base_ref == 'develop') ||
- (github.event_name == 'push' && github.ref == 'refs/heads/develop')
- continue-on-error: true
- name: Identify if translation migrations need to be generated
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: app
- needs: [ui]
- steps:
- - uses: actions/checkout@v4
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
-
- - uses: actions/download-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
-
- - name: Identify if translation migrations need to be generated
- run: |
- if pnpm translatte:generate; then
- # The step should fail if generation is possible
- exit 1
- fi
-
- unused:
- name: Identify unused files
- runs-on: ubuntu-latest
- needs: [ui]
- steps:
- - uses: actions/checkout@v4
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
-
- - name: Initialize types
- run: pnpm initialize:type
- working-directory: app
-
- - name: Identify unused files
- run: pnpm lint:unused
-
- lint:
- name: Lint JS
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: app
- needs: [ui]
- steps:
- - uses: actions/checkout@v4
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
-
- - uses: actions/download-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
-
- - name: Lint JS
- run: pnpm lint:js
-
- lint-css:
- name: Lint CSS
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: app
- needs: [ui]
- steps:
- - uses: actions/checkout@v4
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
-
- - uses: actions/download-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
-
- - name: Lint CSS
- run: pnpm lint:css
-
- # FIXME: Identify a way to generate schema before we run typecheck
- # typecheck:
- # name: Typecheck
- # runs-on: ubuntu-latest
- # steps:
- # - uses: actions/checkout@v4
- # - name: Install pnpm
- # uses: pnpm/action-setup@v4
- # - name: Install Node.js
- # uses: actions/setup-node@v4
- # with:
- # node-version: 20
- # cache: 'pnpm'
- # - name: Install dependencies
- # run: pnpm install
- #
- # - name: Typecheck
- # run: pnpm typecheck
-
- typos:
- name: Spell Check with Typos
- runs-on: ubuntu-latest
- steps:
- - name: Checkout Actions Repository
- uses: actions/checkout@v4
-
- - name: Check spelling
- uses: crate-ci/typos@v1.29.4
-
- build:
- name: Build GO Web App
- environment: 'test'
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: app
- needs: [lint, lint-css, test, ui]
- steps:
- - uses: actions/checkout@v4
- - name: Install pnpm
- uses: pnpm/action-setup@v4
- - name: Install Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: 'pnpm'
- - name: Install dependencies
- run: pnpm install
-
- - uses: actions/download-artifact@v4
- with:
- name: ui-build
- path: packages/ui/dist
-
- - name: Build
- run: pnpm build
-
- validate_helm:
- name: Validate Helm
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@main
-
- - name: Install Helm
- uses: azure/setup-helm@v4
-
- - name: Helm lint
- run: helm lint ./nginx-serve/helm --values ./nginx-serve/helm/values-test.yaml
-
- - name: Helm template
- run: helm template ./nginx-serve/helm --values ./nginx-serve/helm/values-test.yaml
diff --git a/go-web-app-develop/.github/workflows/publish-nginx-serve.yml b/go-web-app-develop/.github/workflows/publish-nginx-serve.yml
deleted file mode 100644
index 28c91fbc56be483ef9e53bbc4c619fb8297b6e0b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/workflows/publish-nginx-serve.yml
+++ /dev/null
@@ -1,147 +0,0 @@
-name: Publish Helm
-
-on:
- workflow_dispatch:
- push:
- branches:
- - develop
- - project/*
-
-permissions:
- packages: write
-
-
-jobs:
- publish_image:
- name: Publish Docker Image
- runs-on: ubuntu-latest
-
- outputs:
- docker_image_name: ${{ steps.prep.outputs.tagged_image_name }}
- docker_image_tag: ${{ steps.prep.outputs.tag }}
- docker_image: ${{ steps.prep.outputs.tagged_image }}
-
- steps:
- - uses: actions/checkout@main
-
- - name: Login to GitHub Container Registry
- uses: docker/login-action@v3
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: 🐳 Prepare Docker
- id: prep
- env:
- IMAGE_NAME: ghcr.io/${{ github.repository }}
- run: |
- BRANCH_NAME=$(echo $GITHUB_REF_NAME | sed 's|[/:]|-|' | tr '[:upper:]' '[:lower:]' | sed 's/_/-/g' | cut -c1-100 | sed 's/-*$//')
-
- # XXX: Check if there is a slash in the BRANCH_NAME eg: project/add-docker
- if [[ "$BRANCH_NAME" == *"/"* ]]; then
- # XXX: Change the docker image package to -alpha
- IMAGE_NAME="$IMAGE_NAME-alpha"
- TAG="$(echo "$BRANCH_NAME" | sed 's|/|-|g').$(echo $GITHUB_SHA | head -c7)"
- else
- TAG="$BRANCH_NAME.$(echo $GITHUB_SHA | head -c7)"
- fi
-
- IMAGE_NAME=$(echo $IMAGE_NAME | tr '[:upper:]' '[:lower:]')
- echo "tagged_image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
- echo "tag=${TAG}" >> $GITHUB_OUTPUT
- echo "tagged_image=${IMAGE_NAME}:${TAG}" >> $GITHUB_OUTPUT
- echo "::notice::Tagged docker image: ${IMAGE_NAME}:${TAG}"
-
- - name: 🐳 Set up Docker Buildx
- id: buildx
- uses: docker/setup-buildx-action@v3
-
- - name: 🐳 Cache Docker layers
- uses: actions/cache@v4
- with:
- path: /tmp/.buildx-cache
- key: ${{ runner.os }}-buildx-${{ github.ref }}
- restore-keys: |
- ${{ runner.os }}-buildx-refs/develop
- ${{ runner.os }}-buildx-
-
- - name: 🐳 Docker build
- uses: docker/build-push-action@v6
- with:
- context: .
- builder: ${{ steps.buildx.outputs.name }}
- file: nginx-serve/Dockerfile
- target: nginx-serve
- load: true
- push: true
- tags: ${{ steps.prep.outputs.tagged_image }}
- cache-from: type=local,src=/tmp/.buildx-cache
- cache-to: type=local,dest=/tmp/.buildx-cache-new
- build-args: |
- "APP_SENTRY_TRACES_SAMPLE_RATE=0.8"
- "APP_SENTRY_REPLAYS_SESSION_SAMPLE_RATE=0.8"
- "APP_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE=0.8"
-
- - name: 🐳 Move docker cache
- run: |
- rm -rf /tmp/.buildx-cache
- mv /tmp/.buildx-cache-new /tmp/.buildx-cache
-
- publish_helm:
- name: Publish Helm
- needs: publish_image
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Login to GitHub Container Registry
- uses: docker/login-action@v3
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Install Helm
- uses: azure/setup-helm@v3
-
- - name: Tag docker image in Helm Chart values.yaml
- env:
- IMAGE_NAME: ${{ needs.publish_image.outputs.docker_image_name }}
- IMAGE_TAG: ${{ needs.publish_image.outputs.docker_image_tag }}
- run: |
- # Update values.yaml with latest docker image
- sed -i "s|SET-BY-CICD-IMAGE|$IMAGE_NAME|" nginx-serve/helm/values.yaml
- sed -i "s/SET-BY-CICD-TAG/$IMAGE_TAG/" nginx-serve/helm/values.yaml
-
- - name: Package Helm Chart
- id: set-variables
- run: |
- # XXX: Check if there is a slash in the BRANCH_NAME eg: project/add-docker
- if [[ "$GITHUB_REF_NAME" == *"/"* ]]; then
- # XXX: Change the helm chart to -alpha
- sed -i 's/^name: \(.*\)/name: \1-alpha/' nginx-serve/helm/Chart.yaml
- fi
-
- SHA_SHORT=$(git rev-parse --short HEAD)
- sed -i "s/SET-BY-CICD/$SHA_SHORT/g" nginx-serve/helm/Chart.yaml
- helm package ./nginx-serve/helm -d .helm-charts
-
- - name: Push Helm Chart
- env:
- IMAGE: ${{ needs.publish_image.outputs.docker_image }}
- OCI_REPO: oci://ghcr.io/${{ github.repository }}
- run: |
- OCI_REPO=$(echo $OCI_REPO | tr '[:upper:]' '[:lower:]')
- PACKAGE_FILE=$(ls .helm-charts/*.tgz | head -n 1)
- echo "# Helm Chart" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "Tagged Image: **$IMAGE**" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "Helm push output" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo '```bash' >> $GITHUB_STEP_SUMMARY
- helm push "$PACKAGE_FILE" $OCI_REPO >> $GITHUB_STEP_SUMMARY
- echo '```' >> $GITHUB_STEP_SUMMARY
diff --git a/go-web-app-develop/.github/workflows/publish-storybook-nginx-serve.yml b/go-web-app-develop/.github/workflows/publish-storybook-nginx-serve.yml
deleted file mode 100644
index 430237923bc504148b1a4f372ee398bba191032a..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.github/workflows/publish-storybook-nginx-serve.yml
+++ /dev/null
@@ -1,127 +0,0 @@
-name: Publish Storybook Helm
-
-on:
- workflow_dispatch:
- push:
- branches:
- - develop
-
-permissions:
- packages: write
-
-
-jobs:
- publish_image:
- name: 🐳 Publish Docker Image
- runs-on: ubuntu-latest
-
- outputs:
- docker_image_name: ${{ steps.prep.outputs.tagged_image_name }}
- docker_image_tag: ${{ steps.prep.outputs.tag }}
- docker_image: ${{ steps.prep.outputs.tagged_image }}
-
- steps:
- - uses: actions/checkout@main
-
- - name: Login to GitHub Container Registry
- uses: docker/login-action@v3
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: 🐳 Prepare Docker
- id: prep
- env:
- IMAGE_NAME: ghcr.io/${{ github.repository }}/go-ui-storybook
- run: |
- BRANCH_NAME=$(echo $GITHUB_REF_NAME | sed 's|[/:]|-|' | tr '[:upper:]' '[:lower:]' | sed 's/_/-/g' | cut -c1-100 | sed 's/-*$//')
- TAG="$BRANCH_NAME.$(echo $GITHUB_SHA | head -c7)"
- IMAGE_NAME=$(echo $IMAGE_NAME | tr '[:upper:]' '[:lower:]')
- echo "tagged_image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
- echo "tag=${TAG}" >> $GITHUB_OUTPUT
- echo "tagged_image=${IMAGE_NAME}:${TAG}" >> $GITHUB_OUTPUT
- echo "::notice::Tagged docker image: ${IMAGE_NAME}:${TAG}"
-
- - name: 🐳 Set up Docker Buildx
- id: buildx
- uses: docker/setup-buildx-action@v3
-
- - name: 🐳 Cache Docker layers
- uses: actions/cache@v4
- with:
- path: /tmp/.buildx-cache
- key: ${{ runner.os }}-buildx-${{ github.ref }}
- restore-keys: |
- ${{ runner.os }}-buildx-refs/develop
- ${{ runner.os }}-buildx-
-
- - name: 🐳 Docker build
- uses: docker/build-push-action@v6
- with:
- context: .
- builder: ${{ steps.buildx.outputs.name }}
- file: packages/go-ui-storybook/nginx-serve/Dockerfile
- target: nginx-serve
- load: true
- push: true
- tags: ${{ steps.prep.outputs.tagged_image }}
- cache-from: type=local,src=/tmp/.buildx-cache
- cache-to: type=local,dest=/tmp/.buildx-cache-new
-
- - name: 🐳 Move docker cache
- run: |
- rm -rf /tmp/.buildx-cache
- mv /tmp/.buildx-cache-new /tmp/.buildx-cache
-
- publish_helm:
- name: ⎈ Publish Helm
- needs: publish_image
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Login to GitHub Container Registry
- uses: docker/login-action@v3
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: ⎈ Install Helm
- uses: azure/setup-helm@v3
-
- - name: ⎈ Tag docker image in Helm Chart values.yaml
- env:
- IMAGE_NAME: ${{ needs.publish_image.outputs.docker_image_name }}
- IMAGE_TAG: ${{ needs.publish_image.outputs.docker_image_tag }}
- run: |
- # Update values.yaml with latest docker image
- sed -i "s|SET-BY-CICD-IMAGE|$IMAGE_NAME|" packages/go-ui-storybook/nginx-serve/helm/values.yaml
- sed -i "s/SET-BY-CICD-TAG/$IMAGE_TAG/" packages/go-ui-storybook/nginx-serve/helm/values.yaml
-
- - name: ⎈ Package Helm Chart
- id: set-variables
- run: |
- SHA_SHORT=$(git rev-parse --short HEAD)
- sed -i "s/SET-BY-CICD/$SHA_SHORT/g" packages/go-ui-storybook/nginx-serve/helm/Chart.yaml
- helm package ./packages/go-ui-storybook/nginx-serve/helm -d .helm-charts
-
- - name: ⎈ Push Helm Chart
- env:
- IMAGE: ${{ needs.publish_image.outputs.docker_image }}
- OCI_REPO: oci://ghcr.io/${{ github.repository }}
- run: |
- OCI_REPO=$(echo $OCI_REPO | tr '[:upper:]' '[:lower:]')
- PACKAGE_FILE=$(ls .helm-charts/*.tgz | head -n 1)
- echo "## 🚀 IFRC GO UI Helm Chart 🚀" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "🐳 Tagged Image: **$IMAGE**" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "⎈ Helm push output" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo '```bash' >> $GITHUB_STEP_SUMMARY
- helm push "$PACKAGE_FILE" $OCI_REPO >> $GITHUB_STEP_SUMMARY
- echo '```' >> $GITHUB_STEP_SUMMARY
diff --git a/go-web-app-develop/.gitignore b/go-web-app-develop/.gitignore
deleted file mode 100644
index 6f710ff8f79403d54017754f5d34528087363516..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.gitignore
+++ /dev/null
@@ -1,43 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-build
-build-ssr
-*.local
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
-
-.env*
-!.env.example
-.eslintcache
-tsconfig.tsbuildinfo
-
-# Custom ignores
-
-stats.html
-generated/
-coverage/
-
-# storybook build
-storybook-static/
-
-# Helm
-.helm-charts/
diff --git a/go-web-app-develop/.npmrc b/go-web-app-develop/.npmrc
deleted file mode 100644
index 6c59086d862516d2fffa5da2035e5003100fb5c7..0000000000000000000000000000000000000000
--- a/go-web-app-develop/.npmrc
+++ /dev/null
@@ -1 +0,0 @@
-enable-pre-post-scripts=true
diff --git a/go-web-app-develop/COLLABORATING.md b/go-web-app-develop/COLLABORATING.md
deleted file mode 100644
index 3596cf283617c189f1f7db3d8027317c5f911376..0000000000000000000000000000000000000000
--- a/go-web-app-develop/COLLABORATING.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# IFRC GO Collaboration Guide
-
-This document offers guidelines for collaborators on codebase maintenance, testing, building and deployment, and issue management.
-
-## Repository
-
-* [Issues and Pull Requests](./collaborating/issues-and-pull-requests.md)
-* [Structure](./collaborating/repository-structure.md)
-* [Linting](./collaborating/linting.md)
-* [Technology Used](./collaborating/technology.md)
-
-## Development
-
-* [Developing](./collaborating/developing.md)
-* [Translation](./collaborating/translation.md)
-* [Building](./collaborating/building.md)
-* [Testing](./collaborating/testing.md)
-* [Release](./collaborating/release.md)
diff --git a/go-web-app-develop/CONTRIBUTING.md b/go-web-app-develop/CONTRIBUTING.md
deleted file mode 100644
index 503e8a4aad5961a5d579646a75e4e41cea880f16..0000000000000000000000000000000000000000
--- a/go-web-app-develop/CONTRIBUTING.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# IFRC GO Web Application Contributing Guide
-
-First off, thanks for taking the time to contribute! ❤️
-
-All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution.
-
-## Table of Contents
-
-* [I Have a Question](#i-have-a-question)
-* [I Want To Contribute](#i-want-to-contribute)
-* [What should I know before I get started?](#what-should-i-know-before-i-get-started)
-* [Reporting Bugs](#reporting-bugs)
-* [Suggesting Enhancements](#suggesting-enhancements)
-* [Becoming a Collaborator](#becoming-a-collaborator)
-
-## I Have a Question
-
-> If you want to ask a question, we assume that you have read the available [documentation](https://go-wiki.ifrc.org/en/home).
-
-Before you ask a question, it is best to search for existing [issues](https://github.com/IFRCGo/go-web-app/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue.
-
-If you then still feel the need to ask a question and need clarification, we recommend the following:
-
-* Open a [discussion](https://github.com/IFRCGo/go-web-app/discussions).
-* Open an [issue](https://github.com/IFRCGo/go-web-app/issues/new/choose).
-* Provide as much context as you can about what you're running into.
-
-## I Want To Contribute
-
-Any individual is welcome to contribute to IFRC GO. The repository currently has two kinds of contribution personas:
-
-* A **Contributor** is any individual who creates an issue/PR, comments on an issue/PR, or contributes in some other way.
-* A **Collaborator** is a contributor with write access to the repository.
-
-### What should I know before I get started?
-
-### IFRC GO and Packages
-
-The project is hosted at .
-
-The project comprises several [repositories](https://github.com/orgs/IFRCGo/repositories), with notable ones including:
-
-* [go-web-app](https://github.com/IFRCGo/go-web-app/) - The frontend repository for the IFRC GO project.
-* [go-api](https://github.com/IFRCGo/go-api) - The backed repository for the IFRC GO project.
-
-### Reporting Bugs
-
-#### Before Submitting a Bug Report
-
-Ensure the issue is not a user error by reviewing the documentation. Check the [existing bug reports](https://github.com/IFRCGo/go-web-app/issues?q=is%3Aissue%20state%3Aopen%20type%3ABug) to confirm if the issue has already been reported.
-
-#### Submitting the Bug Report
-
-1. Open a new [Issue](https://github.com/IFRCGo/go-web-app/issues/new?q=is%3Aissue+state%3Aopen+type%3ABug\&template=01_bug_report.yml).
-2. Provide all relevant details.
-
-#### After Submitting the Issue
-
-* The team will categorize and attempt to reproduce the issue.
-* If reproducible, the team will work on resolving the bug.
-
-### Suggesting Enhancements
-
-#### Before Submitting an Enhancement
-
-* Review the [documentation](https://go-wiki.ifrc.org/en/home) to ensure the functionality isn't already covered.
-* Perform a [search](https://github.com/IFRCGo/go-web-app/issues) to check if the enhancement has been suggested. If so, comment on the existing issue.
-* Confirm that your suggestion aligns with the project’s scope and objectives.
-
-#### How to Submit an Enhancement Suggestion
-
-Enhancements are tracked as [GitHub issues](https://github.com/IFRCGo/go-web-app/issues).
-
-* Open a new [feature request](https://github.com/IFRCGo/go-web-app/issues/new?q=is%3Aissue+state%3Aopen+type%3ABug\&template=02_feature_request.yml) or [Epic ticket](https://github.com/IFRCGo/go-web-app/issues/new?q=is%3Aissue+state%3Aopen+type%3ABug\&template=03_epic_request.yml) depending on the scale of the enhancement.
-* Provide a clear description and submit the ticket.
-
-## Becoming a Collaborator
-
-Collaborators are key members of the IFRC GO Web Application Team, responsible for its development. Members should have expertise in modern web technologies and standards.
-
-For detailed guidelines, refer to the [Collaboration Guide](./COLLABORATING.md).
diff --git a/go-web-app-develop/LICENSE b/go-web-app-develop/LICENSE
deleted file mode 100644
index 04eca71b6e81461214eb8ad3277aa5338f4abd05..0000000000000000000000000000000000000000
--- a/go-web-app-develop/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2023 GO
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/go-web-app-develop/README.md b/go-web-app-develop/README.md
deleted file mode 100644
index 077e001cbdb03504d43441ab3260aea64fb133a4..0000000000000000000000000000000000000000
--- a/go-web-app-develop/README.md
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
-
-
-
-
-
-
-
-# IFRC GO
-
-[IFRC GO](https://go.ifrc.org/) is the platform of the International Federation of Red Cross and Red Crescent, aimed at connecting crucial information on emergency needs with the appropriate response. This repository houses the frontend source code for the application, developed using [React](https://react.dev/), [Vite](https://vitejs.dev/), and associated technologies.
-
-## Built With
-
-[![React][react-shields]][react-url] [![Vite][vite-shields]][vite-url] [![TypeScript][typescript-shields]][typescript-url] [![pnpm][pnpm-shields]][pnpm-url]
-
-## Getting Started
-
-Below are the steps to guide you through preparing your local environment for IFRC GO Web application development. The repository is set up as a [monorepo](https://monorepo.tools/). The [app](https://github.com/IFRCGo/go-web-app/tree/develop/app) directory houses the application code, while the [packages](https://github.com/IFRCGo/go-web-app/tree/develop/packages) directory contains related packages, including the [IFRC GO UI](https://www.npmjs.com/package/@ifrc-go/ui) components library.
-
-### Prerequisites
-
-To begin, ensure you have network access. Then, you'll need the following:
-
-1. [Git](https://git-scm.com/)
-2. [Node.js](https://nodejs.org/en/) as specified under `engines` section in `package.json` file
-3. [pnpm](https://pnpm.io/) as specified under `engines` section in `package.json` file
-4. Alternatively, you can use [Docker](https://www.docker.com/) to build the application.
-
-> \[!NOTE]\
-> Make sure the correct versions of pnpm and Node.js are installed. They are specified under `engines` section in `package.json` file.
-
-### Local Development
-
-1. Clone the repository using HTTPS, SSH, or GitHub CLI:
-
- ```bash
- git clone https://github.com/IFRCGo/go-web-app.git # HTTPS
- git clone git@github.com:IFRCGo/go-web-app.git # SSH
- gh repo clone IFRCGo/go-web-app # GitHub CLI
- ```
-
-2. Install the dependencies:
-
- ```bash
- pnpm install
- ```
-
-3. Create a `.env` file in the `app` directory and add variables from [env.ts](https://github.com/IFRCGo/go-web-app/blob/develop/app/env.ts). Any variables marked with `.optional()` are not mandatory for setup and can be skipped.
-
- ```bash
- cd app
- touch .env
- ```
-
- Example `.env` file
- ```
- APP_TITLE=IFRC GO
- APP_ENVIRONMENT=testing
- ...
- ```
-
-4. Start the development server:
-
- ```bash
- pnpm start:app
- ```
-
-## Contributing
-
-* Check out existing [Issues](https://github.com/IFRCGo/go-web-app/issues) and [Pull Requests](https://github.com/IFRCGo/go-web-app/pulls) to contribute.
-* To request a feature or report a bug, [create a GitHub Issue](https://github.com/IFRCGo/go-web-app/issues/new/choose).
-* [Contribution Guide →](./CONTRIBUTING.md)
-* [Collaboration Guide →](./COLLABORATING.md)
-
-## Additional Packages
-
-The repository hosts multiple packages under the `packages` directory.
-
-1. [IFRC GO UI](https://github.com/IFRCGo/go-web-app/tree/develop/packages/ui) is a React UI components library tailored to meet the specific requirements of the IFRC GO community and its associated projects.
-2. [IFRC GO UI Storybook](https://github.com/IFRCGo/go-web-app/tree/develop/packages/go-ui-storybook) serves as the comprehensive showcase for the IFRC GO UI components library. It is hosted on [Chromatic](https://66557be6b68dacbf0a96db23-zctxglhsnk.chromatic.com/).
-
-## IFRC GO Backend
-
-The backend that serves the frontend application is maintained in a separate [repository](https://github.com/IFRCGo/go-api/).
-
-## Previous Repository
-
-[Go Frontend](https://github.com/IFRCGo/go-frontend) is the previous version of the project which contains the original codebase and project history.
-
-## Community & Support
-
-* Visit the [IFRC GO Wiki](https://go-wiki.ifrc.org/) for documentation of the IFRC GO platform.
-* Stay informed about the latest project updates on [Medium](https://ifrcgoproject.medium.com/).
-
-## License
-
-[MIT](https://github.com/IFRCGo/go-web-app/blob/develop/LICENSE)
-
-
-
-[react-shields]: https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB
-
-[react-url]: https://reactjs.org/
-
-[vite-shields]: https://img.shields.io/badge/vite-%23646CFF.svg?style=for-the-badge&logo=vite&logoColor=white
-
-[vite-url]: https://vitejs.dev/
-
-[typescript-shields]: https://img.shields.io/badge/typescript-%23007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white
-
-[typescript-url]: https://www.typescriptlang.org/
-
-[pnpm-shields]: https://img.shields.io/badge/pnpm-F69220?style=for-the-badge&logo=pnpm&logoColor=fff
-
-[pnpm-url]: https://pnpm.io/
diff --git a/go-web-app-develop/app/CHANGELOG.md b/go-web-app-develop/app/CHANGELOG.md
deleted file mode 100644
index 939d0cceaea6368f223c0c080688919333a38b9c..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/CHANGELOG.md
+++ /dev/null
@@ -1,729 +0,0 @@
-# go-web-app
-
-## 7.21.0-beta.2
-
-### Patch Changes
-
-- b949fcd: Fix use of operational timeframe date in imminent final report form
-
-## 7.21.0-beta.1
-
-### Patch Changes
-
-- 84b4802: - Fix calculation of Operation End date in Final report form
- - Fix icon position issue in the implementation table of DREF PDF export
- - Update the label for last update date in the crisis categorization pop-up
-
-## 7.21.0-beta.0
-
-### Minor Changes
-
-- 039c488: Add Crisis categorization update date
-
- - Add updated date for crisis categorization in emergency page.
- - Add consent checkbox over situational overview in field report form.
-
-- 3ee9979: Add support for DREF imminent v2 in final report
-
- - Add a separate route for the old dref final report form
- - Update dref final report to accomodate imminent v2 changes
-
-## 7.20.2
-
-### Patch Changes
-
-- 8090b9a: Fix other action section visibility condition in DREF export
-
-## 7.20.1
-
-### Patch Changes
-
-- 4418171: Fix DREF form to properly save major coordination mechanism [#1928](https://github.com/IFRCGo/go-web-app/issues/1928)
-
-## 7.20.1-beta.0
-
-### Patch Changes
-
-- 4418171: Fix DREF form to properly save major coordination mechanism [#1928](https://github.com/IFRCGo/go-web-app/issues/1928)
-
-## 7.20.0
-
-### Minor Changes
-
-- 5771a6b: Update DREF application form and export
-
- - add new field hazard date and location
- - update hazard date as forcasted day of event
- - update the section in dref application export
- - remove Current National Society Actions from the export
-
-## 7.20.0-beta.0
-
-### Minor Changes
-
-- 5771a6b: Update DREF application form and export
-
- - add new field hazard date and location
- - update hazard date as forcasted day of event
- - update the section in dref application export
- - remove Current National Society Actions from the export
-
-## 7.19.0
-
-### Minor Changes
-
-- 456a145: Fix versioning
-
-### Patch Changes
-
-- 47786f8: Fix the undefined society name issue in surge page [#1899](https://github.com/IFRCGo/go-web-app/issues/1899)
-
-## 7.18.2
-
-### Patch Changes
-
-- e51a80f: Update the action for the DREF Ops update form for imminent.
- - Remove change to response modal in the ops update form for type imminent.
- - Fix the order of the field in operational timeframe tab.
- - Add description text under upload assessment report button in DREF operation update form
-- Fix the error while viewing PER process [#1838](https://github.com/IFRCGo/go-web-app/issues/1838).
-
-## 7.18.1
-
-### Patch Changes
-
-- 75bf525: Fix logic to disable ops update for old imminents
-
-## 7.18.0
-
-### Minor Changes
-
-- bfcaecf: Address [Dref imminent Application](https://github.com/IFRCGo/go-web-app/issues/1455)
- - Update logic for creation of dref final report for imminent
- - Update allocatioon form for dref imminent
- - Add Activity input in proposed action for dref type imminent
- - Add proposed actions icons
- - Show proposed actions for existing imminent dref applications
- - Hide unused sections for dref imminent export and preserve proposed actions order
- - Prevent selection of past dates for the `hazard_date` in dref imminent
- - Add auto total population calculation in dref
- - Add a confirmation popup before creating ops. update from imminent dref
-
-### Patch Changes
-
-- ee1bd60: Add proper redirect for Non-sovereign country in the country ongoing emergencies page
-- 771d085: Community Based Surveillance updates (Surge CoS Health)
- - Changed page: https://go.ifrc.org/surge/catalogue/health/community-based-surveillance
- - The changes affect team size and some standard components (e.g. kit content)
-- Updated dependencies [bfcaecf]
- - @ifrc-go/ui@1.5.1
-
-## 7.17.4
-
-### Patch Changes
-
-- 14a7f2c: Update People assisted field label in the export of Dref final report.
-
-## 7.17.3
-
-### Patch Changes
-
-- fc8b427: Update field label in DrefFinalReport form and export
-
-## 7.17.2
-
-### Patch Changes
-
-- 54df6ff: Update DREF final report form
-
- - The DREF final report form and export now include a new "Assisted Population" field, replacing the "Targeted Population" field.
-
-## 7.17.1
-
-### Patch Changes
-
-- 215030a: Update DREF forms
-
- - Move Response strategy description from placeholder to below the input
- - Add DREF allocation field in event details for the Loan type Ops. update form
-
-## 7.17.0
-
-### Minor Changes
-
-- 0b351d1: Address [DREF Superticket 2 bugs](https://github.com/IFRCGo/go-web-app/issues/1784)
-
- - Update no of images in for "Description of event" from 2 to 4
- - Update descriptions of few fields
- - Replace \* with bullet in description of planned interventions in DREF import
- - Add some of the missing fields to exports
- - Remove warnings for previously removed fields
-
-## 7.16.2
-
-### Patch Changes
-
-- c086629: Update Learn > Resources > Montandon page
- - Update styling of 'API Access' buttons
- - Reword 'Access API' link to 'Access Montandon API'
- - Reword 'Explore Radiant Earth API' to 'Explore data in STAC browser'
-- 2ee6a1e: Remove a broken image from Catalogue of Surge Services > Health > ERU Hospital page
-
-## 7.16.1
-
-### Patch Changes
-
-- d561dc4: - Update Montandon landing page - Fix typo in Justin's name and email - Update description
- - Fix position and deploying organisation in ongoing RR deployment table
-
-## 7.16.0
-
-### Minor Changes
-
-- 9dcdd38: Add Montandon landing page
-
- - Add a basic landing page for Montandon with links and information
- - Add link to Montandon landing page to Learn > Resources menu
-
-## 7.15.0
-
-### Minor Changes
-
-- c26bda4: Implement [ERU Readiness](https://github.com/IFRCGo/go-web-app/issues/1710)
-
- - Restucture surge page to acommodate ERU
- - Move surge deployment related sections to a new dedicated tab **Active Surge Deployments**
- - Update active deployments to improve scaling of points in the map
- - Add **Active Surge Support per Emergency** section
- - Revamp **Surge Overview** tab
- - Add **Rapid Response Personnel** sub-tab
- - Update existings charts and add new related tables/charts
- - Add **Emergency Response Unit** sub-tab
- - Add section to visualize ERU capacity and readiness
- - Add section to view ongoing ERU deployments
- - Add a form to update ERU Readiness
- - Add option to export ERU Readiness data
- - Update **Respond > Surge/Deployments** menu to include **Active Surge Deployments**
-
-- 9ed8181: Address feedbacks in [DREF superticket feedbacks](https://github.com/IFRCGo/go-web-app/issues/1816)
-
- - Make end date of operation readonly field in all DREF forms
- - Fix font and spacing issues in the DREF exports (caused by link text overflow)
- - Update styling of Risk and Security Considerations section to match that of Previous Operations
- - Update visibility condition of National Society Actions in Final Report export
-
-### Patch Changes
-
-- Updated dependencies [c26bda4]
- - @ifrc-go/ui@1.5.0
-
-## 7.14.0
-
-### Minor Changes
-
-- 18ccc85:
- - Update styling of vertical NavigationTab
- - Hide register URL in the T&C page for logged in user
- - Update styling of T&C page
- - Make the page responsive
- - Make sidebar sticky
- - Update url for [monty docs](https://github.com/IFRCGo/go-web-app/issues/1418#issuecomment-2422371363)
-- 8d3a7bd: Initiate shutdown for 3W
- - Remove "Submit 3W Projects" from the menu Prepare > Global 3W projects
- - Rename "Global 3W Projects" to "Programmatic Partnerships" in Prepare menu
- - Update global 3W page
- - Update title and description for Programmatic Partnerships
- - Remove all the contents related to 3W
- - Replace contents in various places with project shutdown message
- - Regional 3W tab
- - 3W Projects section in Accounts > My Form > 3W
- - Projects tab in Country > Ongoing Activities
- - All Projects page
- - New, edit 3W project form
- - View 3W project page
- - Remove NS Activities section in Country > NS overview > NS Activities page
- - Remove Projects section from search results page
-
-### Patch Changes
-
-- Updated dependencies [18ccc85]
- - @ifrc-go/ui@1.4.0
-
-## 7.13.0
-
-### Minor Changes
-
-- 69fd74f: - Update page title for Emergency to include the name
- - Update page title of Flash update to include the name
- - Fix the user registration link in the Terms & Condition page
-- 680c673: Implement [DREF Superticket 2.0](https://github.com/IFRCGo/go-web-app/issues/1695)
-
-### Patch Changes
-
-- fe4b727: - Upgrade pnpm to v10.6.1
- - Cleanup Dockerfile
- - Configure depandabot to track other dependencies updates
- - Upgrade eslint
- - Use workspace protocol to reference workspace packages
-- 9f20016: Enable user to edit their position field in [#1647](https://github.com/IFRCGo/go-web-app/issues/1647)
-- ef15af1: Add secondary ordering in tables for rows with same date
-- Updated dependencies [fe4b727]
- - @ifrc-go/ui@1.3.1
-
-## 7.12.1
-
-### Patch Changes
-
-- Fix nullable type of assessment for NS capacity
-
-## 7.12.0
-
-### Minor Changes
-
-- f766bc7: Add link to IFRC Survey Designer in the tools section under learn menu
-
-### Patch Changes
-
-- 7f51854: - Surge CoS: Health fix
-- 3a1cac8: Hide focal point details based on user permissions
-- 43d3bf1: - Add Surge CoS Administration section
- - Add Surge CoS Faecal Sludge Management (FSM) section
- - Update Surge CoS IT&T section
- - Update Surge CoS Basecamp section (as OSH)
-
-## 7.11.1
-
-### Patch Changes
-
-- ff426cd: Use current language for field report title generation
-
-## 7.11.0
-
-### Minor Changes
-
-- Field report number generation: Change only when the country or event changes
-
-## 7.10.1
-
-### Patch Changes
-
-- 14567f1: Improved tables by adding default and second-level ordering in [#1633](https://github.com/IFRCGo/go-web-app/issues/1633)
-
- - Appeal Documents table, `emergencies/{xxx}/reports` page
- - Recent Emergencies in Regions – All Appeals table
- - All Deployed Personnel – Default sorting (filters to be added)
- - Deployed ERUs – Changed filter title
- - Key Documents tables in Countries
- - Response documents
- - Main page – Active Operations table
- - The same `AppealsTable` is used in:
- - Active Operations in Regions
- - Previous Operations in Countries
-
-- 78d25b2:
-
- - Update on the ERU MHPSS Module in the Catalogue of Services in [#1648](https://github.com/IFRCGo/go-web-app/issues/1648)
- - Update on a PER role profile in [#1648](https://github.com/IFRCGo/go-web-app/issues/1648)
- - Update link to the IM Technical Competency Framework in [#1483](https://github.com/IFRCGo/go-web-app/issues/1483)
-
-- 44623a7: Undo DREF Imminent changes
-- b57c453: Show the number of people assisted in the DREF Final Report export in [#1665](https://github.com/IFRCGo/go-web-app/issues/1665)
-
-## 7.10.0
-
-### Minor Changes
-
-- 4f89133: Fix DREF PGA export styling
-
-## 7.9.0
-
-### Minor Changes
-
-- 7927522: Update Imminent DREF Application in [#1455](https://github.com/IFRCGo/go-web-app/issues/1455)
-
- - Hide sections/fields
- - Rename sections/fields
- - Remove sections/fields
- - Reflect changes in the PDF export
-
-### Patch Changes
-
-- Updated dependencies [4032688]
- - @ifrc-go/ui@1.3.0
-
-## 7.8.1
-
-### Patch Changes
-
-- 9c51dee: Remove `summary` field from field report form
-- Update @ifrc-go/ui version
-
-## 7.8.0
-
-### Minor Changes
-
-- 4843cb0: Added Operational Learning 2.0
-
- - Key Figures Overview in Operational Learning
- - Map View for Operational Learning
- - Learning by Sector Bar Chart
- - Learning by Region Bar Chart
- - Sources Over Time Line Chart
- - Methodology changes for the prioritization step
- - Added an option to regenerate cached summaries
- - Summary post-processing and cleanup
- - Enabled MDR code search in admin
-
-### Patch Changes
-
-- f96e177: Move field report/emergency title generation logic from client to server
-- e85fc32: Integrate `crate-ci/typos` for code spell checking
-- 4cdea2b: Add redirection logic for `preparedness#operational-learning`
-- 9a50443: Add appeal doc type for appeal documents
-- 817d56d: Display properly formatted appeal type in search results
-- 1159fa4: Redirect obsolete URLs to recent ones
- - redirect `/reports/` to `/field-reports/`
- - redirect `/deployments/` -> `/surge/overview`
-- Updated dependencies [4843cb0]
- - @ifrc-go/ui@1.2.3
-
-## 7.7.0
-
-### Minor Changes
-
-- 3258b96: Add local unit validation workflow
-
-### Patch Changes
-
-- Updated dependencies [c5a446f]
- - @ifrc-go/ui@1.2.2
-
-## 7.6.6
-
-### Patch Changes
-
-- 8cdc946: Hide Local unit contact details on the list view for logged in users in [#1485](https://github.com/ifRCGo/go-web-app/issues/1485)
- Update `tinymce-react` plugin to the latest version and enabled additional plugins, including support for lists in [#1481](https://github.com/ifRCGo/go-web-app/issues/1481)
-- ecca810: Replace the from-communication-copied text of CoS Health header
-- 7cf2514: Prioritize GDACS as the Primary Source for Imminent Risk Watch in [#1547](https://github.com/IFRCGo/go-web-app/issues/1547)
-- 8485076: Add Organization type and Learning type filter in Operational learning in [#1469](https://github.com/IFRCGo/go-web-app/issues/1469)
-- 766d98d: Auto append https:// for incomplete URLs in [#1505](https://github.com/IFRCGo/go-web-app/issues/1505)
-
-## 7.6.5
-
-### Patch Changes
-
-- 478e73b: Update labels for severity control in Imminent Risk Map
- Update navigation for the events in Imminent Risk Map
- Fix issue displayed when opening a DREF import template
- Fix submission issue when importing a DREF import file
-- f82f846: Update Health Section in Catalogue of Surge Services
-- ade84aa: Display ICRC Presence
- - Display ICRC presence across partner countries
- - Highlight key operational countries
-
-## 7.6.4
-
-### Patch Changes
-
-- d85f64d: Update Imminent Events
-
- - Hide WFP ADAM temporarily from list sources
- - Show exposure control for cyclones from GDACS only
-
-## 7.6.3
-
-### Patch Changes
-
-- 7bbf3d2: Update key insights disclaimer text in Ops. Learning
-- 0e40681: Update FDRS data in Country / Context and Structure / NS indicators
-
- - Add separate icon for each field for data year
- - Use separate icon for disaggregation
- - Update descriptions on dref import template (more details on _Missing / to be implemented_ section in https://github.com/IFRCGo/go-web-app/pull/1434#issuecomment-2459034932)
-
-- Updated dependencies [801ec3c]
- - @ifrc-go/ui@1.2.1
-
-## 7.6.2
-
-### Patch Changes
-
-- 4fa6a36: Updated PER terminology and add PER logo in PER PDF export
-- 813e93f: Add link to GO UI storybook in resources page
-- 20dfeb3: Update DREF import template
- - Update guidance
- - Improve template stylings
- - Update message in error popup when import fails
-- 8a18ad8: Add beta tag, URL redirect, and link to old dashboard on Ops Learning
-
-## 7.6.1
-
-### Patch Changes
-
-- 7afaf34: Fix null event in appeal for operational learning
-
-## 7.6.0
-
-### Minor Changes
-
-- Add new Operational Learning Page
-
- - Add link to Operational Learning page under `Learn` navigation menu
- - Integrate LLM summaries for Operational Learning
-
-## 7.5.3
-
-### Patch Changes
-
-- d7f5f53: Revamp risk imminent events for cyclone
- - Visualize storm position, forecast uncertainty, track line and exposed area differently
- - Add option to toggle visibility of these different layers
- - Add severity legend for exposure
- - Update styling for items in event list
- - Update styling for event details page
-- 36a64fa: Integrate multi-select functionality in operational learning filters to allow selection of multiple filter items.
-- 894d00c: Add a new 404 page
-- 7757e54: Add an option to download excel import template for DREF (Response) which user can fill up and import.
-- a8d021d: Update resources page
- - Add a new video for LocalUnits
- - Update ordering of videos
-- aea512d: Prevent users from pasting images into rich text field
-- fd54657: Add Terms and Conditions page
-- bf55ccc: Add Cookie Policy page
-- df80c4f: Fix contact details in Field Report being always required when filled once
-- 81dc3bd: Added color mapping based on PER Area and Rating across all PER charts
-- Updated dependencies [dd92691]
-- Updated dependencies [d7f5f53]
-- Updated dependencies [fe6a455]
-- Updated dependencies [81dc3bd]
- - @ifrc-go/ui@1.2.0
-
-## 7.5.2
-
-### Patch Changes
-
-- 37bba31: Add collaboration guide
-
-## 7.5.1
-
-### Patch Changes
-
-- 2a5e4a1: Add Core Competency Framework link to Resources page in [#1331](https://github.com/IFRCGo/go-web-app/issues/1331)
-- 31eaa97: Add Health Mapping Report to Resources page in [#1331](https://github.com/IFRCGo/go-web-app/issues/1331)
-- 4192da1: - Local Units popup, view/edit mode improvements in [#1178](https://github.com/IFRCGo/go-web-app/issues/1178)
- - Remove ellipsize heading option in local units map popup
- - Local units title on popup are now clickable that opens up a modal to show details
- - Added an Edit button to the View Mode for users with edit permissions
- - Users will now see a **disabled grey button** when the content is already validated
-- 5c7ab88: Display the public visibility field report to public users in [#1743](https://github.com/IFRCGo/go-web-app/issues/1343)
-
-## 7.5.0
-
-### Minor Changes
-
-- 5845699: Clean up Resources page
-
-## 7.4.2
-
-### Patch Changes
-
-- d734e04: - Fix duplication volunteer label in the Field Report details
- - Fix rating visibility in the Country > NS Overview > Strategic priorities page
-
-## 7.4.1
-
-### Patch Changes
-
-- a4f77ab: Fetch and use latest available WorldBank data in [#571](https://github.com/IFRCGo/go-api/issues/2224)
-- ebf033a: Update Technical Competencies Link on the Cash page of the Catalogue of Surge Services in [#1290](https://github.com/IFRCGo/go-web-app/issues/1290)
-- 18d0dc9: Use `molnix status` to filter surge alerts in [#2208](https://github.com/IFRCGo/go-api/issues/2208)
-- b070c66: Check guest user permission for local units
-- 72df1f2: Add new drone icon for UAV team in [#1280](https://github.com/IFRCGo/go-web-app/issues/1280)
-- 2ff7940: Link version number to release notes on GitHub in [#1004](https://github.com/IFRCGo/go-web-app/issues/1004)
- Updated @ifrc-go/icons to v2.0.1
-- Updated dependencies [72df1f2]
- - @ifrc-go/ui@1.1.6
-
-## 7.4.0
-
-### Minor Changes
-
-- b6bd6aa: Implement Guest User Permission in [#1237](https://github.com/IFRCGo/go-web-app/issues/1237)
-
-## 7.3.13
-
-### Patch Changes
-
-- 453a397: - Update Local Unit map, table and form to match the updated design in [#1178](https://github.com/IFRCGo/go-web-app/issues/1178)
- - Add delete button in Local units table and form
- - Use filter prop in container and remove manual stylings
- - Update size of WikiLink to match height of other action items
- - Add error boundary to BaseMap component
-- Updated dependencies [453a397]
- - @ifrc-go/ui@1.1.5
-
-## 7.3.12
-
-### Patch Changes
-
-- ba6734e: Show admin labels in maps in different languages, potentially fixing [#1036](https://github.com/IFRCGo/go-web-app/issues/1036)
-
-## 7.3.11
-
-### Patch Changes
-
-- d9491a2: Fix appeals statistics calculation
-
-## 7.3.10
-
-### Patch Changes
-
-- 3508c83: Add missing validations in DREF forms
-- 3508c83: Fix region filter in All Appeals table
-- 073fa1e: Remove personal detail for focal point in local units table
-- b508475: Add June 2024 Catalogue of Surge Services Updates
-- 3508c83: Handle countries with no bounding box
-- d9491a2: Fix appeals based statistics calculation
-- Updated dependencies [073fa1e]
- - @ifrc-go/ui@1.1.4
-
-## 7.3.9
-
-### Patch Changes
-
-- 49f5410: - Reorder CoS list
- - Update texts in CoS strategic partnerships resource mobilisation
-
-## 7.3.8
-
-### Patch Changes
-
-- 478ab69: Hide contact information from IFRC Presence
-- 3fbe60f: Hide add/edit local units on production environment
-- 90678ed: Show Organization Type properly in Account Details page
-
-## 7.3.7
-
-### Patch Changes
-
-- 909a5e2: Fix Appeals table for Africa Region
-- 5a1ae43: Add presentation mode in local units map
-- 96120aa: Fix DREF exports margins and use consistent date format
-- 8a4f26d: Avoid crash on country pages for countries without bbox
-
-## 7.3.6
-
-### Patch Changes
-
-- 1b4b6df: Add local unit form
-- 2631a9f: Add office type and location information for IFRC delegation office
-- 2d7a6a5: - Enable ability to start PER in IFRC supported languages
- - Make PER forms `readOnly` in case of language mismatch
-- e4bf098: Fix incorrect statistics for past appeals of a country
-- Updated dependencies [0ab207d]
-- Updated dependencies [66151a7]
- - @ifrc-go/ui@1.1.3
-
-## 7.3.5
-
-### Patch Changes
-
-- 894a762: Fix seasonal risk score in regional and global risk watch
-
-## 7.3.4
-
-### Patch Changes
-
-- d368ada: Fix GNI per capita in country profile overview
-
-## 7.3.3
-
-### Patch Changes
-
-- 73e1966: Update CoS pages as mentioned in #913
-- 179a073: Show all head of delegation under IFRC Presence
-- 98d6b62: Fix region operation map to apply filter for Africa
-
-## 7.3.2
-
-### Patch Changes
-
-- f83c12b: Show Local name when available and use English name as fallback for local units data
-
-## 7.3.1
-
-### Patch Changes
-
-- 7f0212b: Integrate mapbox street view for local units map
-- Updated dependencies [7f0212b]
- - @ifrc-go/ui@1.1.2
-
-## 7.3.0
-
-### Minor Changes
-
-- 0dffd52: Add table view in NS local units
-
-## 7.2.5
-
-### Patch Changes
-
-- 556766e: - Refetch token list after new token is created
- - Update link for terms and conditions for Montandon
-
-## 7.2.4
-
-### Patch Changes
-
-- 30eac3c: Add option to generate API token for Montandon in the user profile
-
-## 7.2.3
-
-### Patch Changes
-
-- Fix crash due to undefined ICRC presence in country page
-
-## 7.2.2
-
-### Patch Changes
-
-- - Update country risk page sources
- - Update CoS pages
-- Updated dependencies [a1c0554]
-- Updated dependencies [e9552b4]
- - @ifrc-go/ui@1.1.1
-
-## 7.2.1
-
-### Patch Changes
-
-- Remove personal identifiable information for local units
-
-## 7.2.0
-
-### Minor Changes
-
-- 9657d4b: Update country pages with appropriate source links
-- 66fa7cf: Show FDRS data retrieval year in NS indicators
-- b69e8e5: Update IFRC legal status link
-- 300250a: Show latest strategic plan of National Society under Strategic Priorities
-- 9657d4b: Add GO Wiki links for country page sections
-- b38d9d9: Improve overall styling of country pages
- - Make loading animation consistent across all pages
- - Make empty message consistent
- - Use ChartContainer and update usage of charting hooks
- - Update BaseMap to extend defaultMapOptions (instead of replacing it)
- - Add an option to provide popupClassName in MapPopup
-- 80be711: Rename `Supporting Partners` to `Partners`.
- - Update IFRC legal status link.
- - Update the name of the strategic priorities link to indicate that they were created by the National Society.
-- 176e01b: Simplify usage of PER question group in PER assessment form
- - Add min widths in account table columns
-
-## 7.1.5
-
-### Patch Changes
-
-- Updated dependencies
- - @ifrc-go/ui@1.0.0
diff --git a/go-web-app-develop/app/env.ts b/go-web-app-develop/app/env.ts
deleted file mode 100644
index 15b21e39f86e5effc692e283922162ccd961b39a..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/env.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { defineConfig, Schema } from '@julr/vite-plugin-validate-env';
-
-export default defineConfig({
- APP_TITLE: Schema.string(),
- APP_ENVIRONMENT: (key, value) => {
- // NOTE: APP_ENVIRONMENT_PLACEHOLDER is meant to be used with image builds
- // The value will be later replaced with the actual value
- const regex = /^production|staging|testing|alpha-\d+|development|APP_ENVIRONMENT_PLACEHOLDER$/;
- const valid = !!value && (value.match(regex) !== null);
- if (!valid) {
- throw new Error(`Value for environment variable "${key}" must match regex "${regex}", instead received "${value}"`);
- }
- if (value === 'APP_ENVIRONMENT_PLACEHOLDER') {
- console.warn(`Using ${value} for app environment. Make sure to not use this for builds without helm chart`)
- }
- return value as ('production' | 'staging' | 'testing' | `alpha-${number}` | 'development' | 'APP_ENVIRONMENT_PLACEHOLDER');
- },
- APP_API_ENDPOINT: Schema.string({ format: 'url', protocol: true, tld: false }),
- APP_ADMIN_URL: Schema.string.optional({ format: 'url', protocol: true, tld: false }),
- APP_MAPBOX_ACCESS_TOKEN: Schema.string(),
- APP_TINY_API_KEY: Schema.string(),
- APP_RISK_API_ENDPOINT: Schema.string({ format: 'url', protocol: true }),
- APP_SDT_URL: Schema.string.optional({ format: 'url', protocol: true, tld: false }),
- APP_SENTRY_DSN: Schema.string.optional(),
- APP_SENTRY_TRACES_SAMPLE_RATE: Schema.number.optional(),
- APP_SENTRY_REPLAYS_SESSION_SAMPLE_RATE: Schema.number.optional(),
- APP_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE: Schema.number.optional(),
- APP_GOOGLE_ANALYTICS_ID: Schema.string.optional(),
-});
diff --git a/go-web-app-develop/app/eslint.config.js b/go-web-app-develop/app/eslint.config.js
deleted file mode 100644
index dbe98504a293278684a903af8f11e4e565acaf6b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/eslint.config.js
+++ /dev/null
@@ -1,165 +0,0 @@
-import { FlatCompat } from '@eslint/eslintrc';
-import js from '@eslint/js';
-import json from "@eslint/json";
-import tseslint from "typescript-eslint";
-import process from 'process';
-
-const dirname = process.cwd();
-
-const compat = new FlatCompat({
- baseDirectory: dirname,
- resolvePluginsRelativeTo: dirname,
-});
-
-const appConfigs = compat.config({
- env: {
- node: true,
- browser: true,
- es2020: true,
- },
- root: true,
- extends: [
- 'airbnb',
- 'airbnb/hooks',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:react-hooks/recommended',
- ],
- parser: '@typescript-eslint/parser',
- parserOptions: {
- ecmaVersion: 'latest',
- sourceType: 'module',
- },
- plugins: [
- '@typescript-eslint',
- 'react-refresh',
- 'simple-import-sort',
- 'import-newlines'
- ],
- settings: {
- 'import/parsers': {
- '@typescript-eslint/parser': ['.ts', '.tsx']
- },
- 'import/resolver': {
- typescript: {
- project: [
- './tsconfig.json',
- ],
- },
- },
- },
- rules: {
- 'react-refresh/only-export-components': 'warn',
-
- 'no-unused-vars': 0,
- '@typescript-eslint/no-unused-vars': 1,
-
- 'no-use-before-define': 0,
- '@typescript-eslint/no-use-before-define': 1,
-
- 'no-shadow': 0,
- '@typescript-eslint/no-shadow': ['error'],
-
- '@typescript-eslint/consistent-type-imports': [
- 'warn',
- {
- disallowTypeAnnotations: false,
- fixStyle: 'inline-type-imports',
- prefer: 'type-imports',
- },
- ],
-
- 'import/no-extraneous-dependencies': [
- 'error',
- {
- devDependencies: [
- '**/*.test.{ts,tsx}',
- 'eslint.config.js',
- 'postcss.config.cjs',
- 'stylelint.config.cjs',
- 'vite.config.ts',
- ],
- optionalDependencies: false,
- },
- ],
-
- indent: ['error', 4, { SwitchCase: 1 }],
-
- 'import/no-cycle': ['error', { allowUnsafeDynamicCyclicDependency: true }],
-
- 'react/react-in-jsx-scope': 'off',
- 'camelcase': 'off',
-
- 'react/jsx-indent': ['error', 4],
- 'react/jsx-indent-props': ['error', 4],
- 'react/jsx-filename-extension': ['error', { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
-
- 'import/extensions': ['off', 'never'],
-
- 'react-hooks/rules-of-hooks': 'error',
- 'react-hooks/exhaustive-deps': 'warn',
-
- 'react/require-default-props': ['warn', { ignoreFunctionalComponents: true }],
- 'simple-import-sort/imports': 'warn',
- 'simple-import-sort/exports': 'warn',
- 'import-newlines/enforce': ['warn', 1]
- },
- overrides: [
- {
- files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
- rules: {
- 'simple-import-sort/imports': [
- 'error',
- {
- 'groups': [
- // side effect imports
- ['^\\u0000'],
- // packages `react` related packages come first
- ['^react', '^@?\\w'],
- // internal packages
- ['^#.+$'],
- // parent imports. Put `..` last
- // other relative imports. Put same-folder imports and `.` last
- ['^\\.\\.(?!/?$)', '^\\.\\./?$', '^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
- // style imports
- ['^.+\\.json$', '^.+\\.module.css$'],
- ]
- }
- ]
- }
- }
- ]
-}).map((conf) => ({
- ...conf,
- files: ['src/**/*.tsx', 'src/**/*.jsx', 'src/**/*.ts', 'src/**/*.js'],
- ignores: [
- "node_modules/",
- "build/",
- "coverage/",
- 'src/generated/types.ts'
- ],
-}));
-
-const otherConfig = {
- files: ['*.js', '*.ts', '*.cjs'],
- ...js.configs.recommended,
- ...tseslint.configs.recommended,
-};
-
-const jsonConfig = {
- files: ['**/*.json'],
- language: 'json/json',
- rules: {
- 'json/no-duplicate-keys': 'error',
- },
-};
-
-export default [
- {
- plugins: {
- json,
- },
- },
- ...appConfigs,
- otherConfig,
- jsonConfig,
-];
diff --git a/go-web-app-develop/app/index.html b/go-web-app-develop/app/index.html
deleted file mode 100644
index bb312224511aebc4fe7569dd61ba7c28148b5a8d..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/index.html
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
-
-
-
- %APP_TITLE%
-
-
-
-
-
-
-
-
-
-
-
-
-
- %APP_TITLE% loading...
-
-
-
-
-
diff --git a/go-web-app-develop/app/package.json b/go-web-app-develop/app/package.json
deleted file mode 100644
index b3f3790cb176aa814dbd99d55abfdbbd959ddc07..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/package.json
+++ /dev/null
@@ -1,119 +0,0 @@
-{
- "name": "go-web-app",
- "version": "7.21.0-beta.2",
- "type": "module",
- "private": true,
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/IFRCGo/go-web-app.git",
- "directory": "app"
- },
- "scripts": {
- "translatte": "tsx scripts/translatte/main.ts",
- "translatte:generate": "pnpm translatte generate-migration ../translationMigrations ./src/**/i18n.json ../packages/ui/src/**/i18n.json",
- "translatte:lint": "pnpm translatte lint ./src/**/i18n.json ../packages/ui/src/**/i18n.json",
- "initialize:type": "mkdir -p generated/ && pnpm initialize:type:go-api && pnpm initialize:type:risk-api",
- "initialize:type:go-api": "test -f ./generated/types.ts && true || cp types.stub.ts ./generated/types.ts",
- "initialize:type:risk-api": "test -f ./generated/riskTypes.ts && true || cp types.stub.ts ./generated/riskTypes.ts",
- "generate:type": "pnpm generate:type:go-api && pnpm generate:type:risk-api",
- "generate:type:go-api": "dotenv -- cross-var openapi-typescript \"%APP_API_ENDPOINT%api-docs/\" -o ./generated/types.ts --alphabetize",
- "generate:type:risk-api": "dotenv -- cross-var openapi-typescript \"%APP_RISK_API_ENDPOINT%api-docs/\" -o ./generated/riskTypes.ts --alphabetize",
- "prestart": "pnpm initialize:type",
- "start": "pnpm -F @ifrc-go/ui build && vite",
- "prebuild": "pnpm initialize:type",
- "build": "pnpm -F @ifrc-go/ui build && vite build",
- "preview": "vite preview",
- "pretypecheck": "pnpm initialize:type",
- "typecheck": "tsc",
- "prelint:js": "pnpm initialize:type",
- "lint:js": "eslint src",
- "lint:css": "stylelint \"./src/**/*.css\"",
- "lint:translation": "pnpm translatte:lint",
- "lint": "pnpm lint:js && pnpm lint:css && pnpm lint:translation",
- "lint:fix": "pnpm lint:js --fix && pnpm lint:css --fix",
- "test": "vitest",
- "test:coverage": "vitest run --coverage",
- "surge:deploy": "branch=$(git rev-parse --symbolic-full-name --abbrev-ref HEAD); branch=$(echo $branch | tr ./ -); cp ../build/index.html ../build/200.html; surge -p ../build/ -d https://ifrc-go-$branch.surge.sh",
- "surge:teardown": "branch=$(git rev-parse --symbolic-full-name --abbrev-ref HEAD); branch=$(echo $branch | tr ./ -); surge teardown https://ifrc-go-$branch.surge.sh"
- },
- "dependencies": {
- "@ifrc-go/icons": "^2.0.1",
- "@ifrc-go/ui": "workspace:^",
- "@sentry/react": "^7.81.1",
- "@tinymce/tinymce-react": "^5.1.1",
- "@togglecorp/fujs": "^2.1.1",
- "@togglecorp/re-map": "^0.3.0",
- "@togglecorp/toggle-form": "^2.0.4",
- "@togglecorp/toggle-request": "^1.0.0-beta.3",
- "@turf/bbox": "^6.5.0",
- "@turf/buffer": "^6.5.0",
- "exceljs": "^4.3.0",
- "file-saver": "^2.0.5",
- "html-to-image": "^1.11.11",
- "mapbox-gl": "^1.13.0",
- "papaparse": "^5.4.1",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-router-dom": "^6.18.0",
- "sanitize-html": "^2.10.0"
- },
- "devDependencies": {
- "@eslint/eslintrc": "^3.1.0",
- "@eslint/js": "^9.20.0",
- "@eslint/json": "^0.5.0",
- "@julr/vite-plugin-validate-env": "^1.0.1",
- "@types/file-saver": "^2.0.5",
- "@types/mapbox-gl": "^1.13.0",
- "@types/node": "^20.11.6",
- "@types/papaparse": "^5.3.8",
- "@types/react": "^18.0.28",
- "@types/react-dom": "^18.0.11",
- "@types/sanitize-html": "^2.9.0",
- "@types/yargs": "^17.0.32",
- "@typescript-eslint/eslint-plugin": "^8.11.0",
- "@typescript-eslint/parser": "^8.11.0",
- "@vitejs/plugin-react-swc": "^3.5.0",
- "@vitest/coverage-v8": "^1.2.2",
- "autoprefixer": "^10.4.14",
- "cross-var": "^1.1.0",
- "dotenv-cli": "^7.4.2",
- "eslint": "^9.20.1",
- "eslint-config-airbnb": "^19.0.4",
- "eslint-import-resolver-typescript": "^3.6.3",
- "eslint-plugin-import": "^2.31.0",
- "eslint-plugin-import-exports-imports-resolver": "^1.0.1",
- "eslint-plugin-import-newlines": "^1.3.4",
- "eslint-plugin-jsx-a11y": "^6.10.1",
- "eslint-plugin-react": "^7.37.4",
- "eslint-plugin-react-hooks": "^5.0.0",
- "eslint-plugin-react-refresh": "^0.4.13",
- "eslint-plugin-simple-import-sort": "^12.1.1",
- "fast-glob": "^3.3.2",
- "happy-dom": "^9.18.3",
- "openapi-typescript": "6.5.5",
- "postcss": "^8.5.3",
- "postcss-nested": "^7.0.2",
- "postcss-normalize": "^13.0.1",
- "postcss-preset-env": "^10.1.5",
- "rollup-plugin-visualizer": "^5.9.0",
- "stylelint": "^16.17.0",
- "stylelint-config-concentric": "^2.0.2",
- "stylelint-config-recommended": "^15.0.0",
- "stylelint-value-no-unknown-custom-properties": "^6.0.1",
- "surge": "^0.23.1",
- "ts-md5": "^1.3.1",
- "tsx": "^4.7.2",
- "typescript": "^5.5.2",
- "typescript-eslint": "^8.26.0",
- "vite": "^5.0.10",
- "vite-plugin-checker": "^0.7.0",
- "vite-plugin-compression2": "^0.11.0",
- "vite-plugin-radar": "^0.9.2",
- "vite-plugin-svgr": "^4.2.0",
- "vite-plugin-webfont-dl": "^3.9.4",
- "vite-tsconfig-paths": "^4.2.2",
- "vitest": "^1.2.2",
- "yargs": "^17.7.2"
- }
-}
diff --git a/go-web-app-develop/app/postcss.config.cjs b/go-web-app-develop/app/postcss.config.cjs
deleted file mode 100644
index c1a23f7496e4e16cf1f98ce8dee7885f81b8617c..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/postcss.config.cjs
+++ /dev/null
@@ -1,8 +0,0 @@
-module.exports = {
- plugins: [
- require('postcss-preset-env'),
- require('postcss-nested'),
- require('postcss-normalize'),
- require('autoprefixer'),
- ],
-};
diff --git a/go-web-app-develop/app/public/go-icon.svg b/go-web-app-develop/app/public/go-icon.svg
deleted file mode 100644
index 94198ca8d673c10698724c92037ba039d00f74cc..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/public/go-icon.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/go-web-app-develop/app/scripts/translatte/README.md b/go-web-app-develop/app/scripts/translatte/README.md
deleted file mode 100644
index 4e8b1db69c8fa96b3439f2bd53318bc27a6a64a4..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/README.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# translatte
-
-A simple script to synchronize translations in source code to translations in
-server
-
-## Usecase
-
-### Generating migrations
-
-When adding a new feature or updating existing feature or removing an
-existing feature on the codebase, we may need to update the strings used
-in the application.
-
-Developers can change the translations using their preferred choice of editor.
-
-Once all of the changes have been made, we can generate a migration file for the translations using:
-
-```bash
-pnpm translatte generate-migration ./src/translationMigrations ./src/**/i18n.json
-```
-
-Once the migration file has been created, the migration file can be committed to the VCS.
-
-### Applying migrations
-
-When we are deploying the changes to the server, we will need to update
-the strings in the server.
-
-We can generate the new set of strings for the server using:
-
-```bash
-pnpm translatte apply-migrations ./src/translationMigrations --last-migration "name_of_last_migration" --source "strings_json_from_server.json" --destination "new_strings_json_for_server.json"
-```
-
-### Merge migrations
-
-Once the migrations are applied to the strings in the server, we can merge the migrations into a single file.
-
-To merge migrations, we can run the following command:
-
-```bash
-pnpm translatte merge-migrations ./src/translationMigrations --from 'initial_migration.json' --to 'final_migration.json'
-```
-
-### Checking migrations
-
-We can use the following command to check for valid migrations:
-
-```bash
-pnpm translatte lint ./src/**/i18n.json
-```
-
-### Listing migrations
-
-We can use the following command to list all migrations:
-
-```bash
-pnpm translatte list-migrations ./src/translationMigrations
-```
diff --git a/go-web-app-develop/app/scripts/translatte/commands/applyMigrations.test.ts b/go-web-app-develop/app/scripts/translatte/commands/applyMigrations.test.ts
deleted file mode 100644
index 735dbea4cdca797da16b1d3140e5c60894f7e68c..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/applyMigrations.test.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import { expect } from 'vitest';
-import { mkdirSync } from 'fs';
-import { join } from 'path';
-
-import { testWithTmpDir } from '../testHelpers';
-import {
- writeFilePromisify,
- readJsonFilesContents,
-} from '../utils';
-import {
- migrationContent1,
- migrationContent2,
- migrationContent3,
- migrationContent4,
- migrationContent5,
- migrationContent6,
-
- strings1,
- strings2,
-} from '../mockData';
-import applyMigrations from './applyMigrations';
-import { SourceFileContent } from '../types';
-
-testWithTmpDir('test applyMigrations with no data in server', async ({ tmpdir }) => {
- mkdirSync(join(tmpdir, 'migrations'));
- const migrations = [
- { name: '000001-1000000000000.json', content: migrationContent1 },
- { name: '000002-1000000000000.json', content: migrationContent2 },
- { name: '000003-1000000000000.json', content: migrationContent3 },
- { name: '000004-1000000000000.json', content: migrationContent4 },
- { name: '000005-1000000000000.json', content: migrationContent5 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'migrations', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(migrations);
-
- mkdirSync(join(tmpdir, 'strings'));
-
- const emptySourceFile: SourceFileContent = {
- last_migration: undefined,
- strings: [],
- };
- await writeFilePromisify(
- join(tmpdir, 'strings', 'before.json'),
- JSON.stringify(emptySourceFile),
- 'utf8',
- );
-
- await applyMigrations(
- tmpdir,
- join(tmpdir, 'strings', 'before.json'),
- join(tmpdir, 'strings', 'after.json'),
- 'migrations',
- ['np'],
- undefined,
- false,
- );
-
- const newSourceFiles = await readJsonFilesContents([
- join(tmpdir, 'strings', 'after.json'),
- ]);
- const newSourceFileContent = newSourceFiles[0].content;
-
- expect(newSourceFileContent).toEqual(strings1)
-});
-
-testWithTmpDir('test applyMigrations with data in server', async ({ tmpdir }) => {
- mkdirSync(join(tmpdir, 'migrations'));
- const migrations = [
- { name: '000006-1000000000000.json', content: migrationContent6 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'migrations', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(migrations);
-
- mkdirSync(join(tmpdir, 'strings'));
-
- await writeFilePromisify(
- join(tmpdir, 'strings', 'before.json'),
- JSON.stringify(strings1),
- 'utf8',
- );
-
- await applyMigrations(
- tmpdir,
- join(tmpdir, 'strings', 'before.json'),
- join(tmpdir, 'strings', 'after.json'),
- 'migrations',
- ['np'],
- undefined,
- false,
- );
-
- const newSourceFiles = await readJsonFilesContents([
- join(tmpdir, 'strings', 'after.json'),
- ]);
- const newSourceFileContent = newSourceFiles[0].content;
-
- expect(newSourceFileContent).toEqual(strings2)
-});
diff --git a/go-web-app-develop/app/scripts/translatte/commands/applyMigrations.ts b/go-web-app-develop/app/scripts/translatte/commands/applyMigrations.ts
deleted file mode 100644
index dbcb8b39e512be836b3c23818bd2d2ade3bbbff3..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/applyMigrations.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import { Md5 } from 'ts-md5';
-import { listToMap, isDefined, unique } from '@togglecorp/fujs';
-import { isAbsolute, join, basename } from 'path';
-import {
- readSource,
- getMigrationFilesAttrs,
- readMigrations,
- writeFilePromisify,
-} from '../utils';
-import { merge } from './mergeMigrations';
-import {
- SourceFileContent,
- MigrationFileContent,
- SourceStringItem,
-} from '../types';
-
-function apply(
- strings: SourceStringItem[],
- migrationActions: MigrationFileContent['actions'],
- languages: string[],
-): SourceStringItem[] {
- const stringsMapping = listToMap(
- strings,
- (item) => `${item.page_name}:${item.key}:${item.language}` as string,
- (item) => item,
- );
-
- const newMapping: {
- [key: string]: SourceStringItem | null;
- } = { };
-
- unique(['en', ...languages]).forEach((language) => {
- migrationActions.forEach((action) => {
- const isSourceLanguage = language === 'en';
- const key = `${action.namespace}:${action.key}:${language}`;
- if (action.action === 'add') {
- const hash = Md5.hashStr(action.value);
-
- const prevValue = stringsMapping[key];
- // NOTE: we are comparing hash instead of value so that this works for source language as well as other languages
- if (prevValue && prevValue.hash !== hash) {
- throw `Add: We already have string with different value for namespace '${action.namespace}' and key '${action.key}'`;
- }
-
- if (newMapping[key]) {
- throw `Add: We already have string for namespace '${action.namespace}' and key '${action.key}' in migration`;
- }
-
- newMapping[key] = {
- hash,
- key: action.key,
- page_name: action.namespace,
- language,
- value: isSourceLanguage
- ? action.value
- : '',
- };
- } else if (action.action === 'remove') {
- // NOTE: We can add or move string so we might have value in newMapping
- if (!newMapping[key]) {
- newMapping[key] = null;
- }
- } else {
- const prevValue = stringsMapping[key];
- if (!prevValue) {
- throw `Update: We do not have string with namespace '${action.namespace}' and key '${action.key}'`;
- }
-
- const newKey = action.newKey ?? prevValue.key;
- const newNamespace = action.newNamespace ?? prevValue.page_name;
- const newValue = isSourceLanguage
- ? action.newValue ?? prevValue.value
- : prevValue.value;
- const newHash = isSourceLanguage
- ? Md5.hashStr(newValue)
- : prevValue.hash;
-
- const newCanonicalKey = `${newNamespace}:${newKey}:${language}`;
-
-
- // NOTE: remove the old key and add new key
- if (!newMapping[key]) {
- newMapping[key] = null;
- }
-
- const newItem = {
- hash: newHash,
- key: newKey,
- page_name: newNamespace,
- language,
- value: newValue,
- }
-
- if (newMapping[newCanonicalKey]) {
- throw `Update: We already have string for namespace '${action.namespace}' and key '${action.key}' in migration`;
- }
- newMapping[newCanonicalKey] = newItem;
- }
- });
- });
-
- const finalMapping: typeof newMapping = {
- ...stringsMapping,
- ...newMapping,
- };
-
- return Object.values(finalMapping)
- .filter(isDefined)
- .sort((foo, bar) => (
- foo.page_name.localeCompare(bar.page_name)
- || foo.key.localeCompare(bar.key)
- || foo.language.localeCompare(bar.language)
- ))
-}
-
-async function applyMigrations(
- projectPath: string,
- sourceFileName: string,
- destinationFileName: string,
- migrationFilePath: string,
- languages: string[],
- from: string | undefined,
- dryRun: boolean | undefined,
-) {
- const sourcePath = isAbsolute(sourceFileName)
- ? sourceFileName
- : join(projectPath, sourceFileName)
- const sourceFile = await readSource(sourcePath)
-
- const migrationFilesAttrs = await getMigrationFilesAttrs(projectPath, migrationFilePath);
- const selectedMigrationFilesAttrs = from
- ? migrationFilesAttrs.filter((item) => (item.migrationName > from))
- : migrationFilesAttrs;
-
- console.info(`Found ${selectedMigrationFilesAttrs.length} migration files`);
-
- if (selectedMigrationFilesAttrs.length < 1) {
- throw 'There should be at least 1 migration file';
- }
-
- const selectedMigrations = await readMigrations(
- selectedMigrationFilesAttrs.map((migration) => migration.fileName),
- );
-
- const lastMigration = selectedMigrations[selectedMigrations.length - 1];
-
- const mergedMigrationActions = merge(
- selectedMigrations.map((migration) => migration.content),
- );
-
- const outputSourceFileContent: SourceFileContent = {
- ...sourceFile.content,
- last_migration: basename(lastMigration.file),
- strings: apply(
- sourceFile.content.strings,
- mergedMigrationActions,
- languages,
- ),
- };
-
- const destinationPath = isAbsolute(destinationFileName)
- ? destinationFileName
- : join(projectPath, destinationFileName)
-
- if (dryRun) {
- console.info(`Creating file '${destinationPath}'`);
- console.info(outputSourceFileContent);
- } else {
- await writeFilePromisify(
- destinationPath,
- JSON.stringify(outputSourceFileContent, null, 4),
- 'utf8',
- );
- }
-}
-
-export default applyMigrations;
diff --git a/go-web-app-develop/app/scripts/translatte/commands/exportMigration.ts b/go-web-app-develop/app/scripts/translatte/commands/exportMigration.ts
deleted file mode 100644
index 6572868a188ada0dff7d98ea40efe32d9869fb24..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/exportMigration.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import xlsx from 'exceljs';
-
-import { readMigrations } from '../utils';
-import { isNotDefined } from '@togglecorp/fujs';
-
-async function exportMigration(
- migrationFilePath: string,
- exportFileName: string,
-) {
- const migrations = await readMigrations(
- [migrationFilePath]
- );
-
- const actions = migrations[0].content.actions;
- const workbook = new xlsx.Workbook();
- const now = new Date();
- workbook.created = now;
-
- const yyyy = now.getFullYear();
- const mm = (now.getMonth() + 1).toString().padStart(2, '0');
- const dd = now.getDate().toString().padStart(2, '0');
- const worksheet = workbook.addWorksheet(
- `${yyyy}-${mm}-${dd}`
- );
-
- worksheet.columns = [
- { header: 'Namespace', key: 'namespace' },
- { header: 'Key', key: 'key' },
- { header: 'EN', key: 'en' },
- { header: 'FR', key: 'fr' },
- { header: 'ES', key: 'es' },
- { header: 'AR', key: 'ar' },
- ]
-
- actions.forEach((actionItem) => {
- if (actionItem.action === 'remove') {
- return;
- }
-
- if (actionItem.action === 'update' && isNotDefined(actionItem.newValue)) {
- return;
- }
-
- const value = actionItem.action === 'update'
- ? actionItem.newValue
- : actionItem.value;
-
- worksheet.addRow({
- namespace: actionItem.namespace,
- key: actionItem.key,
- en: value,
- });
- });
-
- const fileName = isNotDefined(exportFileName)
- ? `go-strings-${yyyy}-${mm}-${dd}`
- : exportFileName;
-
- await workbook.xlsx.writeFile(`${fileName}.xlsx`);
-}
-
-export default exportMigration;
diff --git a/go-web-app-develop/app/scripts/translatte/commands/generateMigration.test.ts b/go-web-app-develop/app/scripts/translatte/commands/generateMigration.test.ts
deleted file mode 100644
index 680a61d087e7e0b5208dc8a6a3db30c3768ec193..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/generateMigration.test.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { expect } from 'vitest';
-import { mkdirSync } from 'fs';
-import { join } from 'path';
-
-import generateMigration from './generateMigration';
-import { testWithTmpDir } from '../testHelpers';
-import { writeFilePromisify, readMigrations } from '../utils';
-import {
- migrationContent1,
- migrationContent2,
- migrationContent3,
- migrationContent4,
- migrationContent5,
- loginContent,
- registerContent,
- updatedLoginContent,
- updatedRegisterContent,
- migrationContent6,
-} from '../mockData';
-
-
-testWithTmpDir('test generateMigration with no change', async ({ tmpdir }) => {
- mkdirSync(join(tmpdir, 'migrations'));
- const migrations = [
- { name: '000001-1000000000000.json', content: migrationContent1 },
- { name: '000002-1000000000000.json', content: migrationContent2 },
- { name: '000003-1000000000000.json', content: migrationContent3 },
- { name: '000004-1000000000000.json', content: migrationContent4 },
- { name: '000005-1000000000000.json', content: migrationContent5 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'migrations', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(migrations);
-
- mkdirSync(join(tmpdir, 'src'));
- const translations = [
- { name: 'home.i18n.json', content: loginContent },
- { name: 'register.i18n.json', content: registerContent },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'src', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(translations);
-
- await expect(
- () => generateMigration(
- tmpdir,
- 'migrations',
- 'src/**/*.i18n.json',
- new Date().getTime(),
- false,
- ),
- ).rejects.toThrow('Nothing to do');
-});
-
-testWithTmpDir('test generateMigration with change', async ({ tmpdir }) => {
- mkdirSync(join(tmpdir, 'migrations'));
- const migrations = [
- { name: '000001-1000000000000.json', content: migrationContent1 },
- { name: '000002-1000000000000.json', content: migrationContent2 },
- { name: '000003-1000000000000.json', content: migrationContent3 },
- { name: '000004-1000000000000.json', content: migrationContent4 },
- { name: '000005-1000000000000.json', content: migrationContent5 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'migrations', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(migrations);
-
- mkdirSync(join(tmpdir, 'src'));
-
- const translations = [
- { name: 'home.i18n.json', content: updatedLoginContent },
- { name: 'register.i18n.json', content: updatedRegisterContent },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'src', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(translations);
-
- const timestamp = new Date().getTime();
-
- await generateMigration(
- tmpdir,
- 'migrations',
- 'src/**/*.i18n.json',
- timestamp,
- false,
- );
-
- const generatedMigrations = await readMigrations([
- join(tmpdir, 'migrations', `000006-${timestamp}.json`)
- ]);
- const generatedMigrationContent = generatedMigrations[0].content;
-
- expect(generatedMigrationContent).toEqual(migrationContent6)
-});
diff --git a/go-web-app-develop/app/scripts/translatte/commands/generateMigration.ts b/go-web-app-develop/app/scripts/translatte/commands/generateMigration.ts
deleted file mode 100644
index f42e6525d56f426f093ab3406b00e004a66dc9cd..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/generateMigration.ts
+++ /dev/null
@@ -1,195 +0,0 @@
-import { Md5 } from 'ts-md5';
-import { join, isAbsolute } from 'path';
-
-import {
- writeFilePromisify,
- oneOneMapping,
- readTranslations,
- getTranslationFileNames,
- getMigrationFilesAttrs,
- readMigrations,
- oneOneMappingNonUnique,
-} from '../utils';
-import { MigrationActionItem, MigrationFileContent } from '../types';
-import { merge } from './mergeMigrations';
-
-function getCombinedKey(key: string, namespace: string) {
- return `${namespace}:${key}`;
-}
-
-type StateItem = {
- filename?: string;
- namespace: string;
- key: string;
- value: string;
-}
-
-// FIXME: The output should be stable
-function generateMigration(
- prevState: StateItem[],
- currentState: StateItem[],
-): MigrationActionItem[] {
- /*
- console.info('prevState length', prevState.length);
- console.info('currentState length', currentState.length);
- console.info('Total change', Math.abs(prevState.length - currentState.length));
- */
-
- const {
- // Same, key, namespace and same value
- validCommonItems: identicalStateItems,
-
- // Same, key, namespace but different value
- invalidCommonItems: valueUpdatedStateItems,
-
- // items with different key or namespace or both
- prevStateRemainder: potentiallyRemovedStateItems,
-
- // items with different key or namespace or both
- currentStateRemainder: potentiallyAddedStateItems,
- } = oneOneMapping(
- prevState,
- currentState,
- ({ key, namespace }) => getCombinedKey(key, namespace),
- (prev, current) => prev.value === current.value,
- );
-
- console.info(`Unchanged strings: ${identicalStateItems.length}`)
- console.info(`Value updated strings: ${valueUpdatedStateItems.length}`)
-
- console.info(`Potentially removed: ${potentiallyRemovedStateItems.length}`)
- console.info(`Potentially added: ${potentiallyAddedStateItems.length}`)
-
- const {
- commonItems: namespaceUpdatedStateItems,
- prevStateRemainder: potentiallyRemovedStateItemsAfterNamespaceChange,
- currentStateRemainder: potentiallyAddedStateItemsAfterNamespaceChange,
- } = oneOneMappingNonUnique(
- potentiallyRemovedStateItems,
- potentiallyAddedStateItems,
- (item) => getCombinedKey(item.key, Md5.hashStr(item.value)),
- );
-
- const {
- commonItems: keyUpdatedStateItems,
- prevStateRemainder: removedStateItems,
- currentStateRemainder: addedStateItems,
- } = oneOneMappingNonUnique(
- potentiallyRemovedStateItemsAfterNamespaceChange,
- potentiallyAddedStateItemsAfterNamespaceChange,
- (item) => getCombinedKey(item.namespace, Md5.hashStr(item.value)),
- );
-
- console.info(`Namespace updated strings: ${namespaceUpdatedStateItems.length}`)
- console.info(`Added strings: ${addedStateItems.length}`)
- console.info(`Removed strings: ${removedStateItems.length}`)
-
- return [
- ...valueUpdatedStateItems.map(({ prevStateItem, currentStateItem }) => ({
- action: 'update' as const,
- key: prevStateItem.key,
- namespace: prevStateItem.namespace,
- newValue: currentStateItem.value,
- })),
- ...namespaceUpdatedStateItems.map(({ prevStateItem, currentStateItem }) => ({
- action: 'update' as const,
- key: prevStateItem.key,
- namespace: prevStateItem.namespace,
- newNamespace: currentStateItem.namespace,
- })),
- ...keyUpdatedStateItems.map(({ prevStateItem, currentStateItem }) => ({
- action: 'update' as const,
- key: prevStateItem.key,
- newKey: currentStateItem.key,
- namespace: prevStateItem.namespace,
- })),
- ...addedStateItems.map((item) => ({
- action: 'add' as const,
- key: item.key,
- namespace: item.namespace,
- value: item.value,
- })),
- ...removedStateItems.map((item) => ({
- action: 'remove' as const,
- key: item.key,
- namespace: item.namespace,
- })),
- ].sort((foo, bar) => (
- foo.namespace.localeCompare(bar.namespace)
- || foo.action.localeCompare(bar.action)
- || foo.key.localeCompare(bar.key)
- ));
-}
-
-async function generate(
- projectPath: string,
- migrationFilePath: string,
- translationFileName: string | string[],
- timestamp: number,
- dryRun: boolean | undefined,
-) {
- const migrationFilesAttrs = await getMigrationFilesAttrs(projectPath, migrationFilePath);
- const selectedMigrationFilesAttrs = migrationFilesAttrs;
- console.info(`Found ${selectedMigrationFilesAttrs.length} migration files`);
- const selectedMigrations = await readMigrations(
- selectedMigrationFilesAttrs.map((migration) => migration.fileName),
- );
- const mergedMigrationActions = merge(
- selectedMigrations.map((migration) => migration.content),
- );
-
- const serverState: StateItem[] = mergedMigrationActions.map((item) => {
- if (item.action !== 'add') {
- throw `The action should be "add" but found "${item.action}"`;
- }
- return {
- filename: undefined,
- namespace: item.namespace,
- key: item.key,
- value: item.value,
- }
- });
- const translationFiles = await getTranslationFileNames(
- projectPath,
- Array.isArray(translationFileName) ? translationFileName : [translationFileName],
- );
- const { translations } = await readTranslations(translationFiles);
- const fileState = translations.map((item) => ({
- ...item,
- }));
-
- const migrationActionItems = generateMigration(
- serverState,
- fileState,
- );
-
- if (migrationActionItems.length <= 0) {
- throw 'Nothing to do';
- }
-
- const lastMigration = migrationFilesAttrs[migrationFilesAttrs.length - 1];
-
- const migrationContent: MigrationFileContent = {
- parent: lastMigration?.migrationName,
- actions: migrationActionItems,
- }
-
- const num = String(Number(lastMigration?.num ?? '000000') + 1).padStart(6, '0');
-
- const outputMigrationFile = isAbsolute(migrationFilePath)
- ? join(migrationFilePath, `${num}-${timestamp}.json`)
- : join(projectPath, migrationFilePath, `${num}-${timestamp}.json`)
-
- if (dryRun) {
- console.info(`Creating migration file '${outputMigrationFile}'`);
- console.info(migrationContent);
- } else {
- await writeFilePromisify(
- outputMigrationFile,
- JSON.stringify(migrationContent, null, 4),
- 'utf8',
- );
- }
-}
-
-export default generate;
diff --git a/go-web-app-develop/app/scripts/translatte/commands/lint.test.ts b/go-web-app-develop/app/scripts/translatte/commands/lint.test.ts
deleted file mode 100644
index b3f287199a5877a6fcb79a31b6ac34476f0854fe..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/lint.test.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { expect } from 'vitest';
-import { join } from 'path';
-import { mkdirSync } from 'fs';
-
-import { loginContent, registerContent } from '../mockData';
-import { testWithTmpDir } from '../testHelpers';
-import { writeFilePromisify } from '../utils';
-import lint from './lint';
-
-testWithTmpDir('test lint with duplicate file', async ({ tmpdir }) => {
- mkdirSync(join(tmpdir, 'i18n'));
-
- const writes = [
- { name: 'login.i18n.json', content: loginContent },
- { name: 'register.i18n.json', content: registerContent },
- { name: 'register-form.i18n.json', content: registerContent },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'i18n', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(writes);
-
- await expect(
- () => lint(tmpdir, ['**/*.i18n.json'], false)
- ).rejects.toThrow('Found 12 duplicated strings.');
-});
-
-testWithTmpDir('test lint with duplicate string and same text', async ({ tmpdir }) => {
- mkdirSync(join(tmpdir, 'i18n'));
-
- const writes = [
- { name: 'login.i18n.json', content: loginContent },
- { name: 'register.i18n.json', content: registerContent },
- { name: 'register-form.i18n.json', content: {
- namespace: 'register',
- strings: {
- firstNameLabel: 'First Name',
- },
- } },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'i18n', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(writes);
-
- await expect(
- () => lint(tmpdir, ['**/*.i18n.json'], false)
- ).rejects.toThrow('Found 2 duplicated strings.');
-});
-
-testWithTmpDir('test lint with duplicate string and different text', async ({ tmpdir }) => {
- mkdirSync(join(tmpdir, 'i18n'));
-
- const writes = [
- { name: 'login.i18n.json', content: loginContent },
- { name: 'register.i18n.json', content: registerContent },
- { name: 'register-form.i18n.json', content: {
- namespace: 'register',
- strings: {
- firstNameLabel: 'First Name*',
- },
- } },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'i18n', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(writes);
-
- await expect(
- () => lint(tmpdir, ['**/*.i18n.json'], false)
- ).rejects.toThrow('Found 2 duplicated strings.');
-});
diff --git a/go-web-app-develop/app/scripts/translatte/commands/lint.ts b/go-web-app-develop/app/scripts/translatte/commands/lint.ts
deleted file mode 100644
index c0b8acab693965572d34f60aee1eaf11eeef47ed..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/lint.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import { listToMap } from '@togglecorp/fujs';
-
-import {
- getDuplicateItems,
- getTranslationFileNames,
- readTranslations,
- writeFilePromisify,
-} from '../utils';
-
-function lowercaseFirstChar(chars: string) {
- return chars.charAt(0).toLowerCase() + chars.slice(1);
-}
-
-async function lint(
- projectPath: string,
- translationFileName: string[],
- fix: boolean | undefined,
-) {
- const fileNames = await getTranslationFileNames(projectPath, translationFileName);
- const { translations, filesContents } = await readTranslations(fileNames);
-
- const namespaces = new Set(translations.map((item) => item.namespace));
-
- console.info(`Found ${namespaces.size} namespaces.`);
- console.info(`Found ${translations.length} strings.`);
-
- const duplicates = getDuplicateItems(
- translations,
- (string) => `${string.namespace}:${string.key}`,
- );
-
- if (duplicates.length > 0) {
- console.info(JSON.stringify(duplicates, null, 2));
- throw `Found ${duplicates.length} duplicated strings.`;
- }
-
- // FIXME: We should get these custom rules from config file later
- const customRules: {
- location: string,
- namespace: ((match: RegExpMatchArray) => string) | string;
- }[] = [
- { location: '.*/app/src/views/(\\w+)/(?:.*/)?i18n.json$', namespace: (match) => lowercaseFirstChar(match[1]) },
- { location: '.*/app/src/components/domain/(\\w+)/(?:.*/)?i18n.json$', namespace: (match) => lowercaseFirstChar(match[1]) },
- { location: '.*/app/src/.*/i18n.json$', namespace: 'common' },
- { location: '.*/packages/ui/src/.*/i18n.json$', namespace: 'common' },
- ];
-
- const namespaceErrors: {
- fileName: string,
- expectedNamespace: string,
- receivedNamespace: string,
- }[] = [];
- for (const item of filesContents) {
- const { file: fileName, content: { namespace } } = item;
- for (const rule of customRules) {
- const match = fileName.match(new RegExp(rule.location));
- if (match) {
- const correctNamespace = typeof rule.namespace === 'string'
- ? rule.namespace
- : rule.namespace(match);
- if (correctNamespace !== namespace) {
- namespaceErrors.push({
- fileName,
- expectedNamespace: correctNamespace,
- receivedNamespace: namespace,
- })
- }
- break;
- }
- };
- };
-
- if (namespaceErrors.length > 0) {
- if (fix) {
- const metadataMapping = listToMap(
- filesContents,
- (fileContents) => fileContents.file,
- (fileContents) => fileContents.content,
- );
- const updates = namespaceErrors.map((namespaceError) => {
- const content = metadataMapping[namespaceError.fileName];
- const updatedContent = {
- ...content,
- namespace: namespaceError.expectedNamespace,
- }
- return writeFilePromisify(
- namespaceError.fileName,
- JSON.stringify(updatedContent, null, 4),
- 'utf8',
- );
- });
- await Promise.all(updates);
- console.info(`Fixed namespace in ${namespaceErrors.length} files`);
- } else {
- console.info(JSON.stringify(namespaceErrors, null, 2));
- throw `Found ${namespaceErrors.length} issues with namespaces.`;
- }
- }
-
- // TODO: Throw error
- // - if the naming of migration files is not correct
- // - if the parent field is not correct
- // - if we have duplicates
-}
-
-export default lint;
diff --git a/go-web-app-develop/app/scripts/translatte/commands/listMigrations.test.ts b/go-web-app-develop/app/scripts/translatte/commands/listMigrations.test.ts
deleted file mode 100644
index 5fa7a3699e0648bc5c38b69392d39ecb20baa7c8..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/listMigrations.test.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { expect } from 'vitest';
-import { join } from 'path';
-import { mkdirSync } from 'fs';
-
-import { testWithTmpDir } from '../testHelpers';
-import { writeFilePromisify, getMigrationFilesAttrs } from '../utils';
-import {
- migrationContent1,
- migrationContent2,
- migrationContent3,
- migrationContent4,
- migrationContent5,
-} from '../mockData';
-
-testWithTmpDir('test listMigrations', async ({ tmpdir }) => {
- mkdirSync(join(tmpdir, 'migrations'));
-
- const writes = [
- { name: '001-1000000000000.json', content: migrationContent1 },
- { name: '002-1000000000000.json', content: migrationContent2 },
- { name: '003-1000000000000.json', content: migrationContent3 },
- { name: '004-1000000000000.json', content: migrationContent4 },
- { name: '005-1000000000000.json', content: migrationContent5 },
-
- { name: 'xyz-1000000000000.json', content: migrationContent5 },
- { name: '006-abcdefghijklm.json', content: migrationContent5 },
- { name: '005-1000000000000', content: migrationContent5 },
- { name: 'migration-6.json', content: migrationContent5 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, 'migrations', name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(writes);
-
- expect(
- (await getMigrationFilesAttrs(
- tmpdir,
- 'migrations',
- )).map((item) => ({ ...item, fileName: undefined })),
- ).toEqual([
- { migrationName: '001-1000000000000.json', num: '001', timestamp: '1000000000000' },
- { migrationName: '002-1000000000000.json', num: '002', timestamp: '1000000000000' },
- { migrationName: '003-1000000000000.json', num: '003', timestamp: '1000000000000' },
- { migrationName: '004-1000000000000.json', num: '004', timestamp: '1000000000000' },
- { migrationName: '005-1000000000000.json', num: '005', timestamp: '1000000000000' },
- ]);
-});
diff --git a/go-web-app-develop/app/scripts/translatte/commands/listMigrations.ts b/go-web-app-develop/app/scripts/translatte/commands/listMigrations.ts
deleted file mode 100644
index f131ec13b018f6cb98fe94e4be9b2960fcb8f827..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/listMigrations.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { getMigrationFilesAttrs } from '../utils';
-
-async function listMigrations(projectPath: string, path: string) {
- const migrationFileAttrs = await getMigrationFilesAttrs(projectPath, path);
- console.info(`Found ${migrationFileAttrs.length} migration files.`);
- if (migrationFileAttrs.length > 0) {
- console.info(migrationFileAttrs);
- }
-}
-
-export default listMigrations;
diff --git a/go-web-app-develop/app/scripts/translatte/commands/mergeMigrations.test.ts b/go-web-app-develop/app/scripts/translatte/commands/mergeMigrations.test.ts
deleted file mode 100644
index 33c6c705480b43bc9a6ec7d534c980ac0438867f..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/mergeMigrations.test.ts
+++ /dev/null
@@ -1,371 +0,0 @@
-import { expect, test } from 'vitest';
-import { existsSync } from 'fs';
-import { join } from 'path';
-
-import mergeMigrations, { merge } from './mergeMigrations';
-import { testWithTmpDir } from '../testHelpers';
-import { writeFilePromisify, readMigrations } from '../utils';
-import {
- migrationContent1,
- migrationContent2,
- migrationContent3,
- migrationContent4,
- migrationContent5,
-} from '../mockData';
-
-test('Test merge migrations 1-5', () => {
- expect(merge([
- migrationContent1,
- migrationContent2,
- migrationContent3,
- migrationContent4,
- migrationContent5,
- ])).toEqual([
- {
- "action": "add",
- "namespace": "login",
- "key": "emailLabel",
- "value": "Email/Username"
- },
- {
- "action": "add",
- "namespace": "login",
- "key": "emailPlaceholder",
- "value": "Email/Username*"
- },
- {
- "action": "add",
- "namespace": "login",
- "key": "passwordLabel",
- "value": "Password"
- },
- {
- "action": "add",
- "namespace": "login",
- "key": "loginButton",
- "value": "Login"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "firstNameLabel",
- "value": "First Name"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "lastNameLabel",
- "value": "Last Name"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "emailLabel",
- "value": "Email"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "passwordLabel",
- "value": "Password"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "confirmPasswordLabel",
- "value": "Confirm Password"
- },
- {
- "action": "add",
- "namespace": "login",
- "key": "header",
- "value": "If you are staff, member or volunteer of the Red Cross Red Crescent Movement (National Societies, the IFRC and the ICRC) login with you email and password."
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "registerButton",
- "value": "Register"
- },
- ]);
-});
-
-test('Test merge migrations 2-5', () => {
- expect(merge([
- migrationContent2,
- migrationContent3,
- migrationContent4,
- migrationContent5,
- ])).toEqual([
- {
- "action": "update",
- "namespace": "login",
- "key": "emailLabel",
- "newValue": "Email/Username"
- },
- {
- "action": "update",
- "namespace": "login",
- "key": "passwordLabel",
- "newValue": "Password"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "firstNameLabel",
- "newValue": "First Name"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "lastNameLabel",
- "newValue": "Last Name"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "emailLabel",
- "newValue": "Email"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "passwordLabel",
- "newValue": "Password"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "confirmPasswordLabel",
- "newValue": "Confirm Password"
- },
- {
- "action": "update",
- "key": "signUpButton",
- "namespace": "register",
- "newKey": "registerButton",
- "newNamespace": undefined,
- "newValue": "Register",
- },
- {
- "action": "add",
- "namespace": "login",
- "key": "header",
- "value": "If you are staff, member or volunteer of the Red Cross Red Crescent Movement (National Societies, the IFRC and the ICRC) login with you email and password."
- },
- {
- "action": "remove",
- "namespace": "home",
- "key": "header"
- },
- {
- "action": "remove",
- "namespace": "home",
- "key": "subHeader"
- },
- ]);
-});
-
-test('Test merge migrations 3-5', () => {
- expect(merge([
- migrationContent3,
- migrationContent4,
- migrationContent5,
- ])).toEqual([
- {
- "action": "add",
- "namespace": "login",
- "key": "header",
- "value": "If you are staff, member or volunteer of the Red Cross Red Crescent Movement (National Societies, the IFRC and the ICRC) login with you email and password."
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "signUpButton",
- "newKey": "registerButton"
- },
- {
- "action": "remove",
- "namespace": "home",
- "key": "header"
- },
- {
- "action": "remove",
- "namespace": "home",
- "key": "subHeader"
- },
- ]);
-});
-
-test('Test merge migrations 4-5', () => {
- expect(merge([
- migrationContent4,
- migrationContent5,
- ])).toEqual([
- {
- "action": "remove",
- "namespace": "login",
- "key": "header"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "header",
- "newNamespace": "login"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "signUpButton",
- "newKey": "registerButton"
- },
- {
- "action": "remove",
- "namespace": "home",
- "key": "header"
- },
- {
- "action": "remove",
- "namespace": "home",
- "key": "subHeader"
- },
- ]);
-});
-
-test('Test merge migrations 5-5', () => {
- expect(merge([
- migrationContent5,
- ])).toEqual(migrationContent5.actions)
-})
-
-testWithTmpDir('test mergeMigrations 1-5', async ({ tmpdir }) => {
- const writes = [
- { name: '000001-1000000000000.json', content: migrationContent1 },
- { name: '000002-1000000000000.json', content: migrationContent2 },
- { name: '000003-1000000000000.json', content: migrationContent3 },
- { name: '000004-1000000000000.json', content: migrationContent4 },
- { name: '000005-1000000000000.json', content: migrationContent5 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(writes);
-
- await mergeMigrations(
- tmpdir,
- '.',
- '000001-1000000000000.json',
- '000005-1000000000000.json',
- false,
- );
-
- expect(existsSync(join(tmpdir, '000001-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000002-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000003-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000004-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000005-1000000000000.json'))).toBeTruthy();
-
- const generatedFiles = await readMigrations([join(tmpdir, '000005-1000000000000.json')]);
- const generatedFile = generatedFiles[0];
- expect(generatedFile.content.parent).toBe(undefined);
-});
-
-testWithTmpDir('test mergeMigrations 2-5', async ({ tmpdir }) => {
- const writes = [
- { name: '000001-1000000000000.json', content: migrationContent1 },
- { name: '000002-1000000000000.json', content: migrationContent2 },
- { name: '000003-1000000000000.json', content: migrationContent3 },
- { name: '000004-1000000000000.json', content: migrationContent4 },
- { name: '000005-1000000000000.json', content: migrationContent5 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(writes);
-
- await mergeMigrations(
- tmpdir,
- '.',
- '000002-1000000000000.json',
- '000005-1000000000000.json',
- false,
- );
-
- expect(existsSync(join(tmpdir, '000001-1000000000000.json'))).toBeTruthy();
- expect(existsSync(join(tmpdir, '000002-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000003-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000004-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000005-1000000000000.json'))).toBeTruthy();
-
- const generatedFiles = await readMigrations([join(tmpdir, '000005-1000000000000.json')]);
- const generatedFile = generatedFiles[0];
- expect(generatedFile.content.parent).toBe('000001-1000000000000');
-});
-
-testWithTmpDir('test mergeMigrations 3-5', async ({ tmpdir }) => {
- const writes = [
- { name: '000001-1000000000000.json', content: migrationContent1 },
- { name: '000002-1000000000000.json', content: migrationContent2 },
- { name: '000003-1000000000000.json', content: migrationContent3 },
- { name: '000004-1000000000000.json', content: migrationContent4 },
- { name: '000005-1000000000000.json', content: migrationContent5 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(writes);
-
- await mergeMigrations(
- tmpdir,
- '.',
- '000003-1000000000000.json',
- '000005-1000000000000.json',
- false,
- );
-
- expect(existsSync(join(tmpdir, '000001-1000000000000.json'))).toBeTruthy();
- expect(existsSync(join(tmpdir, '000002-1000000000000.json'))).toBeTruthy();
- expect(existsSync(join(tmpdir, '000003-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000004-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000005-1000000000000.json'))).toBeTruthy();
-
- const generatedFiles = await readMigrations([join(tmpdir, '000005-1000000000000.json')]);
- const generatedFile = generatedFiles[0];
- expect(generatedFile.content.parent).toBe('000002-1000000000000');
-});
-
-testWithTmpDir('test mergeMigrations 4-5', async ({ tmpdir }) => {
- const writes = [
- { name: '000001-1000000000000.json', content: migrationContent1 },
- { name: '000002-1000000000000.json', content: migrationContent2 },
- { name: '000003-1000000000000.json', content: migrationContent3 },
- { name: '000004-1000000000000.json', content: migrationContent4 },
- { name: '000005-1000000000000.json', content: migrationContent5 },
- ].map(({ name, content }) => writeFilePromisify(
- join(tmpdir, name),
- JSON.stringify(content, null, 4),
- 'utf8',
- ));
- await Promise.all(writes);
-
- await mergeMigrations(
- tmpdir,
- '.',
- '000004-1000000000000.json',
- '000005-1000000000000.json',
- false,
- );
-
- expect(existsSync(join(tmpdir, '000001-1000000000000.json'))).toBeTruthy();
- expect(existsSync(join(tmpdir, '000002-1000000000000.json'))).toBeTruthy();
- expect(existsSync(join(tmpdir, '000003-1000000000000.json'))).toBeTruthy();
- expect(existsSync(join(tmpdir, '000004-1000000000000.json'))).toBeFalsy();
- expect(existsSync(join(tmpdir, '000005-1000000000000.json'))).toBeTruthy();
-
- const generatedFiles = await readMigrations([join(tmpdir, '000005-1000000000000.json')]);
- const generatedFile = generatedFiles[0];
- expect(generatedFile.content.parent).toBe('000003-1000000000000');
-});
diff --git a/go-web-app-develop/app/scripts/translatte/commands/mergeMigrations.ts b/go-web-app-develop/app/scripts/translatte/commands/mergeMigrations.ts
deleted file mode 100644
index 4be6b0f0abc1389e7a7ce62d05260d57e68739ad..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/commands/mergeMigrations.ts
+++ /dev/null
@@ -1,220 +0,0 @@
-import { listToMap, isDefined } from '@togglecorp/fujs';
-
-import { MigrationActionItem, MigrationFileContent } from '../types';
-import {
- concat,
- removeUndefinedKeys,
- getMigrationFilesAttrs,
- readMigrations,
- removeFiles,
- writeFilePromisify
-} from '../utils';
-
-function getCanonicalKey(
- item: MigrationActionItem,
- opts: { useNewKey: boolean },
-) {
- if (opts.useNewKey && item.action === 'update') {
- return concat(
- item.newNamespace ?? item.namespace,
- item.newKey ?? item.key,
- );
- }
- return concat(
- item.namespace,
- item.key,
- );
-}
-
-function mergeMigrationActionItems(
- prevMigrationActionItems: MigrationActionItem[],
- nextMigrationActionItems: MigrationActionItem[],
-) {
- interface PrevMappings {
- [key: string]: MigrationActionItem,
- }
-
- const prevCanonicalKeyMappings: PrevMappings = listToMap(
- prevMigrationActionItems,
- (item) => getCanonicalKey(item, { useNewKey: true }),
- (item) => item,
- );
-
- interface NextMappings {
- [key: string]: MigrationActionItem | null,
- }
-
- const nextMappings = nextMigrationActionItems.reduce(
- (acc, nextMigrationActionItem) => {
- const canonicalKey = getCanonicalKey(nextMigrationActionItem, { useNewKey: false })
-
- const prevItemWithCanonicalKey = prevCanonicalKeyMappings[canonicalKey];
- // const prevItemWithKey = prevKeyMappings[nextMigrationActionItem.key];
-
- if (!prevItemWithCanonicalKey) {
- return {
- ...acc,
- [canonicalKey]: nextMigrationActionItem,
- };
- }
-
- if (prevItemWithCanonicalKey.action === 'add' && nextMigrationActionItem.action === 'add') {
- throw `Action 'add' already exists for '${canonicalKey}'`;
- }
- if (prevItemWithCanonicalKey.action === 'add' && nextMigrationActionItem.action === 'remove') {
- return {
- ...acc,
- [canonicalKey]: null,
- };
- }
- if (prevItemWithCanonicalKey.action === 'add' && nextMigrationActionItem.action === 'update') {
- const newKey = nextMigrationActionItem.newKey
- ?? prevItemWithCanonicalKey.key;
- const newNamespace = nextMigrationActionItem.newNamespace
- ?? prevItemWithCanonicalKey.namespace;
-
- const newMigrationItem = removeUndefinedKeys({
- action: 'add',
- namespace: newNamespace,
- key: newKey,
- value: nextMigrationActionItem.newValue
- ?? prevItemWithCanonicalKey.value,
- });
-
- const newCanonicalKey = getCanonicalKey(newMigrationItem, { useNewKey: true });
- if (acc[newCanonicalKey] !== undefined && acc[newCanonicalKey] !== null) {
- throw `Action 'update' cannot be applied to '${newCanonicalKey}' as the key already exists`;
- }
-
- return {
- ...acc,
- // Setting null so that we remove them on the mappings.
- // No need to set null, if we have already overridden with other value
- [canonicalKey]: acc[canonicalKey] === undefined || acc[canonicalKey] === null
- ? null
- : acc[canonicalKey],
- [newCanonicalKey]: newMigrationItem,
- }
- }
- if (prevItemWithCanonicalKey.action === 'remove' && nextMigrationActionItem.action === 'add') {
- return {
- ...acc,
- [canonicalKey]: removeUndefinedKeys({
- action: 'update',
- namespace: prevItemWithCanonicalKey.namespace,
- key: prevItemWithCanonicalKey.key,
- newValue: nextMigrationActionItem.value,
- })
- };
- }
- if (prevItemWithCanonicalKey.action === 'remove' && nextMigrationActionItem.action === 'remove') {
- // pass
- return acc;
- }
- if (prevItemWithCanonicalKey.action === 'remove' && nextMigrationActionItem.action === 'update') {
- throw `Action 'update' cannot be applied to '${canonicalKey}' after action 'remove'`;
- }
- if (prevItemWithCanonicalKey.action === 'update' && nextMigrationActionItem.action === 'add') {
- throw `Action 'add' cannot be applied to '${canonicalKey}' after action 'update'`;
- }
- if (prevItemWithCanonicalKey.action === 'update' && nextMigrationActionItem.action === 'update') {
- return {
- ...acc,
- [canonicalKey]: removeUndefinedKeys({
- action: 'update',
- namespace: prevItemWithCanonicalKey.namespace,
- key: prevItemWithCanonicalKey.key,
- newNamespace: nextMigrationActionItem.newNamespace ?? prevItemWithCanonicalKey.newNamespace,
- newKey: nextMigrationActionItem.newKey ?? prevItemWithCanonicalKey.newKey,
- newValue: nextMigrationActionItem.newValue ?? prevItemWithCanonicalKey.newValue,
- }),
- };
- }
- if (prevItemWithCanonicalKey.action === 'update' && nextMigrationActionItem.action === 'remove') {
- return {
- ...acc,
- [canonicalKey]: removeUndefinedKeys({
- action: 'remove',
- namespace: prevItemWithCanonicalKey.namespace,
- key: prevItemWithCanonicalKey.key,
- }),
- };
- }
- return acc;
- },
- {},
- );
-
- const finalMappings = {
- ...prevCanonicalKeyMappings,
- ...nextMappings,
- };
-
- return Object.values(finalMappings).filter(isDefined);
-}
-
-export function merge(migrationFileContents: MigrationFileContent[]) {
- const migrationActionItems = migrationFileContents.reduce(
- (acc, migrationActionItem) => {
- const newMigrationItems = mergeMigrationActionItems(acc, migrationActionItem.actions)
- return newMigrationItems;
- },
- [],
- );
-
- return migrationActionItems;
-}
-
-async function mergeMigrations(
- projectPath: string,
- path: string,
- from: string,
- to: string,
- dryRun: boolean | undefined,
-) {
- const migrationFilesAttrs = await getMigrationFilesAttrs(projectPath, path);
- const selectedMigrationFilesAttrs = migrationFilesAttrs.filter(
- (item) => (item.migrationName >= from && item.migrationName <= to)
- );
- console.info(`Found ${selectedMigrationFilesAttrs.length} migration files`);
-
- if (selectedMigrationFilesAttrs.length <= 1) {
- throw 'There should be atleast 2 migration files';
- }
- const selectedMigrations = await readMigrations(
- selectedMigrationFilesAttrs.map((migration) => migration.fileName),
- );
-
- const firstMigration= selectedMigrations[0];
- const lastMigration = selectedMigrations[selectedMigrations.length - 1];
-
- const selectedMigrationsFileNames = selectedMigrationFilesAttrs.map((migration) => migration.fileName);
-
- const mergedMigrationContent = {
- actions: merge(selectedMigrations.map((migration) => migration.content)),
- parent: firstMigration.content.parent,
- };
-
- if (dryRun) {
- console.info('Deleting the following migration files');
- console.info(selectedMigrationsFileNames);
- } else {
- await removeFiles(
- selectedMigrationsFileNames,
- );
- }
-
- const newFileName = lastMigration.file;
- if (dryRun) {
- console.info(`Creating migration file '${newFileName}'`);
- console.info(mergedMigrationContent);
- } else {
- await writeFilePromisify(
- newFileName,
- JSON.stringify(mergedMigrationContent, null, 4),
- 'utf8',
- );
- }
-}
-
-export default mergeMigrations;
diff --git a/go-web-app-develop/app/scripts/translatte/main.ts b/go-web-app-develop/app/scripts/translatte/main.ts
deleted file mode 100644
index 079f9c7472fa335937ef8eb6947127f6940d108e..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/main.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import yargs from 'yargs/yargs';
-import { hideBin } from 'yargs/helpers';
-import { cwd } from 'process';
-
-import lint from './commands/lint';
-import listMigrations from './commands/listMigrations';
-import mergeMigrations from './commands/mergeMigrations';
-
-import applyMigrations from './commands/applyMigrations';
-import generateMigration from './commands/generateMigration';
-import exportMigration from './commands/exportMigration';
-import { join, basename } from 'path';
-
-const currentDir = cwd();
-
-// CLI
-
-yargs(hideBin(process.argv))
- .scriptName('translatte')
- .usage('$0 [args]')
- .demandCommand(1)
- .command(
- 'lint ',
- 'Lint i18n.json files for duplicated strings',
- (yargs) => {
- yargs.positional('TRANSLATION_FILE', {
- type: 'string',
- describe: 'Read the files from TRANSLATION_FILE',
- });
- yargs.options({
- 'fix': {
- type: 'boolean',
- description: 'Fix fixable issues',
- },
- });
- },
- async (argv) => {
- await lint(currentDir, argv.TRANSLATION_FILE as string[], argv.fix as boolean | undefined);
- },
- )
- .command(
- 'list-migrations ',
- 'List migration files',
- (yargs) => {
- yargs.positional('MIGRATION_FILE_PATH', {
- type: 'string',
- describe: 'Find the migration files on MIGRATION_FILE_PATH',
- });
- },
- async (argv) => {
- await listMigrations(currentDir, argv.MIGRATION_FILE_PATH as string);
- },
- )
- .command(
- 'merge-migrations ',
- 'Merge migration files',
- (yargs) => {
- yargs.positional('MIGRATION_FILE_PATH', {
- type: 'string',
- describe: 'Find the migration files on MIGRATION_FILE_PATH',
- });
- yargs.options({
- 'from': {
- type: 'string',
- description: 'The first file that will be included in the merge',
- demandOption: true,
- },
- 'to': {
- type: 'string',
- description: 'The to file that will be included in the merge',
- demandOption: true,
- },
- 'dry-run': {
- alias: 'd',
- description: 'Dry run',
- type: 'boolean',
- },
- });
- },
- async (argv) => {
- await mergeMigrations(
- currentDir,
- argv.MIGRATION_FILE_PATH as string,
- argv.from as string,
- argv.to as string,
- argv.dryRun as (boolean | undefined),
- );
- },
- )
- .command(
- 'apply-migrations ',
- 'Apply migrations',
- (yargs) => {
- yargs.positional('MIGRATION_FILE_PATH', {
- type: 'string',
- describe: 'Find the migration file on MIGRATION_FILE_PATH',
- });
- yargs.options({
- 'dry-run': {
- alias: 'd',
- description: 'Dry run',
- type: 'boolean',
- },
- 'last-migration': {
- type: 'string',
- description: 'The file after which the migration will be applied',
- },
- 'source': {
- type: 'string',
- description: 'The source file to which migration is applied',
- demandOption: true,
- },
- 'destination': {
- type: 'string',
- description: 'The file where new source file is saved',
- demandOption: true,
- },
- });
- },
- async (argv) => {
- await applyMigrations(
- currentDir,
- argv.SOURCE_FILE as string,
- argv.DESTINATION_FILE as string,
- argv.MIGRATION_FILE_PATH as string,
- ['es', 'ar', 'fr'],
- argv.lastMigration as (string | undefined),
- argv.dryRun as (boolean | undefined),
- );
- },
- )
- .command(
- 'generate-migration ',
- 'Generate migration file',
- (yargs) => {
- yargs.positional('MIGRATION_FILE_PATH', {
- type: 'string',
- describe: 'Find the migration files on MIGRATION_FILE_PATH',
- });
- yargs.positional('TRANSLATION_FILE', {
- type: 'string',
- describe: 'Read the files from TRANSLATION_FILE',
- });
- yargs.options({
- 'dry-run': {
- alias: 'd',
- description: 'Dry run',
- type: 'boolean',
- },
- });
- },
- async (argv) => {
- await generateMigration(
- currentDir,
- argv.MIGRATION_FILE_PATH as string,
- argv.TRANSLATION_FILE as string,
- new Date().getTime(),
- argv.dryRun as (boolean | undefined),
- );
- },
- )
- .command(
- 'export-migration ',
- 'Export migration file to excel format which can be used to translate the new and updated strings',
- (yargs) => {
- yargs.positional('MIGRATION_FILE_PATH', {
- type: 'string',
- describe: 'Find the migration file on MIGRATION_FILE_PATH',
- });
- yargs.positional('OUTPUT_DIR', {
- type: 'string',
- describe: 'Directory where the output xlsx should be saved',
- });
- },
- async (argv) => {
- const migrationFilePath = (argv.MIGRATION_FILE_PATH as string);
-
- const outputDir = argv.OUTPUT_DIR as string;
-
- // Get only the filename without extension
- const exportFileName = basename(migrationFilePath, '.json');
-
- const exportFilePath = join(outputDir, exportFileName);
-
- await exportMigration(
- argv.MIGRATION_FILE_PATH as string,
- exportFilePath,
- );
- },
- )
- .strictCommands()
- .showHelpOnFail(false)
- .parse()
diff --git a/go-web-app-develop/app/scripts/translatte/mockData.ts b/go-web-app-develop/app/scripts/translatte/mockData.ts
deleted file mode 100644
index a148c129fc6cf00c96c55ef734a8ed3231c3a46c..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/mockData.ts
+++ /dev/null
@@ -1,655 +0,0 @@
-import {
- MigrationFileContent,
- TranslationFileContent,
- SourceFileContent,
-} from './types';
-
-export const migrationContent1: MigrationFileContent = {
- "actions": [
- {
- "action": "add",
- "namespace": "login",
- "key": "emailLabel",
- "value": "Email/Username*"
- },
- {
- "action": "add",
- "namespace": "login",
- "key": "emailPlaceholder",
- "value": "Email/Username*"
- },
- {
- "action": "add",
- "namespace": "login",
- "key": "passwordLabel",
- "value": "Password*"
- },
- {
- "action": "add",
- "namespace": "login",
- "key": "loginButton",
- "value": "Login"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "firstNameLabel",
- "value": "First Name*"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "lastNameLabel",
- "value": "Last Name*"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "emailLabel",
- "value": "Email*"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "passwordLabel",
- "value": "Password*"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "confirmPasswordLabel",
- "value": "Confirm Password*"
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "signUpButton",
- "value": "Sign Up"
- },
- {
- "action": "add",
- "namespace": "home",
- "key": "header",
- "value": "IFRC Disaster Response and Preparedness"
- },
- {
- "action": "add",
- "namespace": "home",
- "key": "subHeader",
- "value": "IFRC GO aims to make all disaster information universally accessible and useful to IFRC responders for better decision making."
- }
- ],
-};
-// Story: using the migration file below
-// 1. we need to remove the asterisks from all the labels
-// 2. we need to use register consistenlty, so use "Register" instead of "Sign Up"
-export const migrationContent2: MigrationFileContent = {
- "parent": "000001-1000000000000",
- "actions": [
- {
- "action": "update",
- "namespace": "login",
- "key": "emailLabel",
- "newValue": "Email/Username"
- },
- {
- "action": "update",
- "namespace": "login",
- "key": "passwordLabel",
- "newValue": "Password"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "firstNameLabel",
- "newValue": "First Name"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "lastNameLabel",
- "newValue": "Last Name"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "emailLabel",
- "newValue": "Email"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "passwordLabel",
- "newValue": "Password"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "confirmPasswordLabel",
- "newValue": "Confirm Password"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "signUpButton",
- "newValue": "Register"
- }
- ],
-};
-// Story: using the migration file below
-// 1. we need to add header for login and register page
-export const migrationContent3: MigrationFileContent = {
- "parent": "000002-1000000000000",
- "actions": [
- {
- "action": "add",
- "namespace": "login",
- "key": "header",
- "value": "Staff, members and volunteers of the Red Cross Red Crescent Movement (National Societies, the IFRC and the ICRC) are welcome to register for a user account on GO, to access information for the Membership. Other responders and members of the public may browse the public areas of the site without registering for an account."
- },
- {
- "action": "add",
- "namespace": "register",
- "key": "header",
- "value": "If you are staff, member or volunteer of the Red Cross Red Crescent Movement (National Societies, the IFRC and the ICRC) login with you email and password."
- }
- ],
-};
-// Story: using the migration file below
-// 1. we need to fix the header text for login and register page. They have been mistakenly swapped
-// 2. we need to use "register" consistently, even on keys.
-export const migrationContent4: MigrationFileContent = {
- "parent": "000003-1000000000000",
- "actions": [
- {
- "action": "update",
- "namespace": "login",
- "key": "header",
- "newNamespace": "register"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "header",
- "newNamespace": "login"
- },
- {
- "action": "update",
- "namespace": "register",
- "key": "signUpButton",
- "newKey": "registerButton"
- }
- ],
-};
-// Story: using the migration file below
-// 1. we need to delete unused strings for home page
-// 2. we need to delete unused strings for register page
-export const migrationContent5: MigrationFileContent = {
- "parent": "000004-1000000000000",
- "actions": [
- {
- "action": "remove",
- "namespace": "home",
- "key": "header"
- },
- {
- "action": "remove",
- "namespace": "home",
- "key": "subHeader"
- },
- {
- "action": "remove",
- "namespace": "register",
- "key": "header"
- }
- ]
-};
-
-// Story: we now have 2 translations files after applying the above migrations
-export const loginContent = {
- namespace: 'login',
- strings: {
- emailLabel: 'Email/Username',
- emailPlaceholder: 'Email/Username*',
- passwordLabel: 'Password',
- loginButton: 'Login',
- header: 'If you are staff, member or volunteer of the Red Cross Red Crescent Movement (National Societies, the IFRC and the ICRC) login with you email and password.',
- },
-} satisfies TranslationFileContent;
-export const registerContent = {
- namespace: 'register',
- strings: {
- firstNameLabel: 'First Name',
- lastNameLabel: 'Last Name',
- emailLabel: 'Email',
- passwordLabel: 'Password',
- confirmPasswordLabel: 'Confirm Password',
- registerButton: 'Register',
- },
-} satisfies TranslationFileContent;
-
-// Story: if the server has no translation data and we push the
-// above migrations, we get the following strings in server
-export const strings1: SourceFileContent = {
- strings: [
- {
- hash: 'c1e1b9ea3f9cfc3eb3e7b8804139ed00',
- key: 'emailLabel',
- page_name: 'login',
- language: 'en',
- value: 'Email/Username',
- },
- {
- hash: 'c1e1b9ea3f9cfc3eb3e7b8804139ed00',
- key: 'emailLabel',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: '7257816a20f2dda660c44809235ea44a',
- key: 'emailPlaceholder',
- language: 'en',
- page_name: 'login',
- value: 'Email/Username*',
- },
- {
- hash: '7257816a20f2dda660c44809235ea44a',
- key: 'emailPlaceholder',
- language: 'np',
- page_name: 'login',
- value: '',
- },
- {
- hash: 'd7d3cc6191dfa630aaa8bcf8d83d8a71',
- key: 'header',
- page_name: 'login',
- language: 'en',
- value: 'If you are staff, member or volunteer of the Red Cross Red Crescent Movement (National Societies, the IFRC and the ICRC) login with you email and password.',
- },
- {
- hash: 'd7d3cc6191dfa630aaa8bcf8d83d8a71',
- key: 'header',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: '99dea78007133396a7b8ed70578ac6ae',
- key: 'loginButton',
- page_name: 'login',
- language: 'en',
- value: 'Login',
- },
- {
- hash: '99dea78007133396a7b8ed70578ac6ae',
- key: 'loginButton',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: 'dc647eb65e6711e155375218212b3964',
- key: 'passwordLabel',
- page_name: 'login',
- language: 'en',
- value: 'Password',
- },
- {
- hash: 'dc647eb65e6711e155375218212b3964',
- key: 'passwordLabel',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: '887f7db126221fe60d18c895d41dc8f6',
- key: 'confirmPasswordLabel',
- page_name: 'register',
- language: 'en',
- value: 'Confirm Password',
- },
- {
- hash: '887f7db126221fe60d18c895d41dc8f6',
- key: 'confirmPasswordLabel',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- {
- hash: 'ce8ae9da5b7cd6c3df2929543a9af92d',
- key: 'emailLabel',
- page_name: 'register',
- language: 'en',
- value: 'Email',
- },
- {
- hash: 'ce8ae9da5b7cd6c3df2929543a9af92d',
- key: 'emailLabel',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- {
- hash: 'bc910f8bdf70f29374f496f05be0330c',
- key: 'firstNameLabel',
- page_name: 'register',
- language: 'en',
- value: 'First Name',
- },
- {
- hash: 'bc910f8bdf70f29374f496f05be0330c',
- key: 'firstNameLabel',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- {
- hash: '77587239bf4c54ea493c7033e1dbf636',
- key: 'lastNameLabel',
- page_name: 'register',
- language: 'en',
- value: 'Last Name',
- },
- {
- hash: '77587239bf4c54ea493c7033e1dbf636',
- key: 'lastNameLabel',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- {
- hash: 'dc647eb65e6711e155375218212b3964',
- key: 'passwordLabel',
- page_name: 'register',
- language: 'en',
- value: 'Password',
- },
- {
- hash: 'dc647eb65e6711e155375218212b3964',
- key: 'passwordLabel',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- {
- hash: '0ba7583639a274c434bbe6ef797115a4',
- key: 'registerButton',
- page_name: 'register',
- language: 'en',
- value: 'Register',
- },
- {
- hash: '0ba7583639a274c434bbe6ef797115a4',
- key: 'registerButton',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- ],
- last_migration: '000005-1000000000000.json',
-};
-
-// Story: code has been updated as presented below by the developers
-export const updatedLoginContent: TranslationFileContent = {
- ...loginContent,
- strings: {
- ...loginContent.strings,
- }
-};
-export const updatedRegisterContent: TranslationFileContent = {
- ...registerContent,
- strings: {
- ...registerContent.strings,
- }
-};
-// Delete
-delete updatedLoginContent.strings.header;
-// Add
-updatedLoginContent.strings.footer = 'All rights reserved.';
-// Update key
-updatedLoginContent.strings.loginBtn = updatedLoginContent.strings.loginButton;
-delete updatedLoginContent.strings.loginButton;
-// Update content
-updatedLoginContent.strings.emailLabel = 'Email';
-// Delete
-delete updatedRegisterContent.strings.confirmPasswordLabel;
-// Add
-updatedRegisterContent.strings.backLink = 'Back to login';
-// Update key
-updatedRegisterContent.strings.registerBtn = updatedRegisterContent.strings.registerButton;
-delete updatedRegisterContent.strings.registerButton;
-// Update content
-updatedRegisterContent.strings.emailLabel = 'Email or Username';
-// Update namespace
-delete updatedRegisterContent.strings.firstNameLabel;
-updatedLoginContent.strings.firstNameLabel = 'First Name';
-delete updatedRegisterContent.strings.lastNameLabel;
-updatedLoginContent.strings.lastNameLabel = 'Last Name';
-
-// Story: we can get the following migration file after generating
-// migration comparing the code with previous migrations
-export const migrationContent6: MigrationFileContent = {
- parent: '000005-1000000000000.json',
- actions: [
- {
- action: 'add',
- key: 'footer',
- namespace: 'login',
- value: 'All rights reserved.',
- },
- {
- action: 'remove',
- key: 'header',
- namespace: 'login',
- },
- {
- action: 'update',
- key: 'emailLabel',
- namespace: 'login',
- newValue: 'Email',
- },
- {
- action: 'update',
- key: 'loginButton',
- namespace: 'login',
- newKey: 'loginBtn',
- },
- {
- action: 'add',
- key: 'backLink',
- namespace: 'register',
- value: 'Back to login',
- },
- {
- action: 'remove',
- key: 'confirmPasswordLabel',
- namespace: 'register',
- },
- {
- action: 'update',
- key: 'emailLabel',
- namespace: 'register',
- newValue: 'Email or Username',
- },
- {
- action: 'update',
- key: 'firstNameLabel',
- namespace: 'register',
- newNamespace: 'login',
- },
- {
- action: 'update',
- key: 'lastNameLabel',
- namespace: 'register',
- newNamespace: 'login',
- },
- {
- action: 'update',
- key: 'registerButton',
- namespace: 'register',
- newKey: 'registerBtn',
- },
- ],
-}
-
-// Story: if we push new migration above, we get the following strings in server
-export const strings2: SourceFileContent = {
- strings: [
- {
- hash: 'ce8ae9da5b7cd6c3df2929543a9af92d',
- key: 'emailLabel',
- page_name: 'login',
- language: 'en',
- value: 'Email',
- },
- {
- hash: 'c1e1b9ea3f9cfc3eb3e7b8804139ed00',
- key: 'emailLabel',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: '7257816a20f2dda660c44809235ea44a',
- key: 'emailPlaceholder',
- language: 'en',
- page_name: 'login',
- value: 'Email/Username*',
- },
- {
- hash: '7257816a20f2dda660c44809235ea44a',
- key: 'emailPlaceholder',
- language: 'np',
- page_name: 'login',
- value: '',
- },
- {
- hash: 'bc910f8bdf70f29374f496f05be0330c',
- key: 'firstNameLabel',
- page_name: 'login',
- language: 'en',
- value: 'First Name',
- },
- {
- hash: 'bc910f8bdf70f29374f496f05be0330c',
- key: 'firstNameLabel',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: '1efc109bdddbb6e51e9b69cc0a1b0701',
- key: 'footer',
- page_name: 'login',
- language: 'en',
- value: 'All rights reserved.',
- },
- {
- hash: '1efc109bdddbb6e51e9b69cc0a1b0701',
- key: 'footer',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: '77587239bf4c54ea493c7033e1dbf636',
- key: 'lastNameLabel',
- page_name: 'login',
- language: 'en',
- value: 'Last Name',
- },
- {
- hash: '77587239bf4c54ea493c7033e1dbf636',
- key: 'lastNameLabel',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: '99dea78007133396a7b8ed70578ac6ae',
- key: 'loginBtn',
- page_name: 'login',
- language: 'en',
- value: 'Login',
- },
- {
- hash: '99dea78007133396a7b8ed70578ac6ae',
- key: 'loginBtn',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: 'dc647eb65e6711e155375218212b3964',
- key: 'passwordLabel',
- page_name: 'login',
- language: 'en',
- value: 'Password',
- },
- {
- hash: 'dc647eb65e6711e155375218212b3964',
- key: 'passwordLabel',
- page_name: 'login',
- language: 'np',
- value: '',
- },
- {
- hash: '463e58c1d35fb5a4a8d717c99a60d257',
- key: 'backLink',
- page_name: 'register',
- language: 'en',
- value: 'Back to login',
- },
- {
- hash: '463e58c1d35fb5a4a8d717c99a60d257',
- key: 'backLink',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- {
- hash: 'b7a9ca78ae5ec6f8c6af39f000b07da9',
- key: 'emailLabel',
- page_name: 'register',
- language: 'en',
- value: 'Email or Username',
- },
- {
- hash: 'ce8ae9da5b7cd6c3df2929543a9af92d',
- key: 'emailLabel',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- {
- hash: 'dc647eb65e6711e155375218212b3964',
- key: 'passwordLabel',
- page_name: 'register',
- language: 'en',
- value: 'Password',
- },
- {
- hash: 'dc647eb65e6711e155375218212b3964',
- key: 'passwordLabel',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- {
- hash: '0ba7583639a274c434bbe6ef797115a4',
- key: 'registerBtn',
- page_name: 'register',
- language: 'en',
- value: 'Register',
- },
- {
- hash: '0ba7583639a274c434bbe6ef797115a4',
- key: 'registerBtn',
- page_name: 'register',
- language: 'np',
- value: '',
- },
- ],
- last_migration: '000006-1000000000000.json',
-};
diff --git a/go-web-app-develop/app/scripts/translatte/testHelpers.ts b/go-web-app-develop/app/scripts/translatte/testHelpers.ts
deleted file mode 100644
index 0426bf07186047311aaea18ce50c033984d2bb40..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/testHelpers.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { test } from "vitest";
-import os from "node:os";
-import fs from "node:fs/promises";
-import path from "node:path";
-
-interface TmpDirFixture {
- tmpdir: string;
-}
-
-async function createTempDir() {
- const ostmpdir = os.tmpdir();
- const tmpdir = path.join(ostmpdir, "unit-test-");
- return await fs.mkdtemp(tmpdir);
-}
-
-export const testWithTmpDir = test.extend({
- tmpdir: async ({}, use) => {
- const directory = await createTempDir();
-
- await use(directory);
-
- await fs.rm(directory, { recursive: true });
- },
-});
diff --git a/go-web-app-develop/app/scripts/translatte/types.ts b/go-web-app-develop/app/scripts/translatte/types.ts
deleted file mode 100644
index 37e0b90639a554b8e95d0fdeced6a085032b3b8f..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/types.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-interface MigrationAddActionItem {
- action: 'add',
- key: string,
- value: string,
- namespace: string,
-}
-
-interface MigrationRemoveActionItem {
- action: 'remove',
- key: string,
- namespace: string,
-}
-
-interface MigrationUpdateActionItem {
- action: 'update',
- key: string,
- namespace: string,
- value?: string,
- newValue?: string,
- newKey?: string,
- newNamespace?: string,
-}
-
-export type MigrationActionItem = MigrationAddActionItem | MigrationRemoveActionItem | MigrationUpdateActionItem;
-
-export interface TranslationFileContent {
- namespace: string,
- strings: {
- [key: string]: string,
- },
-}
-
-export interface MigrationFileContent {
- parent?: string;
- actions: MigrationActionItem[],
-}
-
-export interface SourceStringItem {
- hash: string;
- // id: string;
- key: string;
- language: string;
- page_name: string;
- value: string;
-}
-
-export interface SourceFileContent {
- last_migration?: string;
- strings: SourceStringItem[];
-}
diff --git a/go-web-app-develop/app/scripts/translatte/utils.ts b/go-web-app-develop/app/scripts/translatte/utils.ts
deleted file mode 100644
index 84543278f6c90832ded9802def7a051282dafea4..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/scripts/translatte/utils.ts
+++ /dev/null
@@ -1,325 +0,0 @@
-import { join, isAbsolute, basename } from 'path';
-import { promisify } from 'util';
-import { readFile, writeFile, unlink } from 'fs';
-import glob from 'fast-glob';
-import {
- isDefined,
- intersection,
- listToMap,
- mapToList,
- unique,
- difference,
-} from '@togglecorp/fujs';
-
-import {
- TranslationFileContent,
- MigrationFileContent,
- SourceFileContent,
-} from './types';
-
-const readFilePromisify = promisify(readFile);
-export const writeFilePromisify = promisify(writeFile);
-const unlinkPromisify = promisify(unlink);
-
-// Utilities
-
-export function oneOneMapping(
- prevState: T[],
- currentState: T[],
- keySelector: (item: T) => K,
- validate: (prevStateItem: T, currentStateItem: T) => boolean,
-) {
- const prevStateMapping = listToMap(
- prevState,
- keySelector,
- (item) => item,
- );
- const currentStateMapping = listToMap(
- currentState,
- keySelector,
- (item) => item,
- );
-
- const prevStateKeySet = new Set(
- Object.keys(prevStateMapping) as Array
- );
- const currentStateKeySet = new Set(
- Object.keys(currentStateMapping) as Array,
- );
-
- const commonKeySet = intersection(prevStateKeySet, currentStateKeySet);
- const prevStateExclusiveKeySet = difference(prevStateKeySet, commonKeySet);
- const currentStateExclusiveKeySet = difference(currentStateKeySet, commonKeySet);
-
- const commonItems = [...commonKeySet].map(
- (key) => ({
- key,
- prevStateItem: prevStateMapping[key],
- currentStateItem: currentStateMapping[key],
- })
- )
-
- const commonItemsMap = listToMap(
- commonItems,
- ({ key }) => key,
- );
-
- const validCommonItems = commonItems.filter(
- ({ prevStateItem, currentStateItem }) => validate(prevStateItem, currentStateItem)
- );
-
- const validCommonItemsKeySet = new Set(
- validCommonItems.map(({ key }) => key)
- );
-
- const invalidCommonItemsKeySet = difference(commonKeySet, validCommonItemsKeySet);
- const invalidCommonItems = Array.from(invalidCommonItemsKeySet).map(
- (key) => commonItemsMap[key],
- );
-
- return {
- validCommonItems,
- invalidCommonItems,
- prevStateRemainder: Array.from(prevStateExclusiveKeySet).map((key) => prevStateMapping[key]),
- currentStateRemainder: Array.from(currentStateExclusiveKeySet).map((key) => currentStateMapping[key]),
- };
-}
-
-export function oneOneMappingNonUnique(
- prevState: T[],
- currentState: T[],
- keySelector: (item: T) => K,
-) {
- const prevStateWithKey = prevState.map(
- (item) => {
- const key = keySelector(item);
-
- return {
- key,
- item,
- };
- }
- );
-
- const currentStateWithKey = currentState.map(
- (item) => {
- const key = keySelector(item);
-
- return {
- key,
- item,
- };
- }
- );
-
- const {
- commonItems,
- prevStateRemainder,
- currentStateRemainder,
- } = prevStateWithKey.reduce(
- (acc, prevStateItem) => {
- const matchIndex = acc.currentStateRemainder.findIndex(
- ({ key }) => prevStateItem.key === key,
- )
-
- if (matchIndex === -1) {
- return acc;
- }
-
- const prevStateMatchIndex = acc.prevStateRemainder.findIndex(
- ({ key }) => prevStateItem.key === key,
- );
-
- if (prevStateMatchIndex === -1) {
- return acc;
- }
-
- return {
- commonItems: [
- ...acc.commonItems,
- {
- prevStateItem: prevStateItem,
- currentStateItem: acc.currentStateRemainder[matchIndex],
- },
- ],
- prevStateRemainder: [
- ...acc.prevStateRemainder.slice(0, prevStateMatchIndex),
- ...acc.prevStateRemainder.slice(prevStateMatchIndex + 1),
- ],
- currentStateRemainder: [
- ...acc.currentStateRemainder.slice(0, matchIndex),
- ...acc.currentStateRemainder.slice(matchIndex + 1),
- ],
- }
- },
- {
- prevStateRemainder: [...prevStateWithKey],
- commonItems: [] as {
- prevStateItem: { key: K, item: T },
- currentStateItem: { key: K, item: T },
- }[],
- currentStateRemainder: [...currentStateWithKey],
- },
- );
-
- return {
- commonItems: commonItems.map(
- ({ prevStateItem, currentStateItem }) => ({
- prevStateItem: prevStateItem.item,
- currentStateItem: currentStateItem.item,
- })
- ),
- prevStateRemainder: prevStateRemainder.map(
- ({ item }) => item,
- ),
- currentStateRemainder: currentStateRemainder.map(
- ({ item }) => item,
- ),
- };
-}
-
-export function getDuplicateItems(
- list: T[],
- keySelector: (value: T) => string,
-) {
- if (!list) {
- return [];
- }
- const counts = listToMap(
- list,
- keySelector,
- (_, key, __, acc) => {
- const value = acc[key];
- return isDefined(value) ? value + 1 : 1;
- },
- );
-
- return list
- .filter((item) => counts[keySelector(item)] > 1)
- .sort((foo, bar) => keySelector(foo).localeCompare(keySelector(bar)));
-}
-
-export function concat(...args: string[]) {
- return args.join(":");
-}
-
-export function removeUndefinedKeys(itemFromArgs: T) {
- const item = {...itemFromArgs};
- Object.keys(item).forEach(key => {
- if (item[key as keyof T] === undefined) {
- delete item[key as keyof T];
- }
- });
- return item;
-}
-
-export async function getMigrationFilesAttrs(basePath: string, pathName: string) {
- const fullPath = isAbsolute(pathName)
- ? join(pathName, '[0-9]+-[0-9]+.json')
- : join(basePath, pathName, '[0-9]+-[0-9]+.json')
-
- const files = await glob(fullPath, { ignore: ['node_modules'], absolute: true });
-
- interface MigrationFileAttrs {
- migrationName: string;
- fileName: string;
- num: string;
- timestamp: string;
- }
-
- const migrationFiles = files
- .map((file): MigrationFileAttrs | undefined => {
- const migrationName = basename(file);
- const attrs = migrationName.match(/(?[0-9]+)-(?[0-9]+)/)?.groups as (Omit | undefined)
- if (attrs) {
- return {
- ...attrs,
- migrationName,
- fileName: file,
- }
- }
- return undefined;
- })
- .filter(isDefined)
- .sort((a, b) => a.migrationName.localeCompare(b.migrationName));
- return migrationFiles;
-}
-
-export async function getTranslationFileNames(basePath: string, pathNames: string[]) {
- const fullPathNames = pathNames.map((pathName) => (
- isAbsolute(pathName)
- ? pathName
- : join(basePath, pathName)
- ));
-
- const fileNamesPromise = fullPathNames.map(async (fullPathName) => {
- return glob(fullPathName, { ignore: ['node_modules'], absolute: true });
- });
- const fileNames = (await Promise.all(fileNamesPromise)).flat();
- return unique(fileNames);
-}
-
-export async function readJsonFilesContents(fileNames: string[]) {
- const contentsPromise = fileNames.map(async (fileName) => {
- const fileDescriptor = await readFilePromisify(fileName);
- try {
- const content = JSON.parse(fileDescriptor.toString());
- return {
- file: fileName,
- content,
- };
- } catch {
- throw `Error while parsing JSON for ${fileName}}`;
- }
- });
- const contents = await Promise.all(contentsPromise);
- return contents;
-}
-
-export async function readTranslations(fileNames: string[]) {
- const filesContents = await readJsonFilesContents(fileNames);
-
- const translations = filesContents.flatMap((fileContent) => {
- // TODO: validate the schema for content
- const {
- file,
- content,
- } = fileContent as {
- file: string,
- content: TranslationFileContent,
- };
-
- return mapToList(
- content.strings,
- (item, key) => ({
- file,
- namespace: content.namespace,
- key,
- value: item,
- }),
- );
- });
-
- return { translations, filesContents };
-}
-
-export async function readMigrations(fileNames: string[]) {
- const fileContents = await readJsonFilesContents(fileNames);
- // TODO: validate the schema for content
- return fileContents as { file: string, content: MigrationFileContent }[];
-}
-
-export async function readSource(fileName: string) {
- const fileContents = await readJsonFilesContents([fileName]);
- // TODO: validate the schema for content
- return fileContents[0] as {
- file: string, content: SourceFileContent
- };
-}
-
-export async function removeFiles(files: string[]) {
- const removePromises = files.map(async (file) => (
- unlinkPromisify(file)
- ));
- await Promise.all(removePromises);
-}
diff --git a/go-web-app-develop/app/src/App/Auth.tsx b/go-web-app-develop/app/src/App/Auth.tsx
deleted file mode 100644
index 999e9dd14a28f16c805c3f4203d70f9554503274..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/Auth.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import {
- Fragment,
- type ReactElement,
-} from 'react';
-import {
- Navigate,
- useParams,
-} from 'react-router-dom';
-
-import FourHundredThree from '#components/FourHundredThree';
-import useAuth from '#hooks/domain/useAuth';
-import usePermissions from '#hooks/domain/usePermissions';
-
-import { type ExtendedProps } from './routes/common';
-
-interface Props {
- children: ReactElement,
- context: ExtendedProps,
- absolutePath: string,
-}
-function Auth(props: Props) {
- const {
- context,
- children,
- absolutePath,
- } = props;
-
- const urlParams = useParams();
- const perms = usePermissions();
-
- const { isAuthenticated } = useAuth();
-
- if (context.visibility === 'is-authenticated' && !isAuthenticated) {
- return (
-
- );
- }
- if (context.visibility === 'is-not-authenticated' && isAuthenticated) {
- return (
-
- );
- }
-
- if (context.permissions) {
- const hasPermission = context.permissions(perms, urlParams);
-
- if (!hasPermission) {
- return (
-
- );
- }
- }
-
- return (
-
- {children}
-
- );
-}
-
-export default Auth;
diff --git a/go-web-app-develop/app/src/App/PageError/i18n.json b/go-web-app-develop/app/src/App/PageError/i18n.json
deleted file mode 100644
index cae3ca02dc7d9642313627cd7e65faf3b1f26f83..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/PageError/i18n.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "namespace": "common",
- "strings": {
- "errorPageIssueMessage": "Oops! Looks like we ran into some issue!",
- "errorPageUnexpectedMessage": "Something unexpected happened!",
- "errorPageHide": "Hide Error",
- "errorPageShowError": "Show Full Error",
- "errorPageStackTrace": "Stack trace not available",
- "errorSeeDeveloperConsole": "See the developer console for more details",
- "errorPageGoBack": "Go back to homepage",
- "errorPageReload": "Reload"
- }
-}
\ No newline at end of file
diff --git a/go-web-app-develop/app/src/App/PageError/index.tsx b/go-web-app-develop/app/src/App/PageError/index.tsx
deleted file mode 100644
index 6a10d7acb62cb64f99bfe9abc984312a53a272b7..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/PageError/index.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import {
- useCallback,
- useEffect,
-} from 'react';
-import { useRouteError } from 'react-router-dom';
-import { Button } from '@ifrc-go/ui';
-import {
- useBooleanState,
- useTranslation,
-} from '@ifrc-go/ui/hooks';
-
-import Link from '#components/Link';
-
-import i18n from './i18n.json';
-import styles from './styles.module.css';
-
-function PageError() {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const errorResponse = useRouteError() as unknown as any;
- const strings = useTranslation(i18n);
-
- useEffect(
- () => {
- // eslint-disable-next-line no-console
- console.error(errorResponse);
- },
- [errorResponse],
- );
-
- const [
- fullErrorVisible,
- {
- toggle: toggleFullErrorVisibility,
- },
- ] = useBooleanState(false);
-
- const handleReloadButtonClick = useCallback(
- () => {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- window.location.reload(true);
- },
- [],
- );
-
- return (
-
-
-
-
- {strings.errorPageIssueMessage}
-
-
- {errorResponse?.error?.message
- ?? errorResponse?.message
- ?? strings.errorPageUnexpectedMessage}
-
-
- {fullErrorVisible && (
- <>
-
- {errorResponse?.error?.stack
- ?? errorResponse?.stack ?? strings.errorPageStackTrace}
-
-
- {strings.errorSeeDeveloperConsole}
-
- >
- )}
-
-
- {/* NOTE: using the anchor element as it will refresh the page */}
-
- {strings.errorPageGoBack}
-
-
-
-
-
- );
-}
-
-export default PageError;
diff --git a/go-web-app-develop/app/src/App/PageError/styles.module.css b/go-web-app-develop/app/src/App/PageError/styles.module.css
deleted file mode 100644
index cea5a9d39b84cf31c4d6819c8f3bc84f0d7480c0..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/PageError/styles.module.css
+++ /dev/null
@@ -1,49 +0,0 @@
-.page-error {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100vw;
- height: 100vh;
-
- .container {
- display: flex;
- flex-direction: column;
- border-top: var(--go-ui-width-separator-large) solid var(--go-ui-color-primary-red);
- border-radius: var(--go-ui-border-radius-xl);
- box-shadow: var(--go-ui-box-shadow-2xl);
- background-color: var(--go-ui-color-white);
- padding: var(--go-ui-spacing-2xl);
- width: calc(100% - var(--go-ui-spacing-2xl));
- max-width: 60rem;
- max-height: 60rem;
- gap: var(--go-ui-spacing-2xl);
-
- .content {
- display: flex;
- flex-direction: column;
- gap: var(--go-ui-spacing-md);
-
- .heading {
- margin: 0;
- font-weight: var(--go-ui-font-weight-medium);
- }
-
- .stack {
- flex-grow: 1;
- background-color: var(--go-ui-color-background);
- padding: var(--go-ui-spacing-md);
- width: 100%;
- overflow: auto;
- white-space: pre;
- font-family: var(--go-ui-font-family-mono);
- }
- }
-
- .footer {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- gap: var(--go-ui-spacing-md);
- }
- }
-}
diff --git a/go-web-app-develop/app/src/App/index.tsx b/go-web-app-develop/app/src/App/index.tsx
deleted file mode 100644
index ba1e866f064da56f8be640bddc8c385f57764d5f..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/index.tsx
+++ /dev/null
@@ -1,269 +0,0 @@
-import {
- useCallback,
- useEffect,
- useMemo,
- useState,
-} from 'react';
-import {
- createBrowserRouter,
- RouterProvider,
-} from 'react-router-dom';
-import {
- AlertContext,
- type AlertContextProps,
- type AlertParams,
- type Language,
- LanguageContext,
- type LanguageContextProps,
- type LanguageNamespaceStatus,
-} from '@ifrc-go/ui/contexts';
-import * as Sentry from '@sentry/react';
-import {
- isDefined,
- unique,
-} from '@togglecorp/fujs';
-import mapboxgl from 'mapbox-gl';
-
-import goLogo from '#assets/icons/go-logo-2020.svg';
-import {
- appTitle,
- mbtoken,
-} from '#config';
-import RouteContext from '#contexts/route';
-import UserContext, {
- type UserAuth,
- type UserContextProps,
-} from '#contexts/user';
-import {
- KEY_LANGUAGE_STORAGE,
- KEY_USER_STORAGE,
-} from '#utils/constants';
-import {
- getFromStorage,
- removeFromStorage,
- setToStorage,
-} from '#utils/localStorage';
-import { RequestContext } from '#utils/restRequest';
-import {
- processGoError,
- processGoOptions,
- processGoResponse,
- processGoUrls,
-} from '#utils/restRequest/go';
-
-import wrappedRoutes, { unwrappedRoutes } from './routes';
-
-import styles from './styles.module.css';
-
-const requestContextValue = {
- transformUrl: processGoUrls,
- transformOptions: processGoOptions,
- transformResponse: processGoResponse,
- transformError: processGoError,
-};
-const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouter(createBrowserRouter);
-const router = sentryCreateBrowserRouter(unwrappedRoutes);
-mapboxgl.accessToken = mbtoken;
-mapboxgl.setRTLTextPlugin(
- 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.2.3/mapbox-gl-rtl-text.js',
- // eslint-disable-next-line no-console
- (err) => { console.error(err); },
- true,
-);
-
-function Application() {
- // ALERTS
-
- const [alerts, setAlerts] = useState([]);
-
- const addAlert = useCallback((alert: AlertParams) => {
- setAlerts((prevAlerts) => unique(
- [...prevAlerts, alert],
- (a) => a.name,
- ) ?? prevAlerts);
- }, [setAlerts]);
-
- const removeAlert = useCallback((name: AlertParams['name']) => {
- setAlerts((prevAlerts) => {
- const i = prevAlerts.findIndex((a) => a.name === name);
- if (i === -1) {
- return prevAlerts;
- }
-
- const newAlerts = [...prevAlerts];
- newAlerts.splice(i, 1);
-
- return newAlerts;
- });
- }, [setAlerts]);
-
- const updateAlert = useCallback((name: AlertParams['name'], paramsWithoutName: Omit) => {
- setAlerts((prevAlerts) => {
- const i = prevAlerts.findIndex((a) => a.name === name);
- if (i === -1) {
- return prevAlerts;
- }
-
- const updatedAlert = {
- ...prevAlerts[i],
- paramsWithoutName,
- };
-
- const newAlerts = [...prevAlerts];
- newAlerts.splice(i, 1, updatedAlert);
-
- return newAlerts;
- });
- }, [setAlerts]);
-
- const alertContextValue: AlertContextProps = useMemo(() => ({
- alerts,
- addAlert,
- updateAlert,
- removeAlert,
- }), [alerts, addAlert, updateAlert, removeAlert]);
-
- // AUTH
-
- const [userAuth, setUserAuth] = useState();
-
- const hydrateUserAuth = useCallback(() => {
- const userDetailsFromStorage = getFromStorage(KEY_USER_STORAGE);
- if (userDetailsFromStorage) {
- setUserAuth(userDetailsFromStorage);
- }
- }, []);
-
- const removeUserAuth = useCallback(() => {
- removeFromStorage(KEY_USER_STORAGE);
- setUserAuth(undefined);
- }, []);
-
- const setAndStoreUserAuth = useCallback((newUserDetails: UserAuth) => {
- setUserAuth(newUserDetails);
- setToStorage(
- KEY_USER_STORAGE,
- newUserDetails,
- );
- }, []);
-
- // Translation
-
- const [strings, setStrings] = useState({});
- const [currentLanguage, setCurrentLanguage] = useState('en');
- const [
- languageNamespaceStatus,
- setLanguageNamespaceStatus,
- ] = useState>({});
-
- const setAndStoreCurrentLanguage = useCallback(
- (newLanguage: Language) => {
- setCurrentLanguage(newLanguage);
- setToStorage(KEY_LANGUAGE_STORAGE, newLanguage);
- },
- [],
- );
-
- const registerLanguageNamespace = useCallback(
- (namespace: string, fallbackStrings: Record) => {
- setStrings(
- (prevValue) => {
- if (isDefined(prevValue[namespace])) {
- return {
- ...prevValue,
- [namespace]: {
- ...fallbackStrings,
- ...prevValue[namespace],
- },
- };
- }
-
- return {
- ...prevValue,
- [namespace]: fallbackStrings,
- };
- },
- );
-
- setLanguageNamespaceStatus((prevValue) => {
- if (isDefined(prevValue[namespace])) {
- return prevValue;
- }
-
- return {
- ...prevValue,
- // NOTE: This will fetch if the data is not already fetched
- [namespace]: prevValue[namespace] === 'fetched' ? 'fetched' : 'queued',
- };
- });
- },
- [setStrings],
- );
-
- // Hydration
- useEffect(() => {
- hydrateUserAuth();
-
- const language = getFromStorage(KEY_LANGUAGE_STORAGE);
- setCurrentLanguage(language ?? 'en');
- }, [hydrateUserAuth]);
-
- const userContextValue = useMemo(
- () => ({
- userAuth,
- hydrateUserAuth,
- setUserAuth: setAndStoreUserAuth,
- removeUserAuth,
- }),
- [userAuth, hydrateUserAuth, setAndStoreUserAuth, removeUserAuth],
- );
-
- const languageContextValue = useMemo(
- () => ({
- languageNamespaceStatus,
- setLanguageNamespaceStatus,
- currentLanguage,
- setCurrentLanguage: setAndStoreCurrentLanguage,
- strings,
- setStrings,
- registerNamespace: registerLanguageNamespace,
- }),
- [
- languageNamespaceStatus,
- setLanguageNamespaceStatus,
- currentLanguage,
- setAndStoreCurrentLanguage,
- strings,
- registerLanguageNamespace,
- ],
- );
-
- return (
-
-
-
-
-
-
-
- {`${appTitle} loading...`}
-
- )}
- />
-
-
-
-
-
- );
-}
-
-const App = Sentry.withProfiler(Application);
-export default App;
diff --git a/go-web-app-develop/app/src/App/routes/CountryRoutes.tsx b/go-web-app-develop/app/src/App/routes/CountryRoutes.tsx
deleted file mode 100644
index ec04b860e8218088c2b37d9cace16020001d955d..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/routes/CountryRoutes.tsx
+++ /dev/null
@@ -1,515 +0,0 @@
-import {
- generatePath,
- Navigate,
- useParams,
-} from 'react-router-dom';
-import {
- isDefined,
- isTruthyString,
-} from '@togglecorp/fujs';
-
-import { countryIdToRegionIdMap } from '#utils/domain/country';
-
-import Auth from '../Auth';
-import {
- customWrapRoute,
- rootLayout,
-} from './common';
-import regionRoutes from './RegionRoutes';
-import SmartNavigate from './SmartNavigate';
-
-type DefaultCountriesChild = 'ongoing-activities';
-const countriesLayout = customWrapRoute({
- parent: rootLayout,
- path: 'countries/:countryId',
- forwardPath: 'ongoing-activities' satisfies DefaultCountriesChild,
- component: {
- render: () => import('#views/Country'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Country',
- visibility: 'anything',
- },
-});
-
-interface Props {
- to?: string;
-}
-
-// eslint-disable-next-line react-refresh/only-export-components
-function CountryNavigate(props: Props) {
- // FIXME: this function might not be necessary anymore
- const { to } = props;
-
- const params = useParams<{ countryId: string }>();
-
- const countryId = isTruthyString(params.countryId) ? parseInt(params.countryId, 10) : undefined;
- const regionId = isDefined(countryId) ? countryIdToRegionIdMap[countryId] : undefined;
-
- if (isDefined(regionId)) {
- const regionPath = generatePath(
- regionRoutes.regionIndex.absoluteForwardPath,
- { regionId },
- );
- return (
-
- );
- }
-
- if (to) {
- return (
-
- );
- }
-
- return (
-
- );
-}
-
-const countryIndex = customWrapRoute({
- parent: countriesLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: CountryNavigate,
- props: {},
- },
- context: {
- title: 'Country',
- visibility: 'anything',
- },
-});
-
-type DefaultOngoingActivitiesChild = 'emergencies';
-const countryOngoingActivitiesLayout = customWrapRoute({
- parent: countriesLayout,
- path: 'ongoing-activities',
- forwardPath: 'emergencies' satisfies DefaultOngoingActivitiesChild,
- component: {
- render: () => import('#views/CountryOngoingActivities'),
- props: {},
- },
- context: {
- title: 'Country Ongoing Activities',
- visibility: 'anything',
- },
-});
-
-const countryOngoingActivitiesIndex = customWrapRoute({
- parent: countryOngoingActivitiesLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'emergencies' satisfies DefaultOngoingActivitiesChild,
- replace: true,
- },
- },
- context: {
- title: 'Country Ongoing Activities Index',
- visibility: 'anything',
- },
-});
-
-const countryOngoingActivitiesEmergencies = customWrapRoute({
- parent: countryOngoingActivitiesLayout,
- path: 'emergencies' satisfies DefaultOngoingActivitiesChild,
- component: {
- render: () => import('#views/CountryOngoingActivitiesEmergencies'),
- props: {},
- },
- context: {
- title: 'Country Ongoing Emergencies',
- visibility: 'anything',
- },
-});
-
-const countryOngoingActivitiesThreeWActivities = customWrapRoute({
- parent: countryOngoingActivitiesLayout,
- path: 'three-w/activities',
- component: {
- render: () => import('#views/CountryOngoingActivitiesThreeWActivities'),
- props: {},
- },
- context: {
- title: 'Country 3W Activities',
- visibility: 'anything',
- },
-});
-
-const countryOngoingActivitiesThreeWProjects = customWrapRoute({
- parent: countryOngoingActivitiesLayout,
- path: 'three-w/projects',
- component: {
- render: () => import('#views/ThreeWDecommission'),
- props: {},
- },
- context: {
- title: 'Country 3W Projects',
- visibility: 'anything',
- },
-});
-
-type DefaultNsOverviewChild = 'activities';
-const countryNsOverviewLayout = customWrapRoute({
- parent: countriesLayout,
- path: 'ns-overview',
- forwardPath: 'activities' satisfies DefaultNsOverviewChild,
- component: {
- render: () => import('#views/CountryNsOverview'),
- props: {},
- },
- context: {
- title: 'Country NS Overview',
- visibility: 'anything',
- },
-});
-
-const countryNsOverviewIndex = customWrapRoute({
- parent: countryNsOverviewLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'activities' satisfies DefaultNsOverviewChild,
- replace: true,
- },
- },
- context: {
- title: 'Country National Society Overview Index',
- visibility: 'anything',
- },
-});
-
-const countryNsOverviewActivities = customWrapRoute({
- parent: countryNsOverviewLayout,
- path: 'activities',
- component: {
- render: () => import('#views/CountryNsOverviewActivities'),
- props: {},
- },
- context: {
- title: 'Country NS Activities',
- visibility: 'anything',
- },
-});
-
-const countryNsOverviewContextAndStructure = customWrapRoute({
- parent: countryNsOverviewLayout,
- path: 'context-and-structure',
- component: {
- render: () => import('#views/CountryNsOverviewContextAndStructure'),
- props: {},
- },
- context: {
- title: 'Country NS Context and Structure',
- visibility: 'anything',
- },
-});
-
-const countryNsOverviewStrategicPriorities = customWrapRoute({
- parent: countryNsOverviewLayout,
- path: 'strategic-priorities',
- component: {
- render: () => import('#views/CountryNsOverviewStrategicPriorities'),
- props: {},
- },
- context: {
- title: 'Country NS Strategic Priorities',
- visibility: 'anything',
- },
-});
-
-const countryNsOverviewCapacity = customWrapRoute({
- parent: countryNsOverviewLayout,
- path: 'capacity',
- component: {
- render: () => import('#views/CountryNsOverviewCapacity'),
- props: {},
- },
- context: {
- title: 'Country NS Capacity',
- visibility: 'anything',
- },
-});
-
-const countryNsOverviewSupportingPartners = customWrapRoute({
- parent: countryNsOverviewLayout,
- path: 'partners',
- component: {
- render: () => import('#views/CountryNsOverviewSupportingPartners'),
- props: {},
- },
- context: {
- title: 'Country NS Partners',
- visibility: 'anything',
- },
-});
-
-const countryPreparedness = customWrapRoute({
- parent: countriesLayout,
- path: 'ns-overview/per/:perId',
- component: {
- render: () => import('#views/CountryPreparedness'),
- props: {},
- },
- context: {
- title: 'Country Preparedness',
- visibility: 'anything',
- },
-});
-
-const perExport = customWrapRoute({
- parent: rootLayout,
- path: 'countries/:countryId/per/:perId/export',
- component: {
- render: () => import('#views/PerExport'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'PER Export',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-type DefaultCountryProfileChild = 'overview';
-const countryProfileLayout = customWrapRoute({
- parent: countriesLayout,
- path: 'profile',
- forwardPath: 'overview' satisfies DefaultCountryProfileChild,
- component: {
- render: () => import('#views/CountryProfile'),
- props: {},
- },
- context: {
- title: 'Country Profile',
- visibility: 'anything',
- },
-});
-
-const countryProfileIndex = customWrapRoute({
- parent: countryProfileLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'overview' satisfies DefaultCountryProfileChild,
- replace: true,
- },
- },
- context: {
- title: 'Country Profile Index',
- visibility: 'anything',
- },
-});
-
-const countryProfileOverview = customWrapRoute({
- parent: countryProfileLayout,
- path: 'overview',
- component: {
- render: () => import('#views/CountryProfileOverview'),
- props: {},
- },
- context: {
- title: 'Country Profile Overview',
- visibility: 'anything',
- },
-});
-
-const countryProfilePreviousEvents = customWrapRoute({
- parent: countryProfileLayout,
- path: 'previous-events',
- component: {
- render: () => import('#views/CountryProfilePreviousEvents'),
- props: {},
- },
- context: {
- title: 'Country Profile Previous Events',
- visibility: 'anything',
- },
-});
-
-const countryProfileSeasonalRisks = customWrapRoute({
- parent: countryProfileLayout,
- path: 'risk-watch',
- component: {
- render: () => import('#views/CountryProfileRiskWatch'),
- props: {},
- },
- context: {
- title: 'Country Profile Seasonal Risks',
- visibility: 'anything',
- },
-});
-
-const countryAdditionalInfo = customWrapRoute({
- parent: countriesLayout,
- path: 'additional-info',
- component: {
- render: () => import('#views/CountryAdditionalInfo'),
- props: {},
- },
- context: {
- title: 'Country Additional Info',
- visibility: 'anything',
- },
-});
-
-// Redirect routes
-const countryOperations = customWrapRoute({
- parent: countriesLayout,
- path: 'operations',
- component: {
- eagerLoad: true,
- render: CountryNavigate,
- props: {
- to: countryOngoingActivitiesEmergencies.absolutePath,
- },
- },
- context: {
- title: 'Country Ongoing Activities Emergencies',
- visibility: 'anything',
- },
-});
-
-const countriesThreeW = customWrapRoute({
- parent: countriesLayout,
- path: 'three-w/ns-projects',
- component: {
- eagerLoad: true,
- render: CountryNavigate,
- props: {
- to: countryNsOverviewActivities.absolutePath,
- },
- },
- context: {
- title: 'Country 3W Activities',
- visibility: 'anything',
- },
-});
-
-const countriesThreeWProjects = customWrapRoute({
- parent: countriesLayout,
- path: 'three-w/projects',
- component: {
- eagerLoad: true,
- render: CountryNavigate,
- props: {
- to: countryOngoingActivitiesThreeWProjects.absolutePath,
- },
- },
- context: {
- title: 'Country 3W Projects',
- visibility: 'anything',
- },
-});
-
-const countryRiskWatch = customWrapRoute({
- parent: countriesLayout,
- path: 'risk-watch',
- component: {
- eagerLoad: true,
- render: CountryNavigate,
- props: {
- to: countryProfileSeasonalRisks.absolutePath,
- },
- },
- context: {
- title: 'Country Profile Seasonal Risks',
- visibility: 'anything',
- },
-});
-
-const countryPreparednessRedirect = customWrapRoute({
- parent: countriesLayout,
- path: 'preparedness',
- component: {
- eagerLoad: true,
- render: CountryNavigate,
- props: {
- to: countryNsOverviewCapacity.absolutePath,
- },
- },
- context: {
- title: 'Country NS Capacity',
- visibility: 'anything',
- },
-});
-
-const countryPlan = customWrapRoute({
- parent: countriesLayout,
- path: 'plan',
- component: {
- eagerLoad: true,
- render: CountryNavigate,
- props: {
- to: countryNsOverviewStrategicPriorities.absolutePath,
- },
- },
- context: {
- title: 'Country NS Strategic Priorities',
- visibility: 'anything',
- },
-});
-
-export default {
- countriesLayout,
- countryIndex,
-
- countryOngoingActivitiesLayout,
- countryOngoingActivitiesIndex,
- countryOngoingActivitiesEmergencies,
- countryOngoingActivitiesThreeWActivities,
- countryOngoingActivitiesThreeWProjects,
-
- countryNsOverviewLayout,
- countryNsOverviewIndex,
- countryNsOverviewActivities,
- countryNsOverviewContextAndStructure,
- countryNsOverviewStrategicPriorities,
- countryNsOverviewCapacity,
- countryPreparedness,
- perExport,
-
- countryProfileLayout,
- countryProfileIndex,
- countryProfileOverview,
- countryNsOverviewSupportingPartners,
- countryProfilePreviousEvents,
- countryProfileSeasonalRisks,
-
- countryAdditionalInfo,
-
- // Redirects
- countryOperations,
- countriesThreeW,
- countriesThreeWProjects,
- countryRiskWatch,
- countryPreparednessRedirect,
- countryPlan,
-};
diff --git a/go-web-app-develop/app/src/App/routes/RegionRoutes.tsx b/go-web-app-develop/app/src/App/routes/RegionRoutes.tsx
deleted file mode 100644
index c75ead1e7d0e2e6b700bc0cf38eb3beaf23465c4..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/routes/RegionRoutes.tsx
+++ /dev/null
@@ -1,186 +0,0 @@
-import { Navigate } from 'react-router-dom';
-
-import Auth from '../Auth';
-import {
- customWrapRoute,
- rootLayout,
-} from './common';
-import SmartNavigate from './SmartNavigate';
-
-type DefaultRegionsChild = 'operations';
-const regionsLayout = customWrapRoute({
- parent: rootLayout,
- path: 'regions/:regionId',
- forwardPath: 'operations' satisfies DefaultRegionsChild,
- component: {
- render: () => import('#views/Region'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Region',
- visibility: 'anything',
- },
-});
-
-const regionIndex = customWrapRoute({
- parent: regionsLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: SmartNavigate,
- props: {
- to: 'operations' satisfies DefaultRegionsChild,
- replace: true,
- hashToRouteMap: {
- '#operations': 'operations',
- '#3w': 'three-w',
- '#risk-watch': 'risk-watch',
- '#regional-profile': 'profile',
- '#preparedness': 'preparedness',
- '#additional-info': 'additional-info',
- },
- },
- },
- context: {
- title: 'Region',
- visibility: 'anything',
- },
-});
-
-const regionOperations = customWrapRoute({
- parent: regionsLayout,
- path: 'operations' satisfies DefaultRegionsChild,
- component: {
- render: () => import('#views/RegionOperations'),
- props: {},
- },
- context: {
- title: 'Region Operations',
- visibility: 'anything',
- },
-});
-
-const regionThreeW = customWrapRoute({
- parent: regionsLayout,
- path: 'three-w',
- component: {
- render: () => import('#views/ThreeWDecommission'),
- props: {},
- },
- context: {
- title: 'Region 3W',
- visibility: 'anything',
- },
-});
-
-type DefaultRegionRiskWatchChild = 'seasonal';
-const regionRiskWatchLayout = customWrapRoute({
- parent: regionsLayout,
- path: 'risk-watch',
- forwardPath: 'seasonal' satisfies DefaultRegionRiskWatchChild,
- component: {
- render: () => import('#views/RegionRiskWatch'),
- props: {},
- },
- context: {
- title: 'Region Risk Watch',
- visibility: 'anything',
- },
-});
-
-const regionRiskIndex = customWrapRoute({
- parent: regionRiskWatchLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'seasonal' satisfies DefaultRegionRiskWatchChild,
- replace: true,
- },
- },
- context: {
- title: 'Region Risk Watch',
- visibility: 'anything',
- },
-});
-
-const regionImminentRiskWatch = customWrapRoute({
- parent: regionRiskWatchLayout,
- path: 'imminent',
- component: {
- render: () => import('#views/RegionRiskWatchImminent'),
- props: {},
- },
- context: {
- title: 'Region Imminent Risk Watch',
- visibility: 'anything',
- },
-});
-
-const regionSeasonalRiskWatch = customWrapRoute({
- parent: regionRiskWatchLayout,
- path: 'seasonal' satisfies DefaultRegionRiskWatchChild,
- component: {
- render: () => import('#views/RegionRiskWatchSeasonal'),
- props: {},
- },
- context: {
- title: 'Region Seasonal Risk Watch',
- visibility: 'anything',
- },
-});
-
-const regionPreparedness = customWrapRoute({
- parent: regionsLayout,
- path: 'preparedness',
- component: {
- render: () => import('#views/RegionPreparedness'),
- props: {},
- },
- context: {
- title: 'Region Preparedness',
- visibility: 'anything',
- },
-});
-
-const regionProfile = customWrapRoute({
- parent: regionsLayout,
- path: 'profile',
- component: {
- render: () => import('#views/RegionProfile'),
- props: {},
- },
- context: {
- title: 'Region Profile',
- visibility: 'anything',
- },
-});
-
-const regionAdditionalInfo = customWrapRoute({
- parent: regionsLayout,
- path: 'additional-info',
- component: {
- render: () => import('#views/RegionAdditionalInfo'),
- props: {},
- },
- context: {
- title: 'Region Additional Info',
- visibility: 'anything',
- },
-});
-
-export default {
- regionsLayout,
- regionIndex,
- regionOperations,
- regionThreeW,
- regionRiskWatchLayout,
- regionRiskIndex,
- regionImminentRiskWatch,
- regionSeasonalRiskWatch,
- regionPreparedness,
- regionProfile,
- regionAdditionalInfo,
-};
diff --git a/go-web-app-develop/app/src/App/routes/SmartNavigate.tsx b/go-web-app-develop/app/src/App/routes/SmartNavigate.tsx
deleted file mode 100644
index e278fc052ca7b085ce28ac0c03aaf4b19ac9d4fc..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/routes/SmartNavigate.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import {
- Navigate,
- type NavigateProps,
- useLocation,
-} from 'react-router-dom';
-import {
- isDefined,
- isTruthyString,
-} from '@togglecorp/fujs';
-
-type RouteKey = string;
-
-interface Props extends NavigateProps {
- hashToRouteMap: Record
;
- forwardUnmatchedHashTo?: string;
-}
-
-function SmartNavigate(props: Props) {
- const {
- hashToRouteMap,
- forwardUnmatchedHashTo,
- ...navigateProps
- } = props;
-
- const location = useLocation();
- const newRoute = isTruthyString(location.hash)
- ? (hashToRouteMap[location.hash] ?? forwardUnmatchedHashTo)
- : undefined;
-
- if (isDefined(newRoute)) {
- return (
-
- );
- }
-
- return (
-
- );
-}
-
-export default SmartNavigate;
diff --git a/go-web-app-develop/app/src/App/routes/SurgeRoutes.tsx b/go-web-app-develop/app/src/App/routes/SurgeRoutes.tsx
deleted file mode 100644
index 1d9b63a271b1c6cfac9d376236e404f7900ba63f..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/routes/SurgeRoutes.tsx
+++ /dev/null
@@ -1,1702 +0,0 @@
-import {
- Navigate,
- Outlet,
- useParams,
-} from 'react-router-dom';
-import {
- isDefined,
- isTruthyString,
-} from '@togglecorp/fujs';
-
-import type { MyOutputNonIndexRouteObject } from '#utils/routes';
-
-import Auth from '../Auth';
-import {
- customWrapRoute,
- type ExtendedProps,
- rootLayout,
-} from './common';
-
-type DefaultSurgeChild = 'active-surge-deployments';
-const surgeLayout = customWrapRoute({
- parent: rootLayout,
- path: 'surge',
- forwardPath: 'active-surge-deployments' satisfies DefaultSurgeChild,
- component: {
- render: () => import('#views/Surge'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Surge',
- visibility: 'anything',
- },
-});
-
-const surgeIndex = customWrapRoute({
- parent: surgeLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'active-surge-deployments' satisfies DefaultSurgeChild,
- replace: true,
- },
- },
- context: {
- title: 'Surge',
- visibility: 'anything',
- },
-});
-
-const activeSurgeDeployments = customWrapRoute({
- parent: surgeLayout,
- path: 'active-surge-deployments',
- component: {
- render: () => import('#views/ActiveSurgeDeployments'),
- props: {},
- },
- context: {
- title: 'Active Surge Deployments',
- visibility: 'anything',
- },
-});
-
-type DefaultSurgeOverviewChild = 'rapid-response-personnel';
-
-const surgeOverviewLayout = customWrapRoute({
- parent: surgeLayout,
- path: 'overview',
- forwardPath: 'rapid-response-personnel' satisfies DefaultSurgeOverviewChild,
- component: {
- render: () => import('#views/SurgeOverview'),
- props: {},
- },
- context: {
- title: 'Surge Overview',
- visibility: 'anything',
- },
-});
-
-const surgeOverviewIndex = customWrapRoute({
- parent: surgeOverviewLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'rapid-response-personnel' satisfies DefaultSurgeOverviewChild,
- replace: true,
- },
- },
- context: {
- title: 'Surge Overview',
- visibility: 'anything',
- },
-});
-
-const rapidResponsePersonnel = customWrapRoute({
- parent: surgeOverviewLayout,
- path: 'rapid-response-personnel',
- component: {
- render: () => import('#views/SurgeOverview/RapidResponsePersonnel'),
- props: {},
- },
- context: {
- title: 'Rapid Response Personnel',
- visibility: 'anything',
- },
-});
-
-const emergencyResponseUnit = customWrapRoute({
- parent: surgeOverviewLayout,
- path: 'emergency-response-unit',
- component: {
- render: () => import('#views/SurgeOverview/EmergencyResponseUnit'),
- props: {},
- },
- context: {
- title: 'Emergency Response Unit',
- visibility: 'anything',
- },
-});
-
-const eruReadinessForm = customWrapRoute({
- parent: rootLayout,
- path: 'eru-readiness',
- component: {
- render: () => import('#views/EruReadinessForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'ERU Readiness Update Form',
- visibility: 'is-authenticated',
- permissions: ({
- isRegionalOrCountryAdmin,
- isSuperUser,
- }) => isSuperUser || isRegionalOrCountryAdmin,
- },
-});
-
-const surgeOperationalToolbox = customWrapRoute({
- parent: surgeLayout,
- path: 'operational-toolbox',
- component: {
- render: () => import('#views/SurgeOperationalToolbox'),
- props: {},
- },
- context: {
- title: 'Surge Operational Toolbox',
- visibility: 'anything',
- },
-});
-
-type DefaultSurgeCatalogueChild = 'overview';
-const surgeCatalogueLayout = customWrapRoute({
- parent: surgeLayout,
- path: 'catalogue',
- forwardPath: 'overview' satisfies DefaultSurgeCatalogueChild,
- component: {
- render: () => import('#views/SurgeCatalogue'),
- props: {},
- },
- context: {
- title: 'Surge Catalogue',
- visibility: 'anything',
- },
-});
-
-const catalogueIndex = customWrapRoute({
- parent: surgeCatalogueLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'overview' satisfies DefaultSurgeCatalogueChild,
- replace: true,
- },
- },
- context: {
- title: 'Surge Catalogue',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOverview = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'overview' satisfies DefaultSurgeCatalogueChild,
- component: {
- render: () => import('#views/SurgeCatalogueOverview'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Surge Catalogue Overview',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueEmergencyNeedsAssessment = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'emergency-needs-assessment',
- component: {
- render: () => import('#views/SurgeCatalogueEmergencyNeedsAssessment'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergency Needs Assessment',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueEmergencyNeedsAssessmentCell = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'emergency-needs-assessment/cell',
- component: {
- render: () => import('#views/SurgeCatalogueEmergencyNeedsAssessmentCell'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Assessment Cell',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueAdministration = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'administration',
- component: {
- render: () => import('#views/SurgeCatalogueAdministration'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Administration',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueBasecamp = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'basecamp',
- component: {
- render: () => import('#views/SurgeCatalogueBasecamp'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Basecamp Catalogue',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueBasecampEruSmall = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'basecamp/eru-small',
- component: {
- render: () => import('#views/SurgeCatalogueBasecampEruSmall'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Basecamp ERU Small',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueBasecampEruMedium = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'basecamp/eru-medium',
- component: {
- render: () => import('#views/SurgeCatalogueBasecampEruMedium'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Basecamp ERU Medium',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueBasecampEruLarge = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'basecamp/eru-large',
- component: {
- render: () => import('#views/SurgeCatalogueBasecampEruLarge'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Basecamp ERU Large',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueBasecampFacilityManagement = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'basecamp/facility-management',
- component: {
- render: () => import('#views/SurgeCatalogueBasecampFacilityManagement'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Basecamp Facility Management',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueBasecampOffice = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'basecamp/office',
- component: {
- render: () => import('#views/SurgeCatalogueBasecampOffice'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Basecamp Office',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueBasecampWelcome = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'basecamp/welcome',
- component: {
- render: () => import('#views/SurgeCatalogueBasecampWelcome'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Basecamp Admin and Welcome Service',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueCash = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'cash',
- component: {
- render: () => import('#views/SurgeCatalogueCash'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Cash and Vouchers Assistance',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueCashRapidResponse = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'cash/rapid-response',
- component: {
- render: () => import('#views/SurgeCatalogueCashRapidResponse'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Cash and Vouchers Assistance - Rapid Response',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueCommunityEngagement = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'community-engagement',
- component: {
- render: () => import('#views/SurgeCatalogueCommunityEngagement'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Community Engagement and Accountability (CEA)',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueCommunityEngagementRapidResponse = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'community/rapid-response',
- component: {
- render: () => import('#views/SurgeCatalogueCommunityEngagementRapidResponse'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Community Engagement and Accountability (CEA) - Rapid Response',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueCommunication = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'communication',
- component: {
- render: () => import('#views/SurgeCatalogueCommunication'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Communication',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueCommunicationErtOne = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'communication/cert-1',
- component: {
- render: () => import('#views/SurgeCatalogueCommunicationErtOne'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Communication Emergency Response Tool 1',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueCommunicationErtTwo = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'communication/cert-2',
- component: {
- render: () => import('#views/SurgeCatalogueCommunicationErtTwo'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Communication Emergency Response Tool 2',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueCommunicationErtThree = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'communication/cert-3',
- component: {
- render: () => import('#views/SurgeCatalogueCommunicationErtThree'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Communication Emergency Response Tool 3',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealth = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health',
- component: {
- render: () => import('#views/SurgeCatalogueHealth'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Health',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthEruClinic = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/eru-clinic',
- component: {
- render: () => import('#views/SurgeCatalogueHealthEruClinic'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'ERU Red Cross Red Crescent Emergency Clinic',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthEruHospital = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/eru-hospital',
- component: {
- render: () => import('#views/SurgeCatalogueHealthEruHospital'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'ERU Red Cross Red Crescent Emergency Hospital',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthEruSurgical = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/eru-surgical',
- component: {
- render: () => import('#views/SurgeCatalogueHealthEruSurgical'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Health Surgical',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthMaternalNewbornClinic = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/maternal-newborn-clinic',
- component: {
- render: () => import('#views/SurgeCatalogueHealthMaternalNewbornClinic'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Maternal NewBorn Health Clinic',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthEmergencyClinic = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/emergency-clinic',
- component: {
- render: () => import('#views/SurgeCatalogueHealthEmergencyClinic'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergency Mobile Clinic',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthEruCholeraTreatment = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/eru-cholera-treatment',
- component: {
- render: () => import('#views/SurgeCatalogueHealthEruCholeraTreatment'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergency Response Unit Cholera Treatment Center',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthCommunityCaseManagementCholera = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/community-case-management-cholera',
- component: {
- render: () => import('#views/SurgeCatalogueHealthCommunityCaseManagementCholera'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Community Case Management of Cholera',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthCommunityBasedSurveillance = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/community-based-surveillance',
- component: {
- render: () => import('#views/SurgeCatalogueHealthCommunityBasedSurveillance'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Community Based Surveillance',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthSafeDignifiedBurials = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/safe-dignified-burials',
- component: {
- render: () => import('#views/SurgeCatalogueHealthSafeDignifiedBurials'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Safe and Dignified Burials',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthInfectionPreventionAndControl = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/infection-prevention-and-control',
- component: {
- render: () => import('#views/SurgeCatalogueHealthInfectionPreventionAndControl'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Infection Prevention and Control',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthCommunityManagementMalnutrition = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/community-management-malnutrition',
- component: {
- render: () => import('#views/SurgeCatalogueHealthCommunityManagementMalnutrition'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Community Case Management of Malnutrition',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueHealthEruPsychosocialSupport = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'health/eru-psychosocial-support',
- component: {
- render: () => import('#views/SurgeCatalogueHealthEruPsychosocialSupport'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergency Response Unit Psychosocial Support',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueInformationManagement = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'information-management',
- component: {
- render: () => import('#views/SurgeCatalogueInformationManagement'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Information Management',
- visibility: 'anything',
- },
-
-});
-
-const surgeCatalogueInformationManagementSatelliteImagery = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'information-management/satellite-imagery',
- component: {
- render: () => import('#views/SurgeCatalogueInformationManagementSatelliteImagery'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Satellite Imagery',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueInformationManagementRolesResponsibility = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'information-management/roles-responsibility',
- component: {
- render: () => import('#views/SurgeCatalogueInformationManagementRolesResponsibility'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Roles and Responsibilities',
- visibility: 'anything',
- },
-});
-
-// TODO: update view name
-const surgeCatalogueInformationManagementRegionalOfficeSupport = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'information-management/regional-office-support',
- component: {
- render: () => import('#views/SurgeCatalogueInformationManagementSupport'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Information Management Support - Regional Office',
- visibility: 'anything',
- },
-});
-
-// TODO: update view name
-const surgeCatalogueInformationManagementGenevaSupport = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'information-management/geneva-support',
- component: {
- render: () => import('#views/SurgeCatalogueInformationManagementOperationSupport'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Information Management Support - Geneva',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueInformationManagementComposition = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'information-management/composition',
- component: {
- render: () => import('#views/SurgeCatalogueInformationManagementComposition'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Composition of IM Resources',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueInformationTechnology = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'information-technology',
- component: {
- render: () => import('#views/SurgeCatalogueInformationTechnology'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Information Technology',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueInformationTechnologyEruItTelecom = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'information-technology/eru-it-telecom',
- component: {
- render: () => import('#views/SurgeCatalogueInformationTechnologyEruItTelecom'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Information Technology Service',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueLivelihood = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'livelihood',
- component: {
- render: () => import('#views/SurgeCatalogueLivelihood'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Livelihoods and Basic Needs',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueLivelihoodServices = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'livelihood/services',
- component: {
- render: () => import('#views/SurgeCatalogueLivelihoodServices'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Livelihood Service',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueLogistics = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'logistics',
- component: {
- render: () => import('#views/SurgeCatalogueLogistics'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Logistics',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueLogisticsEru = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'logistics/eru',
- component: {
- render: () => import('#views/SurgeCatalogueLogisticsEru'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergency Response Unit',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueLogisticsLpscmNs = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'logistics/lpscm-ns',
- component: {
- render: () => import('#views/SurgeCatalogueLogisticsLpscmNs'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'LPSCM for National Societies',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOperationsManagement = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'operations-management',
- component: {
- render: () => import('#views/SurgeCatalogueOperationsManagement'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Operations Management',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOperationManagementHeops = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'operations-management/heops',
- component: {
- render: () => import('#views/SurgeCatalogueOperationsManagementHeops'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Head of Emergency Operations (HEOPS)',
- visibility: 'anything',
- },
-});
-
-const surgeCataloguePgi = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'pgi',
- component: {
- render: () => import('#views/SurgeCataloguePgi'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Protection, Gender and inclusion (PGI)',
- visibility: 'anything',
- },
-});
-
-const surgeCataloguePgiServices = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'pgi/services',
- component: {
- render: () => import('#views/SurgeCataloguePgiServices'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Protection Gender and Inclusion - Services',
- visibility: 'anything',
- },
-});
-
-const surgeCataloguePmer = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'pmer',
- component: {
- render: () => import('#views/SurgeCataloguePmer'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Planning, Monitoring, Evaluation And Reporting (PMER)',
- visibility: 'anything',
- },
-});
-
-const surgeCataloguePmerEmergencyPlanAction = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'pmer/emergency-plan-action',
- component: {
- render: () => import('#views/SurgeCataloguePmerEmergencyPlanAction'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergency plan of action EPOA monitoring evaluation plan',
- visibility: 'anything',
- },
-});
-
-const surgeCataloguePmerRealTimeEvaluation = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'pmer/real-time-evaluation',
- component: {
- render: () => import('#views/SurgeCataloguePmerRealTimeEvaluation'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Real time evaluation RTE and guidance',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueShelter = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'shelter',
- component: {
- render: () => import('#views/SurgeCatalogueShelter'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Shelter',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueShelterCoordinatorTeam = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'shelter/coordinator-team',
- component: {
- render: () => import('#views/SurgeCatalogueShelterCoordinatorTeam'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Shelter Surge Coordinator',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueShelterTechnicalTeam = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'shelter/technical-team',
- component: {
- render: () => import('#views/SurgeCatalogueShelterTechnicalTeam'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Shelter Technical Team',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWash = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash',
- component: {
- render: () => import('#views/SurgeCatalogueWash'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Water, Sanitation and Hygiene (WASH)',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWashKit2 = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash/kit-2',
- component: {
- render: () => import('#views/SurgeCatalogueWashKit2'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'WASH Kit-2',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWashKit5 = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash/kit-5',
- component: {
- render: () => import('#views/SurgeCatalogueWashKit5'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'WASH Kit-5',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWashKitM15Eru = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash/m15-eru',
- component: {
- render: () => import('#views/SurgeCatalogueWashKitM15Eru'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'WASH Kit-M15 ERU',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWashKitMsm20Eru = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash/msm20-eru',
- component: {
- render: () => import('#views/SurgeCatalogueWashKitMsm20Eru'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Wash Kit-MSM20 ERU',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWashKitM40Eru = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash/m40-eru',
- component: {
- render: () => import('#views/SurgeCatalogueWashKitM40Eru'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Wash Kit-M40 ERU',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWashWaterSupplyRehabilitation = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash/water-supply-rehabilitation',
- component: {
- render: () => import('#views/SurgeCatalogueWashWaterSupplyRehabilitation'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Water Supply Rehabilitation',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWashHwts = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash/hwts',
- component: {
- render: () => import('#views/SurgeCatalogueWashHwts'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Household Water Treatment and Safe Storage (HWTS)',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueWashSludge = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'wash/sludge',
- component: {
- render: () => import('#views/SurgeCatalogueWashSludge'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Faecal Sludge Management',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueRelief = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'relief',
- component: {
- render: () => import('#views/SurgeCatalogueRelief'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Relief',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueReliefEru = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'relief/eru',
- component: {
- render: () => import('#views/SurgeCatalogueReliefEru'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Relief ERU',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueSecurity = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'security',
- component: {
- render: () => import('#views/SurgeCatalogueSecurity'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Security',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueSecurityManagement = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'security/management',
- component: {
- render: () => import('#views/SurgeCatalogueSecurityManagement'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Security Management',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherCivilMilitaryRelations = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/civil-military-relations',
- component: {
- render: () => import('#views/SurgeCatalogueOtherCivilMilitaryRelations'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Civil Military Relations',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherDisasterRiskReduction = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/disaster-risk-reduction',
- component: {
- render: () => import('#views/SurgeCatalogueOtherDisasterRiskReduction'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Disaster Risk Reduction',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherHumanitarianDiplomacy = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/humanitarian-diplomacy',
- component: {
- render: () => import('#views/SurgeCatalogueOtherHumanitarianDiplomacy'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Humanitarian Diplomacy',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherHumanResources = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/human-resources',
- component: {
- render: () => import('#views/SurgeCatalogueOtherHumanResources'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Human Resources',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherInternationalDisasterResponseLaw = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/international-disaster-response-law',
- component: {
- render: () => import('#views/SurgeCatalogueOtherInternationalDisasterResponseLaw'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'International Disaster Response Law',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherMigration = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/migration',
- component: {
- render: () => import('#views/SurgeCatalogueOtherMigration'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Migration',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherNationalSocietyDevelopment = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/national-society-development',
- component: {
- render: () => import('#views/SurgeCatalogueOtherNationalSocietyDevelopment'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'National Society Development',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherStrategicPartnershipsResourceMobilisation = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/strategic-partnership-resource-mobilisation',
- component: {
- render: () => import('#views/SurgeCatalogueOtherStrategicPartnershipsResourceMobilisation'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Partnership Resource Development',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherPreparednessEffectiveResponse = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/preparedness-effective-response',
- component: {
- render: () => import('#views/SurgeCatalogueOtherPreparednessEffectiveResponse'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Preparedness Effective Response',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherRecovery = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/recovery',
- component: {
- render: () => import('#views/SurgeCatalogueOtherRecovery'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Recovery',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherGreenResponse = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/green-response',
- component: {
- render: () => import('#views/SurgeCatalogueOtherGreenResponse'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Green Response',
- visibility: 'anything',
- },
-});
-
-const surgeCatalogueOtherUAV = customWrapRoute({
- parent: surgeCatalogueLayout,
- path: 'other/uav',
- component: {
- render: () => import('#views/SurgeCatalogueOtherUAV'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Uncrewed Aerial Vehicles (Drones)',
- visibility: 'anything',
- },
-});
-
-const allDeployedPersonnel = customWrapRoute({
- parent: rootLayout,
- path: 'deployed-personnels/all',
- component: {
- render: () => import('#views/AllDeployedPersonnel'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'All Deployed Personnel',
- visibility: 'anything',
- },
-});
-
-const allDeployedEmergencyResponseUnits = customWrapRoute({
- parent: rootLayout,
- path: 'deployed-erus/all',
- component: {
- render: () => import('#views/AllDeployedEmergencyResponseUnits'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'All Deployed Emergency Response Units',
- visibility: 'anything',
- },
-});
-
-// eslint-disable-next-line react-refresh/only-export-components
-function DeploymentNavigate() {
- const params = useParams<{ surgeId: string }>();
-
- const deploymentRouteMap: Record> = {
- overview: surgeOverviewLayout,
- 'operational-toolbox': surgeOperationalToolbox,
- personnel: allDeployedPersonnel,
- erus: allDeployedEmergencyResponseUnits,
- };
-
- const newRoute = isDefined(params.surgeId)
- ? deploymentRouteMap[params.surgeId]
- : undefined;
-
- const path = isDefined(newRoute)
- ? newRoute.absoluteForwardPath
- : surgeOverviewLayout.absoluteForwardPath;
-
- return (
-
- );
-}
-
-const deploymentOthers = customWrapRoute({
- parent: rootLayout,
- path: 'deployments/:surgeId/*',
- component: {
- eagerLoad: true,
- render: DeploymentNavigate,
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Catalogue of surge services',
- visibility: 'anything',
- },
-});
-
-const deploymentCatalogueLayout = customWrapRoute({
- parent: rootLayout,
- path: 'deployments/catalogue',
- component: {
- eagerLoad: true,
- render: () => ,
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Catalogue of surge services',
- visibility: 'anything',
- },
-});
-
-// eslint-disable-next-line react-refresh/only-export-components
-function DeploymentCatalogueNavigate() {
- const params = useParams<{
- catalogueId: string,
- subCatalogueId: string,
- }>();
-
- type WrappedRoute = MyOutputNonIndexRouteObject;
-
- const catalogueRouteMap: Record = {
- overview: surgeCatalogueOverview,
- emergency: surgeCatalogueEmergencyNeedsAssessment,
- administration: surgeCatalogueAdministration,
- basecamp: surgeCatalogueBasecamp,
- cash: surgeCatalogueCash,
- community: surgeCatalogueCommunityEngagement,
- communications: surgeCatalogueCommunication,
- health: surgeCatalogueHealth,
- infoMgt: surgeCatalogueInformationManagement,
- informationTech: surgeCatalogueInformationTechnology,
- livelihoods: surgeCatalogueLivelihood,
- logistics: surgeCatalogueLogistics,
- operations: surgeCatalogueOperationsManagement,
- protection: surgeCataloguePgi,
- planning: surgeCataloguePmer,
- relief: surgeCatalogueRelief,
- security: surgeCatalogueSecurity,
- shelter: surgeCatalogueShelter,
- water: surgeCatalogueWash,
- };
-
- const subCatalogueRouteMap: Record> = {
- emergency: {
- 'assessment-cell': surgeCatalogueEmergencyNeedsAssessmentCell,
- },
- basecamp: {
- 'eru-base-camp-small': surgeCatalogueBasecampEruSmall,
- 'eru-base-camp-medium': surgeCatalogueBasecampEruMedium,
- 'eru-base-camp-large': surgeCatalogueBasecampEruLarge,
- 'facility-management': surgeCatalogueBasecampFacilityManagement,
- office: surgeCatalogueBasecampOffice,
- welcome: surgeCatalogueBasecampWelcome,
- },
- cash: {
- cva: surgeCatalogueCashRapidResponse,
- },
- community: {
- 'community-engagement-and-accountability': surgeCatalogueCommunityEngagementRapidResponse,
- },
- communications: {
- 'communications-emergency-response-tool-cert-3': surgeCatalogueCommunicationErtThree,
- 'communications-emergency-response-tool-cert-2': surgeCatalogueCommunicationErtTwo,
- 'communications-emergency-response-tool-cert-1': surgeCatalogueCommunicationErtOne,
- },
- health: {
- 'eru-pss-module': surgeCatalogueHealthEruPsychosocialSupport,
- 'community-case-management-of-malnutrition-ccmm': surgeCatalogueHealthCommunityManagementMalnutrition,
- 'safe-and-dignified-burials': surgeCatalogueHealthSafeDignifiedBurials,
- 'infection-prevention-and-control': surgeCatalogueHealthInfectionPreventionAndControl,
- 'community-based-surveillance-cbs': surgeCatalogueHealthCommunityBasedSurveillance,
- 'community-case-management-of-cholera-ccmc': surgeCatalogueHealthCommunityCaseManagementCholera,
- 'eru-cholera-treatment-center': surgeCatalogueHealthEruCholeraTreatment,
- 'emergency-mobile-clinic': surgeCatalogueHealthEmergencyClinic,
- 'maternal-newborn-health-clinic': surgeCatalogueHealthMaternalNewbornClinic,
- 'surgical-surge': surgeCatalogueHealthEruSurgical,
- 'eru-red-cross-red-crescent-emergency-hospital': surgeCatalogueHealthEruHospital,
- 'eru-red-cross-red-crescent-emergency-clinic': surgeCatalogueHealthEruClinic,
- },
- infoMgt: {
- // NOTE: sims was probably replace with link to its site
- // 'surge-information-management-support-sims': ,
- 'roles-and-resps': surgeCatalogueInformationManagementRolesResponsibility,
- 'im-support-for-op': surgeCatalogueInformationManagementRegionalOfficeSupport,
- 'ifrc-geneva-im': surgeCatalogueInformationManagementGenevaSupport,
- 'composition-of-im-res': surgeCatalogueInformationManagementComposition,
- 'Satellite-imagery': surgeCatalogueInformationManagementSatelliteImagery,
- },
- informationTech: {
- 'eru-it-telecom': surgeCatalogueInformationTechnologyEruItTelecom,
- },
- livelihoods: {
- 'livelihoods-and-basic-needs': surgeCatalogueLivelihoodServices,
- },
- logistics: {
- 'lpscm-for-national-societies': surgeCatalogueLogisticsLpscmNs,
- 'logistics-eru': surgeCatalogueLogisticsEru,
- },
- operations: {
- 'head-of-emergency-operations-heops': surgeCatalogueOperationManagementHeops,
- },
- protection: {
- 'protection-gender-and-inclusion': surgeCataloguePgiServices,
- },
- planning: {
- 'real-time-evaluation-rte-and-guidance': surgeCataloguePmerRealTimeEvaluation,
- 'emergency-plan-of-action-epoa-monitoring-evaluation-plan': surgeCataloguePmerEmergencyPlanAction,
- },
- relief: {
- 'eru-relief': surgeCatalogueReliefEru,
- },
- security: {
- 'security-management': surgeCatalogueSecurityManagement,
- },
- shelter: {
- 'stt-shelter-technical-team': surgeCatalogueShelterTechnicalTeam,
- 'sct-shelter-coordination-team': surgeCatalogueShelterCoordinatorTeam,
- },
- water: {
- 'faecal-sludge-management': surgeCatalogueWashSludge,
- 'household-water-treatment-and-safe-storage-hwts': surgeCatalogueWashHwts,
- 'water-supply-rehabilitation-wsr': surgeCatalogueWashWaterSupplyRehabilitation,
- 'm40-eru': surgeCatalogueWashKitM40Eru,
- 'msm20-eru': surgeCatalogueWashKitMsm20Eru,
- 'm15-eru': surgeCatalogueWashKitM15Eru,
- 'kit-5': surgeCatalogueWashKit5,
- 'kit-2': surgeCatalogueWashKit2,
- },
- other: {
- 'civil-military-relations': surgeCatalogueOtherCivilMilitaryRelations,
- 'disaster-risk-reduction-drr': surgeCatalogueOtherDisasterRiskReduction,
- 'humanitarian-diplomacy': surgeCatalogueOtherHumanitarianDiplomacy,
- 'human-resources': surgeCatalogueOtherHumanResources,
- 'international-disaster-response-law': surgeCatalogueOtherInternationalDisasterResponseLaw,
- migration: surgeCatalogueOtherMigration,
- 'national-society-development': surgeCatalogueOtherNationalSocietyDevelopment,
- 'strategic-partnership-resource-mobilisation': surgeCatalogueOtherStrategicPartnershipsResourceMobilisation,
- 'preparedness-for-effective-response-per': surgeCatalogueOtherPreparednessEffectiveResponse,
- recovery: surgeCatalogueOtherRecovery,
- greenresponse: surgeCatalogueOtherGreenResponse,
- uav: surgeCatalogueOtherUAV,
- },
- };
-
- const newCatalogueRoute = isTruthyString(params.catalogueId)
- ? catalogueRouteMap[params.catalogueId]
- : undefined;
-
- const newSubCatalogueRoute = isTruthyString(params.catalogueId)
- && isTruthyString(params.subCatalogueId)
- ? subCatalogueRouteMap[params.catalogueId]?.[params.subCatalogueId]
- : undefined;
-
- const path = newSubCatalogueRoute?.absoluteForwardPath
- ?? newCatalogueRoute?.absoluteForwardPath
- ?? surgeCatalogueOverview.absoluteForwardPath;
-
- return (
-
- );
-}
-
-const deploymentCatalogueIndex = customWrapRoute({
- parent: deploymentCatalogueLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: '/surge/catalogue',
- replace: true,
- },
- },
- wrapperComponent: Auth,
- context: {
- title: 'Catalogue of Surge Services',
- visibility: 'anything',
- },
-});
-
-const deploymentCatalogueChildren = customWrapRoute({
- parent: deploymentCatalogueLayout,
- path: ':catalogueId/:subCatalogueId?',
- component: {
- eagerLoad: true,
- render: DeploymentCatalogueNavigate,
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Catalogue of Surge Services',
- visibility: 'anything',
- },
-});
-
-const obsoleteUrlDeployments = customWrapRoute({
- parent: rootLayout,
- path: 'deployments',
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: surgeOverviewLayout.absolutePath,
- },
- },
- wrapperComponent: Auth,
- context: {
- title: 'Surge Overview',
- visibility: 'anything',
- },
-});
-
-export default {
- surgeLayout,
- surgeOverviewLayout,
- surgeOperationalToolbox,
- surgeCatalogueLayout,
- surgeOverviewIndex,
- surgeIndex,
- catalogueIndex,
- surgeCatalogueOverview,
- surgeCatalogueEmergencyNeedsAssessment,
- surgeCatalogueEmergencyNeedsAssessmentCell,
- surgeCatalogueAdministration,
- surgeCatalogueBasecamp,
- surgeCatalogueBasecampEruSmall,
- surgeCatalogueBasecampEruMedium,
- surgeCatalogueBasecampEruLarge,
- surgeCatalogueBasecampFacilityManagement,
- surgeCatalogueBasecampOffice,
- surgeCatalogueBasecampWelcome,
- surgeCatalogueCash,
- surgeCatalogueCashRapidResponse,
- surgeCatalogueCommunityEngagement,
- surgeCatalogueCommunityEngagementRapidResponse,
- surgeCatalogueCommunication,
- surgeCatalogueCommunicationErtOne,
- surgeCatalogueCommunicationErtTwo,
- surgeCatalogueCommunicationErtThree,
- surgeCatalogueHealth,
- surgeCatalogueHealthEruClinic,
- surgeCatalogueHealthEruHospital,
- surgeCatalogueHealthEruSurgical,
- surgeCatalogueHealthMaternalNewbornClinic,
- surgeCatalogueHealthEmergencyClinic,
- surgeCatalogueHealthEruCholeraTreatment,
- surgeCatalogueHealthCommunityCaseManagementCholera,
- surgeCatalogueHealthCommunityBasedSurveillance,
- surgeCatalogueHealthSafeDignifiedBurials,
- surgeCatalogueHealthInfectionPreventionAndControl,
- surgeCatalogueHealthCommunityManagementMalnutrition,
- surgeCatalogueHealthEruPsychosocialSupport,
- surgeCatalogueInformationManagement,
- surgeCatalogueInformationManagementSatelliteImagery,
- surgeCatalogueInformationManagementRolesResponsibility,
- surgeCatalogueInformationManagementRegionalOfficeSupport,
- surgeCatalogueInformationManagementGenevaSupport,
- surgeCatalogueInformationManagementComposition,
- surgeCatalogueInformationTechnology,
- surgeCataloguePmer,
- surgeCataloguePmerEmergencyPlanAction,
- surgeCataloguePmerRealTimeEvaluation,
- surgeCatalogueInformationTechnologyEruItTelecom,
- surgeCatalogueLivelihood,
- surgeCatalogueLivelihoodServices,
- surgeCatalogueSecurity,
- surgeCatalogueSecurityManagement,
- surgeCatalogueLogistics,
- surgeCatalogueLogisticsEru,
- surgeCatalogueLogisticsLpscmNs,
- surgeCatalogueOperationsManagement,
- surgeCatalogueOperationManagementHeops,
- surgeCataloguePgi,
- surgeCatalogueRelief,
- surgeCatalogueReliefEru,
- surgeCataloguePgiServices,
- surgeCatalogueShelter,
- surgeCatalogueShelterTechnicalTeam,
- surgeCatalogueShelterCoordinatorTeam,
- surgeCatalogueWash,
- surgeCatalogueWashKit2,
- surgeCatalogueWashKit5,
- surgeCatalogueWashKitM15Eru,
- surgeCatalogueWashKitMsm20Eru,
- surgeCatalogueWashKitM40Eru,
- surgeCatalogueWashWaterSupplyRehabilitation,
- surgeCatalogueWashHwts,
- surgeCatalogueWashSludge,
- surgeCatalogueOtherCivilMilitaryRelations,
- surgeCatalogueOtherDisasterRiskReduction,
- surgeCatalogueOtherHumanitarianDiplomacy,
- surgeCatalogueOtherHumanResources,
- surgeCatalogueOtherInternationalDisasterResponseLaw,
- surgeCatalogueOtherMigration,
- surgeCatalogueOtherNationalSocietyDevelopment,
- surgeCatalogueOtherStrategicPartnershipsResourceMobilisation,
- surgeCatalogueOtherPreparednessEffectiveResponse,
- surgeCatalogueOtherRecovery,
- surgeCatalogueOtherGreenResponse,
- surgeCatalogueOtherUAV,
-
- allDeployedPersonnel,
- allDeployedEmergencyResponseUnits,
-
- // Redirect routes
- deploymentCatalogueLayout,
- deploymentCatalogueIndex,
- deploymentCatalogueChildren,
- deploymentOthers,
- obsoleteUrlDeployments,
- activeSurgeDeployments,
- rapidResponsePersonnel,
- emergencyResponseUnit,
- eruReadinessForm,
-};
diff --git a/go-web-app-develop/app/src/App/routes/common.tsx b/go-web-app-develop/app/src/App/routes/common.tsx
deleted file mode 100644
index 4cd480efda22a640b469ef814f97a017413fb959..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/routes/common.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import {
- type MyInputIndexRouteObject,
- type MyInputNonIndexRouteObject,
- type MyOutputIndexRouteObject,
- type MyOutputNonIndexRouteObject,
- wrapRoute,
-} from '#utils/routes';
-import { Component as RootLayout } from '#views/RootLayout';
-
-import Auth from '../Auth';
-import PageError from '../PageError';
-
-interface Perms {
- isDrefRegionalCoordinator: (regionId: number | undefined) => boolean,
- isRegionAdmin: (regionId: number | undefined) => boolean,
- isCountryAdmin: (countryId: number | undefined) => boolean,
- isRegionPerAdmin: (regionId: number | undefined) => boolean,
- isCountryPerAdmin: (countryId: number | undefined) => boolean,
- isRegionalOrCountryAdmin: boolean,
- isPerAdmin: boolean,
- isIfrcAdmin: boolean,
- isSuperUser: boolean,
- isGuestUser: boolean,
-}
-
-export type ExtendedProps = {
- title: string,
- visibility: 'is-authenticated' | 'is-not-authenticated' | 'anything',
- permissions?: (
- permissions: Perms,
- params: Record | undefined | null,
- ) => boolean;
-};
-
-interface CustomWrapRoute {
- (
- myRouteOptions: MyInputIndexRouteObject
- ): MyOutputIndexRouteObject
- (
- myRouteOptions: MyInputNonIndexRouteObject
- ): MyOutputNonIndexRouteObject
-}
-
-export const customWrapRoute: CustomWrapRoute = wrapRoute;
-
-// NOTE: We should not use layout or index routes in links
-
-export const rootLayout = customWrapRoute({
- path: '/',
- errorElement: ,
- component: {
- eagerLoad: true,
- render: RootLayout,
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'IFRC GO',
- visibility: 'anything',
- },
-});
diff --git a/go-web-app-develop/app/src/App/routes/index.tsx b/go-web-app-develop/app/src/App/routes/index.tsx
deleted file mode 100644
index 8a92455042854ed74fb9a1e6b88f0505fb5cf820..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/routes/index.tsx
+++ /dev/null
@@ -1,1371 +0,0 @@
-import {
- generatePath,
- Navigate,
- useParams,
-} from 'react-router-dom';
-
-import { unwrapRoute } from '#utils/routes';
-
-import Auth from '../Auth';
-import {
- customWrapRoute,
- rootLayout,
-} from './common';
-import countryRoutes from './CountryRoutes';
-import regionRoutes from './RegionRoutes';
-import SmartNavigate from './SmartNavigate';
-import surgeRoutes from './SurgeRoutes';
-
-const fourHundredFour = customWrapRoute({
- parent: rootLayout,
- path: '*',
- component: {
- render: () => import('#views/FourHundredFour'),
- props: {},
- },
- context: {
- title: '404',
- visibility: 'anything',
- },
-});
-
-const login = customWrapRoute({
- parent: rootLayout,
- path: 'login',
- component: {
- render: () => import('#views/Login'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Login',
- visibility: 'is-not-authenticated',
- },
-});
-
-const register = customWrapRoute({
- parent: rootLayout,
- path: 'register',
- component: {
- render: () => import('#views/Register'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Register',
- visibility: 'is-not-authenticated',
- },
-});
-
-const recoverAccount = customWrapRoute({
- parent: rootLayout,
- path: 'recover-account',
- component: {
- render: () => import('#views/RecoverAccount'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Recover Account',
- visibility: 'is-not-authenticated',
- },
-});
-
-const recoverAccountConfirm = customWrapRoute({
- parent: rootLayout,
- path: 'recover-account/:username/:token',
- component: {
- render: () => import('#views/RecoverAccountConfirm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Recover Account Confirm',
- visibility: 'is-not-authenticated',
- },
-});
-
-const resendValidationEmail = customWrapRoute({
- parent: rootLayout,
- path: 'resend-validation-email',
- component: {
- render: () => import('#views/ResendValidationEmail'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Resend Validation Email',
- visibility: 'is-not-authenticated',
- },
-});
-
-const home = customWrapRoute({
- parent: rootLayout,
- index: true,
- component: {
- render: () => import('#views/Home'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Home',
- visibility: 'anything',
- },
-});
-
-const emergencies = customWrapRoute({
- parent: rootLayout,
- path: 'emergencies',
- component: {
- render: () => import('#views/Emergencies'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergencies',
- visibility: 'anything',
- },
-});
-const cookiePolicy = customWrapRoute({
- parent: rootLayout,
- path: 'cookie-policy',
- component: {
- render: () => import('#views/CookiePolicy'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Cookie Policy',
- visibility: 'anything',
- },
-});
-
-type DefaultEmergenciesChild = 'details';
-const emergenciesLayout = customWrapRoute({
- parent: rootLayout,
- path: 'emergencies/:emergencyId',
- forwardPath: 'details' satisfies DefaultEmergenciesChild,
- component: {
- render: () => import('#views/Emergency'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergency',
- visibility: 'anything',
- },
-});
-
-const emergencySlug = customWrapRoute({
- parent: rootLayout,
- path: 'emergencies/slug/:slug',
- component: {
- render: () => import('#views/EmergencySlug'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Emergency',
- visibility: 'anything',
- },
-});
-
-const emergencyFollow = customWrapRoute({
- parent: rootLayout,
- path: 'emergencies/:emergencyId/follow',
- component: {
- render: () => import('#views/EmergencyFollow'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Follow Emergency',
- visibility: 'is-authenticated',
- },
-});
-
-const emergencyIndex = customWrapRoute({
- parent: emergenciesLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: SmartNavigate,
- props: {
- to: 'details' satisfies DefaultEmergenciesChild,
- replace: true,
- hashToRouteMap: {
- '#details': 'details',
- '#reports': 'reports',
- '#activities': 'activities',
- '#surge': 'surge',
- },
- // TODO: make this typesafe
- forwardUnmatchedHashTo: 'additional-info',
- },
- },
- context: {
- title: 'Emergency',
- visibility: 'anything',
- },
-});
-
-const emergencyDetails = customWrapRoute({
- parent: emergenciesLayout,
- path: 'details' satisfies DefaultEmergenciesChild,
- component: {
- render: () => import('#views/EmergencyDetails'),
- props: {},
- },
- context: {
- title: 'Emergency Details',
- visibility: 'anything',
- },
-});
-
-const emergencyReportsAndDocuments = customWrapRoute({
- parent: emergenciesLayout,
- path: 'reports',
- component: {
- render: () => import('#views/EmergencyReportAndDocument'),
- props: {},
- },
- context: {
- title: 'Emergency Reports and Documents',
- visibility: 'anything',
- },
-});
-
-const emergencyActivities = customWrapRoute({
- parent: emergenciesLayout,
- path: 'activities',
- component: {
- render: () => import('#views/EmergencyActivities'),
- props: {},
- },
- context: {
- title: 'Emergency Activities',
- visibility: 'anything',
- },
-});
-const emergencySurge = customWrapRoute({
- parent: emergenciesLayout,
- path: 'surge',
- component: {
- render: () => import('#views/EmergencySurge'),
- props: {},
- },
- context: {
- title: 'Emergency Surge',
- visibility: 'anything',
- },
-});
-
-// TODO: remove this route
-const emergencyAdditionalInfoOne = customWrapRoute({
- parent: emergenciesLayout,
- path: 'additional-info-1',
- component: {
- render: () => import('#views/EmergencyAdditionalTab'),
- props: {
- infoPageId: 1,
- },
- },
- context: {
- title: 'Emergency Additional Tab 1',
- visibility: 'anything',
- },
-});
-// TODO: remove this route
-const emergencyAdditionalInfoTwo = customWrapRoute({
- parent: emergenciesLayout,
- path: 'additional-info-2',
- component: {
- render: () => import('#views/EmergencyAdditionalTab'),
- props: {
- infoPageId: 2,
- },
- },
- context: {
- title: 'Emergency Additional Tab 2',
- visibility: 'anything',
- },
-});
-// TODO: remove this route
-const emergencyAdditionalInfoThree = customWrapRoute({
- parent: emergenciesLayout,
- path: 'additional-info-3',
- component: {
- render: () => import('#views/EmergencyAdditionalTab'),
- props: {
- infoPageId: 3,
- },
- },
- context: {
- title: 'Emergency Additional Tab 3',
- visibility: 'anything',
- },
-});
-
-const emergencyAdditionalInfo = customWrapRoute({
- parent: emergenciesLayout,
- path: 'additional-info/:tabId?',
- component: {
- render: () => import('#views/EmergencyAdditionalTab'),
- props: {},
- },
- context: {
- title: 'Emergency Additional Info Tab',
- visibility: 'anything',
- },
-});
-
-type DefaultPreparednessChild = 'global-summary';
-const preparednessLayout = customWrapRoute({
- parent: rootLayout,
- path: 'preparedness',
- forwardPath: 'global-summary' satisfies DefaultPreparednessChild,
- component: {
- render: () => import('#views/Preparedness'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Preparedness',
- visibility: 'anything',
- },
-});
-
-const preparednessIndex = customWrapRoute({
- parent: preparednessLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: SmartNavigate,
- props: {
- to: 'global-summary' satisfies DefaultPreparednessChild,
- replace: true,
- hashToRouteMap: {
- '#global-summary': 'global-summary',
- '#global-performance': 'global-performance',
- '#resources-catalogue': 'resources-catalogue',
- '#operational-learning': 'operational-learning',
- },
- },
- },
- context: {
- title: 'Preparedness',
- visibility: 'anything',
- },
-});
-
-const preparednessGlobalSummary = customWrapRoute({
- parent: preparednessLayout,
- path: 'global-summary' satisfies DefaultPreparednessChild,
- component: {
- render: () => import('#views/PreparednessGlobalSummary'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Preparedness - Global Summary',
- visibility: 'anything',
- },
-});
-
-const preparednessGlobalPerformance = customWrapRoute({
- parent: preparednessLayout,
- path: 'global-performance',
- component: {
- render: () => import('#views/PreparednessGlobalPerformance'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Preparedness - Global Performance',
- visibility: 'anything',
- },
-});
-
-const preparednessGlobalCatalogue = customWrapRoute({
- parent: preparednessLayout,
- path: 'resources-catalogue',
- component: {
- render: () => import('#views/PreparednessCatalogueResources'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Preparedness - Catalogue of Learning',
- visibility: 'anything',
- },
-});
-
-const globalThreeW = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/projects',
- component: {
- render: () => import('#views/GlobalThreeW'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Global 3W',
- visibility: 'anything',
- },
-});
-
-const newThreeWProject = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/projects/new',
- component: {
- render: () => import('#views/ThreeWDecommission'),
- props: { variant: 'page' },
- },
- wrapperComponent: Auth,
- context: {
- title: 'New 3W Project',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const threeWProjectDetail = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/projects/:projectId/',
- component: {
- render: () => import('#views/ThreeWDecommission'),
- props: { variant: 'page' },
- },
- wrapperComponent: Auth,
- context: {
- title: '3W Project Details',
- visibility: 'anything',
- },
-});
-
-const threeWProjectEdit = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/projects/:projectId/edit',
- component: {
- render: () => import('#views/ThreeWDecommission'),
- props: { variant: 'page' },
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit 3W Project',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const newThreeWActivity = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/activities/new',
- component: {
- render: () => import('#views/ThreeWActivityForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'New 3W Activity',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const threeWActivityDetail = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/activities/:activityId',
- component: {
- render: () => import('#views/ThreeWActivityDetail'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: '3W Activity Detail',
- visibility: 'anything',
- },
-});
-
-const threeWActivityEdit = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/activities/:activityId/edit',
- component: {
- render: () => import('#views/ThreeWActivityForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit 3W Activity',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-type DefaultRiskWatchChild = 'seasonal';
-const riskWatchLayout = customWrapRoute({
- parent: rootLayout,
- path: 'risk-watch',
- forwardPath: 'seasonal' satisfies DefaultRiskWatchChild,
- component: {
- render: () => import('#views/RiskWatch'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Risk Watch',
- visibility: 'anything',
- },
-});
-
-const riskWatchIndex = customWrapRoute({
- parent: riskWatchLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'seasonal' satisfies DefaultRiskWatchChild,
- replace: true,
- },
- },
- context: {
- title: 'Risk Watch',
- visibility: 'anything',
- },
-});
-
-const riskWatchSeasonal = customWrapRoute({
- parent: riskWatchLayout,
- path: 'seasonal' satisfies DefaultRiskWatchChild,
- component: {
- render: () => import('#views/RiskWatchSeasonal'),
- props: {},
- },
- context: {
- title: 'Seasonal Risk Watch',
- visibility: 'anything',
- },
-});
-
-const riskWatchImminent = customWrapRoute({
- parent: riskWatchLayout,
- path: 'imminent',
- component: {
- render: () => import('#views/RiskWatchImminent'),
- props: {},
- },
- context: {
- title: 'Imminent Risk Watch',
- visibility: 'anything',
- },
-});
-
-type DefaultAccountChild = 'details';
-const accountLayout = customWrapRoute({
- parent: rootLayout,
- path: 'account',
- forwardPath: 'details' satisfies DefaultAccountChild,
- component: {
- render: () => import('#views/Account'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Account',
- visibility: 'is-authenticated',
- },
-});
-
-const accountIndex = customWrapRoute({
- parent: accountLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: SmartNavigate,
- props: {
- to: 'details' satisfies DefaultAccountChild,
- replace: true,
- hashToRouteMap: {
- '#account-information': 'details',
- '#notifications': 'notifications',
- '#per-forms': 'my-forms/per',
- '#my-dref-applications': 'my-forms/dref',
- '#three-w-forms': 'my-forms/three-w',
- },
- },
- },
- context: {
- title: 'Account',
- visibility: 'anything',
- },
-});
-
-const accountDetails = customWrapRoute({
- parent: accountLayout,
- path: 'details' satisfies DefaultAccountChild,
- component: {
- render: () => import('#views/AccountDetails'),
- props: {},
- },
- context: {
- title: 'Account Details',
- visibility: 'is-authenticated',
- },
-});
-
-const termsAndConditions = customWrapRoute({
- parent: rootLayout,
- path: 'terms-and-conditions',
- component: {
- render: () => import('#views/TermsAndConditions'),
- props: {},
- },
- context: {
- title: 'Terms And Conditions',
- visibility: 'anything',
- },
-});
-type DefaultAccountMyFormsChild = 'field-report';
-const accountMyFormsLayout = customWrapRoute({
- parent: accountLayout,
- path: 'my-forms',
- forwardPath: 'field-report' satisfies DefaultAccountMyFormsChild,
- component: {
- render: () => import('#views/AccountMyFormsLayout'),
- props: {},
- },
- context: {
- title: 'Account - My Forms',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const accountMyFormsIndex = customWrapRoute({
- parent: accountMyFormsLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'field-report' satisfies DefaultAccountMyFormsChild,
- replace: true,
- },
- },
- context: {
- title: 'Account - My Forms',
- visibility: 'anything',
- },
-});
-
-const accountMyFormsFieldReport = customWrapRoute({
- parent: accountMyFormsLayout,
- path: 'field-report' satisfies DefaultAccountMyFormsChild,
- component: {
- render: () => import('#views/AccountMyFormsFieldReport'),
- props: {},
- },
- context: {
- title: 'Account - Field Report Forms',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const accountMyFormsPer = customWrapRoute({
- parent: accountMyFormsLayout,
- path: 'per',
- component: {
- render: () => import('#views/AccountMyFormsPer'),
- props: {},
- },
- context: {
- title: 'Account - PER Forms',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const accountMyFormsDref = customWrapRoute({
- parent: accountMyFormsLayout,
- path: 'dref',
- component: {
- render: () => import('#views/AccountMyFormsDref'),
- props: {},
- },
- context: {
- title: 'Account - DREF Applications',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const accountMyFormsThreeW = customWrapRoute({
- parent: accountMyFormsLayout,
- path: 'three-w',
- component: {
- render: () => import('#views/AccountMyFormsThreeW'),
- props: {},
- },
- context: {
- title: 'Account - 3W',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const accountNotifications = customWrapRoute({
- parent: accountLayout,
- path: 'notifications',
- component: {
- render: () => import('#views/AccountNotifications'),
- props: {},
- },
- context: {
- title: 'Account - Notifications',
- visibility: 'is-authenticated',
- },
-});
-
-const resources = customWrapRoute({
- parent: rootLayout,
- path: 'resources',
- component: {
- render: () => import('#views/Resources'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Resources',
- visibility: 'anything',
- },
-});
-const operationalLearning = customWrapRoute({
- parent: rootLayout,
- path: 'operational-learning',
- component: {
- render: () => import('#views/OperationalLearning'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Operational Learning',
- visibility: 'anything',
- },
-});
-
-const search = customWrapRoute({
- parent: rootLayout,
- path: 'search',
- component: {
- render: () => import('#views/Search'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Search',
- visibility: 'anything',
- },
-});
-
-const allThreeWProject = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/projects/all',
- component: {
- render: () => import('#views/ThreeWDecommission'),
- props: { variant: 'page' },
- },
- wrapperComponent: Auth,
- context: {
- title: 'All 3W Projects',
- visibility: 'anything',
- },
-});
-
-const allThreeWActivity = customWrapRoute({
- parent: rootLayout,
- path: 'three-w/activities/all',
- component: {
- render: () => import('#views/AllThreeWActivity'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'All 3W Activities',
- visibility: 'anything',
- },
-});
-
-const allAppeals = customWrapRoute({
- parent: rootLayout,
- path: 'appeals/all',
- component: {
- render: () => import('#views/AllAppeals'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'All Appeals',
- visibility: 'anything',
- },
-});
-
-const allEmergencies = customWrapRoute({
- parent: rootLayout,
- path: 'emergencies/all',
- component: {
- render: () => import('#views/AllEmergencies'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'All Emergencies',
- visibility: 'anything',
- },
-});
-
-const allFieldReports = customWrapRoute({
- parent: rootLayout,
- path: 'field-reports/all',
- component: {
- render: () => import('#views/AllFieldReports'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'All Field Reports',
- visibility: 'anything',
- },
-});
-
-const allFlashUpdates = customWrapRoute({
- parent: rootLayout,
- path: 'flash-updates/all',
- component: {
- render: () => import('#views/AllFlashUpdates'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'All Flash Updates',
- visibility: 'is-authenticated',
- permissions: ({ isIfrcAdmin }) => isIfrcAdmin,
- },
-});
-
-const flashUpdateFormNew = customWrapRoute({
- parent: rootLayout,
- path: 'flash-updates/new',
- component: {
- render: () => import('#views/FlashUpdateForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'New Flash Update',
- visibility: 'is-authenticated',
- permissions: ({ isIfrcAdmin }) => isIfrcAdmin,
- },
-});
-
-const flashUpdateFormEdit = customWrapRoute({
- parent: rootLayout,
- path: 'flash-updates/:flashUpdateId/edit',
- component: {
- render: () => import('#views/FlashUpdateForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit Flash Update',
- visibility: 'is-authenticated',
- permissions: ({ isIfrcAdmin }) => isIfrcAdmin,
- },
-});
-
-// FIXME: rename this route to flashUpdateDetails
-const flashUpdateFormDetails = customWrapRoute({
- parent: rootLayout,
- path: 'flash-updates/:flashUpdateId',
- component: {
- render: () => import('#views/FlashUpdateDetails'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Flash Update Details',
- visibility: 'anything',
- permissions: ({ isIfrcAdmin }) => isIfrcAdmin,
- },
-});
-
-const allSurgeAlerts = customWrapRoute({
- parent: rootLayout,
- path: 'alerts/all',
- component: {
- render: () => import('#views/AllSurgeAlerts'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'All Surge Alerts',
- visibility: 'anything',
- },
-});
-
-const newDrefApplicationForm = customWrapRoute({
- parent: rootLayout,
- path: 'dref-applications/new',
- component: {
- render: () => import('#views/DrefApplicationForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'New DREF Application Form',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const drefApplicationForm = customWrapRoute({
- parent: rootLayout,
- path: 'dref-applications/:drefId/edit',
- component: {
- render: () => import('#views/DrefApplicationForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit DREF Application Form',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const drefApplicationExport = customWrapRoute({
- path: 'dref-applications/:drefId/export',
- component: {
- render: () => import('#views/DrefApplicationExport'),
- props: {},
- },
- parent: rootLayout,
- wrapperComponent: Auth,
- context: {
- title: 'DREF Application Export',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const drefOperationalUpdateForm = customWrapRoute({
- parent: rootLayout,
- path: 'dref-operational-updates/:opsUpdateId/edit',
- component: {
- render: () => import('#views/DrefOperationalUpdateForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit DREF Operational Update Form',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const drefOperationalUpdateExport = customWrapRoute({
- path: 'dref-operational-updates/:opsUpdateId/export',
- component: {
- render: () => import('#views/DrefOperationalUpdateExport'),
- props: {},
- },
- parent: rootLayout,
- wrapperComponent: Auth,
- context: {
- title: 'DREF Operational Update Export',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-const drefFinalReportForm = customWrapRoute({
- parent: rootLayout,
- path: 'dref-final-reports/:finalReportId/edit',
- component: {
- render: () => import('#views/DrefFinalReportForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit DREF Final Report Form',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const drefFinalReportExport = customWrapRoute({
- path: 'dref-final-reports/:finalReportId/export',
- component: {
- render: () => import('#views/DrefFinalReportExport'),
- props: {},
- },
- parent: rootLayout,
- wrapperComponent: Auth,
- context: {
- title: 'DREF Final Report Export',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-// TODO: Remove me after implementation of DrefFinalReport for imminent
-const oldDrefFinalReportForm = customWrapRoute({
- parent: rootLayout,
- path: 'old-dref-final-reports/:finalReportId/edit',
- component: {
- render: () => import('#views/OldDrefFinalReportForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit DREF Final Report Form',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const oldDrefFinalReportExport = customWrapRoute({
- path: 'old-dref-final-reports/:finalReportId/export',
- component: {
- render: () => import('#views/OldDrefFinalReportExport'),
- props: {},
- },
- parent: rootLayout,
- wrapperComponent: Auth,
- context: {
- title: 'DREF Final Report Export',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const fieldReportFormNew = customWrapRoute({
- parent: rootLayout,
- path: 'field-reports/new',
- component: {
- render: () => import('#views/FieldReportForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'New Field Report Form',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const fieldReportFormEdit = customWrapRoute({
- parent: rootLayout,
- path: 'field-reports/:fieldReportId/edit',
- component: {
- render: () => import('#views/FieldReportForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit Field Report Form',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const fieldReportDetails = customWrapRoute({
- parent: rootLayout,
- path: 'field-reports/:fieldReportId',
- component: {
- render: () => import('#views/FieldReportDetails'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Field Report Details',
- visibility: 'anything',
- },
-});
-
-type DefaultPerProcessChild = 'new';
-const perProcessLayout = customWrapRoute({
- parent: rootLayout,
- path: 'per-process',
- forwardPath: 'new' satisfies DefaultPerProcessChild,
- component: {
- render: () => import('#views/PerProcessForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'PER Process',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const perProcessFormIndex = customWrapRoute({
- parent: perProcessLayout,
- index: true,
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: 'new' satisfies DefaultPerProcessChild,
- replace: true,
- },
- },
- context: {
- title: 'PER Process',
- visibility: 'anything',
- },
-});
-
-const newPerOverviewForm = customWrapRoute({
- parent: perProcessLayout,
- path: 'new' satisfies DefaultPerProcessChild,
- component: {
- render: () => import('#views/PerOverviewForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'New PER Overview',
- visibility: 'is-authenticated',
- permissions: ({
- isSuperUser,
- isPerAdmin,
- }) => isSuperUser || isPerAdmin,
- },
-});
-
-const perOverviewForm = customWrapRoute({
- parent: perProcessLayout,
- path: ':perId/overview',
- component: {
- render: () => import('#views/PerOverviewForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit PER Overview',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const perAssessmentForm = customWrapRoute({
- parent: perProcessLayout,
- path: ':perId/assessment',
- component: {
- render: () => import('#views/PerAssessmentForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit PER Assessment',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const perPrioritizationForm = customWrapRoute({
- parent: perProcessLayout,
- path: ':perId/prioritization',
- component: {
- render: () => import('#views/PerPrioritizationForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit PER Prioritization',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-const perWorkPlanForm = customWrapRoute({
- parent: perProcessLayout,
- path: ':perId/work-plan',
- component: {
- render: () => import('#views/PerWorkPlanForm'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Edit PER Work Plan',
- visibility: 'is-authenticated',
- permissions: ({ isGuestUser }) => !isGuestUser,
- },
-});
-
-// Redirect Routes
-const preparednessOperationalLearning = customWrapRoute({
- parent: preparednessLayout,
- path: 'operational-learning',
- component: {
- eagerLoad: true,
- render: Navigate,
- props: {
- to: operationalLearning.absolutePath,
- },
- },
- wrapperComponent: Auth,
- context: {
- title: 'Operational Learning',
- visibility: 'anything',
- },
-});
-
-// eslint-disable-next-line react-refresh/only-export-components
-function ObsoleteFieldReportRedirection() {
- const params = useParams<{
- fieldReportId: string,
- }>();
-
- const path = generatePath(
- fieldReportDetails.absoluteForwardPath,
- { fieldReportId: params.fieldReportId },
- );
-
- return (
-
- );
-}
-
-const obsoleteFieldReportDetails = customWrapRoute({
- parent: rootLayout,
- path: 'reports/:fieldReportId',
- component: {
- eagerLoad: true,
- render: ObsoleteFieldReportRedirection,
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Field Report Details',
- visibility: 'anything',
- },
-});
-
-const montandonLandingPage = customWrapRoute({
- parent: rootLayout,
- path: 'montandon-landing',
- component: {
- render: () => import('#views/MontandonLandingPage'),
- props: {},
- },
- wrapperComponent: Auth,
- context: {
- title: 'Montandon',
- visibility: 'anything',
- },
-});
-
-const wrappedRoutes = {
- fourHundredFour,
- rootLayout,
- login,
- register,
- recoverAccount,
- recoverAccountConfirm,
- resendValidationEmail,
- home,
- emergencies,
- cookiePolicy,
- emergencySlug,
- emergencyFollow,
- emergenciesLayout,
- emergencyDetails,
- emergencyIndex,
- emergencyReportsAndDocuments,
- emergencyActivities,
- emergencySurge,
- emergencyAdditionalInfoOne,
- emergencyAdditionalInfoTwo,
- emergencyAdditionalInfoThree,
- emergencyAdditionalInfo,
- preparednessLayout,
- preparednessGlobalSummary,
- preparednessGlobalPerformance,
- preparednessGlobalCatalogue,
- preparednessIndex,
- perProcessFormIndex,
- globalThreeW,
- newThreeWProject,
- threeWProjectEdit,
- threeWActivityEdit,
- threeWActivityDetail,
- newThreeWActivity,
- accountLayout,
- accountIndex,
- accountDetails,
- accountMyFormsLayout,
- accountMyFormsIndex,
- accountNotifications,
- accountMyFormsFieldReport,
- accountMyFormsPer,
- accountMyFormsDref,
- accountMyFormsThreeW,
- resources,
- search,
- allThreeWProject,
- allThreeWActivity,
- allAppeals,
- allEmergencies,
- allFieldReports,
- allSurgeAlerts,
- allFlashUpdates,
- newDrefApplicationForm,
- drefApplicationForm,
- drefApplicationExport,
- drefOperationalUpdateForm,
- drefOperationalUpdateExport,
- drefFinalReportForm,
- drefFinalReportExport,
- fieldReportFormNew,
- fieldReportFormEdit,
- fieldReportDetails,
- flashUpdateFormNew,
- flashUpdateFormDetails,
- flashUpdateFormEdit,
- riskWatchLayout,
- riskWatchIndex,
- riskWatchImminent,
- riskWatchSeasonal,
- perProcessLayout,
- perOverviewForm,
- newPerOverviewForm,
- perAssessmentForm,
- perPrioritizationForm,
- perWorkPlanForm,
- threeWProjectDetail,
- termsAndConditions,
- operationalLearning,
- montandonLandingPage,
- ...regionRoutes,
- ...countryRoutes,
- ...surgeRoutes,
-
- // TODO: Remove me after implementation of DrefFinalReport for imminent
- oldDrefFinalReportForm,
- oldDrefFinalReportExport,
- // Redirects
- preparednessOperationalLearning,
- obsoleteFieldReportDetails,
-};
-
-export const unwrappedRoutes = unwrapRoute(Object.values(wrappedRoutes));
-
-export default wrappedRoutes;
-
-export type WrappedRoutes = typeof wrappedRoutes;
diff --git a/go-web-app-develop/app/src/App/styles.module.css b/go-web-app-develop/app/src/App/styles.module.css
deleted file mode 100644
index ea94ad144d1300c1ab97473d814151b6028e6d26..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/App/styles.module.css
+++ /dev/null
@@ -1,27 +0,0 @@
-.fallback-element {
- display: flex;
- align-items: center;
- flex-direction: column;
- justify-content: center;
- background-color: var(--go-ui-color-background);
- width: 100vw;
- height: 100vh;
- gap: 1rem;
-
- .go-logo {
- margin-top: -4rem;
- height: 3rem;
- animation: slide-up var(--go-ui-duration-animation-slow) ease-in-out forwards;
- }
-}
-
-@keyframes slide-up {
- from {
- opacity: 0;
- margin-top: -4rem;
- }
- to {
- opacity: 1;
- margin-top: 0;
- }
-}
diff --git a/go-web-app-develop/app/src/assets/content/four_hundred_four.svg b/go-web-app-develop/app/src/assets/content/four_hundred_four.svg
deleted file mode 100644
index 61679fc0569b326dbcbfd3d908d177eaa9de75ac..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/content/four_hundred_four.svg
+++ /dev/null
@@ -1,224 +0,0 @@
-
\ No newline at end of file
diff --git a/go-web-app-develop/app/src/assets/content/four_hundred_four_background.svg b/go-web-app-develop/app/src/assets/content/four_hundred_four_background.svg
deleted file mode 100644
index 04c548070e8aa658634f43ebbf7f36b771df3ed4..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/content/four_hundred_four_background.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/content/operational_timeline_body.svg b/go-web-app-develop/app/src/assets/content/operational_timeline_body.svg
deleted file mode 100644
index 2f259cddc5959a1db919ac99d24202fcdfdac255..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/content/operational_timeline_body.svg
+++ /dev/null
@@ -1,1023 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/content/operational_timeline_title.svg b/go-web-app-develop/app/src/assets/content/operational_timeline_title.svg
deleted file mode 100644
index af703ef0fd8f42b37c12e1cbe3b71aba9d2d92c2..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/content/operational_timeline_title.svg
+++ /dev/null
@@ -1,62 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/content/per_approach_notext.svg b/go-web-app-develop/app/src/assets/content/per_approach_notext.svg
deleted file mode 100644
index e846fa9d5b3e9281de320b8df7e4dee12131de40..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/content/per_approach_notext.svg
+++ /dev/null
@@ -1,830 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/icons/arc_logo.png b/go-web-app-develop/app/src/assets/icons/arc_logo.png
deleted file mode 100644
index a475a47a22b2926fcbe04ed181faa6b9e56d0dfb..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/arc_logo.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:575f079564d18af5c22349026d3fb2066245a48f86ca0cdf560ed03a91db04b6
-size 9605
diff --git a/go-web-app-develop/app/src/assets/icons/aurc_logo.svg b/go-web-app-develop/app/src/assets/icons/aurc_logo.svg
deleted file mode 100644
index f961ba64e03caa0a22d6ecb08b1b30811120bc5b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/aurc_logo.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/icons/brc_logo.png b/go-web-app-develop/app/src/assets/icons/brc_logo.png
deleted file mode 100644
index a5ab19de8b54807322fc51171ce68350863caebb..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/brc_logo.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:48de29c95428cfd12dcf9bf811a2861a35443b1330114a00dfe0e24364e6d2bc
-size 43856
diff --git a/go-web-app-develop/app/src/assets/icons/crc_logo.png b/go-web-app-develop/app/src/assets/icons/crc_logo.png
deleted file mode 100644
index 903ee353e6f4142d1fb8974094635412d8ed1219..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/crc_logo.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e26f53a76b5254429d985dea503f6d3a563e0de79036c0805996d2aa00c7b65d
-size 7209
diff --git a/go-web-app-develop/app/src/assets/icons/dnk_logo.png b/go-web-app-develop/app/src/assets/icons/dnk_logo.png
deleted file mode 100644
index 353b624584e0177deaaa1ce7b8fbe1feb0629d32..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/dnk_logo.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:1952a53b1c018a8c7f86f7da3653fcea6e8e59fb80c361360a762cc42d10f8c4
-size 1810
diff --git a/go-web-app-develop/app/src/assets/icons/early_actions.svg b/go-web-app-develop/app/src/assets/icons/early_actions.svg
deleted file mode 100644
index 3b93b121b462b7f2f1e2103b0dffdee1ac795e68..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/early_actions.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/icons/early_response.svg b/go-web-app-develop/app/src/assets/icons/early_response.svg
deleted file mode 100644
index c7f796fecb9c582073fb40302af8c45fe77c8fec..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/early_response.svg
+++ /dev/null
@@ -1,15 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/icons/ericsson_logo.png b/go-web-app-develop/app/src/assets/icons/ericsson_logo.png
deleted file mode 100644
index 67b8b300c7e2e58835a06e694629c2bbb241cfc0..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/ericsson_logo.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:82a8d71f11f1954eabfda04e4174b6c4b386825bb838d21ac03d6874a66cf1dd
-size 8472
diff --git a/go-web-app-develop/app/src/assets/icons/eru.jpg b/go-web-app-develop/app/src/assets/icons/eru.jpg
deleted file mode 100644
index 3bef2c7052a05ea0af7f416a1e5893447c22caa2..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/eru.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b31e6df96b68830109c9d3648183fc56d5bc2f03e73af951947231d6551074cd
-size 17503
diff --git a/go-web-app-develop/app/src/assets/icons/esp_logo.svg b/go-web-app-develop/app/src/assets/icons/esp_logo.svg
deleted file mode 100644
index 40388c0418f667790cac5e5e558f113d9fcbbd70..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/esp_logo.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/icons/frc_logo.png b/go-web-app-develop/app/src/assets/icons/frc_logo.png
deleted file mode 100644
index 764181f4cde691f347ed0f2afdcbc41146d17dc5..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/frc_logo.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6ddfd2063a46d009d62e86c0ccf06d03e559b81466a87699221af49807096fbe
-size 5598
diff --git a/go-web-app-develop/app/src/assets/icons/go-logo-2020.svg b/go-web-app-develop/app/src/assets/icons/go-logo-2020.svg
deleted file mode 100644
index e73b08f7c2648f946092c65ff2e93dad7a1354b9..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/go-logo-2020.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/icons/ifrc-square.png b/go-web-app-develop/app/src/assets/icons/ifrc-square.png
deleted file mode 100644
index 416494e6817d2fc3bd7c4450ba9276ae972c133c..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/ifrc-square.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:6091cea7550ff40df5074d8be9620214b980fbe381b281a625280f752b3ad8bb
-size 7158
diff --git a/go-web-app-develop/app/src/assets/icons/jrc_logo.png b/go-web-app-develop/app/src/assets/icons/jrc_logo.png
deleted file mode 100644
index 1e550849d298a158bfbdc8243a9b6683a5b2330e..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/jrc_logo.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f377c674cfdd72b81e57eab13b6c094a03e322508172b292eb32630a22a659d8
-size 1116
diff --git a/go-web-app-develop/app/src/assets/icons/nlrc_logo.jpg b/go-web-app-develop/app/src/assets/icons/nlrc_logo.jpg
deleted file mode 100644
index d2feae13025e54d1bd6c734e33e8336ac26c43be..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/nlrc_logo.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f07b2494d1602ae7f8af627e5be6203c5c8be92831eb21b2acc7ab6c6d2376d4
-size 59286
diff --git a/go-web-app-develop/app/src/assets/icons/pdc_logo.svg b/go-web-app-develop/app/src/assets/icons/pdc_logo.svg
deleted file mode 100644
index 32fcd7f726f8c0d0d20a2a978e37fe2aa09a5d3f..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/pdc_logo.svg
+++ /dev/null
@@ -1,32 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/icons/per_logo.png b/go-web-app-develop/app/src/assets/icons/per_logo.png
deleted file mode 100644
index 2fa710240f7361dea988ade9f3bb6284a95042ae..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/per_logo.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e916c7202df7c4889963b92aac2452a60930866e19f8e66923f828e3349ed5e0
-size 19209
diff --git a/go-web-app-develop/app/src/assets/icons/risk/cyclone.png b/go-web-app-develop/app/src/assets/icons/risk/cyclone.png
deleted file mode 100644
index dd233c0c6cd3b1bdf1c3b810f93c039d96903364..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/risk/cyclone.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5ef7ff0c5eb55ff365d09cfe1dfd09c591efb50c452d9bd36d72042b03850e6d
-size 471
diff --git a/go-web-app-develop/app/src/assets/icons/risk/drought.png b/go-web-app-develop/app/src/assets/icons/risk/drought.png
deleted file mode 100644
index 4aa837f9ac53c6856db4a841829147b03f3c99f9..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/risk/drought.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f6584fd26c12de94660de02d3c02a2efe2ad8afebf1ccaf6915a2767496c119b
-size 532
diff --git a/go-web-app-develop/app/src/assets/icons/risk/earthquake.png b/go-web-app-develop/app/src/assets/icons/risk/earthquake.png
deleted file mode 100644
index b4a07f936f27b7f77a0eeb9abadfa0867e268f9b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/risk/earthquake.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2d3daa4c92dc8206cc7ee4fa746136e58e27540ff60bf2958b8400843f4a8119
-size 378
diff --git a/go-web-app-develop/app/src/assets/icons/risk/flood.png b/go-web-app-develop/app/src/assets/icons/risk/flood.png
deleted file mode 100644
index d1127853a23c1ebefc7b124d6b95b816451ec979..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/risk/flood.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b67ceba857965f4df441db3d696f61674c041b5754f2257f4f9a425312b0ae84
-size 343
diff --git a/go-web-app-develop/app/src/assets/icons/risk/wildfire.png b/go-web-app-develop/app/src/assets/icons/risk/wildfire.png
deleted file mode 100644
index 7dc993b6c6c22aa35db1f7ac9e8abee7053769ed..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/risk/wildfire.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5c2ba28d6babb2ba095588d331db38d5130889e363f18d1bfa3b5d05ef4e1051
-size 474
diff --git a/go-web-app-develop/app/src/assets/icons/swiss.svg b/go-web-app-develop/app/src/assets/icons/swiss.svg
deleted file mode 100644
index e388e797f0544be2330cc9d9047079e5d80e485b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/swiss.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/icons/us_aid.svg b/go-web-app-develop/app/src/assets/icons/us_aid.svg
deleted file mode 100644
index 475415bfd02f3f3922e85f53d2660759a6069285..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/icons/us_aid.svg
+++ /dev/null
@@ -1,66 +0,0 @@
-
diff --git a/go-web-app-develop/app/src/assets/images/surge-im-composition.jpg b/go-web-app-develop/app/src/assets/images/surge-im-composition.jpg
deleted file mode 100644
index 355fee9540d0ca102809defd5addcbf473b60332..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/images/surge-im-composition.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d147e2bf02640fe345c4e1b0b5663c0ed692d71de0fb433cc0763e6d77345fe8
-size 107991
diff --git a/go-web-app-develop/app/src/assets/images/surge-im-pyramid.png b/go-web-app-develop/app/src/assets/images/surge-im-pyramid.png
deleted file mode 100644
index 579881654cb17195fa3052f72b120eaef0d9b2c9..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/images/surge-im-pyramid.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:b233c2838100a88434ddcf3b80580d38a59cbb516c7210def8bf2c85c0e1178c
-size 88772
diff --git a/go-web-app-develop/app/src/assets/images/surge-im-support-responsible-operation.jpg b/go-web-app-develop/app/src/assets/images/surge-im-support-responsible-operation.jpg
deleted file mode 100644
index 718120b85e55bb4883c5d66fc8b7afb3150d81df..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/images/surge-im-support-responsible-operation.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d5ceb3262e47bedb5a56c94a330e7307e6556aea653460fdb3868b35845658d1
-size 135310
diff --git a/go-web-app-develop/app/src/assets/images/surge-per.gif b/go-web-app-develop/app/src/assets/images/surge-per.gif
deleted file mode 100644
index cec015cf3a24fdcde6a9b2655346dbb00e3c0ba7..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/assets/images/surge-per.gif
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fe3049070a038ea52ffda9d1a32e845e5fe5e8de5d47f39d72c491c204208d2d
-size 102351
diff --git a/go-web-app-develop/app/src/components/CatalogueInfoCard/index.tsx b/go-web-app-develop/app/src/components/CatalogueInfoCard/index.tsx
deleted file mode 100644
index 1ddef83253255d0eb456358e1e6404f7d94bd913..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/CatalogueInfoCard/index.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import { useCallback } from 'react';
-import {
- Container,
- List,
-} from '@ifrc-go/ui';
-import { _cs } from '@togglecorp/fujs';
-
-import Link, { type Props as LinkProps } from '#components/Link';
-
-import styles from './styles.module.css';
-
-export type LinkData = LinkProps & {
- title: string;
-}
-
-const catalogueInfoKeySelector = (item: LinkData) => item.title;
-interface Props {
- className?: string;
- title: string;
- data: LinkData[];
- description?: string;
- descriptionClassName?: string;
-}
-
-function CatalogueInfoCard(props: Props) {
- const {
- className,
- title,
- data,
- description,
- descriptionClassName,
- } = props;
-
- const rendererParams = useCallback(
- (_: string, value: LinkData): LinkProps => {
- if (value.external) {
- return {
- href: value.href,
- children: value.title,
- external: true,
- withLinkIcon: value.withLinkIcon,
- };
- }
-
- return {
- to: value.to,
- urlParams: value.urlParams,
- urlSearch: value.urlSearch,
- urlHash: value.urlHash,
- children: value.title,
- withLinkIcon: value.withLinkIcon,
- };
- },
- [],
- );
-
- return (
-
-
-
- );
-}
-
-export default CatalogueInfoCard;
diff --git a/go-web-app-develop/app/src/components/CatalogueInfoCard/styles.module.css b/go-web-app-develop/app/src/components/CatalogueInfoCard/styles.module.css
deleted file mode 100644
index ae165866ae58a0128c34374f25a6bbc735655f71..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/CatalogueInfoCard/styles.module.css
+++ /dev/null
@@ -1,10 +0,0 @@
-.catalogue-info-card {
- border-radius: var(--go-ui-border-radius-md);
- box-shadow: var(--go-ui-box-shadow-md);
-
- .list {
- display: flex;
- flex-direction: column;
- gap: var(--go-ui-spacing-sm);
- }
-}
diff --git a/go-web-app-develop/app/src/components/DiffWrapper/index.tsx b/go-web-app-develop/app/src/components/DiffWrapper/index.tsx
deleted file mode 100644
index 528ae4217efb8e0b6c982a4b1e4a70fd18cb55f6..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/DiffWrapper/index.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { useMemo } from 'react';
-import { isNotDefined } from '@togglecorp/fujs';
-
-interface Props {
- diffContainerClassName?: string;
- value?: T;
- oldValue?: T;
- children: React.ReactNode;
- enabled: boolean;
- showOnlyDiff?: boolean;
-}
-
-function DiffWrapper(props: Props) {
- const {
- diffContainerClassName,
- oldValue,
- value,
- children,
- enabled = false,
- showOnlyDiff,
- } = props;
-
- const hasChanged = useMemo(() => {
- // NOTE: we consider `null` and `undefined` as same for
- // this scenario
- if (isNotDefined(oldValue) && isNotDefined(value)) {
- return false;
- }
-
- return JSON.stringify(oldValue) !== JSON.stringify(value);
- }, [oldValue, value]);
-
- if (!enabled) {
- return children;
- }
-
- if (!hasChanged && showOnlyDiff) {
- return null;
- }
-
- if (!hasChanged) {
- return children;
- }
-
- return (
-
- {children}
-
- );
-}
-
-export default DiffWrapper;
diff --git a/go-web-app-develop/app/src/components/DisplayName/index.tsx b/go-web-app-develop/app/src/components/DisplayName/index.tsx
deleted file mode 100644
index 48b161c4291b93d02f1c36a31fb3b1ef531fa70b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/DisplayName/index.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-interface DisplayNameOutputProps {
- name: string;
-}
-
-function DisplayName({ name }: DisplayNameOutputProps) {
- return name;
-}
-
-export default DisplayName;
diff --git a/go-web-app-develop/app/src/components/DropdownMenuItem/index.tsx b/go-web-app-develop/app/src/components/DropdownMenuItem/index.tsx
deleted file mode 100644
index 32647b00ff4e34f22a013be74b9bedfb68de29a2..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/DropdownMenuItem/index.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import {
- useCallback,
- useContext,
-} from 'react';
-import {
- Button,
- type ButtonProps,
- ConfirmButton,
- type ConfirmButtonProps,
-} from '@ifrc-go/ui';
-import { DropdownMenuContext } from '@ifrc-go/ui/contexts';
-import { isDefined } from '@togglecorp/fujs';
-
-import Link, { type Props as LinkProps } from '#components/Link';
-
-type CommonProp = {
- persist?: boolean;
-}
-
-type ButtonTypeProps = Omit, 'type'> & {
- type: 'button';
-}
-
-type LinkTypeProps = LinkProps & {
- type: 'link';
-}
-
-type ConfirmButtonTypeProps = Omit, 'type'> & {
- type: 'confirm-button',
-}
-
-type Props = CommonProp & (ButtonTypeProps | LinkTypeProps | ConfirmButtonTypeProps);
-
-function DropdownMenuItem(props: Props) {
- const {
- type,
- onClick,
- persist = false,
- } = props;
- const { setShowDropdown } = useContext(DropdownMenuContext);
-
- const handleLinkClick = useCallback(
- () => {
- if (!persist) {
- setShowDropdown(false);
- }
- // TODO: maybe add onClick here?
- },
- [setShowDropdown, persist],
- );
-
- const handleButtonClick = useCallback(
- (name: NAME, e: React.MouseEvent) => {
- if (!persist) {
- setShowDropdown(false);
- }
- if (isDefined(onClick) && type !== 'link') {
- onClick(name, e);
- }
- },
- [setShowDropdown, type, onClick, persist],
- );
-
- if (type === 'link') {
- const {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- type: _,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- persist: __,
- variant = 'dropdown-item',
- ...otherProps
- } = props;
-
- return (
-
- );
- }
-
- if (type === 'button') {
- const {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- type: _,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- persist: __,
- variant = 'dropdown-item',
- ...otherProps
- } = props;
-
- return (
-
- );
- }
-
- if (type === 'confirm-button') {
- const {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- type: _,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- persist: __,
- variant = 'dropdown-item',
- ...otherProps
- } = props;
-
- return (
-
- );
- }
-}
-
-export default DropdownMenuItem;
diff --git a/go-web-app-develop/app/src/components/FourHundredThree/i18n.json b/go-web-app-develop/app/src/components/FourHundredThree/i18n.json
deleted file mode 100644
index 0adadc11515ee21659aff4b7106948d87e3d510c..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/FourHundredThree/i18n.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "namespace": "common",
- "strings": {
- "permissionDeniedPageTitle":"IFRC GO - Permission Denied",
- "permissionDeniedHeading":"Permission Denied",
- "permissionDeniedAreYouSureUrlIsCorrect":"Are you sure the you have the right roles?",
- "permissionDeniedGetInTouch":"Get in touch",
- "permissionDeniedWithThePlatformTeam":"with the platform team.",
- "permissionDeniedExploreOurHomepage":"Explore our homepage",
- "permissionDeniedPageDescription":"Looks like you don't have the correct permissions to view this page.",
- "permissionHeadingLabel": "403"
- }
-}
diff --git a/go-web-app-develop/app/src/components/FourHundredThree/index.tsx b/go-web-app-develop/app/src/components/FourHundredThree/index.tsx
deleted file mode 100644
index b528c1290d1a12356b46189af919cae0a34f3899..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/FourHundredThree/index.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { SearchLineIcon } from '@ifrc-go/icons';
-import { Heading } from '@ifrc-go/ui';
-import { useTranslation } from '@ifrc-go/ui/hooks';
-
-import Link from '#components/Link';
-import Page from '#components/Page';
-
-import i18n from './i18n.json';
-import styles from './styles.module.css';
-
-function FourHundredThree() {
- const strings = useTranslation(i18n);
-
- return (
-
-
-
-
-
-
- {strings.permissionHeadingLabel}
-
-
- {strings.permissionDeniedHeading}
-
-
- {strings.permissionDeniedPageDescription}
-
-
-
- {strings.permissionDeniedAreYouSureUrlIsCorrect}
-
-
- {strings.permissionDeniedGetInTouch}
-
-
- {strings.permissionDeniedWithThePlatformTeam}
-
-
- {strings.permissionDeniedExploreOurHomepage}
-
-
-
- );
-}
-
-export default FourHundredThree;
diff --git a/go-web-app-develop/app/src/components/FourHundredThree/styles.module.css b/go-web-app-develop/app/src/components/FourHundredThree/styles.module.css
deleted file mode 100644
index 0b35e03243e8fe5e08f22420da8ad21a0cf1502b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/FourHundredThree/styles.module.css
+++ /dev/null
@@ -1,54 +0,0 @@
-.four-hundred-three {
- display: flex;
- flex-direction: column;
-
- .main-section-container {
- display: flex;
- flex-direction: column;
- flex-grow: 1;
- }
-
- .main {
- display: flex;
- align-items: center;
- flex-direction: column;
- flex-grow: 1;
- gap: var(--go-ui-spacing-2xl);
-
- .top-section {
- display: flex;
- flex-direction: column;
- flex-grow: 1;
- justify-content: center;
- gap: var(--go-ui-spacing-xs);
-
- .heading {
- display: flex;
- align-items: center;
- flex-direction: column;
- padding: var(--go-ui-spacing-md);
-
- .icons {
- display: flex;
- align-items: flex-start;
-
- .search-icon {
- font-size: 6rem;
- }
- }
- }
-
- .description {
- text-align: center;
- }
- }
-
- .bottom-section {
- display: flex;
- align-items: center;
- flex-direction: column;
- text-align: center;
- gap: var(--go-ui-spacing-xs);
- }
- }
-}
diff --git a/go-web-app-develop/app/src/components/GlobalFooter/i18n.json b/go-web-app-develop/app/src/components/GlobalFooter/i18n.json
deleted file mode 100644
index 5e77c0edc9151b88e5c66d1ce87bfa626d72561b..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/GlobalFooter/i18n.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "namespace": "common",
- "strings": {
- "footerAboutGo":"About GO",
- "footerAboutGoDesc":"IFRC GO is a Red Cross Red Crescent platform to connect information on emergency needs with the right response.",
- "footerFindOutMore":"Find out more",
- "footerHelpfulLinks":"Helpful links",
- "footerOpenSourceCode":"Open Source Code",
- "footerApiDocumentation":"API Documentation",
- "footerOtherResources":"Other Resources",
- "footerGoWiki":"GO Wiki",
- "footerContactUs":"Contact Us",
- "footerIFRC":"© IFRC {year} v{appVersion}",
- "globalFindOut": "Find Out More",
- "policies": "Policies",
- "cookiePolicy": "Cookie Policy",
- "termsAndConditions": "Terms and Conditions",
- "globalHelpfulLinks": "Helpful links"
- }
-}
diff --git a/go-web-app-develop/app/src/components/GlobalFooter/index.tsx b/go-web-app-develop/app/src/components/GlobalFooter/index.tsx
deleted file mode 100644
index f8f3f5f985c6f28990354c0c443acb7186bf4e16..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/GlobalFooter/index.tsx
+++ /dev/null
@@ -1,186 +0,0 @@
-import {
- SocialFacebookIcon,
- SocialMediumIcon,
- SocialYoutubeIcon,
-} from '@ifrc-go/icons';
-import {
- Heading,
- PageContainer,
-} from '@ifrc-go/ui';
-import { useTranslation } from '@ifrc-go/ui/hooks';
-import { resolveToComponent } from '@ifrc-go/ui/utils';
-import { _cs } from '@togglecorp/fujs';
-
-import Link from '#components/Link';
-import {
- api,
- appCommitHash,
- appPackageName,
- appRepositoryUrl,
- appVersion,
-} from '#config';
-import { resolveUrl } from '#utils/resolveUrl';
-
-import i18n from './i18n.json';
-import styles from './styles.module.css';
-
-const date = new Date();
-const year = date.getFullYear();
-
-interface Props {
- className?: string;
-}
-
-function GlobalFooter(props: Props) {
- const {
- className,
- } = props;
-
- const strings = useTranslation(i18n);
- const versionTag = `${appPackageName}@${appVersion}`;
- const versionUrl = `${appRepositoryUrl}/releases/tag/${versionTag}`;
- const copyrightText = resolveToComponent(
- strings.footerIFRC,
- {
- year,
- appVersion: (
-
- {appVersion}
-
- ),
- },
- );
-
- return (
-
-
-
- {strings.footerAboutGo}
-
-
- {strings.footerAboutGoDesc}
-
-
- {copyrightText}
-
-
-
-
- {strings.globalFindOut}
-
-
-
- ifrc.org
-
-
- rcrcsims.org
-
-
- data.ifrc.org
-
-
-
-
-
- {strings.policies}
-
-
-
- {strings.cookiePolicy}
-
-
- {strings.termsAndConditions}
-
-
-
-
-
- {strings.globalHelpfulLinks}
-
-
-
- {strings.footerOpenSourceCode}
-
-
- {strings.footerApiDocumentation}
-
-
- {strings.footerOtherResources}
-
-
- {strings.footerGoWiki}
-
-
-
-
-
- {strings.footerContactUs}
-
-
- im@ifrc.org
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default GlobalFooter;
diff --git a/go-web-app-develop/app/src/components/GlobalFooter/styles.module.css b/go-web-app-develop/app/src/components/GlobalFooter/styles.module.css
deleted file mode 100644
index 7e052fdb023fa8d3374e08198df5c8f0dd69a8ef..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/GlobalFooter/styles.module.css
+++ /dev/null
@@ -1,38 +0,0 @@
-.footer {
- background-color: var(--go-ui-color-primary-gray);
- color: var(--go-ui-color-white);
-
- .content {
- display: flex;
- gap: var(--go-ui-spacing-2xl);
- flex-wrap: wrap;
-
- .section {
- display: flex;
- flex-basis: 12rem;
- flex-direction: column;
- flex-grow: 1;
- gap: var(--go-ui-spacing-lg);
-
- .sub-section {
- display: flex;
- flex-direction: column;
- gap: var(--go-ui-spacing-sm);
- }
-
- .social-icons {
- display: flex;
- gap: var(--go-ui-spacing-sm);
-
- .social-icon {
- flex-shrink: 0;
- font-size: var(--go-ui-height-social-icon);
- }
- }
- }
- }
-
- @media print {
- display: none;
- }
-}
diff --git a/go-web-app-develop/app/src/components/Link/index.tsx b/go-web-app-develop/app/src/components/Link/index.tsx
deleted file mode 100644
index d15c957b1d6341c6007960ebc32f7b1a046d7b1d..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/Link/index.tsx
+++ /dev/null
@@ -1,291 +0,0 @@
-import {
- useContext,
- useMemo,
-} from 'react';
-import {
- generatePath,
- Link as InternalLink,
- type LinkProps as RouterLinkProps,
-} from 'react-router-dom';
-import {
- ChevronRightLineIcon,
- ExternalLinkLineIcon,
-} from '@ifrc-go/icons';
-import type { ButtonFeatureProps } from '@ifrc-go/ui';
-import { useButtonFeatures } from '@ifrc-go/ui/hooks';
-import {
- _cs,
- isDefined,
- isFalsyString,
- isNotDefined,
-} from '@togglecorp/fujs';
-
-import RouteContext from '#contexts/route';
-import useAuth from '#hooks/domain/useAuth';
-import usePermissions from '#hooks/domain/usePermissions';
-
-import { type WrappedRoutes } from '../../App/routes';
-
-import styles from './styles.module.css';
-
-export interface UrlParams {
- [key: string]: string | number | null | undefined;
-}
-
-// eslint-disable-next-line react-refresh/only-export-components
-export function resolvePath(
- to: keyof WrappedRoutes,
- routes: WrappedRoutes,
- urlParams: UrlParams | undefined,
-) {
- const route = routes[to];
- try {
- const resolvedPath = generatePath(route.absoluteForwardPath, urlParams);
- return {
- ...route,
- resolvedPath,
- };
- } catch {
- return {
- ...route,
- resolvedPath: undefined,
- };
- }
-}
-
-// eslint-disable-next-line react-refresh/only-export-components
-export function useLink(props: {
- external: true,
- href: string | undefined | null,
- to?: never,
- urlParams?: never,
-} | {
- external: false | undefined,
- to: keyof WrappedRoutes | undefined | null,
- urlParams?: UrlParams,
- href?: never,
-}) {
- const { isAuthenticated } = useAuth();
- const routes = useContext(RouteContext);
- const perms = usePermissions();
-
- if (props.external) {
- if (isNotDefined(props.href)) {
- return { disabled: true, to: undefined };
- }
- return { disabled: false, to: props.href };
- }
-
- if (isNotDefined(props.to)) {
- return { disabled: true, to: undefined };
- }
-
- const route = resolvePath(props.to, routes, props.urlParams);
- const { resolvedPath } = route;
-
- if (isNotDefined(resolvedPath)) {
- return { disabled: true, to: undefined };
- }
-
- const disabled = (route.visibility === 'is-authenticated' && !isAuthenticated)
- || (route.visibility === 'is-not-authenticated' && isAuthenticated)
- || (route.permissions && !route.permissions(perms, props.urlParams));
-
- return {
- disabled,
- to: resolvedPath,
- };
-}
-
-export type CommonLinkProps = Omit &
-Omit<{
- actions?: React.ReactNode;
- actionsContainerClassName?: string;
- disabled?: boolean;
- icons?: React.ReactNode;
- iconsContainerClassName?: string;
- linkElementClassName?: string;
- // to?: RouterLinkProps['to'];
- variant?: ButtonFeatureProps['variant'];
- withLinkIcon?: boolean;
- withUnderline?: boolean;
- ellipsize?: boolean;
- spacing?: ButtonFeatureProps['spacing'];
-}, OMISSION>
-
-type InternalLinkProps = {
- external?: never;
- to: keyof WrappedRoutes | undefined | null;
- urlParams?: UrlParams;
- urlSearch?: string;
- urlHash?: string;
- href?: never;
-}
-
-export type ExternalLinkProps = {
- external: true;
- href: string | undefined | null;
- urlParams?: never;
- urlSearch?: never;
- urlHash?: never;
- to?: never;
-}
-
-export type Props = CommonLinkProps
- & (InternalLinkProps | ExternalLinkProps)
-
-function Link(props: Props) {
- const {
- actions,
- actionsContainerClassName,
- children: childrenFromProps,
- className,
- disabled: disabledFromProps,
- icons,
- iconsContainerClassName,
- linkElementClassName,
- withUnderline,
- withLinkIcon,
- variant = 'tertiary',
- ellipsize,
- spacing,
-
- external,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- to,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- urlParams,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- urlSearch,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- urlHash,
- ...otherProps
- } = props;
-
- const {
- disabled: disabledLink,
- to: toLink,
- } = useLink(
- // eslint-disable-next-line react/destructuring-assignment
- props.external
- // eslint-disable-next-line react/destructuring-assignment
- ? { href: props.href, external: true }
- // eslint-disable-next-line react/destructuring-assignment
- : { to: props.to, external: false, urlParams: props.urlParams },
- );
-
- const disabled = disabledFromProps || disabledLink;
-
- const nonLink = isFalsyString(toLink);
-
- const {
- children: content,
- className: containerClassName,
- } = useButtonFeatures({
- className: styles.content,
- icons,
- children: childrenFromProps,
- variant,
- ellipsize,
- disabled,
- spacing,
- actions: (isDefined(actions) || withLinkIcon) ? (
- <>
- {actions}
- {withLinkIcon && external && (
-
- )}
- {withLinkIcon && !external && (
-
- )}
- >
- ) : null,
- iconsContainerClassName,
- actionsContainerClassName,
- });
-
- const children = useMemo(
- () => {
- if (isNotDefined(toLink)) {
- return (
-
- {content}
-
- );
- }
- // eslint-disable-next-line react/destructuring-assignment
- if (props.external) {
- return (
-
- {content}
-
- );
- }
-
- return (
-
- {content}
-
- );
- },
- [
- linkElementClassName,
- containerClassName,
- content,
- otherProps,
- toLink,
- // eslint-disable-next-line react/destructuring-assignment
- props.urlSearch,
- // eslint-disable-next-line react/destructuring-assignment
- props.urlHash,
- // eslint-disable-next-line react/destructuring-assignment
- props.external,
- ],
- );
-
- return (
-
- {children}
-
- );
-}
-export default Link;
diff --git a/go-web-app-develop/app/src/components/Link/styles.module.css b/go-web-app-develop/app/src/components/Link/styles.module.css
deleted file mode 100644
index 5e8a0e3b82b91176043375a027f82287f10181ed..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/Link/styles.module.css
+++ /dev/null
@@ -1,57 +0,0 @@
-.link {
- --decoration: none;
-
- display: inline-flex;
- align-items: flex-start;
- flex-wrap: nowrap;
- width: fit-content;
-
- &.ellipsized {
- width: 100%;
- overflow: auto;
- }
-
- &.dropdown-item {
- width: 100%;
- }
-
- &:not(.non-link) {
- font-weight: var(--go-ui-font-weight-medium);
-
- &.underline {
- --decoration: underline;
- }
- }
-
- &:not(.tertiary):not(.dropdown-item) {
- text-align: center;
- }
-
- &:not(.disabled):not(.non-link) {
- cursor: pointer;
-
- &:hover {
- --decoration: underline;
- }
- }
-
- &.disabled {
- pointer-events: none;
- }
-
- .link-element {
- display: inline-flex;
- align-items: center;
- flex-grow: 1;
- text-decoration: var(--decoration);
-
- &:focus-visible {
- outline: var(--go-ui-width-separator-thin) dashed var(--go-ui-color-gray-40);
- }
- }
-
- .forward-icon {
- font-size: var(--go-ui-height-icon-multiplier);
- margin-inline-start: -0.3em;
- }
-}
diff --git a/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/i18n.json b/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/i18n.json
deleted file mode 100644
index 00fb41b79ab3921c34364af971b26cd444cbeab5..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/i18n.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "namespace": "common",
- "strings": {
- "infoLabel": "Sources: ICRC, UN CODs",
- "mapDisclaimer":"The maps used do not imply the expression of any opinion on the part of the International Federation of Red Cross and Red Crescent Societies or National Society concerning the legal status of a territory or of its authorities.",
- "copyrightMapbox":"© Mapbox",
- "copyrightOSM":"© OpenStreetMap",
- "downloadButtonTitle":"Download",
- "failureToDownloadMessage":"Failed to download map. Try again.",
- "improveMapLabel": "Improve this map",
- "mapSourcesLabel": "Sources: ICRC, {uncodsLink}",
- "mapSourceUNCODsLabel": "UNCODs",
- "feedbackAriaLabel" : "Map feedback",
- "mapContainerMapbox": "Mapbox",
- "downloadHeaderLogoAltText":"IFRC GO logo",
- "mapContainerOpenStreetMap": "OpenStreetMap",
- "mapContainerIconButton": "Close"
- }
-}
diff --git a/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/index.tsx b/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/index.tsx
deleted file mode 100644
index ff97dce32117114a2c5f573e387ab9b0cffb8e3c..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/index.tsx
+++ /dev/null
@@ -1,230 +0,0 @@
-import {
- useCallback,
- useRef,
-} from 'react';
-import {
- CloseFillIcon,
- DownloadTwoLineIcon,
-} from '@ifrc-go/icons';
-import {
- Button,
- DateOutput,
- Header,
- IconButton,
- InfoPopup,
- RawButton,
-} from '@ifrc-go/ui';
-import {
- useBooleanState,
- useTranslation,
-} from '@ifrc-go/ui/hooks';
-import { resolveToComponent } from '@ifrc-go/ui/utils';
-import { _cs } from '@togglecorp/fujs';
-import { MapContainer } from '@togglecorp/re-map';
-import FileSaver from 'file-saver';
-import { toPng } from 'html-to-image';
-
-import goLogo from '#assets/icons/go-logo-2020.svg';
-import Link from '#components/Link';
-import { mbtoken } from '#config';
-import useAlert from '#hooks/useAlert';
-
-import i18n from './i18n.json';
-import styles from './styles.module.css';
-
-interface Props {
- className?: string;
- title?: string;
- footer?: React.ReactNode;
- withoutDownloadButton?: boolean;
-}
-
-function MapContainerWithDisclaimer(props: Props) {
- const {
- className,
- title = 'Map',
- footer,
- withoutDownloadButton = false,
- } = props;
-
- const strings = useTranslation(i18n);
-
- const mapSources = resolveToComponent(
- strings.mapSourcesLabel,
- {
- uncodsLink: (
-
- {strings.mapSourceUNCODsLabel}
-
- ),
- },
- );
-
- const [
- printMode,
- {
- setTrue: enterPrintMode,
- setFalse: exitPrintMode,
- },
- ] = useBooleanState(false);
-
- const containerRef = useRef(null);
-
- const alert = useAlert();
- const handleDownloadClick = useCallback(() => {
- if (!containerRef?.current) {
- alert.show(
- strings.failureToDownloadMessage,
- { variant: 'danger' },
- );
- exitPrintMode();
- return;
- }
- toPng(containerRef.current, {
- skipAutoScale: false,
- })
- .then((data) => FileSaver.saveAs(data, title))
- .finally(exitPrintMode);
- }, [
- exitPrintMode,
- title,
- alert,
- strings.failureToDownloadMessage,
- ]);
-
- return (
-
- {printMode && (
-
-
- )}
- >
- {strings.downloadButtonTitle}
-
-
-
-
- >
- )}
- />
- )}
-
- {printMode && (
-
- {title}
-
- >
- )}
- actions={(
-
- )}
- />
- )}
-
-
-
-
- {strings.mapDisclaimer}
-
-
- {mapSources}
-
-
-
- {strings.copyrightMapbox}
-
-
- {strings.copyrightOSM}
-
-
- {strings.improveMapLabel}
-
-
-
- )}
- />
-
- {!printMode && !withoutDownloadButton && (
-
-
-
- )}
- {footer}
-
-
- );
-}
-
-export default MapContainerWithDisclaimer;
diff --git a/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/styles.module.css b/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/styles.module.css
deleted file mode 100644
index bdfa18e4a57d147476d101bd735179b655fb0387..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/MapContainerWithDisclaimer/styles.module.css
+++ /dev/null
@@ -1,94 +0,0 @@
-.map-container {
- display: flex;
- flex-direction: column;
- flex-grow: 1;
-
- .download-header {
- border-bottom: var(--go-ui-width-separator-lg) solid var(--go-ui-color-primary-red);
- background-color: var(--go-ui-color-background);
- padding: var(--go-ui-spacing-sm);
-
- .download-heading {
- align-items: baseline;
- gap: var(--go-ui-spacing-xs);
- color: var(--go-ui-color-primary-red);
-
- .header-date {
- font-size: var(--go-ui-font-size-xs);
- }
- }
-
- .go-icon {
- height: var(--go-ui-height-compact-status-icon);
- }
-}
-
-.map-container-with-disclaimer {
- display: flex;
- position: relative;
- flex-direction: column;
- z-index: 0;
-
- &.print-mode {
- border: var(--go-ui-width-separator-sm) solid var(--go-ui-color-separator);
- }
-
- .map-wrapper {
- display: flex;
- position: relative;
- flex-direction: column;
- flex-grow: 1;
-
- .container {
- flex-grow: 1;
- }
- }
-
- .map-disclaimer {
- position: absolute;
- bottom: var(--go-ui-spacing-xs);
- left: calc(var(--mapbox-icon-width) + var(--go-ui-spacing-sm));
- background-color: var(--go-ui-color-white);
- padding: 0 var(--go-ui-spacing-2xs);
- font-size: var(--go-ui-font-size-sm);
- }
-
- .download-button {
- position: absolute;
- top: 5rem;
- /* NOTE: Exactly as mapbox */
- right: calc(10px - var(--go-ui-width-separator-md));
- border: var(--go-ui-width-separator-md) solid var(--go-ui-color-separator);
- border-radius: var(--go-ui-border-radius-md);
- background-color: var(--go-ui-color-foreground);
- padding: 0 var(--go-ui-spacing-2xs);
- font-size: var(--go-ui-height-icon-multiplier);
-
- &:hover {
- background-color: var(--go-ui-color-background);
- }
- }
-}
-
-.disclaimer-popup-content {
- display: flex;
- flex-direction: column;
- gap: var(--go-ui-spacing-sm);
-
- .attribution {
- display: flex;
- flex-wrap: wrap;
- gap: var(--go-ui-spacing-2xs) var(--go-ui-spacing-sm);
- }
-}
-
-.header {
- background-color: var(--go-ui-color-background);
- padding: var(--go-ui-spacing-md);
-
- .actions {
- align-items: center;
- }
-}
-
-}
diff --git a/go-web-app-develop/app/src/components/MapPopup/i18n.json b/go-web-app-develop/app/src/components/MapPopup/i18n.json
deleted file mode 100644
index 36e06a2a1ea0ef2a4840496d1a7525a8fa53eb55..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/MapPopup/i18n.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "namespace": "common",
- "strings": {
- "messagePopupClose": "Close"
- }
-}
diff --git a/go-web-app-develop/app/src/components/MapPopup/index.tsx b/go-web-app-develop/app/src/components/MapPopup/index.tsx
deleted file mode 100644
index 8d1b6c7932028f25aa71b96ed2ffe595aac0f296..0000000000000000000000000000000000000000
--- a/go-web-app-develop/app/src/components/MapPopup/index.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import { useMemo } from 'react';
-import { CloseLineIcon } from '@ifrc-go/icons';
-import {
- Button,
- Container,
- type ContainerProps,
-} from '@ifrc-go/ui';
-import { useTranslation } from '@ifrc-go/ui/hooks';
-import { _cs } from '@togglecorp/fujs';
-import { MapPopup as BasicMapPopup } from '@togglecorp/re-map';
-
-import i18n from './i18n.json';
-import styles from './styles.module.css';
-
-interface Props extends ContainerProps {
- coordinates: mapboxgl.LngLatLike;
- children: React.ReactNode;
- onCloseButtonClick: () => void;
- popupClassName?: string;
-}
-
-function MapPopup(props: Props) {
- const {
- children,
- coordinates,
- onCloseButtonClick,
- actions,
- childrenContainerClassName,
- popupClassName,
- ...containerProps
- } = props;
-
- const strings = useTranslation(i18n);
-
- const popupOptions = useMemo