Skip to content

Commit cf96c7a

Browse files
committed
Add hasDifference logic for localUnitView component empty message
1 parent 2091118 commit cf96c7a

File tree

6 files changed

+137
-14
lines changed

6 files changed

+137
-14
lines changed

app/src/utils/localUnits.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import {
2+
isNotDefined,
3+
isObject,
4+
} from '@togglecorp/fujs';
5+
import { removeNull } from '@togglecorp/toggle-form';
6+
7+
import { type PartialLocalUnits } from '#views/CountryNsOverviewContextAndStructure/NationalSocietyLocalUnits/LocalUnitsFormModal/LocalUnitsForm/schema';
8+
9+
import { type GoApiResponse } from './restRequest';
10+
11+
type LocalUnitResponse = NonNullable<GoApiResponse<'/api/v2/local-units/{id}/'>>;
12+
13+
export function getFormFields(value: LocalUnitResponse | PartialLocalUnits) {
14+
const {
15+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
16+
created_at,
17+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
18+
created_by_details,
19+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
20+
modified_at,
21+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
22+
modified_by,
23+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
24+
modified_by_details,
25+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
26+
is_locked,
27+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
28+
validated,
29+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
30+
version_id,
31+
health,
32+
...formValues
33+
// Note: the cast is safe as we're only trying to
34+
// remove fields if they exist
35+
} = removeNull(value) as LocalUnitResponse;
36+
37+
const {
38+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
39+
modified_by_details: healthModifiedByDetails,
40+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
41+
modified_at: healthModifiedAt,
42+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
43+
modified_by: healthModifiedby,
44+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
45+
created_at: healthCreatedAt,
46+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
47+
created_by_details: healthCreatedByDetails,
48+
...formHealthValues
49+
} = health ?? {};
50+
51+
return { ...formValues, health: { ...formHealthValues } };
52+
}
53+
54+
// FIXME: this should be gracefully handled
55+
function isObjectWithStringKey(obj: unknown): obj is Record<string, unknown> {
56+
return isObject(obj);
57+
}
58+
59+
export default function hasDifferences(newValue: unknown, oldValue: unknown): boolean {
60+
if (isNotDefined(newValue) && isNotDefined(oldValue)) {
61+
return false;
62+
}
63+
64+
// FIXME: we might need to also consider the order for array
65+
if (Array.isArray(newValue) && Array.isArray(oldValue)) {
66+
if (newValue.length !== oldValue.length) {
67+
return true;
68+
}
69+
70+
return newValue.some(
71+
(_, i) => hasDifferences(newValue[i], oldValue[i]),
72+
);
73+
}
74+
75+
if (isObjectWithStringKey(newValue) && isObjectWithStringKey(oldValue)) {
76+
const newValueKeys = Object.keys(removeNull(newValue));
77+
const oldValueKeys = Object.keys(removeNull(oldValue));
78+
79+
if (newValueKeys.length !== oldValueKeys.length) {
80+
return true;
81+
}
82+
83+
return newValueKeys.some(
84+
(key) => hasDifferences(newValue[key], oldValue[key]),
85+
);
86+
}
87+
88+
return newValue !== oldValue;
89+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"localUnitViewConfirmChangesContentQuestion": "Are you sure you want to have these changes in this local unit?",
8787
"localUnitViewNewLocalUnitDescription": "New local unit",
8888
"localUnitViewLatitude": "Latitude",
89-
"localUnitViewLongitude": "Longitude"
89+
"localUnitViewLongitude": "Longitude",
90+
"localUnitViewNoChanges": "No changes found"
9091
}
9192
}

app/src/views/CountryNsOverviewContextAndStructure/NationalSocietyLocalUnits/LocalUnitView/index.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
} from '../LocalUnitsFormModal/LocalUnitsForm/schema';
3030

3131
import i18n from './i18n.json';
32+
import { useMemo } from 'react';
33+
import hasDifferences, { getFormFields } from '#utils/localUnits';
3234

