From bfcaecf5cd6c3b840bc70cf4d583525a187182a1 Mon Sep 17 00:00:00 2001 From: samshara Date: Tue, 11 Feb 2025 14:28:37 +0545 Subject: [PATCH 1/2] Updates in DREF imminent Application * Revert commit 44623a7eb8793268ef1e71b0ba99eaea76fe4dff. * Change summary information in dref export when type is imminent * Hide and unhide the form fields in eventdetails section in dref when type is imminent * Update names of the submission form field section * Add Activity input in proposed action for the dref type imminent * Integrate feedback for DREF imminent changes * 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 a confirmation popup before creating ops. update from imminent dref * Add auto total population calculation in dref * Add imminent flag for active dref table * Update operational end date label * Update allocation form for dref imminent * Update logic for creation of dref final report for imminent * Disable ops update in old imminent drefs --- .changeset/modern-hats-fry.md | 14 + .changeset/short-otters-boil.md | 5 + app/src/assets/icons/early_actions.svg | 16 + app/src/assets/icons/early_response.svg | 15 + app/src/components/Navbar/i18n.json | 6 +- .../domain/DrefExportModal/i18n.json | 5 +- .../domain/DrefExportModal/index.tsx | 147 +- .../domain/DrefExportModal/styles.module.css | 6 + .../domain/RiskSeasonalMap/i18n.json | 3 +- .../domain/RiskSeasonalMap/index.tsx | 3 +- app/src/contexts/domain.tsx | 6 +- app/src/hooks/domain/usePrimarySector.ts | 52 + app/src/utils/form.ts | 2 +- .../ActiveDrefTable/index.tsx | 13 +- .../DrefTableActions/drefAllocationExport.ts | 4 +- .../DrefTableActions/i18n.json | 4 +- .../DrefTableActions/index.tsx | 47 +- .../DrefApplicationExport/PgaExport/i18n.json | 41 + .../DrefApplicationExport/PgaExport/index.tsx | 206 +++ .../PgaExport/styles.module.css | 34 + app/src/views/DrefApplicationExport/i18n.json | 29 +- app/src/views/DrefApplicationExport/index.tsx | 953 ++++++++---- .../DrefApplicationExport/styles.module.css | 76 + .../DrefApplicationForm/Actions/i18n.json | 5 +- .../DrefApplicationForm/Actions/index.tsx | 22 +- .../DrefApplicationForm/EventDetail/i18n.json | 17 +- .../DrefApplicationForm/EventDetail/index.tsx | 157 +- .../ActivitiesInput/i18n.json | 8 + .../ActivitiesInput/index.tsx | 108 ++ .../Operation/ProposedActionsInput/i18n.json | 12 + .../Operation/ProposedActionsInput/index.tsx | 225 +++ .../ProposedActionsInput/styles.module.css | 25 + .../DrefApplicationForm/Operation/i18n.json | 23 +- .../DrefApplicationForm/Operation/index.tsx | 1280 ++++++++++------- .../Operation/styles.module.css | 4 +- .../DrefApplicationForm/Overview/i18n.json | 8 +- .../DrefApplicationForm/Overview/index.tsx | 174 ++- .../DrefApplicationForm/Submission/i18n.json | 10 +- .../DrefApplicationForm/Submission/index.tsx | 109 +- app/src/views/DrefApplicationForm/common.tsx | 56 +- app/src/views/DrefApplicationForm/i18n.json | 2 + app/src/views/DrefApplicationForm/index.tsx | 181 ++- app/src/views/DrefApplicationForm/schema.ts | 310 +++- .../DrefFinalReportForm/Operation/index.tsx | 34 +- .../DrefFinalReportForm/Overview/i18n.json | 4 +- .../DrefFinalReportForm/Overview/index.tsx | 13 +- app/src/views/DrefFinalReportForm/common.tsx | 12 + app/src/views/DrefFinalReportForm/index.tsx | 1 + .../Operation/index.tsx | 32 +- .../Overview/i18n.json | 9 +- .../Overview/index.tsx | 31 +- .../Submission/i18n.json | 2 +- .../DrefOperationalUpdateForm/common.tsx | 12 + .../views/DrefOperationalUpdateForm/index.tsx | 1 + app/src/views/RootLayout/index.tsx | 19 + app/src/views/Surge/i18n.json | 2 +- .../views/SurgeCatalogueOverview/i18n.json | 2 +- packages/ui/src/utils/common.ts | 33 + .../000015-1739181128201.json | 2 +- .../000038-1750656635055.json | 797 ++++++++++ 60 files changed, 4238 insertions(+), 1191 deletions(-) create mode 100644 .changeset/modern-hats-fry.md create mode 100644 .changeset/short-otters-boil.md create mode 100644 app/src/assets/icons/early_actions.svg create mode 100644 app/src/assets/icons/early_response.svg create mode 100644 app/src/components/domain/DrefExportModal/styles.module.css create mode 100644 app/src/hooks/domain/usePrimarySector.ts create mode 100644 app/src/views/DrefApplicationExport/PgaExport/i18n.json create mode 100644 app/src/views/DrefApplicationExport/PgaExport/index.tsx create mode 100644 app/src/views/DrefApplicationExport/PgaExport/styles.module.css create mode 100644 app/src/views/DrefApplicationForm/Operation/ProposedActionsInput/ActivitiesInput/i18n.json create mode 100644 app/src/views/DrefApplicationForm/Operation/ProposedActionsInput/ActivitiesInput/index.tsx create mode 100644 app/src/views/DrefApplicationForm/Operation/ProposedActionsInput/i18n.json create mode 100644 app/src/views/DrefApplicationForm/Operation/ProposedActionsInput/index.tsx create mode 100644 app/src/views/DrefApplicationForm/Operation/ProposedActionsInput/styles.module.css create mode 100644 translationMigrations/000038-1750656635055.json diff --git a/.changeset/modern-hats-fry.md b/.changeset/modern-hats-fry.md new file mode 100644 index 0000000000..7c86288f30 --- /dev/null +++ b/.changeset/modern-hats-fry.md @@ -0,0 +1,14 @@ +--- +"go-web-app": minor +--- + +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 diff --git a/.changeset/short-otters-boil.md b/.changeset/short-otters-boil.md new file mode 100644 index 0000000000..3a8a487b86 --- /dev/null +++ b/.changeset/short-otters-boil.md @@ -0,0 +1,5 @@ +--- +"@ifrc-go/ui": patch +--- + +Add `addNumDaysToDate` and `ceilToEndOfMonth` date helper functions diff --git a/app/src/assets/icons/early_actions.svg b/app/src/assets/icons/early_actions.svg new file mode 100644 index 0000000000..3b93b121b4 --- /dev/null +++ b/app/src/assets/icons/early_actions.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/assets/icons/early_response.svg b/app/src/assets/icons/early_response.svg new file mode 100644 index 0000000000..c7f796fecb --- /dev/null +++ b/app/src/assets/icons/early_response.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/app/src/components/Navbar/i18n.json b/app/src/components/Navbar/i18n.json index 50ca0da7a1..08282b1343 100644 --- a/app/src/components/Navbar/i18n.json +++ b/app/src/components/Navbar/i18n.json @@ -56,7 +56,7 @@ "userMenuSurge":"The section displays the summary of deployments within current and ongoing emergencies. Login to see available details", "userMenuSurgeGlobalOverview":"Surge Global Overview", "userMenuOperationalToolbox":"Operational Toolbox", - "userMenuCatalogueSurgeServices":"Catalogue of Surge services", + "userMenuCatalogueSurgeServices":"Catalogue of Surge Services", "userMenuLearnLabel":"Learn", "userMenuTools":"Tools", "userMenuResources":"Resources", @@ -65,8 +65,8 @@ "userMenuOperationalLearningDescription":"Operational learning in emergencies is the lesson learned from managing and dealing with crises, refining protocols for resource allocation, decision-making, communication strategies, and others.", "userMenuOperationalToolboxItem":"Operational Toolbox", "userMenuOperationalToolboxItemDescription":"This operational toolbox is a central repository with key operational document helpful for your mission like templates, checklists, guidance and examples.", - "userMenuCatalogueSurgeServicesItem": "Catalogue of Surge services", - "userMenuCatalogueSurgeServicesItemDescription":"Catalogue of Surge services contains all relevant content and materials related to Surge.", + "userMenuCatalogueSurgeServicesItem": "Catalogue of Surge Services", + "userMenuCatalogueSurgeServicesItemDescription":"Catalogue of Surge Services contains all relevant content and materials related to Surge.", "userMenuPERCatalogueItem":"PER Catalogue of Resources", "userMenuPERCatalogueItemDescription":"PER Catalogue of Resources contains resource relevant to strengthening resource and capacity.", "userMenuGoResourcesItem":"GO Resources", diff --git a/app/src/components/domain/DrefExportModal/i18n.json b/app/src/components/domain/DrefExportModal/i18n.json index 0b970146a9..9f20ad6d74 100644 --- a/app/src/components/domain/DrefExportModal/i18n.json +++ b/app/src/components/domain/DrefExportModal/i18n.json @@ -7,6 +7,9 @@ "drefExportFailed": "Export failed", "drefExportSuccessfully": "Export completed successfully", "drefClickDownloadLink": "Click on the download link below!", - "drefDownloadPDF": "Download PDF" + "drefDownloadPDF": "Download PDF", + "drefDownloadPDFWithPGA": "Download PDF with PGA", + "drefDownloadPDFwithoutPGA": "Download PDF without PGA", + "drefFailureToExportMessage":"Failed to export PDF." } } \ No newline at end of file diff --git a/app/src/components/domain/DrefExportModal/index.tsx b/app/src/components/domain/DrefExportModal/index.tsx index ec17823a0e..de14d908e9 100644 --- a/app/src/components/domain/DrefExportModal/index.tsx +++ b/app/src/components/domain/DrefExportModal/index.tsx @@ -1,8 +1,11 @@ import { + useCallback, useMemo, useState, } from 'react'; import { + Button, + Checkbox, Message, Modal, } from '@ifrc-go/ui'; @@ -14,9 +17,18 @@ import { import Link from '#components/Link'; import { type components } from '#generated/types'; -import { useRequest } from '#utils/restRequest'; +import useAlert from '#hooks/useAlert'; +import { + DREF_TYPE_IMMINENT, + type TypeOfDrefEnum, +} from '#utils/constants'; +import { + useLazyRequest, + useRequest, +} from '#utils/restRequest'; import i18n from './i18n.json'; +import styles from './styles.module.css'; type ExportTypeEnum = components<'read'>['schemas']['ExportTypeEnum']; type ExportStatusEnum = components<'read'>['schemas']['ExportStatusEnum']; @@ -29,6 +41,7 @@ interface Props { id: number; onCancel: () => void; applicationType: 'DREF' | 'OPS_UPDATE' | 'FINAL_REPORT'; + drefType?: TypeOfDrefEnum | null; } function DrefExportModal(props: Props) { @@ -36,11 +49,40 @@ function DrefExportModal(props: Props) { id, onCancel, applicationType, + drefType, } = props; const strings = useTranslation(i18n); + const alert = useAlert(); const [exportId, setExportId] = useState(); + const [isPga, setIsPga] = useState(false); + const [isPgaCheckboxVisible, setIsPgaCheckboxVisible] = useState(true); + + const drefExportTriggerBody = useMemo( + () => { + let type: ExportTypeEnum; + if (applicationType === 'OPS_UPDATE') { + type = 'dref-operational-updates'; + } else if (applicationType === 'FINAL_REPORT') { + type = 'dref-final-reports'; + } else { + type = 'dref-applications'; + } + return { + export_id: id, + export_type: type, + is_pga: isPga, + selector: '#pdf-preview-ready', + per_country: undefined, + }; + }, + [ + id, + isPga, + applicationType, + ], + ); const exportTriggerBody = useMemo( () => { @@ -56,19 +98,46 @@ function DrefExportModal(props: Props) { return { export_id: id, export_type: type, + is_pga: isPga, selector: '#pdf-preview-ready', per_country: undefined, is_pga: false, }; }, - [id, applicationType], + [ + id, + isPga, + applicationType, + ], ); + const { + pending: pendingDrefImminentExportTrigger, + error: drefImminentExportError, + trigger: drefImminentExportTrigger, + } = useLazyRequest({ + method: 'POST', + useCurrentLanguageForMutation: true, + url: '/api/v2/pdf-export/', + body: drefExportTriggerBody, + onSuccess: (response) => { + if (isDefined(response.id)) { + setExportId(response.id); + } + }, + onFailure: () => { + alert.show( + strings.drefFailureToExportMessage, + { variant: 'danger' }, + ); + }, + }); + const { pending: pendingExportTrigger, error: exportTriggerError, } = useRequest({ - skip: isDefined(exportId) || isNotDefined(id), + skip: isDefined(exportId) || isNotDefined(id) || drefType === DREF_TYPE_IMMINENT, method: 'POST', useCurrentLanguageForMutation: true, url: '/api/v2/pdf-export/', @@ -78,6 +147,12 @@ function DrefExportModal(props: Props) { setExportId(response.id); } }, + onFailure: () => { + alert.show( + strings.drefFailureToExportMessage, + { variant: 'danger' }, + ); + }, }); const { @@ -98,18 +173,40 @@ function DrefExportModal(props: Props) { }, }); + const handleDrefImminent = useCallback(() => { + setIsPgaCheckboxVisible(false); + drefImminentExportTrigger(drefExportTriggerBody); + }, [ + drefExportTriggerBody, + drefImminentExportTrigger, + ]); + return ( - {pendingExportTrigger && ( + {drefType === DREF_TYPE_IMMINENT + && isPgaCheckboxVisible + && !(pendingExportTrigger + || pendingExportStatus + || exportStatusResponse?.status === EXPORT_STATUS_PENDING) + && ( + + )} + {pendingExportTrigger && pendingDrefImminentExportTrigger && ( )} - {(pendingExportStatus || exportStatusResponse?.status === EXPORT_STATUS_PENDING) && ( + {(pendingExportStatus + || exportStatusResponse?.status === EXPORT_STATUS_PENDING) && ( )} + {!(pendingExportTrigger + || pendingExportStatus + || exportStatusResponse?.status === EXPORT_STATUS_PENDING) + && drefType === DREF_TYPE_IMMINENT + && !drefImminentExportError && ( + exportStatusResponse?.pdf_file ? ( + + {strings.drefDownloadPDF} + + )} + /> + ) : (!exportStatusResponse && ( +
+ +
+ )) + )} {isDefined(exportStatusResponse) && exportStatusResponse.status === EXPORT_STATUS_COMPLETED - && isDefined(exportStatusResponse.pdf_file) && ( + && isDefined(exportStatusResponse.pdf_file) + && drefType !== DREF_TYPE_IMMINENT && ( } {!dataPending && (isNotDefined(filteredData) || filteredData?.length === 0) && ( )} {/* FIXME: use List */} diff --git a/app/src/contexts/domain.tsx b/app/src/contexts/domain.tsx index 881eef62ef..35fe0529c5 100644 --- a/app/src/contexts/domain.tsx +++ b/app/src/contexts/domain.tsx @@ -2,7 +2,7 @@ import { createContext } from 'react'; import { type GoApiResponse } from '#utils/restRequest'; -export type CacheKey = 'country' | 'global-enums' | 'disaster-type' | 'user-me' | 'region' | 'secondary-sector' | 'per-components'; +export type CacheKey = 'country' | 'global-enums' | 'disaster-type' | 'user-me' | 'region' | 'secondary-sector' | 'per-components' | 'primary-sector'; export type GlobalEnums = Partial>; export type Countries = GoApiResponse<'/api/v2/country/'>; @@ -10,6 +10,7 @@ export type DisasterTypes = GoApiResponse<'/api/v2/disaster_type/'>; type UserMe = GoApiResponse<'/api/v2/user/me/'>; export type Regions = GoApiResponse<'/api/v2/region/'>; export type SecondarySectors = GoApiResponse<'/api/v2/secondarysector'>; +export type PrimarySectors = GoApiResponse<'/api/v2/primarysector'>; export type PerComponents = GoApiResponse<'/api/v2/per-formcomponent/'>; export interface Domain { @@ -34,6 +35,9 @@ export interface Domain { secondarySectors?: SecondarySectors; secondarySectorsPending?: boolean; + primarySectors?: PrimarySectors; + primarySectorsPending?: boolean; + perComponents?: PerComponents; perComponentsPending?: boolean; } diff --git a/app/src/hooks/domain/usePrimarySector.ts b/app/src/hooks/domain/usePrimarySector.ts new file mode 100644 index 0000000000..64e1f57a78 --- /dev/null +++ b/app/src/hooks/domain/usePrimarySector.ts @@ -0,0 +1,52 @@ +import { + useContext, + useEffect, + useMemo, +} from 'react'; +import { isDefined } from '@togglecorp/fujs'; + +import DomainContext, { type PrimarySectors } from '#contexts/domain'; + +export type PrimarySector = NonNullable[number]; + +type ListProps = { + id?: never; +} + +type PropsForId = { + id: number; +} + +function usePrimarySector(props?: ListProps): Array | undefined +function usePrimarySector(props: PropsForId): PrimarySector | undefined +function usePrimarySector( + props?: ListProps | PropsForId, +): PrimarySector | undefined | Array | undefined { + const { + register, + primarySectors, + } = useContext(DomainContext); + + useEffect( + () => { + register('primary-sector'); + }, + [register], + ); + + const returnValue = useMemo( + () => { + const id = props?.id; + if (isDefined(id)) { + return primarySectors?.find((primaryTag) => primaryTag.key === id); + } + + return primarySectors; + }, + [primarySectors, props?.id], + ); + + return returnValue; +} + +export default usePrimarySector; diff --git a/app/src/utils/form.ts b/app/src/utils/form.ts index 34295b2e3f..1b24de4825 100644 --- a/app/src/utils/form.ts +++ b/app/src/utils/form.ts @@ -40,7 +40,7 @@ export function dateGreaterThanOrEqualCondition(x: string) { // FIXME: use translations return (value: Maybe) => ( isDefined(value) && (new Date(value).getTime()) < (new Date(x).getTime()) - ? `Field must be greater than ${x}` + ? `Select a date on or after ${x}.` : undefined ); } diff --git a/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx b/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx index 8b46a5b85d..0e0a06f185 100644 --- a/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx +++ b/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx @@ -30,6 +30,7 @@ import { import useUserMe from '#hooks/domain/useUserMe'; import useFilterState from '#hooks/useFilterState'; import { + DREF_TYPE_IMMINENT, DREF_TYPE_LOAN, type TypeOfDrefEnum, } from '#utils/constants'; @@ -227,21 +228,29 @@ function ActiveDrefTable(props: Props) { } const { - // unpublished_final_report_count, unpublished_op_update_count, is_published, has_ops_update, has_final_report, country_details, + is_dref_imminent_v2, } = originalDref; const canAddOpsUpdate = (is_published ?? false) && (applicationType === 'DREF' || applicationType === 'OPS_UPDATE') && !has_final_report - && unpublished_op_update_count === 0; + && unpublished_op_update_count === 0 + // NOTE: Adding this to disable updates just for the old imminents + && ( + item.type_of_dref !== DREF_TYPE_IMMINENT + || (item.type_of_dref === DREF_TYPE_IMMINENT && is_dref_imminent_v2) + ); const canCreateFinalReport = !has_final_report && (applicationType === 'DREF' || applicationType === 'OPS_UPDATE') + // TODO: Remove me after immplementation of DrefFinalReport for imminent + && ((isDefined(is_dref_imminent_v2) && has_ops_update) + || !is_dref_imminent_v2) && (is_published ?? false) && (item.type_of_dref !== DREF_TYPE_LOAN) && ( diff --git a/app/src/views/AccountMyFormsDref/DrefTableActions/drefAllocationExport.ts b/app/src/views/AccountMyFormsDref/DrefTableActions/drefAllocationExport.ts index e566e0825a..1f766214ba 100644 --- a/app/src/views/AccountMyFormsDref/DrefTableActions/drefAllocationExport.ts +++ b/app/src/views/AccountMyFormsDref/DrefTableActions/drefAllocationExport.ts @@ -16,7 +16,7 @@ interface ExportData { noOfPeopleTargeted: number | null | undefined; nsRequestDate: string | null | undefined; disasterStartDate: string | null | undefined; - implementationPeriod: number | null | undefined; + implementationPeriod: string | null | undefined; allocationRequested: number | null | undefined; previousAllocation?: number | null | undefined; totalDREFAllocation: number | null | undefined; @@ -381,7 +381,7 @@ export async function exportDrefAllocation(exportData: ExportData) { worksheet.getCell('E24').value = disasterStartDate; } if (isDefined(implementationPeriod)) { - worksheet.getCell('I24').value = `${implementationPeriod} months`; + worksheet.getCell('I24').value = implementationPeriod; } worksheet.getCell('A23').fill = { type: 'pattern', diff --git a/app/src/views/AccountMyFormsDref/DrefTableActions/i18n.json b/app/src/views/AccountMyFormsDref/DrefTableActions/i18n.json index f0235c5ad0..551bf82d04 100644 --- a/app/src/views/AccountMyFormsDref/DrefTableActions/i18n.json +++ b/app/src/views/AccountMyFormsDref/DrefTableActions/i18n.json @@ -13,6 +13,8 @@ "drefApprovalInProgressTitle": "Approval in progress...", "drefAccountCouldNotCreate": "Could not create new operational update", "drefAccountCouldNotCreateFinalReport": "Could not create final report", - "drefAccountConfirmMessage": "You're about to Approve this DREF. Once approved, it can no longer be edited. Are you sure, you want to Approve?" + "drefAccountConfirmMessage": "You're about to Approve this DREF. Once approved, it can no longer be edited. Are you sure you want to Approve?", + "dropdownActionImminentNewOpsUpdateConfirmationHeading": "Confirm addition of Operational Update", + "dropdownActionImminentNewOpsUpdateConfirmationMessage": "The DREF type will be changed to Response (from Imminent) for the Operational Update. Once created, you'll be able to change it to other types except Imminent. Are you sure you want to add an Operational Update?" } } diff --git a/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx b/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx index 0e7c7fac11..b8d072237a 100644 --- a/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx +++ b/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx @@ -49,12 +49,12 @@ export interface Props { status: DrefStatus | null | undefined; applicationType: 'DREF' | 'OPS_UPDATE' | 'FINAL_REPORT'; - drefType?: TypeOfDrefEnum; canAddOpsUpdate: boolean; canCreateFinalReport: boolean; hasPermissionToApprove?: boolean; onPublishSuccess?: () => void; + drefType?: TypeOfDrefEnum | null | undefined; } function DrefTableActions(props: Props) { @@ -63,11 +63,11 @@ function DrefTableActions(props: Props) { drefId: drefIdFromProps, status, applicationType, - drefType, canAddOpsUpdate, canCreateFinalReport, hasPermissionToApprove, onPublishSuccess, + drefType, } = props; const { navigate } = useRouting(); @@ -105,10 +105,12 @@ function DrefTableActions(props: Props) { noOfPeopleTargeted: response?.num_assisted, nsRequestDate: response?.ns_request_date, disasterStartDate: response?.event_date, - implementationPeriod: response?.operation_timeframe, - allocationRequested: response?.amount_requested, - previousAllocation: undefined, + implementationPeriod: response?.type_of_dref === DREF_TYPE_IMMINENT + ? `${response.operation_timeframe_imminent} days` : `${response?.operation_timeframe} months`, totalDREFAllocation: response?.amount_requested, + previousAllocation: undefined, + allocationRequested: response.type_of_dref === DREF_TYPE_IMMINENT + ? response.total_cost : response?.amount_requested, // FIXME: use translations toBeAllocatedFrom: response?.type_of_dref === DREF_TYPE_IMMINENT ? 'Anticipatory Pillar' : 'Response Pillar', focalPointName: response?.regional_focal_point_name, @@ -129,27 +131,27 @@ function DrefTableActions(props: Props) { ), onSuccess: (response) => { const exportData = { - allocationFor: response?.type_of_dref_display === 'Loan' ? 'Emergency Appeal' : 'DREF Operation', + allocationFor: response?.type_of_dref === DREF_TYPE_LOAN ? 'Emergency Appeal' : 'DREF Operation', appealManager: response?.ifrc_appeal_manager_name, projectManager: response?.ifrc_project_manager_name, affectedCountry: response?.country_details?.name, name: response?.title, disasterType: response?.disaster_type_details?.name, responseType: - response?.type_of_dref_display === 'Imminent' - // FIXME: can't compare imminent with Imminent Crisis directly + response?.type_of_dref === DREF_TYPE_IMMINENT + // FIXME: add translations ? 'Imminent Crisis' : response?.type_of_onset_display, nsRequestDate: response?.ns_request_date, disasterStartDate: response?.event_date, - implementationPeriod: response?.total_operation_timeframe, + implementationPeriod: `${response?.total_operation_timeframe} months`, allocationRequested: response?.additional_allocation, previousAllocation: response?.dref_allocated_so_far ?? 0, totalDREFAllocation: response?.total_dref_allocation, noOfPeopleTargeted: response?.number_of_people_targeted, toBeAllocatedFrom: - response?.type_of_dref_display === 'Imminent' - // FIXME: can't compare imminent with Anticipatory Pillar + response?.type_of_dref === DREF_TYPE_IMMINENT + // FIXME: add translations ? 'Anticipatory Pillar' : 'Response Pillar', focalPointName: response?.regional_focal_point_name, @@ -381,6 +383,8 @@ function DrefTableActions(props: Props) { const canApprove = status === DREF_STATUS_IN_PROGRESS && hasPermissionToApprove; + const shouldConfirmImminentAddOpsUpdate = drefType === DREF_TYPE_IMMINENT; + const disabled = fetchingDref || fetchingOpsUpdate || publishDrefPending @@ -419,7 +423,25 @@ function DrefTableActions(props: Props) { {strings.dropdownActionAllocationFormLabel} )} - {canAddOpsUpdate && ( + {canAddOpsUpdate && shouldConfirmImminentAddOpsUpdate && ( + } + confirmHeading={ + strings.dropdownActionImminentNewOpsUpdateConfirmationHeading + } + confirmMessage={ + strings.dropdownActionImminentNewOpsUpdateConfirmationMessage + } + onConfirm={handleAddOpsUpdate} + disabled={disabled} + persist + > + {strings.dropdownActionAddOpsUpdateLabel} + + )} + {canAddOpsUpdate && !shouldConfirmImminentAddOpsUpdate && ( )} {showShareModal && ( diff --git a/app/src/views/DrefApplicationExport/PgaExport/i18n.json b/app/src/views/DrefApplicationExport/PgaExport/i18n.json new file mode 100644 index 0000000000..07cae9c5d5 --- /dev/null +++ b/app/src/views/DrefApplicationExport/PgaExport/i18n.json @@ -0,0 +1,41 @@ +{ + "namespace": "drefApplicationExport", + "strings": { + "imminentDREFRequestHeading": "Imminent DREF Request and Obligations", + "requestHeading": "Request:", + "requestDescription": "The National Society hereby requests an Imminent DREF funding of CHF 75,000 to implement anticipatory actions to mitigate the risk of the foreseen disaster and possibly a few immediate response activities should the disaster materialize.", + "nationalSocietyHeading": "National Society Obligations:", + "nationalSocietyDescriptionOne": "The National Society commits to use the Imminent DREF funding solely for the proposed activities as per the budget indicated on page 1 and in accordance with the DREF Guidelines.", + "nationalSocietyDescriptionTwo": "The maximum timeframe for implementing the proposed activities shall be 45 days after the date of approval of the Imminent DREF request, with no possibility of extension.", + "nationalSocietyDescriptionThree": "In the event the foreseen disaster materializes, and the National Society requires additional funding from the DREF,", + "nationalSocietyDescriptionFour": "The National Society shall promptly but no later than 14 days from the date of the disaster occurrence, submit a DREF application for the next operation (using the Operations Update form via GO). Upon approval of the response DREF application, the National Society shall promptly but no later than 15 days from receiving the Project Funding Agreement from the IFRC, sign such Project Funding Agreement.", + "nationalSocietyDescriptionFive": "The Project Funding Agreement shall indicate the amount already paid out as Imminent DREF funding which shall be included in the total DREF cap allocation.", + "nationalSocietyDescriptionSix": "The DREF application budget shall reflect the full funding needed for the operation, including the activities carried out using the Imminent DREF funding.", + "nationalSocietyDescriptionSeven": "The National Society shall provide the following reports:", + "nationalSocietyDescriptionEight": "two narrative reports:", + "nationalSocietyDescriptionNine": "Operations Update to scale up through response DREF request and update on Imminent DREF implemented activities", + "nationalSocietyDescriptionTen": "Imminent DREF and response DREF report in one common final report", + "nationalSocietyDescriptionEleven": "One financial report of the final Imminent DREF and the response DREF operations.", + "nationalSocietyDescriptionTwelve": "In the event the disaster materializes, however the National Society does not wish to request international support, the National Society shall:", + "nationalSocietyDescriptionThirteen": "Provide a final financial and narrative report on the use of the Imminent DREF request within 30 days after the 45 days implementation period set out in paragraph 4 above,", + "nationalSocietyDescriptionFourteen": "In the event the disaster does not materialize, the National Society shall:", + "nationalSocietyDescriptionFifteen": "Refund the amount dedicated to immediate response activities of the Imminent DREF request within 30 days after the implementation period set out in paragraph 4 above.", + "nationalSocietyDescriptionSixteen": "By signing this Imminent DREF request, the National Society commits to zero tolerance for, and shall take appropriate measures against, fraud, corruption, and any form of abuse against any person, in particular children, including without limitation sexual exploitation, abuse and harassment. The IFRC reserves the right to perform audits, financial reviews, checks and verifications and/or investigations to ensure the proper use of the Imminent DREF and compliance with the above-mentioned obligations. In order to facilitate such audits, financial reviews, checks and verifications and/or investigations, the National Society shall grant the IFRC access to its documents, records, and premises as well as its staff, agents, volunteers, beneficiaries, sub-grantees and contractors. Any breach of the above obligations shall entitle the IFRC to request a full refund of this Imminent DREF request.", + "nationalSocietyBankDetails": "Bank details of the National Society", + "nationalSocietyBankDescription": "to which the IFRC shall transfer the funds will be transferred.", + "nationalSocietyBankName": "Bank Name and Address", + "nationalSocietyBankAccountNumber": "Bank Account Number and Currency", + "nationalSocietySwiftCode": "IBAN / SWIFT Code", + "nationalSocietyAmount": "Amount(CHF)", + "nationalSocietyAmountCHF": "CHF 75,000/CHF 90800", + "nationalSocietyAdvancePayment": "For the Purpose of Advance Payment", + "nationalSocietyBankFooter": "Please find attached letter from the Bank confirming banking relationship details.", + "imminentDrefRequest": "This Imminent DREF request is signed on behalf of:", + "imminentDrefSigned": "SIGNED by the authorised representative of the National Society", + "imminentIFRCSigned": "SIGNED by the authorised representative of the IFRC", + "imminentSignature": "Signature", + "imminentPrintedSignatory": "Printed Signatory's Name", + "imminentTitle": "Title", + "imminentDate": "Date" + } +} \ No newline at end of file diff --git a/app/src/views/DrefApplicationExport/PgaExport/index.tsx b/app/src/views/DrefApplicationExport/PgaExport/index.tsx new file mode 100644 index 0000000000..a60f16922d --- /dev/null +++ b/app/src/views/DrefApplicationExport/PgaExport/index.tsx @@ -0,0 +1,206 @@ +import { useParams } from 'react-router-dom'; +import { useTranslation } from '@ifrc-go/ui/hooks'; +import { + Container, + DescriptionText, + Heading, + Signature, + TextOutput, + type TextOutputProps, +} from '@ifrc-go/ui/printable'; +import { + isDefined, + isFalsyString, +} from '@togglecorp/fujs'; + +import { useRequest } from '#utils/restRequest'; + +import i18n from './i18n.json'; +import styles from './styles.module.css'; + +export function BlockTextOutput(props: TextOutputProps + & { variant?: never, withoutLabelColon?: never }) { + return ( + + ); +} + +function PgaExport() { + const { drefId } = useParams<{ drefId: string }>(); + const strings = useTranslation(i18n); + + const { + response: drefResponse, + } = useRequest({ + skip: isFalsyString(drefId), + url: '/api/v2/dref/{id}/', + pathVariables: isDefined(drefId) ? { + id: drefId, + } : undefined, + }); + + return ( +
+ + {strings.imminentDREFRequestHeading} + + + +
    +
  1. + {strings.requestDescription} +
  2. +
+
+
+ + +
    +
  1. + {strings.nationalSocietyDescriptionOne} +
  2. +
  3. + {strings.nationalSocietyDescriptionTwo} +
  4. +
  5. + {strings.nationalSocietyDescriptionThree} +
      +
    1. + {strings.nationalSocietyDescriptionFour} +
    2. +
    3. + {strings.nationalSocietyDescriptionFive} +
    4. +
    5. + {strings.nationalSocietyDescriptionSix} +
    6. +
    7. + {strings.nationalSocietyDescriptionSeven} +
        +
      1. + {strings.nationalSocietyDescriptionEight} +
        + {strings.nationalSocietyDescriptionNine} +
      2. +
      3. + {strings.nationalSocietyDescriptionTen} +
        + {strings.nationalSocietyDescriptionEleven} +
      4. +
      +
    8. +
    +
  6. +
  7. + {strings.nationalSocietyDescriptionTwelve} +
      +
    1. + {strings.nationalSocietyDescriptionThirteen} +
    2. +
    +
  8. +
  9. + {strings.nationalSocietyDescriptionFourteen} +
      +
    1. + {strings.nationalSocietyDescriptionThirteen} +
    2. +
    3. + {strings.nationalSocietyDescriptionFifteen} +
    4. +
    +
  10. +
+
+
+ + + {strings.nationalSocietyDescriptionSixteen} + + + + + {strings.nationalSocietyBankDescription} + +
+ + + + + +
+ {strings.nationalSocietyBankFooter} +
+ +
+ + + + + + + + + + +
+
+
+ ); +} + +export default PgaExport; diff --git a/app/src/views/DrefApplicationExport/PgaExport/styles.module.css b/app/src/views/DrefApplicationExport/PgaExport/styles.module.css new file mode 100644 index 0000000000..fb10e9f797 --- /dev/null +++ b/app/src/views/DrefApplicationExport/PgaExport/styles.module.css @@ -0,0 +1,34 @@ +.pga-export { + .page-break { + break-before: page; + } + + .description { + margin: 0; + padding-inline-start: var(--go-ui-spacing-sm); + } + + .table-description { + font-weight: var(--go-ui-font-weight-bold); + } + + .bank-details { + display: grid; + grid-gap: var(--go-ui-width-separator-md); + grid-template-columns: 1fr 1fr; + page-break-inside: avoid; + } + + .dref-signed { + display: grid; + grid-gap: var(--go-ui-spacing-xl); + grid-template-columns: 1fr 1fr; + page-break-inside: avoid; + + .dref-table { + display: flex; + align-items: center; + flex-direction: column; + } + } +} diff --git a/app/src/views/DrefApplicationExport/i18n.json b/app/src/views/DrefApplicationExport/i18n.json index f3227bf266..4c577e7d25 100644 --- a/app/src/views/DrefApplicationExport/i18n.json +++ b/app/src/views/DrefApplicationExport/i18n.json @@ -2,7 +2,9 @@ "namespace": "drefApplicationExport", "strings": { "exportTitle": "DREF Operation", + "exportDrefImminentTitle": "Imminent DREF Operation", "appealLabel": "Appeal", + "drefAllocatedLabel": "DREF Allocated", "countryLabel": "Country", "hazardLabel": "Hazard", "typeOfDrefLabel": "Type of DREF", @@ -19,6 +21,7 @@ "operationStartDateLabel": "Operation Start Date", "operationTimeframeLabel": "Operation Timeframe", "monthsSuffix": " months", + "daysSuffix": " days", "operationEndDateLabel": "Operation End Date", "drefPublishedLabel": "DREF Published", "targetedAreasLabel": "Targeted Regions", @@ -27,7 +30,6 @@ "whatWhereWhenSectionHeading": "What happened, where and when?", "dateOfEventSlowHeading": "Date of event", "dateWhenTriggerWasMetHeading": "Date when the trigger was met", - "situationUpdateSectionHeading": "Provide any updates in the situation since the field report and explain what is expected to happen.", "anticipatoryActionsHeading": "Why your National Society is acting now and what criteria is used to launch this operation.", "scopeAndScaleSectionHeading": "Scope and Scale", "previousOperationsSectionHeading": "Previous Operations", @@ -41,7 +43,6 @@ "completeChildSafeguardingRiskLabel": "Did you complete the Child Safeguarding Risk Analysis in previous operations, what was risk level?", "childSafeguardingRiskLevelLabel": "What was the risk level for Child Safeguarding Risk Analysis?", "currentNationalSocietyActionsHeading": "Current National Society Actions", - "nationalSocietyActionsHeading": "National Society anticipatory actions started", "drefFormNsResponseStarted": "Start date of National Society actions", "movementPartnersActionsHeading": "IFRC Network Actions Related To The Current Event", "secretariatLabel": "Secretariat", @@ -88,7 +89,8 @@ "hasChildProtectionPolicy": "Does your National Society have child protection/child safeguarding policy?", "hasWhistleblowerProtectionPolicy": "Does your National Society have whistleblower protection policy?", "hasAntiSexualHarassmentPolicy": "Does your National Society have anti-sexual harassment policy?", - "surgePersonnelDeployedHeading": "Will surge personnel be deployed? Please provide the role profile needed.", + "surgePersonnelDeployedHeading": "Will surge personnel be deployed? If yes, please provide the role profile needed.", + "humanitarianImpactsHeading": "Which of the expected severe humanitarian impacts of the hazard are your actions addressing? Why were these impacts chosen?", "logisticCapacityHeading": "If there is procurement, will it be done by National Society or IFRC?", "pmerHeading": "How will this operation be monitored?", "communicationHeading": "Please briefly explain the National Societies communication strategy for this operation", @@ -107,10 +109,29 @@ "drefApplicationExportMitigation": "Mitigation action", "drefApplicationSupportingDocumentation": "Supporting Documentation", "drefAssessmentReportLink": "Assessment Report", + "drefFormRiskPeopleLabel": "Population at risk (if available)", + "drefApplicationExportForecastedLabel": "Forecasted day of event(or peak)", "sourceInformationSectionHeading": "Source Information", "sourceInformationSourceNameTitle": "Source Name", "sourceInformationSourceLinkTitle": "Source Link", "crisisCategorySupportingDocumentLabel": "Crisis Category Supporting Document", - "targetingStrategySupportingDocument": "Targeting Strategy Supporting Document" + "targetingStrategySupportingDocument": "Targeting Strategy Supporting Document", + "contingencyPlanDocument": "National Society developed contingency plans, or recently carried out PER assessment Document", + "proposedActions": "Proposed Actions", + "proposedActionsActivities": "Activities", + "priorityActionsBudget": "Budget (CHF)", + "priorityActionsSubTotal": "Sub-total", + "priorityActionsSurgeDeployment": "Surge Deployment (if applicable)", + "priorityActionsIndirectCost": "Indirect Cost", + "priorityActionsTotal": "Total", + "scenarioAnalysis": "Scenario analysis", + "hazardDate": "When and where is the hazard expected to happen?", + "hazardRisk": "Explain the underlying vulnerabilities and risks the hazard poses for at-risk communities?", + "sourceInformationAttachments": "Include the link to the source of information or indicate if shared as attachments.", + "plan": "Plan", + "plannedInterventionAltText": "Planned Intervention Image", + "proposedActionsSector": "Sector", + "yes": "Yes", + "no": "No" } } diff --git a/app/src/views/DrefApplicationExport/index.tsx b/app/src/views/DrefApplicationExport/index.tsx index 6cd9ca1e39..9503c4e405 100644 --- a/app/src/views/DrefApplicationExport/index.tsx +++ b/app/src/views/DrefApplicationExport/index.tsx @@ -4,7 +4,10 @@ import { useState, } from 'react'; import { useParams } from 'react-router-dom'; -import { DateOutput } from '@ifrc-go/ui'; +import { + DateOutput, + NumberOutput, +} from '@ifrc-go/ui'; import { useTranslation } from '@ifrc-go/ui/hooks'; import { Container, @@ -12,19 +15,28 @@ import { Heading, Image, TextOutput, - type TextOutputProps, } from '@ifrc-go/ui/printable'; -import { DEFAULT_PRINT_DATE_FORMAT } from '@ifrc-go/ui/utils'; +import { + DEFAULT_PRINT_DATE_FORMAT, + sumSafe, +} from '@ifrc-go/ui/utils'; import { _cs, isDefined, isFalsyString, isNotDefined, isTruthyString, + listToGroupList, + mapToList, } from '@togglecorp/fujs'; +import earlyActionsIcon from '#assets/icons/early_actions.svg'; +import earlyResponseIcon from '#assets/icons/early_response.svg'; import ifrcLogo from '#assets/icons/ifrc-square.png'; import Link from '#components/printable/Link'; +import SelectOutput from '#components/SelectOutput'; +import usePrimarySector, { type PrimarySector } from '#hooks/domain/usePrimarySector'; +import useUrlSearchState from '#hooks/useUrlSearchState'; import { DISASTER_CATEGORY_ORANGE, DISASTER_CATEGORY_RED, @@ -32,6 +44,8 @@ import { type DisasterCategory, DREF_TYPE_ASSESSMENT, DREF_TYPE_IMMINENT, + DREF_TYPE_LOAN, + DREF_TYPE_RESPONSE, ONSET_SLOW, } from '#utils/constants'; import { @@ -40,36 +54,41 @@ import { plannedInterventionOrder, } from '#utils/domain/dref'; import { useRequest } from '#utils/restRequest'; +import { + calculateProposedActionsCost, + EARLY_ACTION, + EARLY_RESPONSE, + TYPE_IMMINENT, +} from '#views/DrefApplicationForm/common'; + +import PgaExport, { BlockTextOutput } from './PgaExport'; import i18n from './i18n.json'; import styles from './styles.module.css'; -function BlockTextOutput(props: TextOutputProps & { variant?: never, withoutLabelColon?: never }) { - return ( - - ); -} - const colorMap: Record = { [DISASTER_CATEGORY_YELLOW]: styles.yellow, [DISASTER_CATEGORY_ORANGE]: styles.orange, [DISASTER_CATEGORY_RED]: styles.red, }; +function primarySectoryLabelSelector(option: PrimarySector) { + return option.label; +} + +function primarySectoryKeySelector(option: PrimarySector) { + return option.key; +} + /** @knipignore */ // eslint-disable-next-line import/prefer-default-export export function Component() { const { drefId } = useParams<{ drefId: string }>(); const [previewReady, setPreviewReady] = useState(false); const strings = useTranslation(i18n); + const primarySectorOptions = usePrimarySector(); const { - // pending: fetchingDref, response: drefResponse, } = useRequest({ skip: isFalsyString(drefId), @@ -179,35 +198,109 @@ export function Component() { [drefResponse], ); - const eventDescriptionDefined = isTruthyString(drefResponse?.event_description?.trim()); - const eventScopeDefined = drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT + const groupedProposedActions = useMemo(() => { + if (isNotDefined(drefResponse) || isNotDefined(drefResponse.proposed_action)) { + return []; + } + + const typeGroupedActions = listToGroupList( + drefResponse.proposed_action.map((action) => { + const { + proposed_type, + activities, + ...other + } = action; + + if (isNotDefined(proposed_type) + || isNotDefined(activities) + || activities.length === 0 + ) { + return undefined; + } + + return { + ...other, + activities, + proposed_type, + }; + }).filter(isDefined), + ({ proposed_type }) => proposed_type, + ); + + const proposedActivityIconMap: Record = { + [EARLY_ACTION]: earlyActionsIcon, + [EARLY_RESPONSE]: earlyResponseIcon, + }; + + return mapToList( + typeGroupedActions, + (list, key) => { + const numActivities = sumSafe( + list.map(({ activities }) => activities.length), + ); + + return { + key, + title: list[0].proposed_type_display, + numActivities, + actions: list, + icon: proposedActivityIconMap[key], + }; + }, + ); + }, [drefResponse]); + + const eventDescriptionDefined = isDefined(drefResponse) + && drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT + && isTruthyString(drefResponse?.event_description?.trim()); + const eventScopeDefined = drefResponse?.type_of_dref === DREF_TYPE_RESPONSE && isTruthyString(drefResponse?.event_scope?.trim()); - const sourceInformationDefined = isDefined(drefResponse) + const isDefinedSourceInformation = isDefined(drefResponse) && isDefined(drefResponse.source_information) - && drefResponse.source_information.length > 0; + && drefResponse.source_information.length > 0; const imagesFileDefined = isDefined(drefResponse) + && drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT && isDefined(drefResponse.images_file) && drefResponse.images_file.length > 0; - const anticipatoryActionsDefined = drefResponse?.type_of_dref === DREF_TYPE_IMMINENT - && isTruthyString(drefResponse?.anticipatory_actions?.trim()); const eventDateDefined = drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT && isDefined(drefResponse?.event_date); - const eventTextDefined = drefResponse?.type_of_dref === DREF_TYPE_IMMINENT - && isTruthyString(drefResponse?.event_text?.trim()); + const isDefinedScenarioAnalysisSupportingDocument = ( + drefResponse?.type_of_dref === DREF_TYPE_IMMINENT + && isDefined(drefResponse.scenario_analysis_supporting_document) + ); const showEventDescriptionSection = eventDescriptionDefined || eventScopeDefined || imagesFileDefined - || anticipatoryActionsDefined - || eventTextDefined || eventDateDefined - || sourceInformationDefined + || isDefinedSourceInformation + || isDefinedScenarioAnalysisSupportingDocument || isDefined(drefResponse?.event_map_file?.file); + const isDefinedHazardDate = drefResponse?.type_of_dref === DREF_TYPE_IMMINENT + && isDefined(drefResponse?.hazard_date); + const isDefinedHazardRisk = drefResponse?.type_of_dref === DREF_TYPE_IMMINENT + && drefResponse.hazard_vulnerabilities_and_risks; + + const riskRegions = drefResponse?.district_details.map( + (district) => district.name, + ).filter(isDefined).join(', '); + + const drefAllocated = useMemo(() => { + if (isNotDefined(drefResponse)) { + return undefined; + } + return calculateProposedActionsCost(drefResponse); + }, [drefResponse]); + + const showScenarioAnalysis = isDefinedHazardDate + || isDefinedHazardRisk + || isDefinedSourceInformation; + const lessonsLearnedDefined = isTruthyString(drefResponse?.lessons_learned?.trim()); const childSafeguardingRiskLevelDefined = isTruthyString( drefResponse?.child_safeguarding_risk_level?.trim(), ); - const showPreviousOperations = drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + const showPreviousOperations = drefResponse?.type_of_dref === DREF_TYPE_RESPONSE && ( isDefined(drefResponse?.did_it_affect_same_area) || isDefined(drefResponse?.did_it_affect_same_population) || isDefined(drefResponse?.did_ns_respond) @@ -219,14 +312,21 @@ export function Component() { const ifrcActionsDefined = isTruthyString(drefResponse?.ifrc?.trim()); const partnerNsActionsDefined = isTruthyString(drefResponse?.partner_national_society?.trim()); - const showMovementPartnersActionsSection = ifrcActionsDefined || partnerNsActionsDefined; + const showMovementPartnersActionsSection = isDefined(drefResponse) + && drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT + && (ifrcActionsDefined || partnerNsActionsDefined); + + const showProposedActions = groupedProposedActions.length > 0; const showNsAction = isDefined(drefResponse) + && drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT && isDefined(drefResponse.national_society_actions) && drefResponse.national_society_actions.length > 0 && isDefined(nsActions); - const icrcActionsDefined = isTruthyString(drefResponse?.icrc?.trim()); + const icrcActionsDefined = isDefined(drefResponse) + && drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT + && isTruthyString(drefResponse?.icrc?.trim()); const governmentRequestedAssistanceDefined = isDefined( drefResponse?.government_requested_assistance, @@ -236,10 +336,14 @@ export function Component() { const majorCoordinationMechanismDefined = isDefined( drefResponse?.major_coordination_mechanism?.trim(), ); - const showOtherActorsActionsSection = governmentRequestedAssistanceDefined - || nationalAuthoritiesDefined - || unOrOtherActorDefined - || majorCoordinationMechanismDefined; + const showOtherActorsActionsSection = (governmentRequestedAssistanceDefined + && isDefined(drefResponse) + && drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT) + && ( + nationalAuthoritiesDefined + || unOrOtherActorDefined + || majorCoordinationMechanismDefined + ); const identifiedGapsDefined = drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT && isTruthyString(drefResponse?.identified_gaps?.trim()); @@ -254,25 +358,36 @@ export function Component() { const showNeedsIdentifiedSection = isDefined(drefResponse) && drefResponse.type_of_dref !== DREF_TYPE_ASSESSMENT + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT && (identifiedGapsDefined || needsIdentifiedDefined || assessmentReportDefined); const operationObjectiveDefined = isTruthyString(drefResponse?.operation_objective?.trim()); const responseStrategyDefined = isTruthyString(drefResponse?.response_strategy?.trim()); - const showOperationStrategySection = operationObjectiveDefined || responseStrategyDefined; + const showOperationStrategySection = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && (operationObjectiveDefined || responseStrategyDefined); const peopleAssistedDefined = isTruthyString(drefResponse?.people_assisted?.trim()); const selectionCriteriaDefined = isTruthyString(drefResponse?.selection_criteria?.trim()); const targetingStrategySupportingDocumentDefined = isDefined( drefResponse?.targeting_strategy_support_file_details, ); - const showTargetingStrategySection = peopleAssistedDefined + const showTargetingStrategySection = (isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && drefResponse.type_of_dref !== DREF_TYPE_LOAN + ) && ( + peopleAssistedDefined || selectionCriteriaDefined - || targetingStrategySupportingDocumentDefined; + || targetingStrategySupportingDocumentDefined + ); const riskSecurityDefined = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT && isDefined(drefResponse.risk_security) && drefResponse.risk_security.length > 0; - const riskSecurityConcernDefined = isTruthyString(drefResponse?.risk_security_concern?.trim()); + const riskSecurityConcernDefined = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && isTruthyString(drefResponse?.risk_security_concern?.trim()); const hasAntiFraudPolicy = isDefined(drefResponse?.has_anti_fraud_corruption_policy); const hasSexualAbusePolicy = isDefined(drefResponse?.has_sexual_abuse_policy); const hasChildProtectionPolicy = isDefined(drefResponse?.has_child_protection_policy); @@ -282,9 +397,13 @@ export function Component() { const hasAntiSexualHarassmentPolicy = isDefined( drefResponse?.has_anti_sexual_harassment_policy, ); - const hasChildrenSafeguardingDefined = isDefined( - drefResponse?.has_child_safeguarding_risk_analysis_assessment, - ); + + const hasChildrenSafeguardingDefined = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && isDefined( + drefResponse?.has_child_safeguarding_risk_analysis_assessment, + ); + const hasRiskAndSecurityPoliciesDefined = hasAntiFraudPolicy || hasSexualAbusePolicy || hasChildProtectionPolicy @@ -293,33 +412,60 @@ export function Component() { const showRiskAndSecuritySection = riskSecurityDefined || riskSecurityConcernDefined + || hasAntiFraudPolicy + || hasSexualAbusePolicy + || hasChildProtectionPolicy + || hasWhistleblowerProtectionPolicy || hasRiskAndSecurityPoliciesDefined - || hasChildrenSafeguardingDefined; + || hasAntiSexualHarassmentPolicy; const plannedInterventionDefined = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT && isDefined(drefResponse.planned_interventions) && drefResponse.planned_interventions.length > 0 && isDefined(plannedInterventions); - const humanResourceDefined = isTruthyString(drefResponse?.human_resource?.trim()); + const humanResourceDefined = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && isTruthyString(drefResponse?.human_resource?.trim()); const isVolunteerTeamDiverseDefined = isTruthyString( drefResponse?.is_volunteer_team_diverse?.trim(), ); const surgePersonnelDeployedDefined = isTruthyString( drefResponse?.surge_personnel_deployed?.trim(), ); - const logisticCapacityOfNsDefined = isTruthyString( - drefResponse?.logistic_capacity_of_ns?.trim(), - ); - const pmerDefined = isTruthyString(drefResponse?.pmer?.trim()); - const communicationDefined = isTruthyString(drefResponse?.communication?.trim()); + const humanitarianImpactsDefined = isDefined(drefResponse) + && drefResponse.type_of_dref === DREF_TYPE_IMMINENT + && isTruthyString(drefResponse?.addressed_humanitarian_impacts?.trim()); + const logisticCapacityOfNsDefined = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && isTruthyString( + drefResponse?.logistic_capacity_of_ns?.trim(), + ); + const pmerDefined = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && isTruthyString(drefResponse?.pmer?.trim()); + const communicationDefined = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && isTruthyString(drefResponse?.communication?.trim()); + const contingencyPlanDocument = isDefined(drefResponse) + && drefResponse.type_of_dref === DREF_TYPE_IMMINENT + && isTruthyString( + drefResponse?.contingency_plans_supporting_document_details?.file, + ); + const showAboutSupportServicesSection = humanResourceDefined || surgePersonnelDeployedDefined || logisticCapacityOfNsDefined || pmerDefined - || communicationDefined; + || communicationDefined + || humanitarianImpactsDefined + || showProposedActions + || contingencyPlanDocument; - const showBudgetOverview = isTruthyString(drefResponse?.budget_file_details?.file); + const showBudgetOverview = isDefined(drefResponse) + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + && isTruthyString(drefResponse?.budget_file_details?.file); const nsContactText = [ drefResponse?.national_society_contact_name, @@ -376,6 +522,18 @@ export function Component() { || mediaContactDefined || nationalSocietyIntegrityContactDefined || nationalSocietyHotlineDefined; + + const [pgaExport] = useUrlSearchState( + 'is_pga', + (value) => { + if (value === 'true') { + return true; + } + return undefined; + }, + (is_pga) => is_pga, + ); + return (
@@ -385,9 +543,15 @@ export function Component() { alt={strings.imageLogoIFRCAlt} />
- - {strings.exportTitle} - + {drefResponse?.type_of_dref === DREF_TYPE_IMMINENT ? ( + + {strings.exportDrefImminentTitle} + + ) : ( + + {strings.exportTitle} + + )}
{drefResponse?.title}
@@ -399,8 +563,9 @@ export function Component() { && ( {drefResponse.title )} @@ -411,100 +576,173 @@ export function Component() { value={drefResponse?.appeal_code} strongValue /> - + {drefResponse?.type_of_dref === TYPE_IMMINENT && ( + + )} + {drefResponse?.type_of_dref !== TYPE_IMMINENT && ( + + )} - - + {drefResponse?.type_of_dref === TYPE_IMMINENT && ( + <> + + + + +
+ + )} + {drefResponse?.type_of_dref !== TYPE_IMMINENT && ( + <> + + + + )} - - - - - - - - + {drefResponse?.type_of_dref === DREF_TYPE_IMMINENT && ( + <> + + +
+ + )} + {drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT && ( + + )} + {drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT && ( + <> + + + + + + + + + )} - {showEventDescriptionSection && ( +
+ {showScenarioAnalysis && ( <> -
- {strings.eventDescriptionSectionHeading} + {drefResponse.type_of_dref !== DREF_TYPE_IMMINENT + ? strings.eventDescriptionSectionHeading + : strings.scenarioAnalysis} - {drefResponse?.disaster_category_analysis_details?.file && ( - - - {strings.crisisCategorySupportingDocumentLabel} - + {isDefinedHazardDate && ( + + + {drefResponse?.hazard_date} + + + {riskRegions} + )} - {eventTextDefined && ( - + {isDefinedHazardRisk && ( + - {drefResponse?.event_text} + {drefResponse?.hazard_vulnerabilities_and_risks} )} + + )} + {showEventDescriptionSection && ( + <> + {drefResponse?.disaster_category_analysis_details?.file + && drefResponse.type_of_dref !== DREF_TYPE_IMMINENT && ( + + + {strings.crisisCategorySupportingDocumentLabel} + + + )} {eventDateDefined && ( )} - {isTruthyString(drefResponse?.event_map_file?.file) && ( + {isTruthyString(drefResponse?.event_map_file?.file) + && drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT && ( {drefResponse?.event_description} @@ -578,15 +836,6 @@ export function Component() { )} )} - {anticipatoryActionsDefined && ( - - - {drefResponse?.anticipatory_actions} - - - )} {eventScopeDefined && ( )} - {sourceInformationDefined && ( + {isDefinedSourceInformation && (
{strings.sourceInformationSourceNameTitle} @@ -631,7 +878,101 @@ export function Component() { ), )} - + + )} + {isDefinedScenarioAnalysisSupportingDocument && ( + + + {strings.drefApplicationSupportingDocumentation} + + + )} + + )} + {showAboutSupportServicesSection && ( + <> + + {drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT + ? strings.aboutSupportServicesSectionHeading + : strings.plan} + + {humanResourceDefined && ( + + + {drefResponse?.human_resource} + + + )} + {isVolunteerTeamDiverseDefined && ( + + + {drefResponse?.is_volunteer_team_diverse} + + + )} + {surgePersonnelDeployedDefined && ( + + + {drefResponse?.is_surge_personnel_deployed + ? strings.yes : strings.no} + + + {drefResponse?.surge_personnel_deployed} + + + )} + {humanitarianImpactsDefined && ( + + + {drefResponse?.addressed_humanitarian_impacts} + + + )} + {contingencyPlanDocument && ( + + + {strings.contingencyPlanDocument} + + + )} + {logisticCapacityOfNsDefined && ( + + + {drefResponse?.logistic_capacity_of_ns} + + + )} + {pmerDefined && ( + + + {drefResponse?.pmer} + + + )} + {communicationDefined && ( + + + {drefResponse?.communication} + )} @@ -712,11 +1053,7 @@ export function Component() { {drefResponse?.ns_respond_date && ( )} - - {drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + {showProposedActions && ( + +
+
+ {strings.proposedActionsSector} +
+
+ {strings.proposedActionsActivities} +
+
+ {strings.priorityActionsBudget} +
+ {groupedProposedActions.map((proposedAction) => ( + +
+ +
+ {proposedAction.title} +
+
+ {proposedAction.actions.map((action) => ( + + {action.activities.map((activity, i) => ( + + +
+ {activity.activity} +
+ {i === 0 && ( +
+ +
+ )} +
+ ))} +
+ ))} +
+ ))} +
+ {strings.priorityActionsSubTotal} +
+ + {isDefined(drefResponse?.surge_deployment_cost) && ( + <> +
+ {strings.priorityActionsSurgeDeployment} +
+ + + )} +
+ {strings.priorityActionsIndirectCost} +
+ +
+ {strings.priorityActionsTotal} +
+ + + )} + {drefResponse?.type_of_dref !== DREF_TYPE_IMMINENT && ( + + {drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + + )} - )} - - {drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + {drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + + )} - )} - - {drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + {drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + + )} - )} - - {drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + {drefResponse?.type_of_dref !== DREF_TYPE_ASSESSMENT && ( + + )} +
- )} -
- - + + )} {showRiskAndSecuritySection && ( {plannedIntervention.title_display} @@ -1139,67 +1581,6 @@ export function Component() { ))} )} - {showAboutSupportServicesSection && ( - <> - - {strings.aboutSupportServicesSectionHeading} - - {humanResourceDefined && ( - - - {drefResponse?.human_resource} - - - )} - {isVolunteerTeamDiverseDefined && ( - - - {drefResponse?.is_volunteer_team_diverse} - - - )} - {surgePersonnelDeployedDefined && ( - - - {drefResponse?.surge_personnel_deployed} - - - )} - {logisticCapacityOfNsDefined && ( - - - {drefResponse?.logistic_capacity_of_ns} - - - )} - {pmerDefined && ( - - - {drefResponse?.pmer} - - - )} - {communicationDefined && ( - - - {drefResponse?.communication} - - - )} - - )} {showBudgetOverview && ( <>
@@ -1291,6 +1672,12 @@ export function Component() { )} + {pgaExport && ( + <> +
+ + + )} {previewReady &&
}
); diff --git a/app/src/views/DrefApplicationExport/styles.module.css b/app/src/views/DrefApplicationExport/styles.module.css index 45f3576203..181c7e93fa 100644 --- a/app/src/views/DrefApplicationExport/styles.module.css +++ b/app/src/views/DrefApplicationExport/styles.module.css @@ -57,6 +57,13 @@ color: var(--go-ui-color-orange); } } + .meta-actions-item { + display: flex; + flex-direction: column; + background-color: var(--pdf-element-bg); + padding: var(--go-ui-spacing-xs); + break-inside: avoid; + } .budget { display: flex; @@ -244,4 +251,73 @@ color: var(--go-ui-color-primary-red); } } + + .bank-details { + display: grid; + grid-gap: var(--go-ui-width-separator-md); + grid-template-columns: 1fr 1fr; + page-break-inside: avoid; + } + + .proposed-actions { + display: grid; + grid-gap: var(--go-ui-width-separator-md); + grid-template-columns: 4fr 5fr 5fr 4fr; + page-break-inside: avoid; + + .action-title-label { + background-color: var(--go-ui-color-element-background); + padding: var(--go-ui-spacing-sm); + font-weight: var(--go-ui-font-weight-semibold); + } + + .proposed-action { + display: flex; + align-items: center; + flex-direction: column; + background-color: var(--pdf-element-bg); + padding: var(--go-ui-spacing-sm); + + .icon { + height: 3rem; + } + + .title { + font-size: var(--go-ui-font-size-md); + font-weight: var(--go-ui-font-weight-semibold); + } + } + + .sector, + .activity, + .budget { + background-color: var(--pdf-element-bg); + padding: var(--go-ui-spacing-sm); + break-inside: avoid; + } + + .activity { + white-space: pre-wrap; + } + + .budget { + display: flex; + align-items: center; + } + + .cost-label { + grid-column: span 3; + background-color: var(--pdf-element-bg); + padding: var(--go-ui-spacing-sm); + break-inside: avoid; + text-align: right; + font-weight: var(--go-ui-font-weight-semibold); + } + + .cost-value { + background-color: var(--pdf-element-bg); + padding: var(--go-ui-spacing-sm); + break-inside: avoid; + } + } } diff --git a/app/src/views/DrefApplicationForm/Actions/i18n.json b/app/src/views/DrefApplicationForm/Actions/i18n.json index 6804883a35..464b179780 100644 --- a/app/src/views/DrefApplicationForm/Actions/i18n.json +++ b/app/src/views/DrefApplicationForm/Actions/i18n.json @@ -7,7 +7,6 @@ "drefFormCoordinationMechanism": "Are there major coordination mechanisms in place?", "drefFormCoordinationMechanismDescription": "List coordination mechanisms/platform in place at local/district and national level. Indicate the lead authorities/agencies. How the National Society is involved/positioned in this coordination. Does the NS in any lead/co-lead role? Any identified gap/overlap in the coordination (e.g., sector missing…)?", "drefFormActionDescription": "Description", - "drefFormDidNationalSocietyStartedImminent": "Did the National Society started any anticipatory actions?", "drefFormDidNationalSocietyStartedSlow": "Has the National Society started any actions?", "drefFormGapsInAssessment": "Any identified gaps/limitations in the assessment", "drefFormGapsInAssessmentDescriptionHeading": "Consider the following:", @@ -16,11 +15,11 @@ "drefFormGapsInAssessmentDescriptionPoint3": "Operational challenges: mention any operational constraints that are preventing a full response to the needs (e.g., logistical issues, insufficient capacity).", "drefFormGapsInAssessmentDescriptionPoint4": "Coordination issues: note any challenges in coordinating with other actors or agencies that have resulted in gaps in service delivery or response coverage.", "drefFormGapsInAssessmentDescriptionPoint5": "Vulnerable groups: identify any specific vulnerable groups whose needs may not have been fully captured or addressed during the assessment (e.g., displaced persons, elderly, people with disabilities).", + "drefFormUploadTargetingSupportingDescription": "Assessment report file type: pdf, docx, pptx, xlsx.", "drefFormIcrc": "ICRC", "drefFormIcrcDescription": "Presence or not of ICRC in country, and support directly provided for this emergency response. Other programs and support provided outside of the scope of this emergency should not be indicated here.", "drefFormIfrc": "IFRC", "drefFormIfrcDescription": "Presence or not of IFRC in country (if not, indicate the cluster covering), support provided for this response, domestic coordination, technical, strategic, surge. Explain what support provided in terms of Secretariat services: PMER, Finance, Admin, HR, Security, logistics, NSD.", - "drefFormImminentNeedsIdentified": "Anticipated Needs", "drefFormInternationalAssistance": "Government has requested international assistance", "ifrcNetworkActionsHeading": "IFRC Network Actions Related To The Current Event", "icrcActionsHeading": "ICRC Actions Related To The Current Event", @@ -31,8 +30,6 @@ "drefFormNationalSocietiesActionsDescription": "Please indicate a description of the ongoing response with if possible: Branches involved, number of volunteers/staff involved in actions, assets deployed/distributed, number of people reach. Impact/added value of the NS in the response already ongoing.", "drefFormNationalSocietiesActionsLabel": "Select the actions that apply.", "drefFormNeedsIdentified": "Needs (Gaps) Identified", - "drefFormUploadTargetingSupportingDescription": "Assessment report file types: pdf, docx, pptx, xlsx", - "drefFormNSAnticipatoryAction": "National Society anticipatory actions started", "drefFormNsResponseStarted": "Start date of National Society actions", "drefFormPartnerNationalSociety": "Participating National Societies", "drefFormPartnerNationalSocietyDescription": "Briefly set out which PNS are present and give details of PNS contributions/roles on the ground and remotely for this specific operation", diff --git a/app/src/views/DrefApplicationForm/Actions/index.tsx b/app/src/views/DrefApplicationForm/Actions/index.tsx index 06c9aedb13..d70672049a 100644 --- a/app/src/views/DrefApplicationForm/Actions/index.tsx +++ b/app/src/views/DrefApplicationForm/Actions/index.tsx @@ -198,11 +198,7 @@ function Actions(props: Props) { heading={strings.drefFormNationalSocietiesActions} > {value.did_national_society && ( {/* NOTE: Only when RESPONSE */} {value?.type_of_dref !== TYPE_IMMINENT && ( - + - {value.type_of_dref !== TYPE_ASSESSMENT && value.type_of_dref !== TYPE_LOAN && ( + {value.type_of_dref === TYPE_RESPONSE && ( - {value.type_of_dref === TYPE_IMMINENT ? ( - -