From 02ed1fc7a28115c4773c03b27c68c189072a1746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Gonz=C3=A1lez=20Mu=C3=B1oz?= Date: Wed, 4 Sep 2024 14:39:15 +0200 Subject: [PATCH] feature bulk edition: only modified changes --- .../bulk-action-menu/modals/edit/index.tsx | 132 ++++++++++++------ .../grid-setup/features/target-spf/index.tsx | 82 ++++++++--- app/store/slices/scenarios/edit.ts | 16 ++- 3 files changed, 163 insertions(+), 67 deletions(-) diff --git a/app/layout/project/sidebar/scenario/grid-setup/features/target-spf/bulk-action-menu/modals/edit/index.tsx b/app/layout/project/sidebar/scenario/grid-setup/features/target-spf/bulk-action-menu/modals/edit/index.tsx index 5a95be5a51..9cc7d258ee 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/features/target-spf/bulk-action-menu/modals/edit/index.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/features/target-spf/bulk-action-menu/modals/edit/index.tsx @@ -4,6 +4,9 @@ import { Form as FormRFF, Field as FieldRFF, FormProps } from 'react-final-form' import { useRouter } from 'next/router'; +import { useAppSelector, useAppDispatch } from 'store/hooks'; +import { getScenarioEditSlice } from 'store/slices/scenarios/edit'; + import { useSaveSelectedFeatures, useSelectedFeatures } from 'hooks/features'; import { useToasts } from 'hooks/toast'; @@ -36,6 +39,11 @@ const EditModal = ({ const formRef = useRef['form']>(null); const selectedFeaturesMutation = useSaveSelectedFeatures({}); + const dispatch = useAppDispatch(); + + const scenarioSlice = getScenarioEditSlice(sid); + const { setOriginalFeatureValues } = scenarioSlice.actions; + const { originalFeatureValues } = useAppSelector((state) => state[`/scenarios/${sid}/edit`]); const selectedFeaturesQuery = useSelectedFeatures( sid, @@ -122,8 +130,8 @@ const EditModal = ({ kind, marxanSettings: selectedFeatures.find((f) => f.id === featureId) ? { - prop: target / 100 || 0.5, - fpf: +spf, + prop: targetChanged || unChangedFields ? target / 100 : sf.marxanSettings.prop, + fpf: spfChanged || unChangedFields ? +spf : sf.marxanSettings.fpf, } : sf.marxanSettings, }; @@ -140,6 +148,8 @@ const EditModal = ({ onDone?.(res); handleModal('edit', false); + dispatch(setOriginalFeatureValues({})); + addToast( 'success-edit-features', <> @@ -175,9 +185,35 @@ const EditModal = ({ handleModal, sid, onDone, + dispatch, + setOriginalFeatureValues, ] ); + const [targetChanged, spfChanged] = useMemo(() => { + const newValues = selectedFeatures.reduce((acc, feature) => { + return { + ...acc, + [feature.id]: { + prop: feature.marxanSettings.prop, + fpf: feature.marxanSettings.fpf, + }, + }; + }, {}); + + const changed = Object.keys(newValues).map((id) => ({ + id, + target: originalFeatureValues[id] + ? originalFeatureValues[id].prop !== newValues[id].prop + : false, + fpf: originalFeatureValues[id] ? originalFeatureValues[id].fpf !== newValues[id].fpf : false, + })); + + return [changed.some((c) => c.target), changed.some((c) => c.fpf)]; + }, [selectedFeatures, originalFeatureValues]); + + const unChangedFields = !targetChanged && !spfChanged; + return ( initialValues={{ @@ -196,50 +232,54 @@ const EditModal = ({

Edit selected features

- - name="target" - validate={composeValidators([{ presence: true }])} - > - {(fprops) => ( - - - - - - )} - - - - name="spf" - validate={composeValidators([{ presence: true }])} - > - {(fprops) => ( - - - - - - )} - + {(targetChanged || unChangedFields) && ( + + name="target" + validate={composeValidators([{ presence: true }])} + > + {(fprops) => ( + + + + + + )} + + )} + + {(spfChanged || unChangedFields) && ( + + name="spf" + validate={composeValidators([{ presence: true }])} + > + {(fprops) => ( + + + + + + )} + + )}
diff --git a/app/layout/project/sidebar/scenario/grid-setup/features/target-spf/index.tsx b/app/layout/project/sidebar/scenario/grid-setup/features/target-spf/index.tsx index e51764cd5a..c477808b7b 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/features/target-spf/index.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/features/target-spf/index.tsx @@ -68,8 +68,10 @@ const TargetAndSPFFeatures = (): JSX.Element => { const dispatch = useAppDispatch(); const scenarioSlice = getScenarioEditSlice(sid); - const { setLayerSettings } = scenarioSlice.actions; - const { layerSettings } = useAppSelector((state) => state[`/scenarios/${sid}/edit`]); + const { setLayerSettings, setOriginalFeatureValues } = scenarioSlice.actions; + const { layerSettings, originalFeatureValues } = useAppSelector( + (state) => state[`/scenarios/${sid}/edit`] + ); const allFeaturesQuery = useAllFeatures( pid, @@ -274,15 +276,38 @@ const TargetAndSPFFeatures = (): JSX.Element => { [targetedFeatures] ); - const handleSelectFeature = useCallback((evt: ChangeEvent) => { - if (evt.target.checked) { - setSelectedFeatureIds((prevFeatureIds) => [...prevFeatureIds, evt.target.value]); - } else { - setSelectedFeatureIds((prevFeatureIds) => - prevFeatureIds.filter((featureId) => featureId !== evt.target.value) - ); - } - }, []); + const handleSelectFeature = useCallback( + (evt: ChangeEvent) => { + if (evt.target.checked) { + setSelectedFeatureIds((prevFeatureIds) => [...prevFeatureIds, evt.target.value]); + + const selectedFeature = selectedFeaturesQuery.data?.find( + ({ id: _id }) => _id === evt.target.value + ); + + dispatch( + setOriginalFeatureValues({ + ...originalFeatureValues, + [evt.target.value]: { + ...selectedFeature.marxanSettings, + prop: selectedFeature.marxanSettings.prop * 100, + fpf: selectedFeature.marxanSettings.spf || selectedFeature.marxanSettings.fpf, + }, + }) + ); + } else { + setSelectedFeatureIds((prevFeatureIds) => + prevFeatureIds.filter((featureId) => featureId !== evt.target.value) + ); + + const clonedOriginalFeatureValues = { ...originalFeatureValues }; + delete clonedOriginalFeatureValues[evt.target.value]; + + dispatch(setOriginalFeatureValues(clonedOriginalFeatureValues)); + } + }, + [originalFeatureValues, setOriginalFeatureValues, dispatch, selectedFeaturesQuery.data] + ); const onSubmit = useCallback(() => { const data = { @@ -343,15 +368,32 @@ const TargetAndSPFFeatures = (): JSX.Element => { }); }, [sid, selectedFeaturesMutation, featureValues, selectedFeaturesQuery.data, targetedFeatures]); - const handleRowValues = useCallback((id, values) => { - setFeatureValues((prevValues) => ({ - ...prevValues, - [id]: { - ...prevValues[id], - ...values, - }, - })); - }, []); + const handleRowValues = useCallback( + (id: string, values) => { + const selectedFeature = selectedFeaturesQuery.data?.find(({ id: _id }) => _id === id); + + setFeatureValues((prevValues) => { + dispatch( + setOriginalFeatureValues({ + ...originalFeatureValues, + [id]: { + prop: selectedFeature.marxanSettings.prop * 100, + fpf: selectedFeature.marxanSettings.fpf, + }, + }) + ); + + return { + ...prevValues, + [id]: { + ...prevValues[id], + ...values, + }, + }; + }); + }, + [selectedFeaturesQuery.data, dispatch, setOriginalFeatureValues, originalFeatureValues] + ); const handleRowDeletion = useCallback( (featureToRemove) => { diff --git a/app/store/slices/scenarios/edit.ts b/app/store/slices/scenarios/edit.ts index a5aa30b631..9f3819eccf 100644 --- a/app/store/slices/scenarios/edit.ts +++ b/app/store/slices/scenarios/edit.ts @@ -25,6 +25,14 @@ export interface ScenarioEditStateProps { selectedContinuousFeatures: Feature['id'][]; preHighlightFeatures: string[]; postHighlightFeatures: string[]; + originalFeatureValues: + | { + [key: Feature['id']]: { + prop: number; + fpf: number; + }; + } + | {}; // Cost Surface selectedCostSurface: CostSurface['id']; @@ -80,6 +88,7 @@ const initialState = { postHighlightFeatures: [], selectedCostSurface: null, + originalFeatureValues: {}, // ADJUST PLANNING UNITS cache: Date.now(), @@ -162,7 +171,12 @@ export function getScenarioEditSlice(id) { setPostHighlightFeatures: (state, action: PayloadAction) => { state.postHighlightFeatures = action.payload; }, - + setOriginalFeatureValues: ( + state, + action: PayloadAction + ) => { + state.originalFeatureValues = action.payload; + }, setSelectedCostSurface: ( state, action: PayloadAction