3335
type VisibilityOptions = NonNullable<GoApiResponse<'/api/v2/global-enums/'>['api_visibility_choices']>[number]
3436
type LocalUnitResponse = NonNullable<GoApiResponse<'/api/v2/local-units/{id}/'>>;
@@ -68,7 +70,6 @@ function LocalUnitView(props: Props) {
6870
const {
6971
response: localUnitPreviousResponse,
7072
pending: localUnitPreviousResponsePending,
71-
// error: localUnitPreviousResponseError,
7273
} = useRequest({
7374
skip: isDefined(locallyChangedValue) || isNotDefined(localUnitId),
7475
url: '/api/v2/local-units/{id}/latest-change-request/',
@@ -82,14 +83,24 @@ function LocalUnitView(props: Props) {
8283
? localUnitResponse
8384
: (localUnitPreviousResponse?.previous_data_details as unknown as LocalUnitResponse);
8485

85-
// FIXME: Handle case when there is no change.
86-
// We need to display message to the user
86+
const hasDifference = useMemo(() => {
87+
if (!newValue || !oldValue) {
88+
return false;
89+
}
90+
91+
const newFormFields = getFormFields(newValue);
92+
const oldFormFields = getFormFields(oldValue);
93+
94+
return hasDifferences(newFormFields, oldFormFields);
95+
}, [newValue, oldValue]);
8796

8897
return (
8998
<Container
9099
contentViewType="vertical"
91100
pending={localUnitResponsePending || localUnitPreviousResponsePending}
92101
errored={!!localUnitResponseError}
102+
empty={!hasDifference}
103+
emptyMessage={strings.localUnitViewNoChanges}
93104
>
94105
<DiffWrapper
95106
showOnlyDiff

app/src/views/CountryNsOverviewContextAndStructure/NationalSocietyLocalUnits/LocalUnitViewModal/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { type PartialLocalUnits } from '../LocalUnitsFormModal/LocalUnitsForm/sc
55
import LocalUnitView from '../LocalUnitView';
66

77
import i18n from './i18n.json';
8+
import styles from './styles.module.css';
89

910
interface Props {
1011
footerActions: React.ReactNode;
@@ -30,6 +31,7 @@ function LocalUnitViewModal(props: Props) {
3031
withHeaderBorder
3132
onClose={onClose}
3233
footerActions={footerActions}
34+
childrenContainerClassName={styles.localUnitViewChildrenContainer}
3335
>
3436
<LocalUnitView
3537
localUnitId={localUnitId}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.local-unit-view-children-container {
2+
max-height: 36rem;
3+
}

app/src/views/CountryNsOverviewContextAndStructure/NationalSocietyLocalUnits/LocalUnitsFormModal/LocalUnitsForm/index.tsx

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import useAlert from '#hooks/useAlert';
5555
import { getFirstTruthyString } from '#utils/common';
5656
import { VISIBILITY_PUBLIC } from '#utils/constants';
5757
import { getUserName } from '#utils/domain/user';
58+
import hasDifferences, { getFormFields } from '#utils/localUnits';
5859
import { type CountryOutletContext } from '#utils/outletContext';
5960
import {
6061
type GoApiResponse,
@@ -434,16 +435,6 @@ function LocalUnitsForm(props: Props) {
434435
const healthFormError = getErrorObject(error?.health);
435436
const revertChangesFormError = getErrorObject(revertChangesError);
436437

437-
const submitButton = readOnly ? null : (
438-
<Button
439-
name={undefined}
440-
onClick={handleFormSubmit}
441-
disabled={addLocalUnitsPending || updateLocalUnitsPending}
442-
>
443-
{strings.submitButtonLabel}
444-
</Button>
445-
);
446-
447438
const previousData = (
448439
localUnitPreviousResponse?.previous_data_details as unknown as LocalUnitResponse
449440
);
@@ -455,8 +446,34 @@ function LocalUnitsForm(props: Props) {
455446
}
456447
return false;
457448
}, [previousData]);
449+
458450
const showChanges = !isNewLocalUnit && !!localUnitDetailsResponse?.is_locked;
459451

452+
const hasDifference = useMemo(() => {
453+
if (!value || !previousData) {
454+
return false;
455+
}
456+
457+
const newFormFields = getFormFields(value);
458+
const oldFormFields = getFormFields(previousData);
459+
460+
return hasDifferences(newFormFields, oldFormFields);
461+
}, [value, previousData]);
462+
463+
const submitButton = readOnly ? null : (
464+
<Button
465+
name={undefined}
466+
onClick={handleFormSubmit}
467+
disabled={
468+
!hasDifference
469+
|| addLocalUnitsPending
470+
|| updateLocalUnitsPending
471+
}
472+
>
473+
{strings.submitButtonLabel}
474+
</Button>
475+
);
476+
460477
return (
461478
<div className={styles.localUnitsForm}>
462479
{isDefined(localUnitDetailsResponse)

0 commit comments

Comments
 (0)