Skip to content

Commit c97ef9f

Browse files
authored
Merge pull request #1593 from IFRCGo/project/local-unit-v2
project: Update Local Unit edit, validation, and delete workflow
2 parents 15a6735 + 5d77bc7 commit c97ef9f

File tree

37 files changed

+3522
-977
lines changed

37 files changed

+3522
-977
lines changed

.changeset/pink-ties-smash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"go-web-app": minor
3+
---
4+
5+
Add local unit validation workflow
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useMemo } from 'react';
2+
import { isNotDefined } from '@togglecorp/fujs';
3+
4+
interface Props<T> {
5+
diffContainerClassName?: string;
6+
value?: T;
7+
oldValue?: T;
8+
children: React.ReactNode;
9+
enabled: boolean;
10+
showOnlyDiff?: boolean;
11+
}
12+
13+
function DiffWrapper<T>(props: Props<T>) {
14+
const {
15+
diffContainerClassName,
16+
oldValue,
17+
value,
18+
children,
19+
enabled = false,
20+
showOnlyDiff,
21+
} = props;
22+
23+
const hasChanged = useMemo(() => {
24+
// NOTE: we consider `null` and `undefined` as same for
25+
// this scenario
26+
if (isNotDefined(oldValue) && isNotDefined(value)) {
27+
return false;
28+
}
29+
30+
return JSON.stringify(oldValue) !== JSON.stringify(value);
31+
}, [oldValue, value]);
32+
33+
if (!enabled) {
34+
return children;
35+
}
36+
37+
if (!hasChanged && showOnlyDiff) {
38+
return null;
39+
}
40+
41+
if (!hasChanged) {
42+
return children;
43+
}
44+
45+
return (
46+
<div className={diffContainerClassName}>
47+
{children}
48+
</div>
49+
);
50+
}
51+
52+
export default DiffWrapper;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useMemo } from 'react';
2+
import { TextOutput } from '@ifrc-go/ui';
3+
import { listToMap } from '@togglecorp/fujs';
4+
5+
interface Props<VALUE, OPTION> {
6+
value: VALUE[] | undefined;
7+
options: OPTION[] | undefined;
8+
keySelector: (datum: OPTION) => VALUE;
9+
labelSelector: (datum: OPTION) => React.ReactNode;
10+
label: React.ReactNode;
11+
}
12+
13+
function MultiSelectOutput<VALUE extends string | number, OPTION>(props: Props<VALUE, OPTION>) {
14+
const {
15+
value,
16+
options,
17+
keySelector,
18+
labelSelector,
19+
label,
20+
} = props;
21+
22+
const valueMap = useMemo(
23+
() => listToMap(value ?? [], (val) => val, () => true),
24+
[value],
25+
);
26+
27+
const selectedOptions = useMemo(() => options?.filter(
28+
(option) => valueMap[keySelector(option)],
29+
), [keySelector, options, valueMap]);
30+
31+
const valueLabel = useMemo(
32+
() => selectedOptions?.map(
33+
(selectedOption) => labelSelector(selectedOption),
34+
).join(', ') ?? '--',
35+
[labelSelector, selectedOptions],
36+
);
37+
38+
return (
39+
<TextOutput
40+
label={label}
41+
value={valueLabel}
42+
strongLabel
43+
/>
44+
);
45+
}
46+
47+
export default MultiSelectOutput;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useMemo } from 'react';
2+
import { TextOutput } from '@ifrc-go/ui';
3+
import { isDefined } from '@togglecorp/fujs';
4+
5+
interface Props<VALUE, OPTION> {
6+
value: VALUE | undefined;
7+
options: OPTION[] | undefined;
8+
keySelector: (datum: OPTION) => VALUE;
9+
labelSelector: (datum: OPTION) => React.ReactNode;
10+
label: React.ReactNode;
11+
}
12+
13+
function SelectOutput<VALUE, OPTION>(props: Props<VALUE, OPTION>) {
14+
const {
15+
value,
16+
options,
17+
keySelector,
18+
labelSelector,
19+
label,
20+
} = props;
21+
22+
const selectedOption = useMemo(() => options?.find(
23+
(option) => keySelector(option) === value,
24+
), [options, keySelector, value]);
25+
26+
const valueLabel = useMemo(() => (
27+
isDefined(selectedOption)
28+
? labelSelector(selectedOption)
29+
: '--'
30+
), [labelSelector, selectedOption]);
31+
32+
return (
33+
<TextOutput
34+
label={label}
35+
value={valueLabel}
36+
strongLabel
37+
/>
38+
);
39+
}
40+
41+
export default SelectOutput;

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ interface Props<NAME> extends BaseMapProps {
5656
readOnly?: boolean;
5757
required?: boolean;
5858
error?: ObjectError<Value>;
59+
showChanges: boolean;
60+
latitudeInputSectionClassName?: string;
61+
longitudeInputSectionClassName?: string;
5962
}
6063

