Skip to content

Commit 57765d0

Browse files
committed
feat: add eru capacity and readiness section
1 parent 7178bdd commit 57765d0

File tree

15 files changed

+738
-825
lines changed

15 files changed

+738
-825
lines changed

app/src/App/routes/SurgeRoutes.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ const emergencyResponseUnit = customWrapRoute({
122122
},
123123
});
124124

125-
const updateERUReadinessForm = customWrapRoute({
125+
const eruReadinessForm = customWrapRoute({
126126
parent: rootLayout,
127-
path: 'update-eru-readiness',
127+
path: 'eru-readiness',
128128
component: {
129129
render: () => import('#views/EruReadinessForm'),
130130
props: {},
@@ -1630,5 +1630,5 @@ export default {
16301630
activeSurgeDeployments,
16311631
rapidResponsePersonnel,
16321632
emergencyResponseUnit,
1633-
updateERUReadinessForm,
1633+
eruReadinessForm,
16341634
};

app/src/views/EruReadinessForm/index.tsx

+30-19
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import NonFieldError from '#components/NonFieldError';
3333
import Page from '#components/Page';
3434
import useGlobalEnums from '#hooks/domain/useGlobalEnums';
3535
import useAlertContext from '#hooks/useAlert';
36+
import useRouting from '#hooks/useRouting';
3637
import {
3738
type GoApiResponse,
3839
useLazyRequest,
@@ -55,6 +56,7 @@ type GlobalEnumsResponse = GoApiResponse<'/api/v2/global-enums/'>;
5556
type EruOwners = GoApiResponse<'/api/v2/eru_owner/mini/'>;
5657
type EruOwnerOption = NonNullable<EruOwners['results']>[number];
5758
type EruOption = NonNullable<GlobalEnumsResponse['deployments_eru_type']>[number];
59+
type EruResponse = GoApiResponse<'/api/v2/eru-readiness/{id}/'>;
5860

5961
function eruOwnerKeySelector(option: EruOwnerOption) {
6062
return option.id;
@@ -74,6 +76,7 @@ const defaultFormValues: FormType = {};
7476
export function Component() {
7577
const strings = useTranslation(i18n);
7678

79+
const { goBack } = useRouting();
7780
const alert = useAlertContext();
7881
const {
7982
deployments_eru_type: eruTypeOptions,
@@ -95,6 +98,24 @@ export function Component() {
9598

9699
const [eruReadinessId, setEruReadinessId] = useState<number | undefined>();
97100

101+
const patchEruFormValues = (response: EruResponse) => {
102+
setEruReadinessId(response.id);
103+
setValue({
104+
eru_owner: response.eru_owner_details.id,
105+
eru_types: response.eru_types.map((eruType) => ({
106+
...injectClientId(eruType),
107+
id: eruType.id,
108+
type: eruType.type,
109+
equipment_readiness: eruType.equipment_readiness,
110+
people_readiness: eruType.people_readiness,
111+
funding_readiness: eruType.funding_readiness,
112+
comment: eruType.comment,
113+
has_capacity_to_lead: eruType.has_capacity_to_lead,
114+
has_capacity_to_support: eruType.has_capacity_to_support,
115+
client_id: randomString(),
116+
})),
117+
});
118+
};
98119
const {
99120
trigger: updateEruReadiness,
100121
pending: updateEruReadinessPending,
@@ -103,11 +124,13 @@ export function Component() {
103124
method: 'PATCH',
104125
pathVariables: eruReadinessId ? { id: Number(eruReadinessId) } : undefined,
105126
body: (ctx: EruReadinessPatchBody) => ctx,
106-
onSuccess: () => {
127+
onSuccess: (response) => {
128+
patchEruFormValues(response);
107129
alert.show(
108130
strings.eruFormSuccessfullyUpdated,
109131
{ variant: 'success' },
110132
);
133+
goBack();
111134
},
112135
onFailure: ({
113136
value: {
@@ -135,11 +158,13 @@ export function Component() {
135158
url: '/api/v2/eru-readiness/',
136159
method: 'POST',
137160
body: (ctx: EruReadinessPostBody) => ctx,
138-
onSuccess: () => {
161+
onSuccess: (response) => {
162+
patchEruFormValues(response);
139163
alert.show(
140164
strings.eruFormSuccessfullyCreated,
141165
{ variant: 'success' },
142166
);
167+
goBack();
143168
},
144169
onFailure: ({
145170
value: {
@@ -185,23 +210,7 @@ export function Component() {
185210
onSuccess: (response) => {
186211
const results = response?.results ?? [];
187212
if (results?.length > 0) {
188-
const existingData = results[0];
189-
setEruReadinessId(existingData.id);
190-
setValue({
191-
eru_owner: existingData.eru_owner_details.id,
192-
eru_types: existingData.eru_types.map((eruType) => ({
193-
...injectClientId(eruType),
194-
id: eruType.id,
195-
type: eruType.type,
196-
equipment_readiness: eruType.equipment_readiness,
197-
people_readiness: eruType.people_readiness,
198-
funding_readiness: eruType.funding_readiness,
199-
comment: eruType.comment,
200-
has_capacity_to_lead: eruType.has_capacity_to_lead,
201-
has_capacity_to_support: eruType.has_capacity_to_support,
202-
client_id: randomString(),
203-
})),
204-
});
213+
patchEruFormValues(results[0]);
205214
} else {
206215
setEruReadinessId(undefined);
207216
setValue((oldValues) => ({
@@ -244,7 +253,9 @@ export function Component() {
244253

245254
const handleCancel = useCallback(() => {
246255
setValue(defaultFormValues);
256+
goBack();
247257
}, [
258+
goBack,
248259
setValue,
249260
]);
250261

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import { CheckboxCircleLineIcon } from '@ifrc-go/icons';
2+
import {
3+
Button,
4+
Container,
5+
Modal,
6+
TextOutput,
7+
} from '@ifrc-go/ui';
8+
import {
9+
useBooleanState,
10+
useTranslation,
11+
} from '@ifrc-go/ui/hooks';
12+
import { _cs } from '@togglecorp/fujs';
13+
14+
import { type GoApiResponse } from '#utils/restRequest';
15+
16+
import i18n from './i18n.json';
17+
import styles from './styles.module.css';
18+
19+
function getReadinessColor(rank: number | undefined) {
20+
if (rank === 1) return styles.greenIcon;
21+
if (rank === 2) return styles.yellowIcon;
22+
if (rank === 3) return styles.redIcon;
23+
return styles.grayIcon;
24+
}
25+
26+
type GetEruReadinessResponse = GoApiResponse<'/api/v2/eru-readiness/'>;
27+
28+
export type ReadinessList = Array<NonNullable<NonNullable<GetEruReadinessResponse['results']>[0]>['eru_types'][0] & {
29+
eruOwner: NonNullable<NonNullable<GetEruReadinessResponse['results']>[0]>['eru_owner_details'];
30+
updatedAt: NonNullable<NonNullable<GetEruReadinessResponse['results']>[0]>['updated_at'];
31+
}>
32+
33+
interface Props {
34+
className?: string;
35+
typeDisplay: string;
36+
nationalSocieties: string;
37+
fundingReadiness: number | undefined;
38+
equipmentReadiness: number | undefined;
39+
peopleReadiness: number | undefined;
40+
updatedAt: number | undefined;
41+
readinessList: ReadinessList;
42+
}
43+
44+
function EmergencyResponseUnitTypeCard(props: Props) {
45+
const {
46+
className,
47+
typeDisplay,
48+
nationalSocieties,
49+
fundingReadiness,
50+
equipmentReadiness,
51+
peopleReadiness,
52+
updatedAt,
53+
readinessList,
54+
} = props;
55+
56+
const strings = useTranslation(i18n);
57+
58+
const [
59+
showReadinessInfo,
60+
{
61+
setTrue: setShowReadinessInfoTrue,
62+
setFalse: setShowReadinessInfoFalse,
63+
},
64+
] = useBooleanState(false);
65+
66+
return (
67+
<Container
68+
className={_cs(styles.emergencyResponseTypeCard, className)}
69+
withInternalPadding
70+
withHeaderBorder
71+
heading={typeDisplay}
72+
headerDescription={(
73+
<TextOutput
74+
className={styles.lastUpdated}
75+
label={strings.emergencyResponseUnitOwnerCardLastUpdated}
76+
value={updatedAt}
77+
valueType="date"
78+
/>
79+
)}
80+
withFooterBorder
81+
footerActions={(
82+
<Button
83+
name={undefined}
84+
onClick={setShowReadinessInfoTrue}
85+
variant="tertiary"
86+
title="See readiness info"
87+
>
88+
See readiness info
89+
</Button>
90+
)}
91+
childrenContainerClassName={styles.content}
92+
>
93+
<Container
94+
footerContentClassName={styles.readinessContent}
95+
withFooterBorder
96+
footerContent={(
97+
<>
98+
<TextOutput
99+
className={styles.readiness}
100+
label="Equipment Readiness"
101+
value={(
102+
<CheckboxCircleLineIcon
103+
className={getReadinessColor(equipmentReadiness)}
104+
/>
105+
)}
106+
valueClassName={styles.icon}
107+
withoutLabelColon
108+
/>
109+
<div className={styles.separatorLeft} />
110+
<TextOutput
111+
className={styles.readiness}
112+
label="People Readiness"
113+
value={(
114+
<CheckboxCircleLineIcon
115+
className={getReadinessColor(peopleReadiness)}
116+
/>
117+
)}
118+
valueClassName={styles.icon}
119+
withoutLabelColon
120+
/>
121+
<div className={styles.separatorLeft} />
122+
<TextOutput
123+
className={styles.readiness}
124+
label="Funding Readiness"
125+
value={(
126+
<CheckboxCircleLineIcon
127+
className={getReadinessColor(fundingReadiness)}
128+
/>
129+
)}
130+
valueClassName={styles.icon}
131+
withoutLabelColon
132+
/>
133+
</>
134+
)}
135+
>
136+
<TextOutput
137+
className={styles.eruOwners}
138+
label="National Society"
139+
value={nationalSocieties}
140+
strongValue
141+
withoutLabelColon
142+
/>
143+
</Container>
144+
{showReadinessInfo && (
145+
<Modal
146+
className={styles.modal}
147+
heading={typeDisplay}
148+
headerDescription="Readiness Information by National Society"
149+
onClose={setShowReadinessInfoFalse}
150+
withHeaderBorder
151+
size="md"
152+
contentViewType="vertical"
153+
spacing="comfortable"
154+
>
155+
{readinessList?.map((readiness) => (
156+
<Container
157+
key={readiness.id}
158+
heading={
159+
readiness.eruOwner.national_society_country_details.society_name
160+
}
161+
headingLevel={5}
162+
withHeaderBorder
163+
childrenContainerClassName={styles.readinessContainer}
164+
>
165+
<TextOutput
166+
className={styles.readiness}
167+
label="Equipment Readiness"
168+
value={(
169+
<CheckboxCircleLineIcon
170+
className={getReadinessColor(readiness.equipment_readiness)}
171+
/>
172+
)}
173+
valueClassName={styles.icon}
174+
withoutLabelColon
175+
/>
176+
<div className={styles.separatorLeft} />
177+
<TextOutput
178+
className={styles.readiness}
179+
label="People Readiness"
180+
value={(
181+
<CheckboxCircleLineIcon
182+
className={getReadinessColor(readiness.people_readiness)}
183+
/>
184+
)}
185+
valueClassName={styles.icon}
186+
withoutLabelColon
187+
/>
188+
<div className={styles.separatorLeft} />
189+
<TextOutput
190+
className={styles.readiness}
191+
label="Funding Readiness"
192+
value={(
193+
<CheckboxCircleLineIcon
194+
className={getReadinessColor(readiness.funding_readiness)}
195+
/>
196+
)}
197+
valueClassName={styles.icon}
198+
withoutLabelColon
199+
/>
200+
</Container>
201+
))}
202+
</Modal>
203+
)}
204+
</Container>
205+
);
206+
}
207+
208+
export default EmergencyResponseUnitTypeCard;

0 commit comments

Comments
 (0)