diff --git a/.changeset/five-rooms-follow.md b/.changeset/five-rooms-follow.md new file mode 100644 index 0000000000..44d8bb6fe3 --- /dev/null +++ b/.changeset/five-rooms-follow.md @@ -0,0 +1,8 @@ +--- +"go-web-app": patch +--- + +Update Active Operation Map in homepage + +- Display map data based on emergency severity levels +- Update legend and map to reflect crisis and appeals types diff --git a/app/src/components/domain/ActiveOperationMap/OperationMapContainer/i18n.json b/app/src/components/domain/ActiveOperationMap/OperationMapContainer/i18n.json new file mode 100644 index 0000000000..50c154337d --- /dev/null +++ b/app/src/components/domain/ActiveOperationMap/OperationMapContainer/i18n.json @@ -0,0 +1,11 @@ +{ + "namespace": "activeOperationMap", + "strings": { + "operationPopoverPeopleAffected": "People Targeted", + "operationPopoverAmountRequested": "Amount Requested (CHF)", + "operationPopoverAmountFunded": "Amount Funded (CHF)", + "operationPopoverEmpty": "No Current Operations", + "explanationBubbleScalePoints": "Scale points by", + "lastUpdateLabel": "Last update" + } +} diff --git a/app/src/components/domain/ActiveOperationMap/OperationMapContainer/index.tsx b/app/src/components/domain/ActiveOperationMap/OperationMapContainer/index.tsx new file mode 100644 index 0000000000..59a7f55eed --- /dev/null +++ b/app/src/components/domain/ActiveOperationMap/OperationMapContainer/index.tsx @@ -0,0 +1,346 @@ +import { + useCallback, + useMemo, + useState, +} from 'react'; +import { + Container, + LegendItem, + ListView, + RadioInput, + TextOutput, +} from '@ifrc-go/ui'; +import { useTranslation } from '@ifrc-go/ui/hooks'; +import { sumSafe } from '@ifrc-go/ui/utils'; +import { + compareNumber, + isDefined, + isNotDefined, + listToGroupList, + mapToMap, + unique, +} from '@togglecorp/fujs'; +import { + MapBounds, + MapLayer, + MapSource, +} from '@togglecorp/re-map'; +import { type LngLatBoundsLike } from 'mapbox-gl'; + +import GoMapContainer from '#components/GoMapContainer'; +import Link from '#components/Link'; +import MapPopup from '#components/MapPopup'; +import useCountryRaw from '#hooks/domain/useCountryRaw'; +import useInputState from '#hooks/useInputState'; +import { + DEFAULT_MAP_PADDING, + DURATION_MAP_ZOOM, +} from '#utils/constants'; +import { type GoApiResponse } from '#utils/restRequest'; + +import GlobalMap, { type AdminZeroFeatureProperties } from '../../GlobalMap'; +import { + APPEAL_TYPE_MULTIPLE, + basePointLayerOptions, + optionKeySelector, + optionLabelSelector, + outerCircleLayerOptionsForFinancialRequirements, + outerCircleLayerOptionsForPeopleTargeted, + type ScaleOption, + severityOrderMapping, +} from '../utils'; + +import i18n from './i18n.json'; + +type AppealResponse = GoApiResponse<'/api/v2/appeal/'>; + +const sourceOptions: mapboxgl.GeoJSONSourceRaw = { + type: 'geojson', +}; + +type LegendOptions = { + value: number; + label: string; + color: string; +} + +interface ClickedPoint { + featureProperties: AdminZeroFeatureProperties; + lngLat: mapboxgl.LngLatLike; +} + +interface Props { + variant: 'crisis' | 'appeal'; + presentationModeAdditionalBeforeContent?: React.ReactNode; + presentationModeAdditionalAfterContent?: React.ReactNode; + mapTitle: string; + bbox: LngLatBoundsLike | undefined; + appealResponse?: AppealResponse; + legendOptions: LegendOptions[]; + onPresentationModeChange: (presentation: boolean) => void; + scaleOptions: ScaleOption[]; +} + +function OperationMapContainer(props: Props) { + const { + variant, + presentationModeAdditionalBeforeContent, + presentationModeAdditionalAfterContent, + mapTitle, + bbox, + appealResponse, + legendOptions, + onPresentationModeChange, + scaleOptions, + } = props; + + const [scaleBy, setScaleBy] = useInputState('peopleTargeted'); + const strings = useTranslation(i18n); + + const countryResponse = useCountryRaw(); + + const [clickedPoint, setClickedPoint] = useState(); + + const countryGroupedAppeal = useMemo( + () => listToGroupList( + appealResponse?.results ?? [], + (appeal) => appeal.country.iso3 ?? '', + ), + [appealResponse], + ); + + const countryCentroidGeoJson = useMemo((): GeoJSON.FeatureCollection => { + const countryToOperationTypeMap = mapToMap( + countryGroupedAppeal, + (key) => key, + (appealList) => { + const uniqueAppealList = unique( + appealList.map((appeal) => appeal.atype), + ); + + const uniqueEventList = unique( + appealList.map((severity) => severity.event_details + ?.ifrc_severity_level).filter(isDefined), + ); + + const peopleTargeted = sumSafe( + appealList.map((appeal) => appeal.num_beneficiaries), + ); + const financialRequirements = sumSafe( + appealList.map((appeal) => appeal.amount_requested), + ); + + const severityLevel = (() => { + if (uniqueEventList.length > 1) { + const highestSeverity = uniqueEventList.sort((a, b) => ( + compareNumber(severityOrderMapping[a], severityOrderMapping[b]) + )); + return highestSeverity[0]; + } + if (uniqueEventList.length === 0) return undefined; + return uniqueEventList[0]; + }); + + const appealType = (() => { + if (uniqueAppealList.length > 1) return APPEAL_TYPE_MULTIPLE; + return uniqueAppealList[0]; + }); + + return { + appealType: appealType(), + severityLevel: severityLevel(), + peopleTargeted, + financialRequirements, + }; + }, + ); + + return { + type: 'FeatureCollection' as const, + features: + countryResponse + ?.map((country) => { + if ( + (!country.independent && isNotDefined(country.record_type)) + || isNotDefined(country.centroid) + || isNotDefined(country.iso3) + ) { + return undefined; + } + + const operation = countryToOperationTypeMap[country.iso3]; + if (isNotDefined(operation)) { + return undefined; + } + + return { + type: 'Feature' as const, + geometry: country.centroid as { + type: 'Point'; + coordinates: [number, number]; + }, + properties: { + id: country.iso3, + appealType: operation.appealType, + severityLevel: operation.severityLevel, + variant, + peopleTargeted: operation.peopleTargeted, + financialRequirements: operation.financialRequirements, + }, + }; + }) + .filter(isDefined) ?? [], + }; + }, [countryResponse, countryGroupedAppeal, variant]); + + const handleCountryClick = useCallback( + ( + featureProperties: AdminZeroFeatureProperties, + lngLat: mapboxgl.LngLatLike, + ) => { + setClickedPoint({ + featureProperties, + lngLat, + }); + + return true; + }, + [], + ); + + const handlePointClose = useCallback(() => { + setClickedPoint(undefined); + }, [setClickedPoint]); + + const popupDetails = clickedPoint + ? countryGroupedAppeal[clickedPoint.featureProperties.iso3] + : undefined; + + return ( + + + + + {legendOptions.map((legendItem) => ( + + ))} + + + )} + /> + + + + + {clickedPoint?.lngLat && ( + + {clickedPoint.featureProperties.name} + + )} + withPadding + empty={isNotDefined(popupDetails) || popupDetails.length === 0} + emptyMessage={strings.operationPopoverEmpty} + > + + {popupDetails?.map((appeal) => ( + + )} + headingLevel={6} + spacing="xs" + > + + + + + + + ))} + + + )} + {isDefined(bbox) && ( + + )} + + ); +} + +export default OperationMapContainer; diff --git a/app/src/components/domain/ActiveOperationMap/i18n.json b/app/src/components/domain/ActiveOperationMap/i18n.json index cc06c2fe56..b714d7aeb5 100644 --- a/app/src/components/domain/ActiveOperationMap/i18n.json +++ b/app/src/components/domain/ActiveOperationMap/i18n.json @@ -5,24 +5,24 @@ "operationMapViewAll": "View all Operations", "operationMapViewAllInRegion": "View all Operations in this Region", "operationMapViewAllInCountry": "View all Operations in this Country", - "operationPopoverPeopleAffected": "People Targeted", - "operationPopoverAmountRequested": "Amount Requested (CHF)", - "operationPopoverAmountFunded": "Amount Funded (CHF)", - "operationPopoverEmpty": "No Current Operations", "operationType": "Type of Appeal", "operationFilterTypePlaceholder": "All Appeal Types", "operationDisasterType": "Disaster Type", "operationFilterDisastersPlaceholder": "All Disaster Types", + "crisisTabName": "Crisis Categorisation", + "appealTabName": "Appeal Types", "mapStartDateAfter": "Start After", "mapStartDateBefore": "Start Before", - "explanationBubbleScalePoints": "Scale points by", - "explanationBubblePopulationLabel": "# of people targeted", - "explanationBubbleAmountLabel": "IFRC financial requirements", "explanationBubbleEmergencyAppeal": "Emergency appeal", "explanationBubbleDref": "DREF", "explanationBubbleMultiple": "Multiple types", "operationMapProvinces": "Provinces", "operationMapClearFilters": "Clear Filters", - "operationFilterDistrictPlaceholder": "All Provinces" + "operationFilterDistrictPlaceholder": "All Provinces", + "crisisRedEmergency": "Red Emergency", + "crisisOrangeEmergency": "Orange Emergency", + "crisisYellowEmergency": "Yellow Emergency", + "explanationBubblePopulationLabel": "# of people targeted", + "explanationBubbleAmountLabel": "IFRC financial requirements" } -} \ No newline at end of file +} diff --git a/app/src/components/domain/ActiveOperationMap/index.tsx b/app/src/components/domain/ActiveOperationMap/index.tsx index e790bf3619..f6ba0370ff 100644 --- a/app/src/components/domain/ActiveOperationMap/index.tsx +++ b/app/src/components/domain/ActiveOperationMap/index.tsx @@ -7,44 +7,29 @@ import { Button, Container, DateInput, - LegendItem, - ListView, - RadioInput, + InlineLayout, SelectInput, - TextOutput, + Tab, + TabList, + Tabs, } from '@ifrc-go/ui'; import { useTranslation } from '@ifrc-go/ui/hooks'; import { hasSomeDefinedValue, resolveToComponent, - sumSafe, } from '@ifrc-go/ui/utils'; -import { - isDefined, - isNotDefined, - listToGroupList, - mapToMap, - unique, -} from '@togglecorp/fujs'; -import { - MapBounds, - MapLayer, - MapSource, -} from '@togglecorp/re-map'; +import { isDefined } from '@togglecorp/fujs'; import { type LngLatBoundsLike } from 'mapbox-gl'; import DisasterTypeSelectInput from '#components/domain/DisasterTypeSelectInput'; import DistrictSearchMultiSelectInput, { type DistrictItem } from '#components/domain/DistrictSearchMultiSelectInput'; -import GoMapContainer from '#components/GoMapContainer'; import Link from '#components/Link'; -import MapPopup from '#components/MapPopup'; -import useCountryRaw from '#hooks/domain/useCountryRaw'; import useGlobalEnums from '#hooks/domain/useGlobalEnums'; import useFilterState from '#hooks/useFilterState'; -import useInputState from '#hooks/useInputState'; import { - DEFAULT_MAP_PADDING, - DURATION_MAP_ZOOM, + DISASTER_CATEGORY_ORANGE, + DISASTER_CATEGORY_RED, + DISASTER_CATEGORY_YELLOW, } from '#utils/constants'; import type { GoApiResponse, @@ -52,19 +37,17 @@ import type { } from '#utils/restRequest'; import { useRequest } from '#utils/restRequest'; -import GlobalMap, { type AdminZeroFeatureProperties } from '../GlobalMap'; +import OperationMapContainer from './OperationMapContainer'; import { APPEAL_TYPE_DREF, APPEAL_TYPE_EMERGENCY, APPEAL_TYPE_MULTIPLE, - basePointLayerOptions, COLOR_DREF, COLOR_EMERGENCY_APPEAL, COLOR_MULTIPLE_TYPES, - optionKeySelector, - optionLabelSelector, - outerCircleLayerOptionsForFinancialRequirements, - outerCircleLayerOptionsForPeopleTargeted, + COLOR_ORANGE_SEVERITY, + COLOR_RED_SEVERITY, + COLOR_YELLOW_SEVERITY, type ScaleOption, } from './utils'; @@ -78,14 +61,6 @@ const appealTypeKeySelector = (option: AppealTypeOption) => option.key; const appealTypeLabelSelector = (option: AppealTypeOption) => option.value; const now = new Date().toISOString(); -const sourceOptions: mapboxgl.GeoJSONSourceRaw = { - type: 'geojson', -}; - -interface ClickedPoint { - featureProperties: AdminZeroFeatureProperties; - lngLat: mapboxgl.LngLatLike; -} type BaseProps = { className?: string; @@ -93,20 +68,20 @@ type BaseProps = { presentationModeAdditionalBeforeContent?: React.ReactNode; presentationModeAdditionalAfterContent?: React.ReactNode; mapTitle: string; -} +}; type CountryProps = { variant: 'country'; countryId: number; -} +}; type RegionProps = { variant: 'region'; regionId: number; -} +}; type GlobalProps = { variant: 'global'; -} +}; type Props = BaseProps & (RegionProps | GlobalProps | CountryProps); @@ -121,6 +96,7 @@ function ActiveOperationMap(props: Props) { } = props; const [presentationMode, setPresentationMode] = useState(false); + const [activeTab, setActiveTab] = useState<'crisis' | 'appeal'>('crisis'); const { filter, @@ -130,11 +106,11 @@ function ActiveOperationMap(props: Props) { setFilter, setFilterField, } = useFilterState<{ - appeal?: AppealTypeOption['key'], - district?: number[], - displacement?: number, - startDateAfter?: string, - startDateBefore?: string, + appeal?: AppealTypeOption['key']; + district?: number[]; + displacement?: number; + startDateAfter?: string; + startDateBefore?: string; }>({ filter: {}, pageSize: 9999, @@ -152,6 +128,10 @@ function ActiveOperationMap(props: Props) { dtype: filter.displacement, district: hasSomeDefinedValue(filter.district) ? filter.district : undefined, end_date__gt: now, + // NOTE: Filtering only those appeals that has not been confirmed + // and does not contain event + has_event: true, + needs_confirmation: false, start_date__gte: filter.startDateAfter, start_date__lte: filter.startDateBefore, limit, @@ -170,12 +150,6 @@ function ActiveOperationMap(props: Props) { [variant, regionId, filter, limit, countryId], ); - const [ - clickedPoint, - setClickedPoint, - ] = useState(); - - const [scaleBy, setScaleBy] = useInputState('peopleTargeted'); const strings = useTranslation(i18n); const { api_appeal_type: appealTypeOptionsRaw } = useGlobalEnums(); const { @@ -194,117 +168,6 @@ function ActiveOperationMap(props: Props) { ) ), [appealTypeOptionsRaw]); - const countryResponse = useCountryRaw(); - - const scaleOptions: ScaleOption[] = useMemo(() => ([ - { value: 'peopleTargeted', label: strings.explanationBubblePopulationLabel }, - { value: 'financialRequirements', label: strings.explanationBubbleAmountLabel }, - ]), [ - strings.explanationBubblePopulationLabel, - strings.explanationBubbleAmountLabel, - ]); - - const legendOptions = useMemo(() => ([ - { - value: APPEAL_TYPE_EMERGENCY, - label: strings.explanationBubbleEmergencyAppeal, - color: COLOR_EMERGENCY_APPEAL, - }, - { - value: APPEAL_TYPE_DREF, - label: strings.explanationBubbleDref, - color: COLOR_DREF, - }, - { - value: APPEAL_TYPE_MULTIPLE, - label: strings.explanationBubbleMultiple, - color: COLOR_MULTIPLE_TYPES, - }, - ]), [ - strings.explanationBubbleEmergencyAppeal, - strings.explanationBubbleDref, - // FIXME: string to be removed - // strings.explanationBubbleEAP, - strings.explanationBubbleMultiple, - ]); - - const countryGroupedAppeal = useMemo(() => ( - listToGroupList( - appealResponse?.results ?? [], - (appeal) => appeal.country.iso3 ?? '', - ) - ), [appealResponse]); - - const countryCentroidGeoJson = useMemo( - (): GeoJSON.FeatureCollection => { - const countryToOperationTypeMap = mapToMap( - countryGroupedAppeal, - (key) => key, - (appealList) => { - const uniqueAppealList = unique( - appealList.map((appeal) => appeal.atype), - ); - - const peopleTargeted = sumSafe( - appealList.map((appeal) => appeal.num_beneficiaries), - ); - const financialRequirements = sumSafe( - appealList.map((appeal) => appeal.amount_requested), - ); - - if (uniqueAppealList.length > 1) { - // multiple types - return { - appealType: APPEAL_TYPE_MULTIPLE, - peopleTargeted, - financialRequirements, - }; - } - - return { - appealType: uniqueAppealList[0], - peopleTargeted, - financialRequirements, - }; - }, - ); - - return { - type: 'FeatureCollection' as const, - features: countryResponse - ?.map((country) => { - if ( - (!country.independent && isNotDefined(country.record_type)) - || isNotDefined(country.centroid) - || isNotDefined(country.iso3) - ) { - return undefined; - } - - const operation = countryToOperationTypeMap[country.iso3]; - if (isNotDefined(operation)) { - return undefined; - } - - return { - type: 'Feature' as const, - geometry: country.centroid as { - type: 'Point', - coordinates: [number, number], - }, - properties: { - id: country.iso3, - appealType: operation.appealType, - peopleTargeted: operation.peopleTargeted, - financialRequirements: operation.financialRequirements, - }, - }; - }).filter(isDefined) ?? [], - }; - }, - [countryResponse, countryGroupedAppeal], - ); - const allAppealsType = useMemo(() => { if (isDefined(countryId)) { return { @@ -335,235 +198,193 @@ function ActiveOperationMap(props: Props) { { numAppeals: appealResponse?.count ?? '--' }, ); - const handleCountryClick = useCallback(( - featureProperties: AdminZeroFeatureProperties, - lngLat: mapboxgl.LngLatLike, - ) => { - setClickedPoint({ - featureProperties, - lngLat, - }); - - return true; + const handlePresentationMode = useCallback((presentation: boolean) => { + setPresentationMode(presentation); }, []); - const handlePointClose = useCallback( - () => { - setClickedPoint(undefined); - }, - [setClickedPoint], - ); - const handleClearFiltersButtonClick = useCallback(() => { setFilter({}); }, [setFilter]); - const popupDetails = clickedPoint - ? countryGroupedAppeal[clickedPoint.featureProperties.iso3] - : undefined; - const [districtOptions, setDistrictOptions] = useState(); + const handleTabChanges = useCallback( + (name: 'crisis' | 'appeal') => { + setActiveTab(name); + }, + [], + ); + + const legendOptions = useMemo(() => { + if (activeTab === 'appeal') { + return ([ + { + value: APPEAL_TYPE_EMERGENCY, + label: strings.explanationBubbleEmergencyAppeal, + color: COLOR_EMERGENCY_APPEAL, + }, + { + value: APPEAL_TYPE_DREF, + label: strings.explanationBubbleDref, + color: COLOR_DREF, + }, + { + value: APPEAL_TYPE_MULTIPLE, + label: strings.explanationBubbleMultiple, + color: COLOR_MULTIPLE_TYPES, + }, + ]); + } + return ([ + { + value: DISASTER_CATEGORY_RED, + label: strings.crisisRedEmergency, + color: COLOR_RED_SEVERITY, + }, + { + value: DISASTER_CATEGORY_ORANGE, + label: strings.crisisOrangeEmergency, + color: COLOR_ORANGE_SEVERITY, + }, + { + value: DISASTER_CATEGORY_YELLOW, + label: strings.crisisYellowEmergency, + color: COLOR_YELLOW_SEVERITY, + }, + ]); + }, [strings, activeTab]); + + const scaleOptions: ScaleOption[] = useMemo( + () => [ + { + value: 'peopleTargeted', + label: strings.explanationBubblePopulationLabel, + }, + { + value: 'financialRequirements', + label: strings.explanationBubbleAmountLabel, + }, + ], + [ + strings.explanationBubblePopulationLabel, + strings.explanationBubbleAmountLabel, + ], + ); + return ( - - - - {variant === 'country' && ( - - )} - - - - - )} - headerActions={!presentationMode && ( - - {allAppealsType.title} - - )} + - - + + + {variant === 'country' && ( + + )} + + + + + ) + } + headerActions={ + !presentationMode && ( + + {allAppealsType.title} + + ) + } + > + + + {strings.crisisTabName} + + + {strings.appealTabName} + + + )} + /> + - - - {legendOptions.map((legendItem) => ( - - ))} - - - )} + onPresentationModeChange={handlePresentationMode} + mapTitle={mapTitle} + bbox={bbox} + appealResponse={appealResponse} + scaleOptions={scaleOptions} + legendOptions={legendOptions} /> - - - - - {clickedPoint?.lngLat && ( - - {clickedPoint.featureProperties.name} - - )} - withPadding - empty={isNotDefined(popupDetails) || popupDetails.length === 0} - emptyMessage={strings.operationPopoverEmpty} - > - - {popupDetails?.map( - (appeal) => ( - - - - - - - - ), - )} - - - )} - {isDefined(bbox) && ( - - )} - - + + ); } diff --git a/app/src/components/domain/ActiveOperationMap/utils.ts b/app/src/components/domain/ActiveOperationMap/utils.ts index a8bbeeee88..5e9bba16f1 100644 --- a/app/src/components/domain/ActiveOperationMap/utils.ts +++ b/app/src/components/domain/ActiveOperationMap/utils.ts @@ -4,18 +4,26 @@ import type { } from 'mapbox-gl'; import { - COLOR_BLACK, COLOR_BLUE, + COLOR_DREF_YELLOW, + COLOR_LIGHT_GREY, COLOR_ORANGE, COLOR_RED, - COLOR_YELLOW, + DISASTER_CATEGORY_ORANGE, + DISASTER_CATEGORY_RED, + DISASTER_CATEGORY_YELLOW, + type DisasterCategory, } from '#utils/constants'; export const COLOR_EMERGENCY_APPEAL = COLOR_RED; -export const COLOR_DREF = COLOR_YELLOW; +export const COLOR_DREF = COLOR_DREF_YELLOW; export const COLOR_EAP = COLOR_BLUE; export const COLOR_MULTIPLE_TYPES = COLOR_ORANGE; +export const COLOR_YELLOW_SEVERITY = COLOR_DREF_YELLOW; +export const COLOR_ORANGE_SEVERITY = COLOR_ORANGE; +export const COLOR_RED_SEVERITY = COLOR_RED; + // FIXME: these must be a constant defined somewhere else export const APPEAL_TYPE_DREF = 0; export const APPEAL_TYPE_EMERGENCY = 1; @@ -23,18 +31,42 @@ export const APPEAL_TYPE_EMERGENCY = 1; export const APPEAL_TYPE_EAP = 3; export const APPEAL_TYPE_MULTIPLE = -1; +export const severityOrderMapping: Record = { + [DISASTER_CATEGORY_RED]: 1, + [DISASTER_CATEGORY_ORANGE]: 2, + [DISASTER_CATEGORY_YELLOW]: 3, +}; + const circleColor: CirclePaint['circle-color'] = [ 'match', - ['get', 'appealType'], - APPEAL_TYPE_DREF, - COLOR_DREF, - APPEAL_TYPE_EMERGENCY, - COLOR_EMERGENCY_APPEAL, - APPEAL_TYPE_EAP, - COLOR_EAP, - APPEAL_TYPE_MULTIPLE, - COLOR_MULTIPLE_TYPES, - COLOR_BLACK, + ['get', 'variant'], + 'appeal', + [ + 'match', + ['get', 'appealType'], + APPEAL_TYPE_DREF, + COLOR_DREF, + APPEAL_TYPE_EMERGENCY, + COLOR_EMERGENCY_APPEAL, + APPEAL_TYPE_EAP, + COLOR_EAP, + APPEAL_TYPE_MULTIPLE, + COLOR_MULTIPLE_TYPES, + COLOR_LIGHT_GREY, + ], + 'crisis', + [ + 'match', + ['get', 'severityLevel'], + DISASTER_CATEGORY_ORANGE, + COLOR_ORANGE_SEVERITY, + DISASTER_CATEGORY_RED, + COLOR_RED_SEVERITY, + DISASTER_CATEGORY_YELLOW, + COLOR_YELLOW_SEVERITY, + COLOR_LIGHT_GREY, + ], + COLOR_LIGHT_GREY, ]; const basePointPaint: CirclePaint = { diff --git a/app/src/components/domain/AppealsTable/index.tsx b/app/src/components/domain/AppealsTable/index.tsx index 665405b795..d4295ef840 100644 --- a/app/src/components/domain/AppealsTable/index.tsx +++ b/app/src/components/domain/AppealsTable/index.tsx @@ -236,6 +236,10 @@ function AppealsTable(props: Props) { ordering: orderingWithFallback, atype: filter.appeal, dtype: filter.displacement, + // NOTE: Filtering those appeals that has not been confirmed and does not + // contain event + has_event: true, + needs_confirmation: false, district: hasSomeDefinedValue(filter.district) ? filter.district : undefined, end_date__gt: withPastOperations ? undefined : now, start_date__gte: filter.startDateAfter, diff --git a/app/src/components/domain/SeverityIndicator/index.tsx b/app/src/components/domain/SeverityIndicator/index.tsx index 753498c337..f202492342 100644 --- a/app/src/components/domain/SeverityIndicator/index.tsx +++ b/app/src/components/domain/SeverityIndicator/index.tsx @@ -4,6 +4,12 @@ import { } from '@ifrc-go/ui'; import { isNotDefined } from '@togglecorp/fujs'; +import { + COLOR_ORANGE_SEVERITY, + COLOR_RED_SEVERITY, + COLOR_YELLOW_SEVERITY, +} from '../ActiveOperationMap/utils'; + interface Props extends Omit { level: number | undefined | null; title?: string; @@ -17,9 +23,9 @@ function SeverityIndicator(props: Props) { } = props; const colorMap: Record = { - 0: 'var(--go-ui-color-yellow)', - 1: 'var(--go-ui-color-orange)', - 2: 'var(--go-ui-color-red)', + 0: COLOR_YELLOW_SEVERITY, + 1: COLOR_ORANGE_SEVERITY, + 2: COLOR_RED_SEVERITY, }; if (isNotDefined(level)) { diff --git a/app/src/utils/constants.ts b/app/src/utils/constants.ts index e35e2e9d77..fc0b0b3ae6 100644 --- a/app/src/utils/constants.ts +++ b/app/src/utils/constants.ts @@ -65,6 +65,7 @@ export const COLOR_DARK_GREY = '#a5a5a5'; export const COLOR_BLACK = '#000000'; // const COLOR_LIGHT_YELLOW = '#ffd470'; export const COLOR_YELLOW = '#ff9e00'; +export const COLOR_DREF_YELLOW = '#FFF939'; export const COLOR_BLUE = '#4c5d9b'; export const COLOR_LIGHT_BLUE = '#c7d3e0'; export const COLOR_ORANGE = '#ff8000'; @@ -176,6 +177,10 @@ export const SURGE_ALERT_STATUS_OPEN = 0 satisfies SurgeAlertTypeEnum; export const SURGE_ALERT_STATUS_STOOD_DOWN = 1 satisfies SurgeAlertTypeEnum; export const SURGE_ALERT_STATUS_CLOSED = 2 satisfies SurgeAlertTypeEnum; +type TypeOfNsContributionEnum = components<'read'>['schemas']['DeploymentsEruReadinessNsContributionEnumKey']; +export const NS_CONTRIBUTION_HOLDS_ERU = 1 satisfies TypeOfNsContributionEnum; +export const NS_CONTRIBUTION_SUPPORTS_ERU = 2 satisfies TypeOfNsContributionEnum; + export const NUM_X_AXIS_TICKS_MIN = 3; export const NUM_X_AXIS_TICKS_MAX = 12; diff --git a/app/src/views/EmergencyDetails/i18n.json b/app/src/views/EmergencyDetails/i18n.json index 28b56d96be..6257da0d48 100644 --- a/app/src/views/EmergencyDetails/i18n.json +++ b/app/src/views/EmergencyDetails/i18n.json @@ -3,7 +3,7 @@ "strings": { "emergencyKeyFiguresTitle": "Key Figures", "emergencyOverviewTitle": "Emergency Overview", - "disasterCategorization": "Disaster Categorization", + "disasterCategorisation": "Disaster Categorisation", "disasterType": "Disaster Type", "GLIDENumber": "GLIDE Number", "startDate": "Start Date", diff --git a/app/src/views/EmergencyDetails/index.tsx b/app/src/views/EmergencyDetails/index.tsx index 56eb8eb8b6..788ad2e35a 100644 --- a/app/src/views/EmergencyDetails/index.tsx +++ b/app/src/views/EmergencyDetails/index.tsx @@ -203,7 +203,7 @@ export function Component() { numPreferredGridColumns={3} > ; type ReadinessOption = NonNullable[number]; +type ContributionOption = NonNullable[number]; function readinessKeySelector(option: ReadinessOption) { return option.key; } - -function readinessLabelSelector(option: ReadinessOption) { - return option.value; +function contributionKeySelector(option: ContributionOption) { + return option.key; } const defaultCollectionValue: PartialEruItem = { @@ -57,6 +58,7 @@ function EruInputItem(props: Props) { const { deployments_eru_type: eruTypeOptions, deployments_eru_readiness_status, + deployments_eru_readiness_ns_contribution: nsContributionOptions, } = useGlobalEnums(); const onFieldChange = useFormObject( @@ -76,50 +78,65 @@ function EruInputItem(props: Props) { return ( - - - -