Skip to content

Commit fe7a694

Browse files
shreeyash07frozenhelium
authored andcommitted
fix(eap): update in whole eap module according feedback
- add partners field in both eap forms - pass type props in every DateInput field - remove year option in timeFrame for readiness and prepositioning activities - make ap code form fields view only - update in translation strings - add missing validation for plannedOperations and enablingApproaches form fields - add eap apCode options for both forms - improve eap table action flow - add revise button for creating new version - add additional attachments modal in EAP table - make lead_timeframe_unit and lead_time field required - replace diff library to diff-match-patch - new DiffTextOutput components - update PrintableDescription and PrintableLabel components - add utility function for expected_submission_time field
1 parent 65b2cdc commit fe7a694

54 files changed

Lines changed: 1060 additions & 588 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@
5151
"@togglecorp/toggle-request": "^1.0.0-beta.3",
5252
"@turf/bbox": "^6.5.0",
5353
"@turf/buffer": "^6.5.0",
54+
"@types/diff-match-patch": "^1.0.36",
55+
"diff-match-patch": "^1.0.5",
5456
"exceljs": "^4.4.0",
55-
"diff": "^8.0.2",
5657
"file-saver": "^2.0.5",
5758
"html-to-image": "^1.11.11",
5859
"mapbox-gl": "^1.13.0",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"namespace": "admin2Input",
3+
"strings": {
4+
"heading": "Selected Areas",
5+
"buttonLabel": "Select Areas",
6+
"emptyMessage": "Admin Level 2 data is not available for this country."
7+
}
8+
}