6164
function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
@@ -74,6 +77,8 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
7477
country,
7578
required,
7679
error,
80+
latitudeInputSectionClassName,
81+
longitudeInputSectionClassName,
7782
...otherProps
7883
} = props;
7984

@@ -183,6 +188,7 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
183188
<div className={_cs(styles.baseMapPointInput, className)}>
184189
<div className={styles.locationInputs}>
185190
<NumberInput
191+
inputSectionClassName={latitudeInputSectionClassName}
186192
className={styles.input}
187193
name="lat"
188194
label={strings.latitude}
@@ -193,6 +199,7 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
193199
required={required}
194200
/>
195201
<NumberInput
202+
inputSectionClassName={longitudeInputSectionClassName}
196203
className={styles.input}
197204
name="lng"
198205
label={strings.longitude}

app/src/components/domain/BaseMapPointInput/styles.module.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
flex-direction: column;
44
gap: var(--go-ui-spacing-md);
55

6+
.diff-container{
7+
.changes {
8+
background-color: var(--go-ui-color-semantic-yellow) !important;
9+
}
10+
}
11+
612
.location-inputs {
713
display: flex;
814
gap: var(--go-ui-spacing-sm);

app/src/utils/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export const DREF_TYPE_ASSESSMENT = 1 satisfies TypeOfDrefEnum;
109109
export const DREF_TYPE_RESPONSE = 2 satisfies TypeOfDrefEnum;
110110
export const DREF_TYPE_LOAN = 3 satisfies TypeOfDrefEnum;
111111

112-
type TypeOfOnsetEnum = components<'read'>['schemas']['TypeValidatedEnum'];
112+
type TypeOfOnsetEnum = components<'read'>['schemas']['DrefDrefOnsetTypeEnumKey'];
113113
export const ONSET_SLOW = 1 satisfies TypeOfOnsetEnum;
114114

115115
// Subscriptions
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { _cs } from '@togglecorp/fujs';
2+
3+
import styles from './styles.module.css';
4+
5+
interface Props {
6+
className?: string;
7+
children?: React.ReactNode;
8+
}
9+
10+
function FormGrid(props: Props) {
11+
const {
12+
className,
13+
children,
14+
} = props;
15+
16+
return (
17+
<div className={_cs(styles.formGrid, className)}>
18+
{children}
19+
</div>
20+
);
21+
}
22+
23+
export default FormGrid;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* NOTE: this element is portaled */
2+
.form-grid {
3+
display: grid;
4+
grid-template-columns: 1fr 1fr;
5+
gap: var(--go-ui-spacing-lg);
6+
7+
@media screen and (max-width: 60rem) {
8+
grid-template-columns: 1fr;
9+
}
10+
}

app/src/views/CountryNsOverviewContextAndStructure/NationalSocietyLocalUnits/LocalUnitDeleteButton/i18n.json

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)