diff --git a/.changeset/modern-hats-fry.md b/.changeset/modern-hats-fry.md
new file mode 100644
index 0000000000..2c6304215e
--- /dev/null
+++ b/.changeset/modern-hats-fry.md
@@ -0,0 +1,5 @@
+---
+"go-web-app": minor
+---
+
+Imminent DREF Application Form Updates in [#1455](https://github.com/IFRCGo/go-web-app/issues/1455)
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 bb24ac64c2..4a1eb60237 100644
--- a/app/src/components/Navbar/i18n.json
+++ b/app/src/components/Navbar/i18n.json
@@ -54,7 +54,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",
@@ -63,8 +63,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/PerExportModal/index.tsx b/app/src/components/PerExportModal/index.tsx
index df400901c1..a24efd574e 100644
--- a/app/src/components/PerExportModal/index.tsx
+++ b/app/src/components/PerExportModal/index.tsx
@@ -45,6 +45,7 @@ function PerExportModal(props: Props) {
export_id: Number(perId),
export_type: 'per' as const,
per_country: Number(countryId),
+ is_pga: false,
}),
[perId, countryId],
);
diff --git a/app/src/components/SelectOutput/index.tsx b/app/src/components/SelectOutput/index.tsx
index 9f16ba9248..8df20fde36 100644
--- a/app/src/components/SelectOutput/index.tsx
+++ b/app/src/components/SelectOutput/index.tsx
@@ -3,7 +3,7 @@ import { TextOutput } from '@ifrc-go/ui';
import { isDefined } from '@togglecorp/fujs';
interface Props {
- className: string;
+ className?: string;
value: VALUE | undefined;
options: OPTION[] | undefined;
keySelector: (datum: OPTION) => VALUE;
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 31b0b561a6..96564a94c9 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,18 +98,45 @@ function DrefExportModal(props: Props) {
return {
export_id: id,
export_type: type,
+ is_pga: isPga,
selector: '#pdf-preview-ready',
per_country: undefined, // FIXME: typing is altered by the useRequest function
};
},
- [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/',
@@ -77,6 +146,12 @@ function DrefExportModal(props: Props) {
setExportId(response.id);
}
},
+ onFailure: () => {
+ alert.show(
+ strings.drefFailureToExportMessage,
+ { variant: 'danger' },
+ );
+ },
});
const {
@@ -97,18 +172,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 c637eb963e..909afa6ff9 100644
--- a/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx
+++ b/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx
@@ -223,7 +223,6 @@ function ActiveDrefTable(props: Props) {
}
const {
- // unpublished_final_report_count,
unpublished_op_update_count,
is_published,
has_ops_update,
@@ -259,6 +258,7 @@ function ActiveDrefTable(props: Props) {
canCreateFinalReport,
hasPermissionToApprove: isRegionCoordinator || userMe?.is_superuser,
onPublishSuccess: refetchActiveDref,
+ drefType: item.type_of_dref,
};
},
{ columnClassName: styles.actions },
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 3f8119049d..eb5818227e 100644
--- a/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx
+++ b/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx
@@ -31,6 +31,7 @@ import {
DREF_TYPE_IMMINENT,
DREF_TYPE_LOAN,
type DrefStatus,
+ type TypeOfDrefEnum,
} from '#utils/constants';
import {
type GoApiBody,
@@ -53,6 +54,7 @@ export interface Props {
hasPermissionToApprove?: boolean;
onPublishSuccess?: () => void;
+ drefType?: TypeOfDrefEnum | null | undefined;
}
function DrefTableActions(props: Props) {
@@ -65,6 +67,7 @@ function DrefTableActions(props: Props) {
canCreateFinalReport,
hasPermissionToApprove,
onPublishSuccess,
+ drefType,
} = props;
const { navigate } = useRouting();
@@ -125,15 +128,15 @@ 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,
@@ -142,9 +145,10 @@ function DrefTableActions(props: Props) {
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,
@@ -376,6 +380,8 @@ function DrefTableActions(props: Props) {
const canApprove = status === DREF_STATUS_IN_PROGRESS && hasPermissionToApprove;
+ const shouldConfirmImminentAddOpsUpdate = drefType === DREF_TYPE_IMMINENT;
+
const disabled = fetchingDref
|| fetchingOpsUpdate
|| publishDrefPending
@@ -414,7 +420,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 (
+
);
diff --git a/app/src/views/DrefApplicationExport/styles.module.css b/app/src/views/DrefApplicationExport/styles.module.css
index 7390b4facd..c9ea12304f 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;
@@ -229,4 +236,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 c86ead0232..8c8014100f 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:",
@@ -20,7 +19,6 @@
"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,7 +29,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",
- "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 83587f6161..f267cfa897 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 && (
diff --git a/app/src/views/DrefApplicationForm/EventDetail/i18n.json b/app/src/views/DrefApplicationForm/EventDetail/i18n.json
index bd3a5ae3db..a50fa4ed78 100644
--- a/app/src/views/DrefApplicationForm/EventDetail/i18n.json
+++ b/app/src/views/DrefApplicationForm/EventDetail/i18n.json
@@ -3,16 +3,14 @@
"strings": {
"drefFormAffectedThePopulationTitle": "Did it affect the same population groups?",
"drefFormAffectSameArea": "Has a similar event affected the same area(s) in the last 3 years?",
- "drefFormApproximateDateOfImpact": "Approximate date of impact",
"drefFormDescriptionEvent": "Description of the Event",
"drefFormEventDate": "Date of the Event",
"numericDetailsSectionTitle": "Numeric Details",
"drefFormPeopleAffectedDescriptionSlowSudden": "People Affected include all those whose lives and livelihoods have been impacted as a direct result of the shock or stress.",
"drefFormClickEmergencyResponseFramework": "Click to view Emergency Response Framework",
- "drefFormEstimatedPeopleInNeed": "Estimated people in need (Optional)",
"drefFormPeopleInNeed": "People in need (Optional)",
"drefFormPeopleAffected": "Total affected population",
- "drefFormRiskPeopleLabel": "Total population at risk",
+ "drefFormRiskPeopleLabel": "Total population at risk (if available)",
"drefFormAffectedMaleLabel": "Estimated male affected",
"drefFormAffectedFemaleLabel": "Estimated female affected",
"drefFormAffectedMinorGirlsLabel": "Estimated Girls (under 18)",
@@ -22,7 +20,6 @@
"drefFormPeopleInNeedDescriptionImminent": "Include all those whose physical security, basic rights, dignity, living conditions or livelihoods are threatened or have been disrupted, and whose current level of access to basic services, goods and social protection will be inadequate to re-establish normal living conditions without additional assistance",
"drefFormPeopleInNeedDescriptionSlowSudden": "People in Need (PIN) are those members whose physical security, basic rights, dignity, living conditions or livelihoods are threatened or have been disrupted, and whose current level of access to basic services, goods and social protection is inadequate to re-establish normal living conditions without additional assistance.",
"drefFormPeopleAffectedDescriptionImminent": "Includes all those whose lives and livelihoods are at risk as a direct result of the shock or stress.",
- "drefFormImminentDisaster": "Provide any updates in the situation since the field report and explain what is expected to happen.",
"drefFormLessonsLearnedDescription": "Specify how the lessons learnt from these previous operations are being used to mitigate similar challenges in the current operation",
"drefFormLessonsLearnedTitle": "Lessons learned",
"drefFormNsFundingDetail": "If yes, please specify which operations",
@@ -34,13 +31,10 @@
"drefFormScopeAndScaleDescription": "Describe the extent this hazard will produce negative impacts on lives, livelihoods, well-being and infrastructure. Explain which people are most likely to experience the impacts of this hazard? Where do they live, and why are they vulnerable? Please explain which groups (e.g elderly, children, people with disabilities, IDPs, Refugees, etc.) are most likely to be affected? Provide historic information on how communities have been affected by the magnitude of this hazard in the past?",
"drefFormScopeAndScaleEvent": "Scope and scale of the event",
"drefFormSlowEventDate": "Date when the trigger was met",
- "drefFormTargetCommunities": "Explain why your National Society is acting now and what criteria is used to launch this operation.",
- "drefFormTargetCommunitiesDescription": "If the operation uses triggers related to different phases (such as readiness activities and early actions) add your triggers and data sources",
"drefFormUploadPhotos": "Upload photos (e.g. impact of the events, NS in the field, assessments)",
"drefFormUploadPhotosLimitation": "Add maximum 2 photos",
- "drefFormUploadSupportingDocument": "Upload any supporting documentation if relevant (Optional)",
- "drefFormUploadSupportingDocumentButtonLabel": "Upload document",
- "drefFormUploadSupportingDocumentDescription": "Here the National Society can upload documentation that substantiates their decision to launch this operation now. For example: seasonal outlooks, forecast information, reports from credible sources...etc.",
+ "drefFormUploadSupportingDocument": "Upload any supporting document (Optional)",
+ "drefFormUploadSupportingDocumentButton": "Upload document",
"drefFormWhatWhereWhen": "What happened, where and when?",
"drefFormWhatWhereWhenDescriptionHeading": "Clearly Describe:",
"drefFormWhatWhereWhenDescriptionPoint1": "What happened: Briefly explain the nature of the emergency (e.g., flood, earthquake, epidemic). Include key details such as the intensity, and any unique aspects of the event.",
@@ -51,6 +45,9 @@
"drefFormSelectImages": "Select images",
"drefFormSourceInformationAddButton": "Add New Source Information",
"drefFormSourceInformationTitle": "Source Information",
- "drefFormSourceInformationDescription": "Add the links and the name of the sources, the name will be shown in the export, as an hyperlink."
+ "drefFormSourceInformationDescription": "Add the links and the name of the sources, the name will be shown in the export, as an hyperlink.",
+ "drefHazardExpectedTitle": "When is the hazard expected to happen?",
+ "drefHazardTitle": "Explain the underlying vulnerabilities and risks the hazard poses for at-risk communities?",
+ "drefHazardDescription": "Based on comparison of past events, what humanitarian impacts do you expect? Of what magnitude? (e.g. last time there was a cyclone making landfall with this windspeed, XX houses were destroyed, and we had to launch a DREF/emergency appeal for CHF xxx)."
}
}
diff --git a/app/src/views/DrefApplicationForm/EventDetail/index.tsx b/app/src/views/DrefApplicationForm/EventDetail/index.tsx
index dc2d87a5ac..ce9092556a 100644
--- a/app/src/views/DrefApplicationForm/EventDetail/index.tsx
+++ b/app/src/views/DrefApplicationForm/EventDetail/index.tsx
@@ -28,9 +28,9 @@ import NonFieldError from '#components/NonFieldError';
import {
ONSET_SUDDEN,
- TYPE_ASSESSMENT,
TYPE_IMMINENT,
TYPE_LOAN,
+ TYPE_RESPONSE,
} from '../common';
import { type PartialDref } from '../schema';
@@ -133,7 +133,7 @@ function EventDetail(props: Props) {
return (
+ )
+ }
+ numPreferredColumns={2}
+ >
+
+
+
+
+
+
+ {value.activities?.map((activity, i) => (
+
+ ))}
+
+
+
+
+ );
+}
+
+export default ProposedActionsInput;
diff --git a/app/src/views/DrefApplicationForm/Operation/ProposedActionsInput/styles.module.css b/app/src/views/DrefApplicationForm/Operation/ProposedActionsInput/styles.module.css
new file mode 100644
index 0000000000..df90150d9b
--- /dev/null
+++ b/app/src/views/DrefApplicationForm/Operation/ProposedActionsInput/styles.module.css
@@ -0,0 +1,9 @@
+.proposed-action {
+ display: flex;
+ gap: var(--go-ui-spacing-md);
+
+ .logo {
+ margin-top: -1rem;
+ height: 8rem;
+ }
+}
diff --git a/app/src/views/DrefApplicationForm/Operation/i18n.json b/app/src/views/DrefApplicationForm/Operation/i18n.json
index af068b61b0..fc8f95d92c 100644
--- a/app/src/views/DrefApplicationForm/Operation/i18n.json
+++ b/app/src/views/DrefApplicationForm/Operation/i18n.json
@@ -9,6 +9,7 @@
"drefFormPeopleAssistedThroughOperationDescription": "Explain the logic behind our targets. Which groups are we targeting and why are we targeting these particular groups? Explain how you will target vulnerable groups (e.g., Migrants, refugees, etc.)",
"drefFormPeopleTargetedWithEarlyActions": "Numbers of persons targeted with early actions (if any)",
"drefFormPlannedIntervention": "Planned Intervention",
+ "drefFormProposedActions": "Proposed Actions",
"drefFormRequestAmount": "Requested Amount in CHF",
"drefFormRequestAmountDescription": "General funding requested to fund the interventions. ",
"drefFormRequestAmountDescriptionWithIntervention": "List specific activities that will be carried out as part of the intervention in each sector. The activities should directly address the identified needs and align with the operation’s strategic objectives.",
@@ -55,9 +56,9 @@
"drefFormRiskSecuritySafetyConcernDescriptionPoint2": "What safety risks could impact the well-being of staff, volunteers, or beneficiaries (e.g., dangerous terrain, health risks)?",
"drefFormRiskSecuritySafetyConcernDescriptionPoint3": "Are there any specific security protocols or measures that need to be established or followed during the operation?",
"drefFormRiskSecurityHasChildRiskCompleted": "Has the child safeguarding risk analysis assessment been completed?",
- "drefFormRiskSecurityHasChildRiskCompletedDescription":"The IFRC Child Safeguarding Risk Analysis helps Operations quickly identify and rate their key child safeguarding risks in order to reduce the risk of harm against children, as outlined as a requirement in the IFRC Child Safeguarding Policy. Here are the link to the tool and Q&A",
- "drefChildSafeguardingPolicyDescription":"Child Safeguarding Policy",
- "drefChildSafeguardingRiskAnalysisDescription":"Q&A Child Safeguarding Risk Analysis",
+ "drefFormRiskSecurityHasChildRiskCompletedDescription": "The IFRC Child Safeguarding Risk Analysis helps Operations quickly identify and rate their key child safeguarding risks in order to reduce the risk of harm against children, as outlined as a requirement in the IFRC Child Safeguarding Policy. Here are the link to the tool and Q&A",
+ "drefChildSafeguardingPolicyDescription": "Child Safeguarding Policy",
+ "drefChildSafeguardingRiskAnalysisDescription": "Q&A Child Safeguarding Risk Analysis",
"drefFormSelectionCriteria": "Explain the selection criteria for the targeted population",
"drefFormSelectionCriteriaDescription": "Explain the rational and logic behind which groups are being targeted and why and address vulnerable groups",
"drefFormSupportServices": "About Support Services",
@@ -111,7 +112,21 @@
"drefFormMen": "Men",
"drefFormTotalTargeted": "Total targeted population is different from that in Operation Overview",
"drefFormTotalTargetedPopulation": "Total targeted population is not equal to sum of other population fields",
+ "drefFormUploadDocumentButtonLabel": "Upload any additional support document (Optional)",
+ "drefFormUploadTargetingDocumentButtonLabel": "Upload document",
+ "drefFormProposedActionSelectBudgetNote": "Please note that if Surge Personnel are deployed, the Surge Deployment cost will be CHF 10,000, and the Indirect Costs will be CHF 5,800. Conversely, if Surge Personnel are not deployed, the Surge Deployment cost will not be applicable, and the Indirect Costs will be CHF 5,000.",
+ "drefFormProposedActionSelectActivitiesLabel": "Select the activity",
+ "drefFormProposedActionEarlyActionsLabel": "Early Actions",
+ "drefFormProposedActionEarlyResponseLabel": "Early Response",
+ "drefFormProposedActionAddActivityLabel": "Add Activity",
+ "drefFormProposedActionSubTotal": "Sub-total",
+ "drefFormProposedActionSurgeDeployment": "Surge Deployment",
+ "drefFormProposedActionIndirectCost": "Indirect cost",
+ "drefFormProposedActionTotal": "Total",
"drefFormUploadTargetingSupportingDocument": "Upload any additional support document (Optional)",
- "drefFormUploadTargetingDocumentButtonLabel": "Upload document"
+ "drefHumanitarianTitle": "Which of the expected severe humanitarian impacts of the hazard are your actions addressing? Why were these impacts chosen?",
+ "drefHumanitarianDescription": "Prioritized impact of the foreseen disaster.",
+ "drefFormUploadSupportingDocumentButtonLabel": "Upload document",
+ "drefUploadSupportingDocumentTitle": "If the National Society has developed contingency plans, or recently carried out PER assessment or readiness check for instance, kindly attach those documents here."
}
}
diff --git a/app/src/views/DrefApplicationForm/Operation/index.tsx b/app/src/views/DrefApplicationForm/Operation/index.tsx
index e07b7dfba3..51046da59c 100644
--- a/app/src/views/DrefApplicationForm/Operation/index.tsx
+++ b/app/src/views/DrefApplicationForm/Operation/index.tsx
@@ -29,6 +29,9 @@ import {
type EntriesAsList,
type Error,
getErrorObject,
+ isCallable,
+ type SetBaseValueArg,
+ type SetValueArg,
useFormArray,
} from '@togglecorp/toggle-form';
@@ -40,11 +43,13 @@ import { type GoApiResponse } from '#utils/restRequest';
import { useRequest } from '#utils/restRequest';
import {
+ calculateProposedActionsCost,
TYPE_ASSESSMENT,
TYPE_IMMINENT,
} from '../common';
import { type PartialDref } from '../schema';
import InterventionInput from './InterventionInput';
+import ProposedActionsInput from './ProposedActionsInput';
import RiskSecurityInput from './RiskSecurityInput';
import i18n from './i18n.json';
@@ -55,6 +60,7 @@ type PlannedInterventionOption = NonNullable[number];
+type ProposedActionsFormFields = NonNullable[number];
type RiskSecurityFormFields = NonNullable[number];
function plannedInterventionKeySelector(option: PlannedInterventionOption) {
@@ -64,6 +70,7 @@ function plannedInterventionKeySelector(option: PlannedInterventionOption) {
interface Props {
value: Value;
setFieldValue: (...entries: EntriesAsList) => void;
+ setValue: (value: SetBaseValueArg, partialUpdate?: boolean) => void;
error: Error | undefined;
fileIdToUrlMap: Record;
setFileIdToUrlMap?: React.Dispatch>>;
@@ -83,8 +90,37 @@ function Operation(props: Props) {
fileIdToUrlMap,
setFileIdToUrlMap,
disabled,
+ setValue,
} = props;
+ const handleProposedActionChange = useCallback(
+ (val: SetValueArg, index: number | undefined) => {
+ setValue((oldVal) => {
+ const newProposedValue = [...(oldVal.proposed_action ?? [])];
+ if (isNotDefined(index)) {
+ newProposedValue.push(
+ isCallable(val) ? val(undefined) : val,
+ );
+ } else {
+ newProposedValue[index] = isCallable(val)
+ ? val(newProposedValue[index])
+ : val;
+ }
+
+ const newValue = {
+ ...oldVal,
+ proposed_action: newProposedValue,
+ };
+
+ return {
+ ...newValue,
+ ...calculateProposedActionsCost(newValue),
+ };
+ }, true);
+ },
+ [setValue],
+ );
+
const error = getErrorObject(formError);
const [
@@ -114,6 +150,22 @@ function Operation(props: Props) {
setFieldValue,
);
+ const handleSurgeDeployedChange = useCallback(
+ (val: PartialDref['is_surge_personnel_deployed'] | undefined) => (
+ setValue((oldValue) => {
+ const newValue = {
+ ...oldValue,
+ is_surge_personnel_deployed: val,
+ };
+ return {
+ ...newValue,
+ ...calculateProposedActionsCost(newValue),
+ };
+ }, true)
+ ),
+ [setValue],
+ );
+
const handleInterventionAddButtonClick = useCallback((title: PlannedInterventionOption['key'] | undefined) => {
const newInterventionItem: PlannedInterventionFormFields = {
client_id: randomString(),
@@ -215,579 +267,585 @@ function Operation(props: Props) {
return (