Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@
"@togglecorp/toggle-request": "^1.0.0-beta.3",
"@turf/bbox": "^6.5.0",
"@turf/buffer": "^6.5.0",
"@types/diff-match-patch": "^1.0.36",
"diff-match-patch": "^1.0.5",
"exceljs": "^4.4.0",
"diff": "^8.0.2",
"file-saver": "^2.0.5",
"html-to-image": "^1.11.11",
"mapbox-gl": "^1.13.0",
Expand Down
8 changes: 8 additions & 0 deletions app/src/components/domain/Admin2Input/i18n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"namespace": "admin2Input",
"strings": {
"heading": "Selected Areas",
"buttonLabel": "Select Areas",
"emptyMessage": "Admin Level 2 data is not available for this country."
}
}
35 changes: 30 additions & 5 deletions app/src/components/domain/Admin2Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
ListView,
Modal,
} from '@ifrc-go/ui';
import { useBooleanState } from '@ifrc-go/ui/hooks';
import {
useBooleanState,
useTranslation,
} from '@ifrc-go/ui/hooks';
import {
isDefined,
isNotDefined,
Expand Down Expand Up @@ -53,6 +56,8 @@ import {

import BaseMap from '../BaseMap';

import i18n from './i18n.json';

interface Props<NAME> {
name: NAME;
value: number[] | null | undefined;
Expand All @@ -72,6 +77,8 @@ function Admin2Input<const NAME>(props: Props<NAME>) {
readOnly,
} = props;

const strings = useTranslation(i18n);

const countryDetails = useCountry({ id: countryId });
const iso3 = countryDetails?.iso3;

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

// NOTE: To check if country has admin2 value or not
const {
response: admin2TestResponse,
pending: admin2TestPending,
} = useRequest({
skip: isNotDefined(iso3),
url: '/api/v2/admin2/',
query: {
admin1__country__iso3: iso3 ?? undefined,
// NOTE: we just need 1 value to check
limit: 1,
},
});

const hasAdmin2 = !admin2TestPending
&& isDefined(admin2TestResponse)
&& admin2TestResponse?.results.length > 0;

const { response: admin2Details } = useRequest({
skip: isNotDefined(selectedCodesDebounced) || selectedCodesDebounced.length === 0,
url: '/api/v2/admin2/',
Expand Down Expand Up @@ -287,20 +312,20 @@ function Admin2Input<const NAME>(props: Props<NAME>) {
return (
<ListView layout="block">
<Container
heading="Selected areas"
heading={strings.heading}
headingLevel={6}
footer={(
<Button
name={undefined}
onClick={setShowModalTrue}
disabled={readOnly}
// FIXME: use label from props
disabled={readOnly || !hasAdmin2}
>
Select areas
{strings.buttonLabel}
</Button>
)}
withCompactMessage
empty={!value || value.length === 0}
emptyMessage={!hasAdmin2 ? strings.emptyMessage : undefined}
withBorder
withPadding
>
Expand Down
5 changes: 3 additions & 2 deletions app/src/components/domain/EapIndicatorInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useTranslation } from '@ifrc-go/ui/hooks';
import {
type ArrayError,
getErrorObject,
type LeafError,
type PartialForm,
type SetValueArg,
useFormObject,
Expand All @@ -28,7 +29,7 @@ const defaultIndicatorValue: IndicatorFormFields = {

interface Props {
value: IndicatorFormFields;
error: ArrayError<IndicatorFormFields> | undefined;
error: ArrayError<IndicatorFormFields> | LeafError | undefined;
onChange: (value: SetValueArg<IndicatorFormFields>, index: number) => void;
onRemove: (index: number) => void;
index: number;
Expand All @@ -52,7 +53,7 @@ function EapIndicatorInput(props: Props) {
const onFieldChange = useFormObject(index, onChange, defaultIndicatorValue);

const error = value && value.client_id && errorFromProps
? getErrorObject(errorFromProps?.[value.client_id])
? getErrorObject(getErrorObject(errorFromProps)?.[value.client_id])
: undefined;

return (
Expand Down
8 changes: 4 additions & 4 deletions app/src/components/domain/EapIndicatorInput/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ export type IndicatorFormFields = PartialForm<Indicator> & {

type IndicatorSchema = ObjectSchema<IndicatorFormFields>;

const schema: IndicatorSchema = {
const schema = (isSubmit: boolean): IndicatorSchema => ({
fields: (): ReturnType<IndicatorSchema['fields']> => ({
client_id: {},
id: { defaultValue: undefinedValue },
title: {
// FIXME: add validation for character limit
required: true,
required: isSubmit,
requiredValidation: requiredStringCondition,
},
target: {
required: true,
required: isSubmit,
validations: [positiveNumberCondition],
},
}),
};
});

export default schema;
5 changes: 3 additions & 2 deletions app/src/components/domain/EapIndicatorListInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import {
type ArrayError,
getErrorObject,
type LeafError,
type SetValueArg,
useFormArray,
} from '@togglecorp/toggle-form';
Expand All @@ -32,7 +33,7 @@ interface Props<NAME> {
name: NAME,
value: IndicatorFormFields[] | undefined;
onChange: (newValue: SetValueArg<IndicatorFormFields[]>, name: NAME) => void;
error: ArrayError<IndicatorFormFields> | undefined;
error: ArrayError<IndicatorFormFields> | LeafError | undefined;
}

function EapIndicatorListInput<const NAME extends FormName>(props: Props<NAME>) {
Expand Down Expand Up @@ -113,7 +114,7 @@ function EapIndicatorListInput<const NAME extends FormName>(props: Props<NAME>)
value={activity}
onChange={onReadinessChange}
onRemove={onReadinessRemove}
error={getErrorObject(error?.readiness_activities)}
error={getErrorObject(error)?.indicators}
disabled={disabled}
readOnly={readOnly}
/>
Expand Down
29 changes: 24 additions & 5 deletions app/src/components/domain/EapOperationActivityInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { useCallback } from 'react';
import {
useCallback,
useMemo,
} from 'react';
import { DeleteBinTwoLineIcon } from '@ifrc-go/icons';
import {
Checklist,
Expand All @@ -14,12 +17,14 @@ import {
type ArrayError,
getErrorObject,
getErrorString,
type LeafError,
type SetValueArg,
useFormObject,
} from '@togglecorp/toggle-form';

import { type components } from '#generated/types';
import useGlobalEnums from '#hooks/domain/useGlobalEnums';
import { TIMEFRAME_YEAR } from '#utils/constants';

import { type OperationActivityFormFields } from './schema';
import TimeSpanCheck from './TimeSpanCheck';
Expand All @@ -30,6 +35,8 @@ const defaultActivityValue: OperationActivityFormFields = {
client_id: '-1',
};

export type ActivityInputType = 'readiness_activities' | 'prepositioning_activities' | 'early_action_activities';

type TimeframeOption = components['schemas']['EapTimeframeEnum'];

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

interface Props {
value: OperationActivityFormFields;
error: ArrayError<OperationActivityFormFields> | undefined;
error: ArrayError<OperationActivityFormFields> | LeafError | undefined;
onChange: (value: SetValueArg<OperationActivityFormFields>, index: number) => void;
onRemove: (index: number) => void;
index: number;
disabled?: boolean;
readOnly?: boolean;

name: ActivityInputType;
}

function EapOperationActivityInput(props: Props) {
Expand All @@ -59,6 +68,7 @@ function EapOperationActivityInput(props: Props) {
onRemove,
disabled,
readOnly,
name,
} = props;

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

const error = (value && value.client_id && errorFromProps)
? getErrorObject(errorFromProps?.[value.client_id])
? getErrorObject(getErrorObject(errorFromProps)?.[value.client_id])
: undefined;

const eapTimeframeOption = useMemo(() => {
if (name === 'early_action_activities') {
return eap_timeframe?.filter((item) => item.key !== TIMEFRAME_YEAR);
}
return eap_timeframe;
}, [eap_timeframe, name]);

const eapTimeFrameReadOnly = name === 'readiness_activities' || name === 'prepositioning_activities';

const getTimeValueOptions = useCallback(
(timeframe?: number) => {
switch (timeframe) {
Expand Down Expand Up @@ -143,10 +162,10 @@ function EapOperationActivityInput(props: Props) {
onChange={handleTimeframeChange}
keySelector={timeframeKeySelector}
labelSelector={stringValueSelector}
options={eap_timeframe}
options={eapTimeframeOption}
disabled={disabled}
error={error?.timeframe}
readOnly={readOnly}
readOnly={readOnly || eapTimeFrameReadOnly}
/>
{value?.timeframe && (
<Checklist
Expand Down
6 changes: 3 additions & 3 deletions app/src/components/domain/EapOperationActivityInput/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ export type OperationActivityFormFields = PartialForm<OperationActivity> & {

type OperationActivitySchema = ObjectSchema<OperationActivityFormFields>;

const schema: OperationActivitySchema = {
const schema = (isSubmit: boolean): OperationActivitySchema => ({
fields: (): ReturnType<OperationActivitySchema['fields']> => ({
client_id: {},
id: { defaultValue: undefinedValue },
activity: {
// FIXME: add validation for character limit
required: true,
required: isSubmit,
requiredValidation: requiredStringCondition,
},
time_value: {},
timeframe: {},
}),
};
});

export default schema;
17 changes: 11 additions & 6 deletions app/src/components/domain/EapOperationActivityListInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,31 @@ import {
import {
type ArrayError,
getErrorObject,
type LeafError,
type SetValueArg,
useFormArray,
} from '@togglecorp/toggle-form';

import EapOperationActivityInput from '#components/domain/EapOperationActivityInput';
import EapOperationActivityInput, { type ActivityInputType } from '#components/domain/EapOperationActivityInput';
import { type OperationActivityFormFields } from '#components/domain/EapOperationActivityInput/schema';
import ExplanatoryNote from '#components/ExplanatoryNote';
import Link from '#components/Link';
import NonFieldError from '#components/NonFieldError';
import { TIMEFRAME_YEAR } from '#utils/constants';

import i18n from './i18n.json';

type FormName = 'readiness_activities' | 'prepositioning_activities' | 'early_action_activities';

interface Props<NAME> {
disabled?: boolean;
readOnly?: boolean;

name: NAME,
value: OperationActivityFormFields[] | undefined;
onChange: (newValue: SetValueArg<OperationActivityFormFields[]>, name: NAME) => void;
error: ArrayError<OperationActivityFormFields> | undefined;
error: ArrayError<OperationActivityFormFields> | LeafError | undefined;
}

function EapOperationActivityListInput<const NAME extends FormName>(props: Props<NAME>) {
function EapOperationActivityListInput<const NAME extends ActivityInputType>(props: Props<NAME>) {
const {
disabled,
readOnly,
Expand All @@ -65,8 +65,12 @@ function EapOperationActivityListInput<const NAME extends FormName>(props: Props

const handleReadinessAddButtonClick = useCallback(
() => {
const timeframeValue = name === 'readiness_activities' || name === 'prepositioning_activities'
? TIMEFRAME_YEAR
: undefined;
const newActionItem: OperationActivityFormFields = {
client_id: randomString(),
timeframe: timeframeValue,
};

onChange(
Expand Down Expand Up @@ -162,12 +166,13 @@ function EapOperationActivityListInput<const NAME extends FormName>(props: Props
>
{value?.map((activity, i) => (
<EapOperationActivityInput
name={name}
key={activity.client_id}
index={i}
value={activity}
onChange={onReadinessChange}
onRemove={onReadinessRemove}
error={getErrorObject(error?.readiness_activities)}
error={getErrorObject(error)?.[name]}
disabled={disabled}
readOnly={readOnly}
/>
Expand Down
Loading
Loading