diff --git a/app/views/ProjectEdit/GeoAreas/CustomGeoAddModal/index.tsx b/app/views/ProjectEdit/GeoAreas/CustomGeoAddModal/index.tsx index b11ba1a663..09fcae3482 100644 --- a/app/views/ProjectEdit/GeoAreas/CustomGeoAddModal/index.tsx +++ b/app/views/ProjectEdit/GeoAreas/CustomGeoAddModal/index.tsx @@ -28,17 +28,16 @@ import styles from './styles.css'; type NewRegion = NonNullable['result']>; const CREATE_REGION = gql` -mutation CreateRegion($data: RegionInputType!) { - createRegion( - data: $data - ) { + mutation CreateRegion( + $data: RegionInputType!, + ) { + createRegion(data: $data) { ok errors result { id isPublished } - } } `; diff --git a/app/views/ProjectEdit/GeoAreas/RegionCard/AddAdminLevelPane/AddAdminLevelForm/index.tsx b/app/views/ProjectEdit/GeoAreas/RegionCard/AddAdminLevelPane/AddAdminLevelForm/index.tsx index 4a5d33b451..1ac761d335 100644 --- a/app/views/ProjectEdit/GeoAreas/RegionCard/AddAdminLevelPane/AddAdminLevelForm/index.tsx +++ b/app/views/ProjectEdit/GeoAreas/RegionCard/AddAdminLevelPane/AddAdminLevelForm/index.tsx @@ -1,6 +1,6 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { isDefined } from '@togglecorp/fujs'; +import { isDefined, isFalsyString, isNotDefined } from '@togglecorp/fujs'; import { Button, NumberInput, @@ -18,6 +18,7 @@ import { useForm, getErrorObject, createSubmitHandler, + removeNull, } from '@togglecorp/toggle-form'; import { gql, useMutation } from '@apollo/client'; import { @@ -29,44 +30,47 @@ import { GalleryFileType, AdminLevelType, } from '#generated/types'; +import { + ObjectError, + transformToFormError, +} from '#base/utils/errorTransform'; import NonFieldError from '#components/NonFieldError'; import GalleryFileUpload from '#components/GalleryFileUpload'; import styles from './styles.css'; +type PartialAdminLevel = PartialForm; type AdminLevelInputTypeSafe = AdminLevelInputType & { id: string }; type PartialAdminLevelInput = PartialForm; const CREATE_ADMIN_LEVEL = gql` mutation CreateAdminLevel( - $data: AdminLevelInputType! + $data: AdminLevelInputType!, ) { - createAdminLevel( - data: $data - ) { - ok - errors - result { - tolerance - title - staleGeoAreas - parentNameProp - parentCodeProp - codeProp - nameProp - geoShapeFile { - file { - url - } - id - title - mimeType + createAdminLevel(data: $data) { + ok + errors + result { + tolerance + title + staleGeoAreas + parentNameProp + parentCodeProp + codeProp + nameProp + geoShapeFile { + file { + url } - level id + title + mimeType } + level + id } } + } `; const UPDATE_ADMIN_LEVEL = gql` @@ -123,11 +127,25 @@ const schema: FormSchema = { const adminLevelKeySelector = (d: AdminLevelType) => d.id; const adminLevelLabelSelector = (d: AdminLevelType) => d.title; +function isGalleryFileValid( + galleryFile: Partial | undefined | null, +): galleryFile is GalleryFileType { + if (isNotDefined(galleryFile)) { + return false; + } + + if (isNotDefined(galleryFile.id) || isFalsyString(galleryFile.title)) { + return false; + } + + return true; +} + interface Props { regionId: string; onSave: (adminLevel: AdminLevelType) => void; onDelete: (id: string | undefined) => void; - value: AdminLevelType; + value: PartialAdminLevel; isPublished?: boolean; adminLevelOptions?: AdminLevelType[]; } @@ -153,11 +171,13 @@ function AddAdminLevelForm(props: Props) { ...remainingValues } = valueFromProps; - const initialFormValue: PartialAdminLevelInput = { - ...remainingValues, - geoShapeFile: geoShapeFile?.id, - region: regionId, - }; + const initialFormValue: PartialAdminLevelInput = useMemo(() => ( + { + ...remainingValues, + geoShapeFile: geoShapeFile?.id, + region: regionId, + } + ), [geoShapeFile?.id, regionId, remainingValues]); const { pristine, @@ -169,7 +189,11 @@ function AddAdminLevelForm(props: Props) { setError, } = useForm(schema, initialFormValue); - const [option, setOption] = useState(geoShapeFile ?? undefined); + const [option, setOption] = useState( + isGalleryFileValid(geoShapeFile) + ? geoShapeFile + : undefined, + ); const error = getErrorObject(riskyError); const alert = useAlert(); @@ -183,7 +207,7 @@ function AddAdminLevelForm(props: Props) { CREATE_ADMIN_LEVEL, { onCompleted: (response) => { - if (!response || !response.createAdminLevel || !response.createAdminLevel.result) { + if (!response || !response.createAdminLevel) { return; } @@ -194,13 +218,11 @@ function AddAdminLevelForm(props: Props) { } = response.createAdminLevel; if (errors) { - alert.show( - 'Failed to create admin level.', - { variant: 'error' }, - ); + const formError = transformToFormError(removeNull(errors) as ObjectError[]); + setError(formError); } - if (ok) { + if (isDefined(result) && ok) { alert.show( 'Admin level is successfully created!', { variant: 'success' }, @@ -227,7 +249,7 @@ function AddAdminLevelForm(props: Props) { UPDATE_ADMIN_LEVEL, { onCompleted: (response) => { - if (!response || !response.updateAdminLevel || !response.updateAdminLevel.result) { + if (!response || !response.updateAdminLevel) { return; } @@ -238,13 +260,11 @@ function AddAdminLevelForm(props: Props) { } = response.updateAdminLevel; if (errors) { - alert.show( - 'Failed to update admin level.', - { variant: 'error' }, - ); + const formError = transformToFormError(removeNull(errors) as ObjectError[]); + setError(formError); } - if (ok) { + if (isDefined(result) && ok) { alert.show( 'Admin level is successfully update!', { variant: 'success' }, @@ -271,13 +291,23 @@ function AddAdminLevelForm(props: Props) { updateAdminLevel({ variables: { id: valueFromProps.id, - data: val as AdminLevelInputType, + data: { + ...val, + parentCodeProp: val.parentCodeProp ?? '', + parentNameProp: val.parentNameProp ?? '', + + } as AdminLevelInputType, }, }); } else { createAdminLevel({ variables: { - data: val as AdminLevelInputType, + data: { + ...val, + parentCodeProp: val.parentCodeProp ?? '', + parentNameProp: val.parentNameProp ?? '', + + } as AdminLevelInputType, }, }); } diff --git a/app/views/ProjectEdit/GeoAreas/RegionCard/AddAdminLevelPane/index.tsx b/app/views/ProjectEdit/GeoAreas/RegionCard/AddAdminLevelPane/index.tsx index c30af2c849..f8536dbfc1 100644 --- a/app/views/ProjectEdit/GeoAreas/RegionCard/AddAdminLevelPane/index.tsx +++ b/app/views/ProjectEdit/GeoAreas/RegionCard/AddAdminLevelPane/index.tsx @@ -2,14 +2,17 @@ import React from 'react'; import { TabPanel, } from '@the-deep/deep-ui'; +import { PartialForm } from '@togglecorp/toggle-form'; import { AdminLevelType } from '#generated/types'; import AddAdminLevelForm from './AddAdminLevelForm'; +type PartialAdminLevel = PartialForm; + interface Props { onSave: (adminLevel: AdminLevelType) => void; onDelete: (id: string | undefined) => void; - value: AdminLevelType; + value: PartialAdminLevel; isPublished?: boolean; adminLevelOptions?: AdminLevelType[]; name: string; diff --git a/app/views/ProjectEdit/GeoAreas/RegionCard/index.tsx b/app/views/ProjectEdit/GeoAreas/RegionCard/index.tsx index 64cd5d70a7..d61e5ac4c8 100644 --- a/app/views/ProjectEdit/GeoAreas/RegionCard/index.tsx +++ b/app/views/ProjectEdit/GeoAreas/RegionCard/index.tsx @@ -1,8 +1,8 @@ import React, { useCallback, useMemo, useState } from 'react'; import { - _cs, + _cs, isNotDefined, } from '@togglecorp/fujs'; -import { PartialForm } from '@togglecorp/toggle-form'; +import { PartialForm, internal, removeNull } from '@togglecorp/toggle-form'; import { IoTrashBinOutline, IoInformationCircleOutline, @@ -23,9 +23,6 @@ import { List, useAlert, } from '@the-deep/deep-ui'; -import { - useLazyRequest, -} from '#base/utils/restRequest'; import { RegionsForGeoAreasQuery, PublishRegionMutation, @@ -35,7 +32,10 @@ import { AdminLevelsQueryVariables, DeleteRegionMutation, DeleteRegionMutationVariables, + DeleteAdminLevelMutation, + DeleteAdminLevelMutationVariables, } from '#generated/types'; +import { ObjectError, transformToFormError } from '#base/utils/errorTransform'; import AddAdminLevelPane from './AddAdminLevelPane'; @@ -95,6 +95,15 @@ const ADMIN_LEVELS = gql` } `; +const DELETE_ADMIN_LEVEL = gql` + mutation DeleteAdminLevel($adminLevelId: ID!) { + deleteAdminLevel(adminLevelId: $adminLevelId) { + errors + ok + } + } +`; + interface Props { region: Region; className?: string; @@ -140,26 +149,16 @@ function RegionCard(props: Props) { const adminLevelsLength = adminLevels.length; - const adminLevelsWithTempAdminLevel: AdminLevelType[] = useMemo( + const adminLevelsWithTempAdminLevel: PartialAdminLevel[] = useMemo( () => { - if (!tempAdminLevel) { + if (isNotDefined(tempAdminLevel)) { return adminLevels; } - return adminLevels.map((admin) => { - const { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - title, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - level, - ...otherProperties - } = admin; - return { - title: tempAdminLevel?.title ?? 'title', - level: tempAdminLevel?.level ?? 0, - ...otherProperties, - }; - }); + return [ + ...adminLevels, + tempAdminLevel, + ]; }, [adminLevels, tempAdminLevel], ); @@ -168,7 +167,7 @@ function RegionCard(props: Props) { ? tempAdminLevel.id : activeAdminLevel; - const adminLevelQuery = useMemo( + const adminLevelQueryVariables = useMemo( () => ({ regionId: region.id, }), @@ -177,14 +176,18 @@ function RegionCard(props: Props) { const { loading: adminLevelsPending, - refetch: getAdminLevels, + refetch: refetchAdminLevels, } = useQuery( ADMIN_LEVELS, { skip: !isExpanded, - variables: adminLevelQuery, + variables: adminLevelQueryVariables, + // NOTE: this prop is required for onCompleted to be + // called after refetch (https://github.com/apollographql/apollo-client/issues/11151#issuecomment-1680742854) + notifyOnNetworkStatusChange: true, onCompleted: (response) => { const adminLevelResponse = response.region; + if (!adminLevelResponse || !adminLevelResponse.adminLevels) { return; } @@ -200,19 +203,47 @@ function RegionCard(props: Props) { }, ); - const { - pending: deleteAdminLevelPending, - trigger: deleteAdminLevel, - } = useLazyRequest({ - url: (ctx) => `server://admin-levels/${ctx}/`, - method: 'DELETE', - onSuccess: () => { - getAdminLevels(); - if (onAdminLevelUpdate) { - onAdminLevelUpdate(); - } + const [ + deleteAdminLevel, + { + loading: deleteAdminLevelPending, }, - }); + ] = useMutation( + DELETE_ADMIN_LEVEL, + { + onCompleted: (response) => { + if (!response.deleteAdminLevel) { + return; + } + + const { ok, errors } = response.deleteAdminLevel; + + if (errors) { + const formError = transformToFormError( + removeNull(response.deleteAdminLevel.errors) as ObjectError[], + ); + alert.show( + formError?.[internal] as string, + { variant: 'error' }, + ); + } + + if (ok) { + refetchAdminLevels(); + + if (onAdminLevelUpdate) { + onAdminLevelUpdate(); + } + } + }, + onError: () => { + alert.show( + 'Failed to delete selected admin level.', + { variant: 'error' }, + ); + }, + }, + ); const [ deleteRegion, @@ -321,7 +352,11 @@ function RegionCard(props: Props) { onTempAdminLevelChange(undefined); } } else { - deleteAdminLevel(id); + deleteAdminLevel({ + variables: { + adminLevelId: id, + }, + }); } }, [onTempAdminLevelChange, deleteAdminLevel], @@ -387,7 +422,7 @@ function RegionCard(props: Props) { ); const tabPanelRendererParams = useCallback( - (key: string, data: AdminLevelType) => ({ + (key: string, data: PartialAdminLevel) => ({ name: key, value: data, onSave: handleAdminLevelSave, diff --git a/app/views/ProjectEdit/GeoAreas/index.tsx b/app/views/ProjectEdit/GeoAreas/index.tsx index 0e562765f0..86af685809 100644 --- a/app/views/ProjectEdit/GeoAreas/index.tsx +++ b/app/views/ProjectEdit/GeoAreas/index.tsx @@ -29,27 +29,28 @@ import styles from './styles.css'; type PartialAdminLevel = PartialForm; const REGIONS_FOR_GEO_AREAS = gql` -query RegionsForGeoAreas ($id: ID!) { - project (id: $id) { - regions { - id - isPublished - adminLevels { + query RegionsForGeoAreas ($id: ID!) { + project (id: $id) { + regions { id + isPublished + adminLevels { + id + title + level + nameProp + codeProp + parentNameProp + parentCodeProp + parent + } title - level - nameProp - codeProp - parentNameProp - parentCodeProp - parent + public } - title - public } } -} `; + type Region = NonNullable['regions']>[number]>; type NewRegion = NonNullable['result']>;