+
+ );
+}
diff --git a/app/src/views/AccountMyFormsEap/utils.ts b/app/src/views/AccountMyFormsEap/utils.ts
new file mode 100644
index 0000000000..4287a54b43
--- /dev/null
+++ b/app/src/views/AccountMyFormsEap/utils.ts
@@ -0,0 +1,34 @@
+import {
+ type EAP_TYPE_FULL,
+ type EAP_TYPE_SIMPLIFIED,
+} from '#utils/constants';
+import { type GoApiResponse } from '#utils/restRequest';
+
+type EapResponse = GoApiResponse<'/api/v2/eap-registration/'>;
+export type EapListItem = NonNullable[number];
+
+interface SimplifiedEapDetails {
+ eapType: typeof EAP_TYPE_SIMPLIFIED;
+ data: EapListItem['simplified_eap_details'][number];
+}
+
+interface FullEapDetails {
+ eapType: typeof EAP_TYPE_FULL;
+ data: EapListItem['full_eap_details'][number];
+}
+
+export type EapExpandedListItem = {
+ label: string;
+ lastUpdated?: string;
+ eap: EapListItem;
+ type: 'registration' | 'development' | 'review' | 'revision' | 'validated' | 'approved' | 'signed';
+ disabled?: boolean;
+
+ // Only applicable for development type
+ details: SimplifiedEapDetails | FullEapDetails | undefined;
+};
+
+export type EapExpandedItem = {
+ eap: EapListItem;
+ expandedItems: EapExpandedListItem[];
+};
diff --git a/app/src/views/AccountMyFormsLayout/i18n.json b/app/src/views/AccountMyFormsLayout/i18n.json
index 6b70856db7..c23cf7ce5b 100644
--- a/app/src/views/AccountMyFormsLayout/i18n.json
+++ b/app/src/views/AccountMyFormsLayout/i18n.json
@@ -4,6 +4,7 @@
"fieldReportTabTitle": "Field Report",
"perTabTitle": "PER",
"drefTabTitle": "DREF",
- "threeWTabTitle": "3W"
+ "threeWTabTitle": "3W",
+ "eapApplications": "EAP Applications"
}
}
\ No newline at end of file
diff --git a/app/src/views/AccountMyFormsLayout/index.tsx b/app/src/views/AccountMyFormsLayout/index.tsx
index 5364a38441..c7eaac05d2 100644
--- a/app/src/views/AccountMyFormsLayout/index.tsx
+++ b/app/src/views/AccountMyFormsLayout/index.tsx
@@ -35,6 +35,11 @@ export function Component() {
>
{strings.threeWTabTitle}
+
+ {strings.eapApplications}
+
diff --git a/app/src/views/DrefApplicationForm/index.tsx b/app/src/views/DrefApplicationForm/index.tsx
index 226d5a1c49..58b95680cd 100644
--- a/app/src/views/DrefApplicationForm/index.tsx
+++ b/app/src/views/DrefApplicationForm/index.tsx
@@ -308,6 +308,7 @@ export function Component() {
const loadResponseToFormValue = useCallback((response: GetDrefResponse) => {
handleDrefLoad(response);
+
const {
planned_interventions,
proposed_action,
diff --git a/app/src/views/DrefDetail/i18n.json b/app/src/views/DrefDetail/i18n.json
new file mode 100644
index 0000000000..47ce2fe4c4
--- /dev/null
+++ b/app/src/views/DrefDetail/i18n.json
@@ -0,0 +1,15 @@
+{
+ "namespace": "drefDetail",
+ "strings": {
+ "drefIntroHeading": "DREF Intro",
+ "drefIntroDetailOne": "Every year, small and medium-sized disasters occur in silence. Without media attention or international visibility, they can struggle to attract funding—putting affected communities at risk of being completely neglected.",
+ "drefIntroDetailTwo": "To support these smaller disasters, or to provide initial funding before launching an Emergency Appeal, we rapidly channel funding to Red Cross and Red Crescent Societies through the DREF—enabling them to deliver fast and effective local humanitarian action.",
+ "drefProcessHeading": "Dref Process",
+ "drefProcessSubHeading": "We provide funding in two ways:",
+ "drefProcessListOne": "A loan facility: start-up funding for the IFRC and National Societies to respond to large-scale disasters, which will later be reimbursed by donor contributions to an Emergency Appeal.",
+ "drefProcessListTwo": "A grant facility: funding for National Society responses to small- and medium-sized disasters. This is used when no Emergency Appeal will be launched or when support from other actors is not foreseen.",
+ "drefProcessDetailOne": "The fund is demand-driven and locally-owned. It is open to all 191 National Societies that submit funding applications and plans of action reflecting locally identified priorities and needs.",
+ "drefProcessDetailTwo": "On average, the DREF supports more than 100 responses to small and medium-sized disasters every year.",
+ "drefDrefApplication": "DREF Application"
+ }
+}
diff --git a/app/src/views/DrefDetail/index.tsx b/app/src/views/DrefDetail/index.tsx
new file mode 100644
index 0000000000..33869faf08
--- /dev/null
+++ b/app/src/views/DrefDetail/index.tsx
@@ -0,0 +1,58 @@
+import {
+ Container,
+ ListView,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+
+import SurgeContentContainer from '#components/domain/SurgeContentContainer';
+import Link from '#components/Link';
+
+import i18n from './i18n.json';
+
+/** @knipignore */
+// eslint-disable-next-line import/prefer-default-export
+export function Component() {
+ const strings = useTranslation(i18n);
+
+ return (
+
+
+
+
{strings.drefIntroDetailOne}
+
{strings.drefIntroDetailTwo}
+
+
+
{strings.drefProcessSubHeading}
+
+
+ {strings.drefProcessListOne}
+
+
+ {strings.drefProcessListTwo}
+
+
+
+ {strings.drefProcessDetailOne}
+
+
+ {strings.drefProcessDetailTwo}
+
+
+
+ {strings.drefDrefApplication}
+
+
+
+ );
+}
+
+Component.displayName = 'DrefDetail';
diff --git a/app/src/views/DrefProcess/i18n.json b/app/src/views/DrefProcess/i18n.json
new file mode 100644
index 0000000000..01904a6dda
--- /dev/null
+++ b/app/src/views/DrefProcess/i18n.json
@@ -0,0 +1,9 @@
+{
+ "namespace": "drefProcess",
+ "strings": {
+ "eapHeading": "Disaster Response Emergency Fund (DREF)",
+ "eapDescription": "The IFRC's Disaster Emergency Fund (DREF): rapid, reliable funding for life-saving action.",
+ "eapProcessDrefTab": "Response And Imminent Dref",
+ "eapProcessEapTab": "Early Action Protocols (EAP)"
+ }
+}
diff --git a/app/src/views/DrefProcess/index.tsx b/app/src/views/DrefProcess/index.tsx
new file mode 100644
index 0000000000..2ed605891a
--- /dev/null
+++ b/app/src/views/DrefProcess/index.tsx
@@ -0,0 +1,37 @@
+import { Outlet } from 'react-router-dom';
+import { NavigationTabList } from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+
+import NavigationTab from '#components/NavigationTab';
+import Page from '#components/Page';
+
+import i18n from './i18n.json';
+
+/** @knipignore */
+// eslint-disable-next-line import/prefer-default-export
+export function Component() {
+ const strings = useTranslation(i18n);
+
+ return (
+
+
+
+ {strings.eapProcessDrefTab}
+
+
+ {strings.eapProcessEapTab}
+
+
+
+
+ );
+}
+
+Component.displayName = 'DrefProcess';
diff --git a/app/src/views/EapFullForm/index.tsx b/app/src/views/EapFullForm/index.tsx
new file mode 100644
index 0000000000..e6d0317fee
--- /dev/null
+++ b/app/src/views/EapFullForm/index.tsx
@@ -0,0 +1,12 @@
+import Page from '#components/Page';
+
+/** @knipignore */
+// eslint-disable-next-line import/prefer-default-export
+export function Component() {
+ return (
+
+ {/* TODO: Add EAP form */}
+ Full EAP Form
+
+ );
+}
diff --git a/app/src/views/EapRegistration/i18n.json b/app/src/views/EapRegistration/i18n.json
new file mode 100644
index 0000000000..9a8304afc0
--- /dev/null
+++ b/app/src/views/EapRegistration/i18n.json
@@ -0,0 +1,51 @@
+{
+ "namespace": "eapRegistration",
+ "strings": {
+ "eapRegistrationHeading": "EAP Development Registration",
+ "eapRegistrationDescription": "Use the following page to submit your National Society's interest in following the Early Action Protocol (EAP) application process. Once you submit this form, the EAP team members will contact you to start the procedure. You can find more information on the process and different ways to apply {link}",
+ "eapRegistrationLink": "on this page.",
+ "eapApplicationDetails": "Application Details",
+ "eapNationalSociety": "National Society (NS)",
+ "eapNationalSocietyDescription": "Select National Society that is planning to apply for the EAP",
+ "eapCountry": "Country",
+ "eapCountryDescription": "The country will be pre-populated based on the NS selection, but can be adapted as needed.",
+ "eapDisasterType": "Disaster Type",
+ "eapDisasterTypeDescription": "Select the disaster type for which the EAP is needed.",
+ "eapType": "EAP Type",
+ "eapTypeDescription": "Select the EAP type. Find details of both under this link.",
+ "eapSubmission": "Expected Time of Submission",
+ "eapSubmissionDescription": "Include the proposed time of submission, accounting for the time it will take to deliver the application.",
+ "eapPartnersInvolved": "Partners Involved",
+ "eapPartnersInvolvedDescription": "Select from the list the partners involved in this process. Add as many as needed or select not applicable if no partners involved.",
+ "eapContacts": "Contacts",
+ "eapNSContact": "National Society Contact",
+ "eapNSContactDescription": "National Society contact responsible for the EAP process",
+ "eapNSName": "Name",
+ "eapNSTitle": "Title",
+ "eapNSEmail": "Email",
+ "eapNSPhoneNumber": "Phone Number",
+ "eapIFRCContact": "IFRC Contact",
+ "eapIFRCContactDescription": "The most senior staff in the National Society responsible and knowledgable about the disaster event.",
+ "eapIFRCName": "Name",
+ "eapIFRCTitle": "Title",
+ "eapIFRCEmail": "Email",
+ "eapIFRCPhoneNumber": "Phone Number",
+ "eapFocalPoint": "DREF Focal Point",
+ "eapFocalPointDescription": "The DREF contact person form IFRC",
+ "eapFocalPointName": "Name",
+ "eapFocalPointTitle": "Title",
+ "eapFocalPointEmail": "Email",
+ "eapFocalPointPhoneNumber": "Phone Number",
+ "eapSubmitButton": "Submit",
+ "eapBackButton": "Back",
+ "eapCancelButton": "Cancel",
+ "eapRegistrationFailure": "Sorry could not register new EAP right now!",
+ "eapRegistrationSuccess": "Successfully created a new EAP!",
+ "eapNotSure": "Not Sure",
+ "eapDevelopmentRegistrationHeading": "EAP Development Registration",
+ "eapDevelopmentRegistrationDescription": "Thank you for notifying us about the start of your EAP process. We look forward to your completed application.",
+ "eapFailedToLoad": "Failed to Load",
+ "eapRegistrationUpdateMessage": "EAP Registration updated Successfully.",
+ "eapRegistrationFailureMessage": "Failed to update EAP Registration"
+ }
+}
diff --git a/app/src/views/EapRegistration/index.tsx b/app/src/views/EapRegistration/index.tsx
new file mode 100644
index 0000000000..698e6a4c90
--- /dev/null
+++ b/app/src/views/EapRegistration/index.tsx
@@ -0,0 +1,555 @@
+import {
+ type ElementRef,
+ useCallback,
+ useRef,
+} from 'react';
+import {
+ useLocation,
+ useParams,
+} from 'react-router-dom';
+import {
+ ConfirmButton,
+ Container,
+ DateInput,
+ InputSection,
+ ListView,
+ Radio,
+ RadioInput,
+ TextInput,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import {
+ resolveToComponent,
+ stringValueSelector,
+} from '@ifrc-go/ui/utils';
+import {
+ isDefined,
+ isNotDefined,
+ isTruthyString,
+} from '@togglecorp/fujs';
+import {
+ createSubmitHandler,
+ getErrorObject,
+ getErrorString,
+ useForm,
+} from '@togglecorp/toggle-form';
+
+import CountrySelectInput from '#components/domain/CountrySelectInput';
+import DisasterTypeSelectInput from '#components/domain/DisasterTypeSelectInput';
+import FormFailedToLoadMessage from '#components/domain/FormFailedToLoadMessage';
+import NationalSocietyMultiSelectInput from '#components/domain/NationalSocietyMultiSelectInput';
+import NationalSocietySelectInput from '#components/domain/NationalSocietySelectInput';
+import Link from '#components/Link';
+import Page from '#components/Page';
+import useGlobalEnums from '#hooks/domain/useGlobalEnums';
+import useAlert from '#hooks/useAlert';
+import useRouting from '#hooks/useRouting';
+import {
+ type GoApiBody,
+ type GoApiResponse,
+ useLazyRequest,
+ useRequest,
+} from '#utils/restRequest';
+import { transformObjectError } from '#utils/restRequest/error';
+
+import {
+ defaultFormValue,
+ formSchema,
+} from './schema';
+
+import i18n from './i18n.json';
+
+type EapRegisterRequestBody = GoApiBody<'/api/v2/eap-registration/', 'POST'>;
+type GlobalEnumsResponse = GoApiResponse<'/api/v2/global-enums/'>;
+type EapTypeOption = NonNullable[number];
+
+function eapTypeKeySelector(option: EapTypeOption) {
+ return option.key;
+}
+
+/** @knipignore */
+// eslint-disable-next-line import/prefer-default-export
+export function Component() {
+ const strings = useTranslation(i18n);
+ const alert = useAlert();
+ const { navigate } = useRouting();
+ const { eapId: eapIdFromParams } = useParams<{ eapId: string }>();
+
+ const { state } = useLocation();
+ const eapId = eapIdFromParams ?? state?.eapId as string | undefined;
+ const isReadOnly = state?.mode === 'view';
+
+ const {
+ value,
+ setFieldValue,
+ error: formError,
+ setError,
+ setValue,
+ validate,
+ } = useForm(formSchema, { value: defaultFormValue });
+
+ const {
+ eap_eap_type: eapFormOptions,
+ } = useGlobalEnums();
+
+ const error = getErrorObject(formError);
+ const formContentRef = useRef>(null);
+
+ const {
+ pending: fetchingEap,
+ error: eapError,
+ } = useRequest({
+ skip: isNotDefined(eapId),
+ url: '/api/v2/eap-registration/{id}/',
+ pathVariables: isTruthyString(eapId) ? {
+ id: Number(eapId),
+ } : undefined,
+ onSuccess: (response) => {
+ const {
+ ...formValues
+ } = response;
+ setValue(formValues);
+ },
+ });
+
+ const {
+ pending: eapRegistrationPending,
+ trigger: eapRegister,
+ } = useLazyRequest({
+ method: 'POST',
+ url: '/api/v2/eap-registration/',
+ body: (body: EapRegisterRequestBody) => body,
+ onSuccess: () => {
+ const message = strings.eapRegistrationSuccess;
+ alert.show(
+ message,
+ { variant: 'success' },
+ );
+ navigate('accountMyFormsEap');
+ },
+ onFailure: (err) => {
+ const {
+ value: {
+ formErrors,
+ },
+ } = err;
+
+ setError(transformObjectError(formErrors, () => undefined));
+
+ alert.show(
+ strings.eapRegistrationFailure,
+ { variant: 'danger' },
+ );
+ },
+ });
+
+ const {
+ pending: updateEapRegistrationPending,
+ trigger: updateEapRegistration,
+ } = useLazyRequest({
+ url: '/api/v2/eap-registration/{id}/',
+ method: 'PATCH',
+ pathVariables: {
+ id: Number(eapId),
+ },
+ body: (formFields: EapRegisterRequestBody) => formFields,
+ onSuccess: (response) => {
+ alert.show(
+ strings.eapRegistrationUpdateMessage,
+ { variant: 'success' },
+ );
+ navigate(
+ 'accountMyFormsEap',
+ { params: { eapId: response.id } },
+ );
+ },
+ onFailure: (err) => {
+ const {
+ value: {
+ formErrors,
+ messageForNotification,
+ },
+ } = err;
+
+ setError(transformObjectError(
+ formErrors,
+ () => undefined,
+ ));
+
+ alert.show(
+ strings.eapRegistrationFailureMessage,
+ {
+ variant: 'danger',
+ description: messageForNotification,
+ },
+ );
+ },
+ });
+
+ const handleCountryChange = useCallback(
+ (val: number | undefined, name: 'country') => {
+ setFieldValue(val, name);
+ },
+ [setFieldValue],
+ );
+
+ const handleEapTypeClick = useCallback(() => {
+ if (isReadOnly) {
+ return;
+ }
+ setFieldValue(null, 'eap_type');
+ }, [isReadOnly, setFieldValue]);
+
+ const handleSubmissionTimeClick = useCallback(() => {
+ if (isReadOnly) {
+ return;
+ }
+ setFieldValue(null, 'expected_submission_time');
+ }, [isReadOnly, setFieldValue]);
+
+ const eapRegistration = useCallback(() => {
+ const handler = createSubmitHandler(
+ validate,
+ setError,
+ (formValues) => {
+ if (isNotDefined(eapId)) {
+ eapRegister(formValues as EapRegisterRequestBody);
+ } else {
+ updateEapRegistration({
+ ...formValues,
+ id: eapId,
+ } as EapRegisterRequestBody);
+ }
+ },
+ );
+ handler();
+ }, [
+ setError,
+ validate,
+ eapRegister,
+ updateEapRegistration,
+ eapId,
+ ]);
+
+ const handleFormError = useCallback(() => {
+ setTimeout(() => formContentRef.current?.scrollIntoView(), 200);
+ }, []);
+
+ const handleEapRegistration = useCallback(() => {
+ const handler = createSubmitHandler(
+ validate,
+ setError,
+ eapRegistration,
+ handleFormError,
+ );
+ handler();
+ }, [
+ handleFormError,
+ eapRegistration,
+ validate,
+ setError,
+ ]);
+
+ const disabled = eapRegistrationPending || fetchingEap || updateEapRegistrationPending;
+
+ const handleNationalSocietyInputChange = useCallback((newValue: number | undefined) => {
+ setFieldValue(newValue, 'national_society');
+ setFieldValue(newValue, 'country');
+ }, [setFieldValue]);
+
+ if (isDefined(eapError)) {
+ return (
+
+ );
+ }
+
+ return (
+
+ {strings.eapRegistrationLink}
+
+ ),
+ },
+ )}
+ actions={(
+
+ {eapId ? strings.eapBackButton : strings.eapCancelButton}
+
+ )}
+ elementRef={formContentRef}
+ withBackgroundColorInMainSection
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {strings.eapNotSure}
+
+
+
+
+
+
+ {strings.eapNotSure}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {strings.eapSubmitButton}
+
+
+
+ );
+}
diff --git a/app/src/views/EapRegistration/schema.ts b/app/src/views/EapRegistration/schema.ts
new file mode 100644
index 0000000000..17dca9c0b7
--- /dev/null
+++ b/app/src/views/EapRegistration/schema.ts
@@ -0,0 +1,56 @@
+import {
+ emailCondition,
+ type ObjectSchema,
+ type PartialForm,
+} from '@togglecorp/toggle-form';
+
+import { type GoApiBody } from '#utils/restRequest';
+
+type EapRegisterRequestBody = GoApiBody<'/api/v2/eap-registration/', 'POST'>;
+
+export const defaultFormValue: FormFields = {
+ eap_type: undefined,
+ expected_submission_time: undefined,
+};
+
+type FormFields = PartialForm;
+
+type FormSchema = ObjectSchema;
+type FormSchemaFields = ReturnType
+
+export const formSchema: FormSchema = {
+ fields: (): FormSchemaFields => ({
+ national_society: {
+ required: true,
+ },
+ country: {
+ required: true,
+ },
+ disaster_type: {
+ required: true,
+ },
+ eap_type: {},
+ expected_submission_time: {},
+ partners: {
+ required: true,
+ },
+ national_society_contact_name: {},
+ national_society_contact_title: {},
+ national_society_contact_email: {
+ validations: [emailCondition],
+ },
+ national_society_contact_phone_number: {},
+ ifrc_contact_name: {},
+ ifrc_contact_title: {},
+ ifrc_contact_email: {
+ validations: [emailCondition],
+ },
+ ifrc_contact_phone_number: {},
+ dref_focal_point_name: {},
+ dref_focal_point_title: {},
+ dref_focal_point_email: {
+ validations: [emailCondition],
+ },
+ dref_focal_point_phone_number: {},
+ }),
+};
diff --git a/app/src/views/EarlyActionProtocols/i18n.json b/app/src/views/EarlyActionProtocols/i18n.json
new file mode 100644
index 0000000000..9472e2952a
--- /dev/null
+++ b/app/src/views/EarlyActionProtocols/i18n.json
@@ -0,0 +1,14 @@
+{
+ "namespace": "earlyActionProtocols",
+ "strings": {
+ "eapRegistrationLink": "Register your EAP",
+ "eapContent": "What is an EAP?",
+ "eapContentHeading": "Early Action Protocols (EAPs) are a core mechanism of the IFRC's Forecast-based Financing (FbF) approach, designed to ensure that humanitarian action happens before a disaster strikes, rather than only responding afterwards.",
+ "eapContentSubHeadingOne": "An EAP is a pre-agreed plan developed by a Nation Society together with partners, which outlines:",
+ "eapDescriptionOne": "The triggers (based on scientific forecasts and risk analysis) that indicate when a hazard is likely to impact communities.",
+ "eapDescriptionTwo": "The early actions to be implemented once those triggers are met - practical, life-saving measures that reduce the impacts of the forecasted disaster.",
+ "eapDescriptionThree": "The roles, responsibilities, and budget required to carry out these actions quickly and effectively.",
+ "eapContentSubHeadingTwo": "Why are the EAPs Important?",
+ "eapContentSubHeadingThree": "What is the EAP Application Process?"
+ }
+}
diff --git a/app/src/views/EarlyActionProtocols/index.tsx b/app/src/views/EarlyActionProtocols/index.tsx
new file mode 100644
index 0000000000..98ac8ff321
--- /dev/null
+++ b/app/src/views/EarlyActionProtocols/index.tsx
@@ -0,0 +1,71 @@
+import {
+ Container,
+ ExpandableContainer,
+ ListView,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+
+import Link from '#components/Link';
+
+import i18n from './i18n.json';
+
+/** @knipignore */
+// eslint-disable-next-line import/prefer-default-export
+export function Component() {
+ const strings = useTranslation(i18n);
+
+ return (
+
+ {strings.eapRegistrationLink}
+
+ )}
+ >
+
+
+
+ ))}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ {/*
+
+
+
+ */}
+
+
+
+
+
+
+
+
+
+ );
+}
+
+Component.displayName = 'SimplifiedEapExport';
diff --git a/app/src/views/SimplifiedEapExport/styles.module.css b/app/src/views/SimplifiedEapExport/styles.module.css
new file mode 100644
index 0000000000..c9628d6851
--- /dev/null
+++ b/app/src/views/SimplifiedEapExport/styles.module.css
@@ -0,0 +1,11 @@
+.meta-items {
+ display: grid;
+ grid-gap: var(--go-ui-width-separator-md);
+ grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
+}
+
+.indicator-items {
+ display: grid;
+ grid-gap: var(--go-ui-width-separator-md);
+ grid-template-columns: 2fr 1fr;
+}
diff --git a/app/src/views/SimplifiedEapForm/ContactInputsSection/index.tsx b/app/src/views/SimplifiedEapForm/ContactInputsSection/index.tsx
new file mode 100644
index 0000000000..4f49a02336
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/ContactInputsSection/index.tsx
@@ -0,0 +1,86 @@
+import {
+ InputSection,
+ TextInput,
+} from '@ifrc-go/ui';
+import {
+ type EntriesAsList,
+ type Error,
+ getErrorObject,
+} from '@togglecorp/toggle-form';
+
+import {
+ type PartialSimplifiedEapType,
+ type ValidContactFieldPrefixes,
+} from '../schema';
+
+interface Props {
+ title?: React.ReactNode;
+ description?: React.ReactNode;
+ namePrefix: ValidContactFieldPrefixes;
+ value: PartialSimplifiedEapType;
+ setFieldValue: (...entries: EntriesAsList) => void;
+ error: Error | undefined;
+ disabled?: boolean;
+}
+
+function ContactInputsSection(props: Props) {
+ const {
+ title: sectionTitle,
+ description,
+ namePrefix,
+ value,
+ setFieldValue,
+ error: formError,
+ disabled,
+ } = props;
+
+ const error = getErrorObject(formError);
+
+ const name = `${namePrefix}_name` satisfies keyof PartialSimplifiedEapType;
+ const title = `${namePrefix}_title` satisfies keyof PartialSimplifiedEapType;
+ const email = `${namePrefix}_email` satisfies keyof PartialSimplifiedEapType;
+ const phoneNumber = `${namePrefix}_phone_number` satisfies keyof PartialSimplifiedEapType;
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default ContactInputsSection;
diff --git a/app/src/views/SimplifiedEapForm/DeliveryAndBudget/i18n.json b/app/src/views/SimplifiedEapForm/DeliveryAndBudget/i18n.json
new file mode 100644
index 0000000000..13d35da69f
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/DeliveryAndBudget/i18n.json
@@ -0,0 +1,43 @@
+{
+ "namespace": "simplifiedEapForm",
+ "strings": {
+ "deliverHeading": "Conditions to deliver the Early Action",
+ "deliverEarlyActions": "Experience and/or capacity to implement the early actions",
+ "deliverEarlyActionsDescription": "Assumptions or minimum conditions needed to deliver on the early actions (including issues to be resolved.) Explain how the National Society will be able to delive on the early actions, what experiences and/or capacities they have related to be intervention. Are there issues to be addressed in order for the Naional Society to deliver on these actions? How will these issues be resolved?",
+ "deliverEarlyActionsTooltip": "This is an opportunity for the National Society to describe their experience in dealing with the selected hazard, their capacity in the intervention sectors and early action activities. Where there are gaps in capacity or systemic issues, then these can be addressed through the annual readiness activities. For example, the National Society may have a general response plan, but may use the readiness activities to develop detailed Standard Operating Procedures outlining who will do what when, and then do an annual test simulation of the simplified EAP to ensure that roles and responsibilities are clear in terms of delivering the early actions.",
+ "deliverDescription": "Description",
+ "deliverInvolved": "RCRC Movement partners, Governmental/other agencies consulted/involved",
+ "deliverInvolvedDescription": "Explain who was part of the development of this plan, how were they involved and if they have any role on the implementation of the actions. Add any relevant information of the National Society's role in the National Disaster Response System.",
+ "deliverInvolvedTooltipDescriptionOne": "It is important to establish that the National Society has the mandate to act early, in advance of a hazard and it can reduce the time required to agree to activate the early actions activities if partners government and non-government stakeholders are aware and involved in planning the simplified EAP. This mandate may need to be confirmed at the local level, as well as the region and national level.",
+ "deliverInvolvedTooltipDescriptionTwo": "The simplified EAP aims to make connections",
+ "deliverInvolvedTooltipDescriptionThree": "With national level partners, including government stakeholders, Met Service, civil society and other national or local experts. Planning for a simplified EAP can be an opportunity to develop these relationships and may be useful to define roles and responsibilities in a Memorandum of Understanding (MoU). This could be defined during the development of the simplified EAP or can be done as part of the kickoff activities, once a simplified EAP has been approved for funding. The Red Cross Red Crescent Climate Centre developed this guide – {guideLink} The guide includes some useful information, included under section 4, guiding questions and in annex A, a draft MoU, which could potentially be adapted for other partners.",
+ "guideLink": "Collaborating with national climate and weather agencies: a guide to getting started.",
+ "deliverInvolvedTooltipDescriptionFour": "Other general considerations:",
+ "deliverInvolvedTooltipListOne": "The simplified EAP should plan to target events that are forecasted/predicted to have an above average impact and that have caused humanitarian impacts in the past",
+ "deliverInvolvedTooltipListTwo": "The minimum standard for monitoring and evaluation is that each simplified EAP includes a lessons learned workshop.",
+ "deliverInvolvedTooltipListThree": "While not requested as part of the simplified EAP, National Societies should have a draft plan or SOP in place to monitor the forecasts/indicators, to activate the simplified EAP and deliver the early action activities within the lead time.",
+ "deliverInvolvedTooltipListFour": "The roles and responsibilities should be clear, who will do what at what time.",
+ "budgetHeading": "Budget",
+ "deliverTotalBudget": "Total budget",
+ "deliverTotalBudgetTooltip": "The maximum budget available for a National Society under a simplified EAP is CHF 200,000 over two years. In this field, the National Society should reflect the total amount of the budget (which should match the budget template). The total budget includes 65% maximum for readiness activities and prepositioned stock, with the balance funding the early action activities and needs to be inclusive of the IFRC’s indirect costs (6.5%). Additionally, the IFRC Delegation may add 10% of the total budget to support the simplified EAP implementation bringing the final budget to CHF 220,000 over two years. If a National Society has any questions on the budget, please consult the IFRC’s Country or Country Cluster Delegation. Readiness, pre-positioning, early action: These fields should reflect the plan and budget split across the three types of eligible activities and match the total budget.",
+ "deliverTotalBudgetDescription": "Add the expected budget amount.",
+ "deliverTotalBudgetTooltipDescription": "The budget template for the simplified EAP can be found {hereLink}. Please note that the budget template has a tab with guidelines on how to use it. ",
+ "hereLink": "here",
+ "deliverTotalBudgetTooltipListOne": "For a National Society, the maximum budget of a simplified EAP is CHF 200,000 (less IFRC indirect costs of 6.5%). This is around CHF 187,794.",
+ "deliverTotalBudgetTooltipListTwo": "For the IFRC, the delegation can access a maximum budget of 10% of the National Society Simplified EAP budget (less IFRC indirect costs of 6.5%).",
+ "deliverTotalBudgetTooltipListThree": "The total budget of the simplified EAP, including National Society and IFRC allocations, cannot exceed CHF 220,000. ",
+ "deliverTotalBudgetTooltipListFour": "All the activities in the ‘planned operation’ and ‘enabling approaches’ section need to be reflected in the budget (even if there is no cost related to an activity, a note should be included in the budget to explain that for example, there is no cost for this activity, or costs are combined with another activity (such as training combining two or more topics), or costs are covered by another project or donor. This helps when doing the cross check between the planned intervention matrix and the budget.",
+ "deliverTotalBudgetTooltipListFive": "Readiness activities (column C) should be done in YEAR 1 and/or YEAR 2 (column E) – ideally done in both years, although there may be some activities that only need to be done once. If the activity happens in both years insert the activity twice and select YEAR 1 for the first line and YEAR 2 for the second line.",
+ "deliverTotalBudgetTooltipListSix": "Pre-positioning activities (column C) are done in YEAR 1 (column E) – and should be done as soon as the project agreement is in place and funds are received by the National Society. All stock needs to have a minimum shelf life of two years.",
+ "deliverTotalBudgetTooltipListSeven": "Early Action activities (column C) should be budgeted under “year early action” YEAR EA (column E), these activities will be done only when the trigger is reached (which could be in year one, or year two, or may not happen in the duration of the simplified EAP).",
+ "deliverTotalBudgetTooltipListEight": "A maximum of 65% of the budget can be allocated combined to readiness and pre-positioning activities. You can check these percentages in the tab called “Summary by Year”, under column B.",
+ "deliverTotalBudgetTooltipListNine": "National Societies have flexibility to move between budget headings up to 10% of the approved simplified EAP budget.",
+ "deliverBudgetLabel": "Budget",
+ "deliverReadinessLabel": "Readiness",
+ "deliverPrepositioning": "Prepositioning",
+ "deliverBudgetDetails": "Budget details",
+ "deliverBudgetDetailsDescription": "Add here the page from the budget template called “EAP for publication”. Explain your selection criteria for who will be targeted.",
+ "earlyAction": "Early Action",
+ "upload": "Upload"
+ }
+}
diff --git a/app/src/views/SimplifiedEapForm/DeliveryAndBudget/index.tsx b/app/src/views/SimplifiedEapForm/DeliveryAndBudget/index.tsx
new file mode 100644
index 0000000000..3a7714cf51
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/DeliveryAndBudget/index.tsx
@@ -0,0 +1,248 @@
+import {
+ Container,
+ InfoPopup,
+ InputSection,
+ ListView,
+ NumberInput,
+ TextArea,
+ TextOutput,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import { resolveToComponent } from '@ifrc-go/ui/utils';
+import {
+ type EntriesAsList,
+ type Error,
+ getErrorObject,
+ getErrorString,
+} from '@togglecorp/toggle-form';
+
+import GoSingleFileInput from '#components/domain/GoSingleFileInput';
+import Link from '#components/Link';
+import TabPage from '#components/TabPage';
+
+import { type PartialSimplifiedEapType } from '../schema';
+
+import i18n from './i18n.json';
+
+interface Props {
+ value: PartialSimplifiedEapType;
+ setFieldValue: (...entries: EntriesAsList) => void;
+ error: Error | undefined;
+ disabled?: boolean;
+ fileIdToUrlMap: Record;
+ setFileIdToUrlMap?: React.Dispatch>>;
+}
+
+function DeliveryAndBudget(props: Props) {
+ const {
+ value,
+ setFieldValue,
+ error: formError,
+ disabled,
+ fileIdToUrlMap,
+ setFileIdToUrlMap,
+ } = props;
+
+ const strings = useTranslation(i18n);
+ const error = getErrorObject(formError);
+
+ return (
+
+
+
+
+
+
+
+
+ {strings.deliverTotalBudgetTooltipListSix}
+
+ )}
+ />
+
+ )}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ {strings.upload}
+
+
+
+
+
+ );
+}
+
+export default DeliveryAndBudget;
diff --git a/app/src/views/SimplifiedEapForm/EarlyAction/i18n.json b/app/src/views/SimplifiedEapForm/EarlyAction/i18n.json
new file mode 100644
index 0000000000..f1d2938a32
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/EarlyAction/i18n.json
@@ -0,0 +1,53 @@
+{
+ "namespace": "form",
+ "strings": {
+ "actionHeading": "Early Action Intervention",
+ "intervention": "Overall objective of the intervention",
+ "interventionDescription": "Provide an objective statement that describes the main goal of the intervention.",
+ "interventiontooltipDescription": "In a sentence or two, provide an objective statement that describes the main goal of the intervention. For example:",
+ "interventiontooltipDescriptionListOne": "The operation aims to mitigate the impact of severe cold wave on vulnerable subsistence herders by providing cash and animal care kits to maintain animal health in advance of freezing temperatures.",
+ "interventiontooltipDescriptionListTwo": "The operation aims to mitigate the impact of flooding by providing residents in flood-prone coastal regions with early warning messages, evacuation by boat and water purification material to save lives and prevent the outbreak of waterborne diseases in advance of the peak of flood inundation.",
+ "interventiontooltipDescriptionListThree": "The operation aims to mitigate the impact of cyclone by providing vulnerable people with early warning messages and shelter strengthening kits and will support female and child headed households and elderly to strengthen shelters to save lives and protect homes in advance of a more severe than usual cyclone.",
+ "geographicalRiskArea": "Potential geographical high-risk areas that the simplified EAP would target",
+ "geographicalRiskAreaDescription": "Which high risk regions have been selected for this intervention and why?",
+ "geographicalRiskAreaTooltip": "Simplified EAPs should be developed with national coverage in mind but because of the nature of certain hazards there might be geographical areas that are more at risk, for example: coastal districts (for cyclones), river areas (for riverine floods), international border areas (for population movement), etc. Provide an overview of the geographical areas targeted by this simplified EAP, and why these specific geographical areas are high risk. The National Society could also include a map of the most at risk geographical locations. For more guidance on how to select the geographic area of intervention see the FbF {practitionersManualLink} (Chapter 4.1, Step 3: Who and What is exposed?)",
+ "practitionersManualLink": "Practitioner’s Manual",
+ "actionDescription": "Description",
+ "actionValue": "Value",
+ "actionPeopleTargeted": "People targeted",
+ "actionPeopleTargetedDescription": "Enter the number of people targeted",
+ "actionPeopleTargetedTooltip": "The National Society should reflect how many people they plan to target with their early action intervention. The simplified EAP should target at least 2,000 people.",
+ "actionOperation": "Assisted through the operation",
+ "actionOperationDescription": "List who will be targeted by the early actions in this simplified EAP (these should be groups of people who are most exposed combined with those moose vulnerable to the impacts of the hazard).",
+ "actionOperationTooltip": "Please list here the people that will be targeted with early action activities. Based on the prioritized impacts, there might be groups that are more vulnerable to the hazard, for example, people who live close to the shoreline are more vulnerable to floods than people who live a few hundred meters away from the shoreline (exposure). And within the people who live close to the shoreline, low-income households are more vulnerable as they might not have the resources to evacuate themselves. The National Society should use their experience and knowledge of the context to reflect on who is most exposed, most vulnerable, and therefore should be targeted by the early actions.",
+ "actionCriteria": "Explain your selection criteria for who will be targeted.",
+ "actionCriteriaTooltipHeading": "Explain how they will be selected",
+ "actionCriteriaTooltip": "List here the selection criteria for the groups/individuals targeted. Considering that the National Society may not know in advance which communities will be targeted by the simplified EAP, there should be clear criteria for selecting the people to be targeted (within the preselected groups, based on exposure) in the lead time. For example, the criteria could include woman or child headed households, low-income families, large families, people targeted through government social protection programming, people with disabilities, elderly etc. The National Society should also reflect on how they will gather the information to verify the criteria.",
+ "actionsStatement": "Trigger(s) statement",
+ "actionsStatementDescription": "State clear and precise criteria that will have to be met for the simplified EAP to be activated. If multiple triggers are used, indicate which trigger is linked to which early action.",
+ "actionsStatementDescriptionTooltipDescriptionOne": "A trigger statement needs to be clear and precise and should explain in a sentence or two the condition under which the simplified EAP is activated. Here are some examples of a trigger statement",
+ "actionsStatementDescriptionTooltipDescriptionTwo": "Imprecise trigger",
+ "actionsStatementDescriptionTooltipDescriptionThree": "The trigger will be met when the forecast indicates a high possibility of heavy rainfall in eastern parts of the country in the next few days",
+ "actionsStatementDescriptionTooltipListOne": "Which forecast? There are many different possibilities, from the national hydro met service, or regional or even global providers. The National Society should state which forecast they will use and how they will obtain it.",
+ "actionsStatementDescriptionTooltipListTwo": "Is the terminology (e.g. ‘high possibility’ or ‘heavy rainfall’ used consistently by the forecast provider? If not, they will need to be more precise.",
+ "actionsStatementDescriptionTooltipListThree": "If the simplified EAP targets a specific geographical area, make sure the location for the trigger is well defined.",
+ "actionsStatementDescriptionTooltipListFour": "Make sure to clarify a precise window in which the plan can trigger (remember this will be partly determined by how long is needed to take the proposed actions)",
+ "actionsStatementDescriptionTooltipDescriptionFour": "Precise trigger",
+ "actionsStatementDescriptionTooltipDescriptionFive": "The trigger will be met when the [National Hydro Met Service Forecast] indicates an [80% chance] of [300mm of rainfall in a day] in [Province(s) name] in [72 hours] or The trigger will be met when the [National Hydro Met Service] issues a [red weather warning] for [Province(s) name] for [3 days time]",
+ "actionsStatementDescriptionTooltipDescriptionSix": "If the EAP has more than one trigger (i.e., a phased or staggered triggers) then each trigger statement should clearly explain what action will be taken following each trigger and when the funds for the early action are required.",
+ "actionsStatementDescriptionTooltipDescriptionSeven": "For slow onset, non-weather related or complex hazards, the trigger may be based on a combination of risk factors, forecasts, observation data and expert judgement, especially if the impact is a result of cumulative, or compounding factors. If unconventional triggers are used (e.g., combining multiple indicators, including socio-economic indicators like food prices), clear explanation should be given on which criteria/ conditions was used to assign certain weight to each indicator. If the trigger is based on expert judgement, it should be clear from what source this will come, and that it is an independent, reliable and creditable source. For examples of triggers, visit the {triggerDatabaseLink} on the Anticipation Hub.",
+ "triggerDatabaseLink": "trigger database",
+ "actionsLeadTime": "sEAP Lead Time",
+ "actionsLeadTimeDescription": "This is the time between the trigger being met and the impact of the hazard, this is the period of time when the early actions are undertaken. If the simplified EAP has a short lead time, the National Society may be asked to explain the feasibility of the early actions (funding, capacity etc.).",
+ "actionsOperational": "Operational Timeframe",
+ "actionsOperationalDescription": "The operational timeframe starts from the trigger date and includes the time it takes to implement the early action activities plus the time it takes to finalize the operation, including time to settle the finances, facilitate the lessons learned workshop and prepare the final report. Normally the operational timeframe is the lead time plus 3 months, 2 months to allow the National Society to finalize the reports and 1 month for the IFRC to finalise and publish the final report. (Note that the operational timeframe, expressed in months is not the same as the sEAP timeframe, which is expressed in years).",
+ "justification": "Trigger threshold justification",
+ "justificationDescription": "Explain how the triggers(s) were set and provide information showing that the level chosen has caused humanitarian impact in the past.",
+ "justificationTooltipDescriptionOne": "If there is data on how often the trigger threshold has been reached in the past, please include this here and describe the humanitarian impact caused by the magnitude of disaster on those occasions.",
+ "justificationTooltipDescriptionTwo": "A Stop Mechanism",
+ "justificationTooltipDescriptionThree": "The simplified EAP does not need to have a stop mechanism, however if the simplified EAP has a longer lead time (for example, more than four days), then the National Society may want to consider a deadline to stop doing early action activities if the forecast changes. A stop mechanism may be beneficial as in this situation, i.e. with a longer lead time, may be confusing for local communities if they are no longer at risk but early action activities.",
+ "fullEap": "Next step towards full EAP",
+ "fullEapDescription": "For National Societies that intend to develop a full EAP, outline the next steps you will be taking to continue developing this simplified EAP into a full EAP.",
+ "operationTimeFrame": "Time Frame"
+ }
+}
\ No newline at end of file
diff --git a/app/src/views/SimplifiedEapForm/EarlyAction/index.tsx b/app/src/views/SimplifiedEapForm/EarlyAction/index.tsx
new file mode 100644
index 0000000000..0fb4c504d8
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/EarlyAction/index.tsx
@@ -0,0 +1,348 @@
+import {
+ Container,
+ InputSection,
+ ListView,
+ NumberInput,
+ SelectInput,
+ TextArea,
+ TextOutput,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import {
+ resolveToComponent,
+ stringValueSelector,
+} from '@ifrc-go/ui/utils';
+import { isDefined } from '@togglecorp/fujs';
+import {
+ type EntriesAsList,
+ type Error,
+ getErrorObject,
+ getErrorString,
+} from '@togglecorp/toggle-form';
+
+import Admin2Input from '#components/domain/Admin2Input';
+import Link from '#components/Link';
+import useGlobalEnums from '#hooks/domain/useGlobalEnums';
+import { TIMEFRAME_YEAR } from '#utils/constants';
+import { type GoApiResponse } from '#utils/restRequest';
+
+import { type PartialSimplifiedEapType } from '../schema';
+
+import i18n from './i18n.json';
+
+type GlobalEnumsResponse = GoApiResponse<'/api/v2/global-enums/'>;
+
+type TimeframeOption = NonNullable[number];
+
+function timeframeKeySelector(option: TimeframeOption) {
+ return option.key;
+}
+
+interface Props {
+ value: NonNullable;
+ setFieldValue: (...entries: EntriesAsList) => void;
+ error: Error | undefined;
+ disabled?: boolean;
+ eapRegistrationDetail?: GoApiResponse<'/api/v2/eap-registration/{id}/'>;
+}
+
+function EarlyAction(props: Props) {
+ const {
+ value,
+ setFieldValue,
+ error: formError,
+ disabled,
+ eapRegistrationDetail,
+ } = props;
+
+ const strings = useTranslation(i18n);
+ const error = getErrorObject(formError);
+
+ const {
+ eap_timeframe,
+ } = useGlobalEnums();
+
+ const eapTimeframeOption = eap_timeframe?.filter(
+ (item) => item.key !== TIMEFRAME_YEAR,
+ );
+
+ return (
+
+
+
+ {strings.interventiontooltipDescription}
+
+
+
+ )}
+ withAsteriskOnTitle
+ >
+
+
+
+
+
+
+
+ );
+}
+
+export default EarlyAction;
diff --git a/app/src/views/SimplifiedEapForm/EnablingApproaches/ApproachesInput/i18n.json b/app/src/views/SimplifiedEapForm/EnablingApproaches/ApproachesInput/i18n.json
new file mode 100644
index 0000000000..833bbe05d8
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/EnablingApproaches/ApproachesInput/i18n.json
@@ -0,0 +1,20 @@
+{
+ "namespace": "simplifiedEapForm",
+ "strings": {
+ "approachBudget": "Budget",
+ "approachApCode": "AP Code",
+ "approachRemoveButton": "Remove",
+ "approachAddActivityButton": "Add Activity",
+ "approachReadinessActivities": "Readiness Activities",
+ "approachReadinessActivitiesTooltip": "Readiness activities are done year on year to ensure that the National Society is ready to conduct the early actions. These are activities that will happen irrespective of an activation. Readiness activities may include refresher training, coordination meetings with government, readiness meetings, simulations, etc. Under readiness they can include any ongoing costs and services (human resources and logistics) that are deemed indispensable for subsequent trigger-based early action activities. If, during the simplified EAP development process the National Society finds some areas for improvement to deliver on their selected early actions, these could be addressed with activities included under readiness.",
+ "approachPrepositioningActivities": "Pre-positioning Activities",
+ "approachPrepositioningActivitiesTooltip": "The National Society should preposition the materials needed to undertake the early action, especially those that may require a longer procurement process. For example, prepositioned stocks could include shelter kits (for house reinforcement), sandbags (for protecting infrastructure), or tarpaulins (for protecting water sources), etc. Food, medicine and other items with a shelf life of less than two years are not eligible as pre-positioning, they will have to be procured as part of the early actions. Pre-positioning activities are one-off and done in the first year following approval of the simplified EAP.",
+ "approachEarlyActionActivities": "Early Action Activities",
+ "approachEarlyActionActivitiesTooltip": "Early action activities are implemented once a trigger is reached and before the impact of the hazard. Early actions seek to reduce or mitigate the impact of the hazard. Consider selecting only a few early actions, especially for sudden onset events, as they will have to be implemented within a short timeframe. The early actions will be unique to each hazard and context, but may be activities such as evacuation of at-risk communities and/or livestock, early harvest of crops, cash transfer, shelter strengthening, provision of water treatment, hygiene kits or mosquito nets, etc. For more examples of early action activities, visit the {earlyActionDatabaseLink} on the Anticipation Hub.",
+ "approachEarlyActionDatabaseLink": "early action database",
+ "approachNoActivitiesMessage": "No activities added yet",
+ "approachIndicators": "Indicators",
+ "approachAddIndicators": "Add Indicators",
+ "approachNoindicatorsYet": "No indicators yet"
+ }
+}
\ No newline at end of file
diff --git a/app/src/views/SimplifiedEapForm/EnablingApproaches/ApproachesInput/index.tsx b/app/src/views/SimplifiedEapForm/EnablingApproaches/ApproachesInput/index.tsx
new file mode 100644
index 0000000000..0ceb9babcf
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/EnablingApproaches/ApproachesInput/index.tsx
@@ -0,0 +1,420 @@
+import { useCallback } from 'react';
+import {
+ AddLineIcon,
+ DeleteBinTwoLineIcon,
+} from '@ifrc-go/icons';
+import {
+ Button,
+ Container,
+ ExpandableContainer,
+ InfoPopup,
+ ListView,
+ NumberInput,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import { resolveToComponent } from '@ifrc-go/ui/utils';
+import {
+ isNotDefined,
+ randomString,
+} from '@togglecorp/fujs';
+import {
+ type ArrayError,
+ getErrorObject,
+ type SetValueArg,
+ useFormArray,
+ useFormObject,
+} from '@togglecorp/toggle-form';
+
+import OperationActivityInput from '#components/domain/OperationActivityInput';
+import Link from '#components/Link';
+import NonFieldError from '#components/NonFieldError';
+
+import IndicatorInput from '../../IndicatorInput';
+import { type PartialSimplifiedEapType } from '../../schema';
+
+import i18n from './i18n.json';
+
+type EnableApproachesFormFields = NonNullable[number];
+type EarlyActionFormFields = NonNullable[number];
+type PrepositioningFormFields = NonNullable[number];
+type ReadinessFormFields = NonNullable[number];
+type IndicatorFormFields = NonNullable[number];
+
+const defaultApproachValue: EnableApproachesFormFields = {
+ approach: 10,
+};
+
+interface Props {
+ value: EnableApproachesFormFields;
+ error: ArrayError | undefined;
+ onChange: (value: SetValueArg, index: number) => void;
+ onRemove: (index: number) => void;
+ index: number;
+ disabled?: boolean;
+ approachTitle?: React.ReactNode;
+}
+
+function OperationsBySectorInput(props: Props) {
+ const {
+ error: errorFromProps,
+ onChange,
+ value,
+ index,
+ onRemove,
+ disabled,
+ approachTitle,
+ } = props;
+
+ const strings = useTranslation(i18n);
+ const onFieldChange = useFormObject(index, onChange, defaultApproachValue);
+
+ const error = (value && value.approach && errorFromProps)
+ ? getErrorObject(errorFromProps?.[value.approach])
+ : undefined;
+
+ const {
+ setValue: onEarlyActionChange,
+ removeValue: onEarlyActionRemove,
+ } = useFormArray<'early_action_activities', EarlyActionFormFields>(
+ 'early_action_activities' as const,
+ onFieldChange,
+ );
+ const {
+ setValue: onPrepositioningChange,
+ removeValue: onPrepositioningRemove,
+ } = useFormArray<'prepositioning_activities', PrepositioningFormFields>(
+ 'prepositioning_activities' as const,
+ onFieldChange,
+ );
+ const {
+ setValue: onReadinessChange,
+ removeValue: onReadinessRemove,
+ } = useFormArray<'readiness_activities', ReadinessFormFields>(
+ 'readiness_activities' as const,
+ onFieldChange,
+ );
+ const {
+ setValue: onIndicatorChange,
+ removeValue: onIndicatorRemove,
+ } = useFormArray<'indicators', IndicatorFormFields>(
+ 'indicators' as const,
+ onFieldChange,
+ );
+
+ const handleEarlyActionAddButtonClick = useCallback(
+ () => {
+ const newActionItem: EarlyActionFormFields = {
+ client_id: randomString(),
+ };
+
+ onFieldChange(
+ (oldValue: EarlyActionFormFields[] | undefined) => (
+ [...(oldValue ?? []), newActionItem]
+ ),
+ 'early_action_activities' as const,
+ );
+ },
+ [onFieldChange],
+ );
+
+ const handlePrepositioningAddButtonClick = useCallback(
+ () => {
+ const newActionItem: PrepositioningFormFields = {
+ client_id: randomString(),
+ };
+
+ onFieldChange(
+ (oldValue: PrepositioningFormFields[] | undefined) => (
+ [...(oldValue ?? []), newActionItem]
+ ),
+ 'prepositioning_activities' as const,
+ );
+ },
+ [onFieldChange],
+ );
+
+ const handleReadinessAddButtonClick = useCallback(
+ () => {
+ const newActionItem: ReadinessFormFields = {
+ client_id: randomString(),
+ };
+
+ onFieldChange(
+ (oldValue: ReadinessFormFields[] | undefined) => (
+ [...(oldValue ?? []), newActionItem]
+ ),
+ 'readiness_activities' as const,
+ );
+ },
+ [onFieldChange],
+ );
+
+ const handleIndicatorAddButtonClick = useCallback(
+ () => {
+ const newIndicator: IndicatorFormFields = {
+ client_id: randomString(),
+ };
+
+ onFieldChange(
+ (oldValue: IndicatorFormFields[] | undefined) => (
+ [...(oldValue ?? []), newIndicator]
+ ),
+ 'indicators' as const,
+ );
+ },
+ [onFieldChange],
+ );
+
+ return (
+
+
+
+ )}
+ withPadding
+ withBackground
+ initiallyExpanded
+ >
+
+
+
+
+
+
+ }
+ >
+ {strings.approachAddIndicators}
+
+ )}
+ empty={isNotDefined(value.indicators) || value.indicators.length === 0}
+ emptyMessage={strings.approachNoindicatorsYet}
+ >
+
+ {value.indicators?.map((indicator, i) => (
+
+ ))}
+
+
+
+ {strings.approachReadinessActivities}
+
+ >
+ )}
+ headingLevel={5}
+ footerActions={(
+ }
+ >
+ {strings.approachAddActivityButton}
+
+ )}
+ withCompactMessage
+ empty={isNotDefined(value.readiness_activities)
+ || value.readiness_activities.length === 0}
+ emptyMessage={strings.approachNoActivitiesMessage}
+ headerDescription={(
+
+ )}
+ >
+
+ {value?.readiness_activities?.map((activity, i) => (
+
+ ))}
+
+
+
+ {strings.approachPrepositioningActivities}
+
+ >
+ )}
+ headingLevel={5}
+ footerActions={(
+ }
+ >
+ {strings.approachAddActivityButton}
+
+ )}
+ withCompactMessage
+ empty={isNotDefined(value.prepositioning_activities)
+ || value.prepositioning_activities.length === 0}
+ emptyMessage={strings.approachNoActivitiesMessage}
+ headerDescription={(
+
+ )}
+ >
+
+ {value?.prepositioning_activities?.map((activity, i) => (
+
+ ))}
+
+
+
+ {strings.approachEarlyActionActivities}
+
+ {strings.approachEarlyActionDatabaseLink}
+
+ ),
+ },
+ ))}
+ />
+ >
+ )}
+ headingLevel={5}
+ footerActions={(
+ }
+ >
+ {strings.approachAddActivityButton}
+
+ )}
+ withCompactMessage
+ empty={isNotDefined(value.early_action_activities)
+ || value.early_action_activities.length === 0}
+ emptyMessage={strings.approachNoActivitiesMessage}
+ headerDescription={(
+
+ )}
+ >
+
+ {value?.early_action_activities?.map((activity, i) => (
+
+ ))}
+
+
+
+
+
+ );
+}
+
+export default OperationsBySectorInput;
diff --git a/app/src/views/SimplifiedEapForm/EnablingApproaches/i18n.json b/app/src/views/SimplifiedEapForm/EnablingApproaches/i18n.json
new file mode 100644
index 0000000000..1c44a4b24b
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/EnablingApproaches/i18n.json
@@ -0,0 +1,8 @@
+{
+ "namespace": "simplifiedEapForm",
+ "strings": {
+ "enablingApproachesTitle": "Enabling Approaches",
+ "enablingApproachesDescription": "Select approaches which are used in this Early Action Protocol.",
+ "enablingApproachesTooltip": "In this section you should present the activities that are not sector specific or cross cutting for the operation. For example, work with partners, governments, etc. is usually listed under Enabling Approach 1: Coordination and Partnership. The IFRC support costs to the implementation of the simplified should be reflected under Enabling Action 2: Secretariat Services, while any activities or costs related to the National Society, including volunteer insurance, volunteer visibility, contributions to salaries should be listed here under Enabling Approach 3: National Society Development."
+ }
+}
\ No newline at end of file
diff --git a/app/src/views/SimplifiedEapForm/EnablingApproaches/index.tsx b/app/src/views/SimplifiedEapForm/EnablingApproaches/index.tsx
new file mode 100644
index 0000000000..9cbfaf0c6f
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/EnablingApproaches/index.tsx
@@ -0,0 +1,138 @@
+import {
+ useCallback,
+ useMemo,
+} from 'react';
+import {
+ Checklist,
+ Container,
+ InputSection,
+ ListView,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import { stringValueSelector } from '@ifrc-go/ui/utils';
+import { listToMap } from '@togglecorp/fujs';
+import {
+ type EntriesAsList,
+ type Error,
+ getErrorObject,
+ useFormArray,
+} from '@togglecorp/toggle-form';
+
+import NonFieldError from '#components/NonFieldError';
+import { type components } from '#generated/types';
+import useGlobalEnums from '#hooks/domain/useGlobalEnums';
+
+import { type PartialSimplifiedEapType } from '../schema';
+import ApproachesInput from './ApproachesInput';
+
+import i18n from './i18n.json';
+
+type EapApproach = components['schemas']['EapApproachEnumKey'];
+type EapApproachOption = components['schemas']['EapApproachEnum'];
+
+type EnablingApproachesFormFields = NonNullable[number];
+
+interface Props {
+ value: PartialSimplifiedEapType;
+ error: Error | undefined;
+ disabled?: boolean;
+ setFieldValue: (...entries: EntriesAsList) => void;
+}
+
+function approachesKeySelector(option: EapApproachOption) {
+ return option.key;
+}
+function EnablingApproaches(props: Props) {
+ const {
+ value,
+ error: formError,
+ disabled,
+ setFieldValue,
+ } = props;
+
+ const error = getErrorObject(formError);
+ const strings = useTranslation(i18n);
+
+ const { eap_approach: eapApproachOptions } = useGlobalEnums();
+
+ const eapApproachLabelMapping = useMemo(() => (
+ listToMap(
+ eapApproachOptions,
+ ({ key }) => key,
+ ({ value: label }) => label,
+ )
+ ), [eapApproachOptions]);
+
+ const {
+ setValue: onApproachChange,
+ removeValue: onApproachRemove,
+ } = useFormArray<'enable_approaches', EnablingApproachesFormFields>(
+ 'enable_approaches',
+ setFieldValue,
+ );
+
+ const handleApproachChecklistChange = useCallback((approaches: EapApproach[] | undefined) => {
+ setFieldValue((previousValue: EnablingApproachesFormFields[] | undefined) => {
+ const previousValueMapping = listToMap(
+ previousValue,
+ ({ approach }) => approach,
+ );
+
+ return approaches?.map((approach) => {
+ const prevapproachValue = previousValueMapping?.[approach];
+
+ if (prevapproachValue) {
+ return prevapproachValue;
+ }
+
+ return {
+ approach,
+ } satisfies EnablingApproachesFormFields;
+ });
+ }, 'enable_approaches');
+ }, [setFieldValue]);
+
+ const selectedApproaches = value?.enable_approaches?.map(({ approach }) => approach);
+
+ return (
+
+
+
+
+
+
+ {value?.enable_approaches?.map((approach, index) => (
+
+ ))}
+
+
+ );
+}
+
+export default EnablingApproaches;
diff --git a/app/src/views/SimplifiedEapForm/IndicatorInput/index.tsx b/app/src/views/SimplifiedEapForm/IndicatorInput/index.tsx
new file mode 100644
index 0000000000..a0b49bb9db
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/IndicatorInput/index.tsx
@@ -0,0 +1,98 @@
+import { DeleteBinTwoLineIcon } from '@ifrc-go/icons';
+import {
+ Button,
+ InlineLayout,
+ ListView,
+ NumberInput,
+ TextInput,
+} from '@ifrc-go/ui';
+import {
+ type ArrayError,
+ getErrorObject,
+ type PartialForm,
+ type SetValueArg,
+ useFormObject,
+} from '@togglecorp/toggle-form';
+
+import { type components } from '#generated/types';
+
+type Indicator = components['schemas']['Indicator'] & { client_id: string };
+type IndicatorFormFields = PartialForm
+
+const defaultIndicatorValue: IndicatorFormFields = {
+ client_id: '-1',
+};
+
+interface Props {
+ value: IndicatorFormFields;
+ error: ArrayError | undefined;
+ onChange: (value: SetValueArg, index: number) => void;
+ onRemove: (index: number) => void;
+ index: number;
+ disabled?: boolean;
+ readOnly?: boolean;
+}
+
+function IndicatorInput(props: Props) {
+ const {
+ error: errorFromProps,
+ readOnly,
+ onChange,
+ value,
+ index,
+ onRemove,
+ disabled,
+ } = props;
+
+ const onFieldChange = useFormObject(index, onChange, defaultIndicatorValue);
+
+ const error = (value && value.client_id && errorFromProps)
+ ? getErrorObject(errorFromProps?.[value.client_id])
+ : undefined;
+
+ return (
+
+
+
+ )}
+ spacing="sm"
+ >
+
+
+
+
+
+ );
+}
+
+export default IndicatorInput;
diff --git a/app/src/views/SimplifiedEapForm/Overview/i18n.json b/app/src/views/SimplifiedEapForm/Overview/i18n.json
new file mode 100644
index 0000000000..2b71f756ef
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/Overview/i18n.json
@@ -0,0 +1,32 @@
+{
+ "namespace": "simplifiedEapForm",
+ "strings": {
+ "detailsHeading": "Details",
+ "nationalSociety": "National Society (NS)",
+ "nationalSocietyDescription": "Select National Society that is planning to apply for the EAP",
+ "country": "Country",
+ "countryDescription": "The country will be pre-populated based on the NS selection, but can be adapted as needed.",
+ "disasterType": "Disaster Type",
+ "disasterTypeDescription": "Select the disaster type for which the EAP is needed.",
+ "uploadCoverImage": "Cover Photo",
+ "uploadCoverImageDescription": "Upload a image for the cover page of the publicly published DREF application.",
+ "timeframe": "sEAP Timeframe",
+ "timeframeDescription": "A simplified EAP has a timeframe of two years (unless the early actions are activiated)",
+ "contacts": "Contacts",
+ "nationalHeader": "National",
+ "delegationHeader": "Delegation",
+ "regionalHeader": "Regional and global",
+ "nSContact": "National Society Contact",
+ "nSContactDescription": "National Society contact responsible for the EAP process",
+ "partnerNS": "Partner NS",
+ "partnerNSDescription": "partner National Society contact",
+ "focalPoint": "IFRC Delegation AA Focal Point",
+ "delegation": "IFRC Head of Delegation",
+ "drefFocalPoint": "DREF Focal Point",
+ "drefFocalPointDescription": "The DREF contact person fro IFRC",
+ "regionalFocalPoint": "IFRC Regional AA Focal Point",
+ "regionalManager": "IFRC Regional Ops Manager",
+ "regionalHead": "IFRC Regional Head of DCC",
+ "regionalCoordinator": "IFRC Global Ops Coordinator"
+ }
+}
diff --git a/app/src/views/SimplifiedEapForm/Overview/index.tsx b/app/src/views/SimplifiedEapForm/Overview/index.tsx
new file mode 100644
index 0000000000..b4ce61e0a5
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/Overview/index.tsx
@@ -0,0 +1,259 @@
+import {
+ Container,
+ InputSection,
+ ListView,
+ NumberInput,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import {
+ type EntriesAsList,
+ type Error,
+ getErrorObject,
+ type PartialForm,
+} from '@togglecorp/toggle-form';
+
+import CountrySelectInput from '#components/domain/CountrySelectInput';
+import DisasterTypeSelectInput from '#components/domain/DisasterTypeSelectInput';
+import ImageWithCaptionInput from '#components/domain/ImageWithCaptionInput';
+import NationalSocietySelectInput from '#components/domain/NationalSocietySelectInput';
+import TabPage from '#components/TabPage';
+import { type GoApiBody } from '#utils/restRequest';
+
+import ContactInputsSection from '../ContactInputsSection';
+import { type PartialSimplifiedEapType } from '../schema';
+
+import i18n from './i18n.json';
+
+type EapRegisterRequestBody = GoApiBody<'/api/v2/eap-registration/', 'POST'>;
+type FormFields = PartialForm;
+
+interface Props {
+ value: PartialSimplifiedEapType;
+ setFieldValue: (...entries: EntriesAsList) => void;
+ error: Error | undefined;
+ disabled?: boolean;
+ fileIdToUrlMap: Record;
+ setFileIdToUrlMap?: React.Dispatch>>;
+ eapRegistrationDetail?: FormFields;
+}
+
+function Overview(props: Props) {
+ const {
+ value,
+ setFieldValue,
+ error: formError,
+ disabled,
+ fileIdToUrlMap,
+ setFileIdToUrlMap,
+ eapRegistrationDetail,
+ } = props;
+
+ const strings = useTranslation(i18n);
+ const error = getErrorObject(formError);
+
+ const noop = () => {};
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default Overview;
diff --git a/app/src/views/SimplifiedEapForm/PlannedOperations/OperationsInput/i18n.json b/app/src/views/SimplifiedEapForm/PlannedOperations/OperationsInput/i18n.json
new file mode 100644
index 0000000000..fc2c77e0c1
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/PlannedOperations/OperationsInput/i18n.json
@@ -0,0 +1,21 @@
+{
+ "namespace": "simplifiedEapForm",
+ "strings": {
+ "operationPeopleTargeted": "People Targeted",
+ "operationBudget": "Budget",
+ "operationApCode": "AP Code",
+ "operationRemoveButton": "Remove",
+ "operationAddActivityButton": "Add activity",
+ "operationReadinessActivities": "Readiness Activities",
+ "operationReadinessActivitiesTooltip": "Readiness activities are done year on year to ensure that the National Society is ready to conduct the early actions. These are activities that will happen irrespective of an activation. Readiness activities may include refresher training, coordination meetings with government, readiness meetings, simulations, etc. Under readiness they can include any ongoing costs and services (human resources and logistics) that are deemed indispensable for subsequent trigger-based early action activities. If, during the simplified EAP development process the National Society finds some areas for improvement to deliver on their selected early actions, these could be addressed with activities included under readiness.",
+ "operationPrepositioningActivities": "Pre-positioning Activities",
+ "operationPrepositioningActivitiesTooltip": "The National Society should preposition the materials needed to undertake the early action, especially those that may require a longer procurement process. For example, prepositioned stocks could include shelter kits (for house reinforcement), sandbags (for protecting infrastructure), or tarpaulins (for protecting water sources), etc. Food, medicine and other items with a shelf life of less than two years are not eligible as pre-positioning, they will have to be procured as part of the early actions. Pre-positioning activities are one-off and done in the first year following approval of the simplified EAP.",
+ "operationEarlyActionActivities": "Early Action Activities",
+ "operationEarlyActionActivitiesTooltip": "Early action activities are implemented once a trigger is reached and before the impact of the hazard. Early actions seek to reduce or mitigate the impact of the hazard. Consider selecting only a few early actions, especially for sudden onset events, as they will have to be implemented within a short timeframe. The early actions will be unique to each hazard and context, but may be activities such as evacuation of at-risk communities and/or livestock, early harvest of crops, cash transfer, shelter strengthening, provision of water treatment, hygiene kits or mosquito nets, etc. For more examples of early action activities, visit the {earlyActionDatabaseLink} on the Anticipation Hub.",
+ "earlyActionDatabaseLink": "early action database",
+ "operationNoActivitiesMessage": "No activities added yet",
+ "operationIndicators": "Indicators",
+ "operationAddIndicators": "Add Indicators",
+ "operationNoindicatorsYet": "No indicators yet"
+ }
+}
diff --git a/app/src/views/SimplifiedEapForm/PlannedOperations/OperationsInput/index.tsx b/app/src/views/SimplifiedEapForm/PlannedOperations/OperationsInput/index.tsx
new file mode 100644
index 0000000000..fa341f2c12
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/PlannedOperations/OperationsInput/index.tsx
@@ -0,0 +1,425 @@
+import { useCallback } from 'react';
+import {
+ AddLineIcon,
+ DeleteBinTwoLineIcon,
+} from '@ifrc-go/icons';
+import {
+ Button,
+ Container,
+ ExpandableContainer,
+ InfoPopup,
+ ListView,
+ NumberInput,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import { resolveToComponent } from '@ifrc-go/ui/utils';
+import {
+ isNotDefined,
+ randomString,
+} from '@togglecorp/fujs';
+import {
+ type ArrayError,
+ getErrorObject,
+ type SetValueArg,
+ useFormArray,
+ useFormObject,
+} from '@togglecorp/toggle-form';
+
+import OperationActivityInput from '#components/domain/OperationActivityInput';
+import Link from '#components/Link';
+import NonFieldError from '#components/NonFieldError';
+
+import IndicatorInput from '../../IndicatorInput';
+import { type PartialSimplifiedEapType } from '../../schema';
+
+import i18n from './i18n.json';
+
+type PlannedOperationFormFields = NonNullable[number];
+type EarlyActionFormFields = NonNullable[number];
+type PrepositioningFormFields = NonNullable[number];
+type ReadinessFormFields = NonNullable[number];
+type IndicatorFormFields = NonNullable[number];
+
+const defaultOperationValue: PlannedOperationFormFields = {
+ sector: 101,
+};
+
+interface Props {
+ value: PlannedOperationFormFields;
+ error: ArrayError | undefined;
+ onChange: (value: SetValueArg, index: number) => void;
+ onRemove: (index: number) => void;
+ index: number;
+ disabled?: boolean;
+ operationTitle?: React.ReactNode;
+}
+
+function OperationsBySectorInput(props: Props) {
+ const {
+ error: errorFromProps,
+ onChange,
+ value,
+ index,
+ onRemove,
+ disabled,
+ operationTitle,
+ } = props;
+
+ const strings = useTranslation(i18n);
+ const onFieldChange = useFormObject(index, onChange, defaultOperationValue);
+
+ const error = (value && value.sector && errorFromProps)
+ ? getErrorObject(errorFromProps?.[value.sector])
+ : undefined;
+
+ const {
+ setValue: onEarlyActionChange,
+ removeValue: onEarlyActionRemove,
+ } = useFormArray<'early_action_activities', EarlyActionFormFields>(
+ 'early_action_activities' as const,
+ onFieldChange,
+ );
+ const {
+ setValue: onPrepositioningChange,
+ removeValue: onPrepositioningRemove,
+ } = useFormArray<'prepositioning_activities', PrepositioningFormFields>(
+ 'prepositioning_activities' as const,
+ onFieldChange,
+ );
+ const {
+ setValue: onReadinessChange,
+ removeValue: onReadinessRemove,
+ } = useFormArray<'readiness_activities', ReadinessFormFields>(
+ 'readiness_activities' as const,
+ onFieldChange,
+ );
+ const {
+ setValue: onIndicatorChange,
+ removeValue: onIndicatorRemove,
+ } = useFormArray<'indicators', IndicatorFormFields>(
+ 'indicators' as const,
+ onFieldChange,
+ );
+
+ const handleEarlyActionAddButtonClick = useCallback(
+ () => {
+ const newActionItem: EarlyActionFormFields = {
+ client_id: randomString(),
+ };
+
+ onFieldChange(
+ (oldValue: EarlyActionFormFields[] | undefined) => (
+ [...(oldValue ?? []), newActionItem]
+ ),
+ 'early_action_activities' as const,
+ );
+ },
+ [onFieldChange],
+ );
+
+ const handlePrepositioningAddButtonClick = useCallback(
+ () => {
+ const newActionItem: PrepositioningFormFields = {
+ client_id: randomString(),
+ };
+
+ onFieldChange(
+ (oldValue: PrepositioningFormFields[] | undefined) => (
+ [...(oldValue ?? []), newActionItem]
+ ),
+ 'prepositioning_activities' as const,
+ );
+ },
+ [onFieldChange],
+ );
+
+ const handleReadinessAddButtonClick = useCallback(
+ () => {
+ const newActionItem: ReadinessFormFields = {
+ client_id: randomString(),
+ };
+
+ onFieldChange(
+ (oldValue: ReadinessFormFields[] | undefined) => (
+ [...(oldValue ?? []), newActionItem]
+ ),
+ 'readiness_activities' as const,
+ );
+ },
+ [onFieldChange],
+ );
+
+ const handleIndicatorAddButtonClick = useCallback(
+ () => {
+ const newIndicator: IndicatorFormFields = {
+ client_id: randomString(),
+ };
+
+ onFieldChange(
+ (oldValue: IndicatorFormFields[] | undefined) => (
+ [...(oldValue ?? []), newIndicator]
+ ),
+ 'indicators' as const,
+ );
+ },
+ [onFieldChange],
+ );
+
+ return (
+
+
+
+ )}
+ withPadding
+ withBackground
+ initiallyExpanded
+ withHeaderBorder
+ // FIXME: add non field error and error indicator
+ >
+
+
+
+
+
+
+
+ }
+ >
+ {strings.operationAddIndicators}
+
+ )}
+ empty={isNotDefined(value.indicators) || value.indicators.length === 0}
+ emptyMessage={strings.operationNoindicatorsYet}
+ >
+
+ {value.indicators?.map((indicator, i) => (
+
+ ))}
+
+
+
+ {strings.operationReadinessActivities}
+
+ >
+ )}
+ headingLevel={5}
+ footerActions={(
+ }
+ >
+ {strings.operationAddActivityButton}
+
+ )}
+ withCompactMessage
+ empty={isNotDefined(value.readiness_activities)
+ || value.readiness_activities.length === 0}
+ emptyMessage={strings.operationNoActivitiesMessage}
+ headerDescription={(
+
+ )}
+ >
+
+ {value?.readiness_activities?.map((activity, i) => (
+
+ ))}
+
+
+
+ {strings.operationPrepositioningActivities}
+
+ >
+ )}
+ headingLevel={5}
+ footerActions={(
+ }
+ >
+ {strings.operationAddActivityButton}
+
+ )}
+ withCompactMessage
+ empty={isNotDefined(value.prepositioning_activities)
+ || value.prepositioning_activities.length === 0}
+ emptyMessage={strings.operationNoActivitiesMessage}
+ headerDescription={(
+
+ )}
+ >
+
+ {value?.prepositioning_activities?.map((activity, i) => (
+
+ ))}
+
+
+
+ {strings.operationEarlyActionActivities}
+
+ {strings.earlyActionDatabaseLink}
+
+ ),
+ },
+ ))}
+ />
+ >
+ )}
+ headingLevel={5}
+ footerActions={(
+ }
+ >
+ {strings.operationAddActivityButton}
+
+ )}
+ withCompactMessage
+ empty={isNotDefined(value.early_action_activities)
+ || value.early_action_activities.length === 0}
+ emptyMessage={strings.operationNoActivitiesMessage}
+ headerDescription={(
+
+ )}
+ >
+
+ {value?.early_action_activities?.map((activity, i) => (
+
+ ))}
+
+
+
+
+
+ );
+}
+
+export default OperationsBySectorInput;
diff --git a/app/src/views/SimplifiedEapForm/PlannedOperations/i18n.json b/app/src/views/SimplifiedEapForm/PlannedOperations/i18n.json
new file mode 100644
index 0000000000..7dfd34a52e
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/PlannedOperations/i18n.json
@@ -0,0 +1,8 @@
+{
+ "namespace": "simplifiedEapForm",
+ "strings": {
+ "plannedOperationsTitle": "Planned Operations",
+ "plannedOperationsDescription": "Select sectors which are used in this Early Action Protocol.",
+ "plannedOperationsTooltipDescription": "In this section the National Society should reflect the sectors they plan to work on and delete the sectors that are not needed. We recommend keeping the plan simple, focus on key deliverables and not on all the steps that need to be taken to reach those deliverables. In the budget template the National Society can be much more specific and list all the activities that the plan to undertake (more details regarding the budget are provided in the ‘Budget’ section."
+ }
+}
\ No newline at end of file
diff --git a/app/src/views/SimplifiedEapForm/PlannedOperations/index.tsx b/app/src/views/SimplifiedEapForm/PlannedOperations/index.tsx
new file mode 100644
index 0000000000..b4699f71f7
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/PlannedOperations/index.tsx
@@ -0,0 +1,141 @@
+import {
+ useCallback,
+ useMemo,
+} from 'react';
+import {
+ Checklist,
+ Container,
+ InputSection,
+ ListView,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import { stringValueSelector } from '@ifrc-go/ui/utils';
+import { listToMap } from '@togglecorp/fujs';
+import {
+ type EntriesAsList,
+ type Error,
+ getErrorObject,
+ useFormArray,
+} from '@togglecorp/toggle-form';
+
+import NonFieldError from '#components/NonFieldError';
+import TabPage from '#components/TabPage';
+import { type components } from '#generated/types';
+import useGlobalEnums from '#hooks/domain/useGlobalEnums';
+
+import { type PartialSimplifiedEapType } from '../schema';
+import OperationInput from './OperationsInput';
+
+import i18n from './i18n.json';
+
+type EapSector = components['schemas']['EapSectorEnumKey'];
+type EapSectorOption = components['schemas']['EapSectorEnum'];
+
+type PlannedOperationFormFields = NonNullable[number];
+function sectorKeySelector(option: EapSectorOption) {
+ return option.key;
+}
+
+interface Props {
+ value: PartialSimplifiedEapType;
+ error: Error | undefined;
+ disabled?: boolean;
+ setFieldValue: (...entries: EntriesAsList) => void;
+}
+
+function PlannedOperations(props: Props) {
+ const {
+ value,
+ error: formError,
+ disabled,
+ setFieldValue,
+ } = props;
+
+ const error = getErrorObject(formError);
+ const strings = useTranslation(i18n);
+ const { eap_sector: eapSectorOptions } = useGlobalEnums();
+
+ const eapSectorLabelMapping = useMemo(() => (
+ listToMap(
+ eapSectorOptions,
+ ({ key }) => key,
+ ({ value: label }) => label,
+ )
+ ), [eapSectorOptions]);
+
+ const {
+ setValue: onOperationChange,
+ removeValue: onOperationRemove,
+ } = useFormArray<'planned_operations', PlannedOperationFormFields>(
+ 'planned_operations',
+ setFieldValue,
+ );
+
+ const handleOperationChecklistChange = useCallback((sectors: EapSector[] | undefined) => {
+ setFieldValue((previousValue: PlannedOperationFormFields[] | undefined) => {
+ const previousValueMapping = listToMap(
+ previousValue,
+ ({ sector }) => sector,
+ );
+
+ return sectors?.map((sector) => {
+ const prevSectorValue = previousValueMapping?.[sector];
+
+ if (prevSectorValue) {
+ return prevSectorValue;
+ }
+
+ return {
+ sector,
+ } satisfies PlannedOperationFormFields;
+ });
+ }, 'planned_operations');
+ }, [setFieldValue]);
+
+ const selectedSectors = value?.planned_operations?.map(({ sector }) => sector);
+
+ return (
+
+
+
+
+
+
+
+
+ {value?.planned_operations?.map((operation, index) => (
+
+ ))}
+
+
+
+ );
+}
+
+export default PlannedOperations;
diff --git a/app/src/views/SimplifiedEapForm/RiskAnalysis/i18n.json b/app/src/views/SimplifiedEapForm/RiskAnalysis/i18n.json
new file mode 100644
index 0000000000..03323819bc
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/RiskAnalysis/i18n.json
@@ -0,0 +1,30 @@
+{
+ "namespace": "simplifiedEapForm",
+ "strings": {
+ "riskHeading": "Risk Analysis",
+ "riskDescription": "Which hazard needs to be addressed by aniticipatory actions in the country as a priority? Provide an explanation on the reasons that the selected hazard was chosen for this simplified Eap, why it is a major problem in the country and the humanitarian impacts it has caused in the past. Describe the extent to which this hazard has produced and/or will produce negative impacts on lives, livelihoods, well-being and other development aspects.",
+ "riskTooltipDescription": "To fill out this section the National Society might look at past {drefOperationsLink}, {goPlatformLink}, {reliefwebLink}, {desinventarLink}, {eMdATLink}, {idmcLink}, government sites, newspapers…etc. The National Society might also add explanations based on their own knowledge about direct and indirect impacts. They might also explain why avoiding and/or reducing disaster impacts due to this hazard is a priority for the Government and the National Society.",
+ "drefOperationsLink": "DREF operations",
+ "goPlatformLink": "Go platform",
+ "reliefwebLink": "reliefweb",
+ "desinventarLink": "Desinventar",
+ "eMdATLink": "EM-DAT",
+ "idmcLink": "Internal Displacement Monitoring Centre",
+ "historicalImpact": "Prioritized hazard and its historical Impact",
+ "riskDescriptionLabel": "Description",
+ "riskProtocol": "Risk selected for the protocol",
+ "riskProtocolDescription": "Recognizing that the simplified EAP will not be able to addredd all potential risks and based on the analysis of past impacts, what are the main risks that the National Society has decided to focus on for this plan.",
+ "earlyActionSelection": "Early Action Selection",
+ "selectedEarlyAction": "Selected early actions",
+ "selectedEarlyActionDescription": "Explain how the early actions were selected and how they will mitigate the prioritized risk and bring about the intended outcome for the most vulnerable, at-risk individuals and communities.",
+ "selectedEarlyActionTooltipDescriptionOne": "The early actions selected by the National Society has to linked to the hazard’s impact. If ‘loss of key documentation (such as identifications, house deeds, birth certificates)’ is put forward as one of the prioritized impacts of a flood, then select an early action that will mitigate the risk e.g.: ‘distribution of dry bags before a flood’. In this section the National Society can explain the connection between the risk they want to mitigate and the early action they plan to implement. This chain of events is also called the ‘theory of change’. For more information on this process, please consult the {earlyActionsLink}",
+ "earlyActionsLink": "selection of early actions in the FbF Practitioners Manual.",
+ "selectedEarlyActionTooltipDescriptionTwo": "When deciding on the risk the National Society will prioritize, discuss the following:",
+ "selectedEarlyActionTooltipDescriptionListOne": "Does this risk impact on vulnerable people’s lives?",
+ "selectedEarlyActionTooltipDescriptionListTwo": "Is there something we can do to mitigate this risk in the given lead time?",
+ "selectedEarlyActionTooltipDescriptionListThree": "Does this prioritized risk align with the priorities of communities and local actors?",
+ "selectedEarlyActionTooltipDescriptionListFour": "Is it in the National Society mandate to do early actions to mitigate that risk?",
+ "selectedEarlyActionTooltipDescriptionListFive": "Does the National Society have the capacity and systems to conduct early actions within the lead time for the risk they want to prioritize?",
+ "selectedEarlyActionTooltipDescriptionThree": "For example, a major impact of a flood may be the destruction of public infrastructure, such as bridges or roads. However, the National Red Cross Red Crescent Societies may not have the mandate, or the capacity to do early actions for that impact in the short lead time from the forecast to the impact of the hazard."
+ }
+}
\ No newline at end of file
diff --git a/app/src/views/SimplifiedEapForm/RiskAnalysis/index.tsx b/app/src/views/SimplifiedEapForm/RiskAnalysis/index.tsx
new file mode 100644
index 0000000000..d4b87906c5
--- /dev/null
+++ b/app/src/views/SimplifiedEapForm/RiskAnalysis/index.tsx
@@ -0,0 +1,235 @@
+import {
+ Container,
+ InputSection,
+ ListView,
+ TextArea,
+} from '@ifrc-go/ui';
+import { useTranslation } from '@ifrc-go/ui/hooks';
+import { resolveToComponent } from '@ifrc-go/ui/utils';
+import {
+ type EntriesAsList,
+ type Error,
+ getErrorObject,
+} from '@togglecorp/toggle-form';
+
+import MultiImageWithCaptionInput from '#components/domain/MultiImageWithCaptionInput';
+import Link from '#components/Link';
+import TabPage from '#components/TabPage';
+
+import { type PartialSimplifiedEapType } from '../schema';
+
+import i18n from './i18n.json';
+
+interface Props {
+ value: PartialSimplifiedEapType;
+ setFieldValue: (...entries: EntriesAsList) => void;
+ error: Error | undefined;
+ disabled?: boolean;
+ fileIdToUrlMap: Record;
+ setFileIdToUrlMap?: React.Dispatch>>;
+}
+
+function RiskAnalysis(props: Props) {
+ const {
+ value,
+ setFieldValue,
+ error: formError,
+ disabled,
+ fileIdToUrlMap,
+ setFileIdToUrlMap,
+ } = props;
+
+ const error = getErrorObject(formError);
+ const strings = useTranslation(i18n);
+
+ return (
+
+
+
+
+ {strings.drefOperationsLink}
+
+ ),
+ goPlatformLink: (
+
+ {strings.goPlatformLink}
+
+ ),
+ reliefwebLink: (
+
+ {strings.reliefwebLink}
+
+ ),
+ desinventarLink: (
+
+ {strings.desinventarLink}
+
+ ),
+ eMdATLink: (
+
+ {strings.eMdATLink}
+
+ ),
+ idmcLink: (
+
+ {strings.idmcLink}
+
+ ),
+ },
+ ))}
+ withAsteriskOnTitle
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+