Skip to content

Commit

Permalink
Add validation workflow to local units
Browse files Browse the repository at this point in the history
- feat(local-units): add delete modal and refine permissions
- Add delete modal in Local Units Form and Local Units Table
- Separate delete and validate permissions for local units
- Simplify delete permission logic
  • Loading branch information
shreeyash07 authored and frozenhelium committed Jan 13, 2025
1 parent 15a6735 commit 3258b96
Show file tree
Hide file tree
Showing 37 changed files with 2,818 additions and 978 deletions.
5 changes: 5 additions & 0 deletions .changeset/pink-ties-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"go-web-app": minor
---

Add local unit validation workflow
52 changes: 52 additions & 0 deletions app/src/components/DiffWrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useMemo } from 'react';
import { isNotDefined } from '@togglecorp/fujs';

interface Props<T> {
diffContainerClassName?: string;
value?: T;
oldValue?: T;
children: React.ReactNode;
enabled: boolean;
showOnlyDiff?: boolean;
}

function DiffWrapper<T>(props: Props<T>) {
const {
diffContainerClassName,
oldValue,
value,
children,
enabled = false,
showOnlyDiff,
} = props;

const hasChanged = useMemo(() => {
// NOTE: we consider `null` and `undefined` as same for
// this scenario
if (isNotDefined(oldValue) && isNotDefined(value)) {
return false;
}

return JSON.stringify(oldValue) !== JSON.stringify(value);
}, [oldValue, value]);

if (!enabled) {
return children;
}

if (!hasChanged && showOnlyDiff) {
return null;
}

if (!hasChanged) {
return children;
}

return (
<div className={diffContainerClassName}>
{children}
</div>
);
}

export default DiffWrapper;
47 changes: 47 additions & 0 deletions app/src/components/MultiSelectOutput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useMemo } from 'react';
import { TextOutput } from '@ifrc-go/ui';
import { listToMap } from '@togglecorp/fujs';

interface Props<VALUE, OPTION> {
value: VALUE[] | undefined;
options: OPTION[] | undefined;
keySelector: (datum: OPTION) => VALUE;
labelSelector: (datum: OPTION) => React.ReactNode;
label: React.ReactNode;
}

function MultiSelectOutput<VALUE extends string | number, OPTION>(props: Props<VALUE, OPTION>) {
const {
value,
options,
keySelector,
labelSelector,
label,
} = props;

const valueMap = useMemo(
() => listToMap(value ?? [], (val) => val, () => true),
[value],
);

const selectedOptions = useMemo(() => options?.filter(
(option) => valueMap[keySelector(option)],
), [keySelector, options, valueMap]);

const valueLabel = useMemo(
() => selectedOptions?.map(
(selectedOption) => labelSelector(selectedOption),
).join(', ') ?? '--',
[labelSelector, selectedOptions],
);

return (
<TextOutput
label={label}
value={valueLabel}
strongLabel
/>
);
}

export default MultiSelectOutput;
41 changes: 41 additions & 0 deletions app/src/components/SelectOutput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useMemo } from 'react';
import { TextOutput } from '@ifrc-go/ui';
import { isDefined } from '@togglecorp/fujs';

interface Props<VALUE, OPTION> {
value: VALUE | undefined;
options: OPTION[] | undefined;
keySelector: (datum: OPTION) => VALUE;
labelSelector: (datum: OPTION) => React.ReactNode;
label: React.ReactNode;
}

function SelectOutput<VALUE, OPTION>(props: Props<VALUE, OPTION>) {
const {
value,
options,
keySelector,
labelSelector,
label,
} = props;

const selectedOption = useMemo(() => options?.find(
(option) => keySelector(option) === value,
), [options, keySelector, value]);

const valueLabel = useMemo(() => (
isDefined(selectedOption)
? labelSelector(selectedOption)
: '--'
), [labelSelector, selectedOption]);

return (
<TextOutput
label={label}
value={valueLabel}
strongLabel
/>
);
}