app/src/components/domain/Admin2Input/index.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import {
1313
ListView,
1414
Modal,
1515
} from '@ifrc-go/ui';
16-
import { useBooleanState } from '@ifrc-go/ui/hooks';
16+
import {
17+
useBooleanState,
18+
useTranslation,
19+
} from '@ifrc-go/ui/hooks';
1720
import {
1821
isDefined,
1922
isNotDefined,
@@ -53,6 +56,8 @@ import {
5356

5457
import BaseMap from '../BaseMap';
5558

59+
import i18n from './i18n.json';
60+
5661
interface Props<NAME> {
5762
name: NAME;
5863
value: number[] | null | undefined;
@@ -72,6 +77,8 @@ function Admin2Input<const NAME>(props: Props<NAME>) {
7277
readOnly,
7378
} = props;
7479

80+
const strings = useTranslation(i18n);
81+
7582
const countryDetails = useCountry({ id: countryId });
7683
const iso3 = countryDetails?.iso3;
7784

@@ -91,6 +98,24 @@ function Admin2Input<const NAME>(props: Props<NAME>) {
9198
},
9299
});
93100

101+
// NOTE: To check if country has admin2 value or not
102+
const {
103+
response: admin2TestResponse,
104+
pending: admin2TestPending,
105+
} = useRequest({
106+
skip: isNotDefined(iso3),
107+
url: '/api/v2/admin2/',
108+
query: {
109+
admin1__country__iso3: iso3 ?? undefined,
110+
// NOTE: we just need 1 value to check
111+
limit: 1,
112+
},
113+
});
114+
115+
const hasAdmin2 = !admin2TestPending
116+
&& isDefined(admin2TestResponse)
117+
&& admin2TestResponse?.results.length > 0;
118+
94119
const { response: admin2Details } = useRequest({
95120
skip: isNotDefined(selectedCodesDebounced) || selectedCodesDebounced.length === 0,
96121
url: '/api/v2/admin2/',
@@ -287,20 +312,20 @@ function Admin2Input<const NAME>(props: Props<NAME>) {
287312
return (
288313
<ListView layout="block">
289314
<Container
290-
heading="Selected areas"
315+
heading={strings.heading}
291316
headingLevel={6}
292317
footer={(
293318
<Button
294319
name={undefined}
295320
onClick={setShowModalTrue}
296-
disabled={readOnly}
297-
// FIXME: use label from props
321+
disabled={readOnly || !hasAdmin2}
298322
>
299-
Select areas
323+
{strings.buttonLabel}
300324
</Button>
301325
)}
302326
withCompactMessage
303327
empty={!value || value.length === 0}
328+
emptyMessage={!hasAdmin2 ? strings.emptyMessage : undefined}
304329
withBorder
305330
withPadding
306331
>

app/src/components/domain/EapIndicatorInput/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useTranslation } from '@ifrc-go/ui/hooks';
1010
import {
1111
type ArrayError,
1212
getErrorObject,
13+
type LeafError,
1314
type PartialForm,
1415
type SetValueArg,
1516
useFormObject,
@@ -28,7 +29,7 @@ const defaultIndicatorValue: IndicatorFormFields = {
2829

2930
interface Props {
3031
value: IndicatorFormFields;
31-
error: ArrayError<IndicatorFormFields> | undefined;
32+
error: ArrayError<IndicatorFormFields> | LeafError | undefined;
3233
onChange: (value: SetValueArg<IndicatorFormFields>, index: number) => void;
3334
onRemove: (index: number) => void;
3435
index: number;
@@ -52,7 +53,7 @@ function EapIndicatorInput(props: Props) {
5253
const onFieldChange = useFormObject(index, onChange, defaultIndicatorValue);
5354

5455
const error = value && value.client_id && errorFromProps
55-
? getErrorObject(errorFromProps?.[value.client_id])
56+
? getErrorObject(getErrorObject(errorFromProps)?.[value.client_id])
5657
: undefined;
5758

5859
return (

app/src/components/domain/EapIndicatorInput/schema.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@ export type IndicatorFormFields = PartialForm<Indicator> & {
1616

1717
type IndicatorSchema = ObjectSchema<IndicatorFormFields>;
1818

19-
const schema: IndicatorSchema = {
19+
const schema = (isSubmit: boolean): IndicatorSchema => ({
2020
fields: (): ReturnType<IndicatorSchema['fields']> => ({
2121
client_id: {},
2222
id: { defaultValue: undefinedValue },
2323
title: {
2424
// FIXME: add validation for character limit
25-
required: true,
25+
required: isSubmit,
2626
requiredValidation: requiredStringCondition,
2727
},
2828
target: {
29-
required: true,
29+
required: isSubmit,
3030
validations: [positiveNumberCondition],
3131
},
3232
}),
33-
};
33+
});
3434

3535
export default schema;

app/src/components/domain/EapIndicatorListInput/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import {
1414
type ArrayError,
1515
getErrorObject,
16+
type LeafError,
1617
type SetValueArg,
1718
useFormArray,
1819
} from '@togglecorp/toggle-form';
@@ -32,7 +33,7 @@ interface Props<NAME> {
3233
name: NAME,
3334
value: IndicatorFormFields[] | undefined;
3435
onChange: (newValue: SetValueArg<IndicatorFormFields[]>, name: NAME) => void;
35-
error: ArrayError<IndicatorFormFields> | undefined;
36+
error: ArrayError<IndicatorFormFields> | LeafError | undefined;
3637
}
3738

3839
function EapIndicatorListInput<const NAME extends FormName>(props: Props<NAME>) {
@@ -113,7 +114,7 @@ function EapIndicatorListInput<const NAME extends FormName>(props: Props<NAME>)
113114
value={activity}
114115
onChange={onReadinessChange}
115116
onRemove={onReadinessRemove}
116-
error={getErrorObject(error?.readiness_activities)}
117+
error={getErrorObject(error)?.indicators}
117118
disabled={disabled}
118119
readOnly={readOnly}
119120
/>

app/src/components/domain/EapOperationActivityInput/index.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { useCallback } from 'react';
1+
import {
2+
useCallback,
3+
useMemo,
4+
} from 'react';
25
import { DeleteBinTwoLineIcon } from '@ifrc-go/icons';
36
import {
47
Checklist,
@@ -14,12 +17,14 @@ import {
1417
type ArrayError,
1518
getErrorObject,
1619
getErrorString,
20+
type LeafError,
1721
type SetValueArg,
1822
useFormObject,
1923
} from '@togglecorp/toggle-form';
2024

2125
import { type components } from '#generated/types';
2226
import useGlobalEnums from '#hooks/domain/useGlobalEnums';
27+
import { TIMEFRAME_YEAR } from '#utils/constants';
2328

2429
import { type OperationActivityFormFields } from './schema';
2530
import TimeSpanCheck from './TimeSpanCheck';
@@ -30,6 +35,8 @@ const defaultActivityValue: OperationActivityFormFields = {
3035
client_id: '-1',
3136
};
3237

38+
export type ActivityInputType = 'readiness_activities' | 'prepositioning_activities' | 'early_action_activities';
39+
3340
type TimeframeOption = components['schemas']['EapTimeframeEnum'];
3441

3542
function timeframeKeySelector(option: TimeframeOption) {
@@ -42,12 +49,14 @@ const timeValueKeySelector = (
4249

4350
interface Props {
4451
value: OperationActivityFormFields;
45-
error: ArrayError<OperationActivityFormFields> | undefined;
52+
error: ArrayError<OperationActivityFormFields> | LeafError | undefined;
4653
onChange: (value: SetValueArg<OperationActivityFormFields>, index: number) => void;
4754
onRemove: (index: number) => void;
4855
index: number;
4956
disabled?: boolean;
5057
readOnly?: boolean;
58+
59+
name: ActivityInputType;
5160
}
5261

5362
function EapOperationActivityInput(props: Props) {
@@ -59,6 +68,7 @@ function EapOperationActivityInput(props: Props) {
5968
onRemove,
6069
disabled,
6170
readOnly,
71+
name,
6272
} = props;
6373

6474
const strings = useTranslation(i18n);
@@ -73,9 +83,18 @@ function EapOperationActivityInput(props: Props) {
7383
const onFieldChange = useFormObject(index, onChange, defaultActivityValue);
7484

7585
const error = (value && value.client_id && errorFromProps)
76-
? getErrorObject(errorFromProps?.[value.client_id])
86+
? getErrorObject(getErrorObject(errorFromProps)?.[value.client_id])
7787
: undefined;
7888

89+
const eapTimeframeOption = useMemo(() => {
90+
if (name === 'early_action_activities') {
91+
return eap_timeframe?.filter((item) => item.key !== TIMEFRAME_YEAR);
92+
}
93+
return eap_timeframe;
94+
}, [eap_timeframe, name]);
95+
96+
const eapTimeFrameReadOnly = name === 'readiness_activities' || name === 'prepositioning_activities';
97+
7998
const getTimeValueOptions = useCallback(
8099
(timeframe?: number) => {
81100
switch (timeframe) {
@@ -143,10 +162,10 @@ function EapOperationActivityInput(props: Props) {
143162
onChange={handleTimeframeChange}
144163
keySelector={timeframeKeySelector}
145164
labelSelector={stringValueSelector}
146-
options={eap_timeframe}
165+
options={eapTimeframeOption}
147166
disabled={disabled}
148167
error={error?.timeframe}
149-
readOnly={readOnly}
168+
readOnly={readOnly || eapTimeFrameReadOnly}
150169
/>
151170
{value?.timeframe && (
152171
<Checklist

app/src/components/domain/EapOperationActivityInput/schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ export type OperationActivityFormFields = PartialForm<OperationActivity> & {
1515

1616
type OperationActivitySchema = ObjectSchema<OperationActivityFormFields>;
1717

18-
const schema: OperationActivitySchema = {
18+
const schema = (isSubmit: boolean): OperationActivitySchema => ({
1919
fields: (): ReturnType<OperationActivitySchema['fields']> => ({
2020
client_id: {},
2121
id: { defaultValue: undefinedValue },
2222
activity: {
2323
// FIXME: add validation for character limit
24-
required: true,
24+
required: isSubmit,
2525
requiredValidation: requiredStringCondition,
2626
},
2727
time_value: {},
2828
timeframe: {},
2929
}),
30-
};
30+
});
3131

3232
export default schema;

app/src/components/domain/EapOperationActivityListInput/index.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,31 @@ import {
1818
import {
1919
type ArrayError,
2020
getErrorObject,
21+
type LeafError,
2122
type SetValueArg,
2223
useFormArray,
2324
} from '@togglecorp/toggle-form';
2425

25-
import EapOperationActivityInput from '#components/domain/EapOperationActivityInput';
26+
import EapOperationActivityInput, { type ActivityInputType } from '#components/domain/EapOperationActivityInput';
2627
import { type OperationActivityFormFields } from '#components/domain/EapOperationActivityInput/schema';
2728
import ExplanatoryNote from '#components/ExplanatoryNote';
2829
import Link from '#components/Link';
2930
import NonFieldError from '#components/NonFieldError';
31+
import { TIMEFRAME_YEAR } from '#utils/constants';
3032

3133
import i18n from './i18n.json';
3234

33-
type FormName = 'readiness_activities' | 'prepositioning_activities' | 'early_action_activities';
34-
3535
interface Props<NAME> {
3636
disabled?: boolean;
3737
readOnly?: boolean;
3838

3939
name: NAME,
4040
value: OperationActivityFormFields[] | undefined;
4141
onChange: (newValue: SetValueArg<OperationActivityFormFields[]>, name: NAME) => void;
42-
error: ArrayError<OperationActivityFormFields> | undefined;
42+
error: ArrayError<OperationActivityFormFields> | LeafError | undefined;
4343
}
4444

45-
function EapOperationActivityListInput<const NAME extends FormName>(props: Props<NAME>) {
45+
function EapOperationActivityListInput<const NAME extends ActivityInputType>(props: Props<NAME>) {
4646
const {
4747
disabled,
4848
readOnly,
@@ -65,8 +65,12 @@ function EapOperationActivityListInput<const NAME extends FormName>(props: Props
6565

6666
const handleReadinessAddButtonClick = useCallback(
6767
() => {
68+
const timeframeValue = name === 'readiness_activities' || name === 'prepositioning_activities'
69+
? TIMEFRAME_YEAR
70+
: undefined;
6871
const newActionItem: OperationActivityFormFields = {
6972
client_id: randomString(),
73+
timeframe: timeframeValue,
7074
};
7175

7276
onChange(
@@ -162,12 +166,13 @@ function EapOperationActivityListInput<const NAME extends FormName>(props: Props
162166
>
163167
{value?.map((activity, i) => (
164168
<EapOperationActivityInput
169+
name={name}
165170
key={activity.client_id}
166171
index={i}
167172
value={activity}
168173
onChange={onReadinessChange}
169174
onRemove={onReadinessRemove}
170-
error={getErrorObject(error?.readiness_activities)}
175+
error={getErrorObject(error)?.[name]}
171176
disabled={disabled}
172177
readOnly={readOnly}
173178
/>

0 commit comments

Comments
 (0)