From 4bc55ccc693dffff8206e3abbd8d7962f4e2b450 Mon Sep 17 00:00:00 2001 From: anamontiaga Date: Fri, 15 Dec 2023 13:32:45 +0100 Subject: [PATCH 1/3] hide save protected areas button if threshold slider is not dirty --- .../protected-areas/threshold/index.tsx | 226 +++++++++--------- 1 file changed, 114 insertions(+), 112 deletions(-) diff --git a/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx b/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx index 7517cfcff9..979c85f232 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx @@ -145,7 +145,6 @@ export const WDPAThreshold = ({ onGoBack }: { onGoBack: () => void }): JSX.Eleme level: 'success', } ); - onGoBack(); }, onError: () => { addToast( @@ -206,127 +205,130 @@ export const WDPAThreshold = ({ onGoBack }: { onGoBack: () => void }): JSX.Eleme - {({ values, handleSubmit: RFFhandleSubmit }) => ( -
- - -
-
-
- {/* WDPA */} -
- - {(flprops) => ( - -
- - -
-

- Threshold for Protected Areas -

-
-

- Refers to what percentage of a planning unit must be covered by - a protected area to be considered "protected" by - Marxan. -

-

- The following image shows an example setting a threshold of 50%: -

+ {({ values, handleSubmit: RFFhandleSubmit, form }) => { + return ( + + + +
+
+
+ {/* WDPA */} +
+ + {(flprops) => ( + +
+ + +
+

+ Threshold for Protected Areas +

+
+

+ Refers to what percentage of a planning unit must be covered + by a protected area to be considered "protected" by + Marxan. +

+

+ The following image shows an example setting a threshold of + 50%: +

+
+ + Threshold
- - Threshold -
- -
- -

- Refers to what percentage of a planning unit must be covered by a - protected area to be considered “protected”. -

- - { - flprops.input.onChange(s); - dispatch(setWDPAThreshold(s)); - }} - /> - - )} - + +
+ +

+ Refers to what percentage of a planning unit must be covered by a + protected area to be considered “protected”. +

+ + { + flprops.input.onChange(s); + dispatch(setWDPAThreshold(s)); + }} + /> + + )} + +
+ + {areGlobalPAreasSelected && ( + + )} + + {areProjectPAreasSelected && ( + + )}
- - {areGlobalPAreasSelected && ( - - )} - - {areProjectPAreasSelected && ( - - )}
-
- -
- - - {editable && ( + +
- )} -
- - )} + + {editable && form.getFieldState('wdpaThreshold')?.dirty && ( + + )} +
+ + ); + }} ); From cdeb9c9848bf91ccf956fe0c1b6c4a1265ec4117 Mon Sep 17 00:00:00 2001 From: anamontiaga Date: Mon, 18 Dec 2023 18:39:51 +0100 Subject: [PATCH 2/3] disable features btn ond grid setup when nothing has changed --- .../scenario/grid-setup/features/targets/list/component.tsx | 4 ++-- .../scenario/grid-setup/protected-areas/threshold/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/layout/project/sidebar/scenario/grid-setup/features/targets/list/component.tsx b/app/layout/project/sidebar/scenario/grid-setup/features/targets/list/component.tsx index 1d07c0fc32..16d7c5ae60 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/features/targets/list/component.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/features/targets/list/component.tsx @@ -253,7 +253,7 @@ export const ScenariosFeaturesTargets = ({ onGoBack }: { onGoBack: () => void }) return ( - {({ handleSubmit, values }) => ( + {({ handleSubmit, form, values }) => (
void }) type="submit" theme="primary" size="lg" - disabled={submitting} + disabled={submitting || !form.getFieldState('features')?.dirty} > Save diff --git a/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx b/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx index 979c85f232..2e54618858 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx @@ -314,13 +314,13 @@ export const WDPAThreshold = ({ onGoBack }: { onGoBack: () => void }): JSX.Eleme Back - {editable && form.getFieldState('wdpaThreshold')?.dirty && ( + {editable && ( From 5bd4613dd624f7328bf2d334c43bb048d8de56a4 Mon Sep 17 00:00:00 2001 From: anamontiaga Date: Mon, 18 Dec 2023 19:26:40 +0100 Subject: [PATCH 3/3] disable mutation when there is no changes an add info toast --- .../features/targets/list/component.tsx | 155 ++++++++++-------- .../protected-areas/threshold/index.tsx | 101 +++++++----- 2 files changed, 141 insertions(+), 115 deletions(-) diff --git a/app/layout/project/sidebar/scenario/grid-setup/features/targets/list/component.tsx b/app/layout/project/sidebar/scenario/grid-setup/features/targets/list/component.tsx index 16d7c5ae60..542a5c9ae1 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/features/targets/list/component.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/features/targets/list/component.tsx @@ -12,7 +12,7 @@ import { useDebouncedCallback } from 'use-debounce'; import { useSaveSelectedFeatures, useSelectedFeatures, useTargetedFeatures } from 'hooks/features'; import { useCanEditScenario } from 'hooks/permissions'; -import { useSaveScenario, useScenario } from 'hooks/scenarios'; +import { useToasts } from 'hooks/toast'; import Button from 'components/button'; import ConfirmationPrompt from 'components/confirmation-prompt'; @@ -26,6 +26,8 @@ export const ScenariosFeaturesTargets = ({ onGoBack }: { onGoBack: () => void }) const [confirmationTarget, setConfirmationTarget] = useState(null); const [confirmationFPF, setConfirmationFPF] = useState(null); + const { addToast } = useToasts(); + const queryClient = useQueryClient(); const { query } = useRouter(); const { pid, sid } = query as { pid: string; sid: string }; @@ -39,11 +41,6 @@ export const ScenariosFeaturesTargets = ({ onGoBack }: { onGoBack: () => void }) const editable = useCanEditScenario(pid, sid); const selectedFeaturesMutation = useSaveSelectedFeatures({}); - const saveScenarioMutation = useSaveScenario({ - requestConfig: { - method: 'PATCH', - }, - }); const { data: selectedFeaturesData } = useSelectedFeatures(sid, {}); @@ -53,9 +50,6 @@ export const ScenariosFeaturesTargets = ({ onGoBack }: { onGoBack: () => void }) isFetched: targetedFeaturesIsFetched, } = useTargetedFeatures(sid); - const { data: scenarioData } = useScenario(sid); - const { metadata } = scenarioData || {}; - const INITIAL_VALUES = useMemo(() => { return { features: targetedFeaturesData, @@ -137,76 +131,93 @@ export const ScenariosFeaturesTargets = ({ onGoBack }: { onGoBack: () => void }) }, []); const onSubmit = useCallback( - (values) => { - setSubmitting(true); + (values, form) => { const { features } = values; - const data = { - status: 'created', - features: selectedFeaturesData.map((sf) => { - const { featureId, kind, geoprocessingOperations } = sf; - - if (kind === 'withGeoprocessing') { + const featuresFieldTouched = form.getFieldState('features')?.dirty; + + if (featuresFieldTouched) { + setSubmitting(true); + + const data = { + status: 'created', + features: selectedFeaturesData.map((sf) => { + const { featureId, kind, geoprocessingOperations } = sf; + + if (kind === 'withGeoprocessing') { + return { + featureId, + kind, + geoprocessingOperations: geoprocessingOperations.map((go) => { + const { splits } = go; + + return { + ...go, + splits: splits + .filter((s) => { + return features.find((f) => { + return f.parentId === featureId && f.value === s.value; + }); + }) + .map((s) => { + const { target, fpf } = features.find((f) => { + return f.parentId === featureId && f.value === s.value; + }); + + return { + ...s, + marxanSettings: { + prop: target / 100 || 0.5, + fpf, + }, + }; + }), + }; + }), + }; + } + + const { target, fpf = 1 } = features.find((f) => f.featureId === featureId); return { featureId, kind, - geoprocessingOperations: geoprocessingOperations.map((go) => { - const { splits } = go; - - return { - ...go, - splits: splits - .filter((s) => { - return features.find((f) => { - return f.parentId === featureId && f.value === s.value; - }); - }) - .map((s) => { - const { target, fpf } = features.find((f) => { - return f.parentId === featureId && f.value === s.value; - }); - - return { - ...s, - marxanSettings: { - prop: target / 100 || 0.5, - fpf, - }, - }; - }), - }; - }), + marxanSettings: { + prop: target / 100 || 0.5, + fpf, + }, }; - } + }), + }; - const { target, fpf = 1 } = features.find((f) => f.featureId === featureId); - return { - featureId, - kind, - marxanSettings: { - prop: target / 100 || 0.5, - fpf, - }, - }; - }), - }; - - selectedFeaturesMutation.mutate( - { - id: `${sid}`, - data, - }, - { - onSuccess: async () => { - await queryClient.invalidateQueries(['selected-features', sid]); - }, - onSettled: () => { - setSubmitting(false); + selectedFeaturesMutation.mutate( + { + id: `${sid}`, + data, }, - } - ); + { + onSuccess: async () => { + await queryClient.invalidateQueries(['selected-features', sid]); + }, + onSettled: () => { + setSubmitting(false); + }, + } + ); + } + if (!featuresFieldTouched) { + addToast( + 'save-selected-features', + <> +

+

No modifications have been made to the selected features.

+ , + { + level: 'info', + } + ); + } }, - [sid, queryClient, selectedFeaturesData, selectedFeaturesMutation] + [sid, queryClient, selectedFeaturesData, selectedFeaturesMutation, addToast] ); const toggleSeeOnMap = useCallback( @@ -253,7 +264,7 @@ export const ScenariosFeaturesTargets = ({ onGoBack }: { onGoBack: () => void }) return ( - {({ handleSubmit, form, values }) => ( + {({ handleSubmit, values }) => ( void }) type="submit" theme="primary" size="lg" - disabled={submitting || !form.getFieldState('features')?.dirty} + disabled={submitting} > Save diff --git a/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx b/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx index 2e54618858..7579ecbedc 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/protected-areas/threshold/index.tsx @@ -120,51 +120,66 @@ export const WDPAThreshold = ({ onGoBack }: { onGoBack: () => void }): JSX.Eleme const areProjectPAreasSelected = !!projectPAreasSelectedIds.length; const handleSubmit = useCallback( - (values) => { - setSubmitting(true); - + (values, form) => { const { wdpaThreshold } = values; - saveScenarioProtectedAreasMutation.mutate( - { - id: `${sid}`, - data: { - areas: selectedProtectedAreas, - threshold: +(wdpaThreshold * 100).toFixed(0), - }, - }, - { - onSuccess: () => { - addToast( - 'save-scenario-wdpa', - <> -

Success!

-

Scenario protected areas threshold saved

- , - { - level: 'success', - } - ); - }, - onError: () => { - addToast( - 'error-scenario-wdpa', - <> -

Error!

-

Scenario protected areas threshold not saved

- , - { - level: 'error', - } - ); - }, - onSettled: () => { - setSubmitting(false); + const thresholdTouched = form.getFieldState('wdpaThreshold')?.dirty; + + if (thresholdTouched) { + setSubmitting(true); + saveScenarioProtectedAreasMutation.mutate( + { + id: `${sid}`, + data: { + areas: selectedProtectedAreas, + threshold: +(wdpaThreshold * 100).toFixed(0), + }, }, - } - ); + { + onSuccess: () => { + addToast( + 'save-scenario-wdpa', + <> +

Success!

+

Scenario protected areas threshold saved

+ , + { + level: 'success', + } + ); + }, + onError: () => { + addToast( + 'error-scenario-wdpa', + <> +

Error!

+

Scenario protected areas threshold not saved

+ , + { + level: 'error', + } + ); + }, + onSettled: () => { + setSubmitting(false); + }, + } + ); + } + if (!thresholdTouched) { + addToast( + 'save-scenario-wdpa', + <> +

+

No modifications have been made to the protected areas.

+ , + { + level: 'info', + } + ); + } }, - [saveScenarioProtectedAreasMutation, selectedProtectedAreas, sid, addToast, onGoBack] + [saveScenarioProtectedAreasMutation, selectedProtectedAreas, sid, addToast] ); useEffect(() => { @@ -205,7 +220,7 @@ export const WDPAThreshold = ({ onGoBack }: { onGoBack: () => void }): JSX.Eleme
- {({ values, handleSubmit: RFFhandleSubmit, form }) => { + {({ values, handleSubmit: RFFhandleSubmit }) => { return ( void }): JSX.Eleme size="lg" type="submit" className="relative px-20 md:px-9 lg:px-16 xl:px-20" - disabled={submitting || !form.getFieldState('wdpaThreshold')?.dirty} + disabled={submitting} > Save