export default SelectOutput;
7 changes: 7 additions & 0 deletions app/src/components/domain/BaseMapPointInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ interface Props<NAME> extends BaseMapProps {
readOnly?: boolean;
required?: boolean;
error?: ObjectError<Value>;
showChanges: boolean;
latitudeInputSectionClassName?: string;
longitudeInputSectionClassName?: string;
}

function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
Expand All @@ -74,6 +77,8 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
country,
required,
error,
latitudeInputSectionClassName,
longitudeInputSectionClassName,
...otherProps
} = props;

Expand Down Expand Up @@ -183,6 +188,7 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
<div className={_cs(styles.baseMapPointInput, className)}>
<div className={styles.locationInputs}>
<NumberInput
inputSectionClassName={latitudeInputSectionClassName}
className={styles.input}
name="lat"
label={strings.latitude}
Expand All @@ -193,6 +199,7 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
required={required}
/>
<NumberInput
inputSectionClassName={longitudeInputSectionClassName}
className={styles.input}
name="lng"
label={strings.longitude}
Expand Down
6 changes: 6 additions & 0 deletions app/src/components/domain/BaseMapPointInput/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
flex-direction: column;
gap: var(--go-ui-spacing-md);

.diff-container{
.changes {
background-color: var(--go-ui-color-semantic-yellow) !important;
}
}

.location-inputs {
display: flex;
gap: var(--go-ui-spacing-sm);
Expand Down
43 changes: 42 additions & 1 deletion app/src/utils/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { DEFAULT_INVALID_TEXT } from '@ifrc-go/ui/utils';
import { isTruthyString } from '@togglecorp/fujs';
import {
isDefined,
isNotDefined,
isTruthyString,
} from '@togglecorp/fujs';

import type { GoApiResponse } from '#utils/restRequest';

Expand Down Expand Up @@ -52,3 +56,40 @@ export function getFirstTruthyString(

return invalidText;
}

// TODO: write tests for the function
export function doArraysContainSameElements(
newArray: unknown[] | undefined,
oldArray: unknown[] | undefined,
): boolean {
if (isNotDefined(newArray) && isNotDefined(oldArray)) {
return true;
}
if (isDefined(newArray) && isDefined(oldArray)) {
if (newArray.length !== oldArray.length) {
return false;
}
return newArray.every((id) => oldArray.includes(id));
}
return false;
}

// TODO: write tests for the function
export function flattenObject<T extends Record<string, unknown>>(
inputObject: T,
prefix?: string,
): Record<string, unknown> {
return Object.entries(inputObject).reduce((acc, [key, value]) => {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return { ...acc, ...flattenObject(value as Record<string, unknown>, newKey) };
}
return { ...acc, [newKey]: value };
}, {} as Record<string, unknown>);
}

// TODO: write tests for the function
export function getLastSegment(str: string, delimiter: string) {
const parts = str.split(delimiter);
return parts[parts.length - 1];
}
2 changes: 1 addition & 1 deletion app/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const DREF_TYPE_ASSESSMENT = 1 satisfies TypeOfDrefEnum;
export const DREF_TYPE_RESPONSE = 2 satisfies TypeOfDrefEnum;
export const DREF_TYPE_LOAN = 3 satisfies TypeOfDrefEnum;

type TypeOfOnsetEnum = components<'read'>['schemas']['TypeValidatedEnum'];
type TypeOfOnsetEnum = components<'read'>['schemas']['DrefDrefOnsetTypeEnumKey'];
export const ONSET_SLOW = 1 satisfies TypeOfOnsetEnum;

// Subscriptions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { _cs } from '@togglecorp/fujs';

import styles from './styles.module.css';

interface Props {
className?: string;
children?: React.ReactNode;
}

function FormGrid(props: Props) {
const {
className,
children,
} = props;

return (
<div className={_cs(styles.formGrid, className)}>
{children}
</div>
);
}

export default FormGrid;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* NOTE: this element is portaled */
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--go-ui-spacing-lg);

@media screen and (max-width: 60rem) {
grid-template-columns: 1fr;
}
}

This file was deleted.

Loading

0 comments on commit 3258b96

Please sign in to comment.