Skip to content

Commit

Permalink
feature bulk edition: only modified changes
Browse files Browse the repository at this point in the history
  • Loading branch information
andresgnlez committed Sep 4, 2024
1 parent 481218f commit 02ed1fc
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -36,6 +39,11 @@ const EditModal = ({

const formRef = useRef<FormProps<FormValues>['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,
Expand Down Expand Up @@ -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,
};
Expand All @@ -140,6 +148,8 @@ const EditModal = ({
onDone?.(res);
handleModal('edit', false);

dispatch(setOriginalFeatureValues({}));

addToast(
'success-edit-features',
<>
Expand Down Expand Up @@ -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 (
<FormRFF<FormValues>
initialValues={{
Expand All @@ -196,50 +232,54 @@ const EditModal = ({
<h2 className="font-heading font-bold text-black">Edit selected features</h2>

<div className="flex w-full space-x-2">
<FieldRFF<FormValues['target']>
name="target"
validate={composeValidators([{ presence: true }])}
>
{(fprops) => (
<Field id="target" {...fprops} className="flex-1">
<Label theme="light" className="mb-3 text-xs font-semibold uppercase">
Target (%)
</Label>

<input
{...fprops.input}
type="number"
className={INPUT_CLASSES}
defaultValue={fprops.input.value}
min={0}
max={100}
step={0.01}
/>
</Field>
)}
</FieldRFF>

<FieldRFF<FormValues['spf']>
name="spf"
validate={composeValidators([{ presence: true }])}
>
{(fprops) => (
<Field id="spf" {...fprops} className="flex-1">
<Label theme="light" className="mb-3 text-xs font-semibold uppercase">
SPF
</Label>

<input
{...fprops.input}
type="number"
className={INPUT_CLASSES}
defaultValue={fprops.input.value}
min={1}
step={0.01}
/>
</Field>
)}
</FieldRFF>
{(targetChanged || unChangedFields) && (
<FieldRFF<FormValues['target']>
name="target"
validate={composeValidators([{ presence: true }])}
>
{(fprops) => (
<Field id="target" {...fprops} className="flex-1">
<Label theme="light" className="mb-3 text-xs font-semibold uppercase">
Target (%)
</Label>

<input
{...fprops.input}
type="number"
className={INPUT_CLASSES}
defaultValue={fprops.input.value}
min={0}
max={100}
step={0.01}
/>
</Field>
)}
</FieldRFF>
)}

{(spfChanged || unChangedFields) && (
<FieldRFF<FormValues['spf']>
name="spf"
validate={composeValidators([{ presence: true }])}
>
{(fprops) => (
<Field id="spf" {...fprops} className="flex-1">
<Label theme="light" className="mb-3 text-xs font-semibold uppercase">
SPF
</Label>

<input
{...fprops.input}
type="number"
className={INPUT_CLASSES}
defaultValue={fprops.input.value}
min={1}
step={0.01}
/>
</Field>
)}
</FieldRFF>
)}
</div>

<div className="mt-16 flex justify-center space-x-6">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -274,15 +276,38 @@ const TargetAndSPFFeatures = (): JSX.Element => {
[targetedFeatures]
);

const handleSelectFeature = useCallback((evt: ChangeEvent<HTMLInputElement>) => {
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<HTMLInputElement>) => {
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 = {
Expand Down Expand Up @@ -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) => {
Expand Down
16 changes: 15 additions & 1 deletion app/store/slices/scenarios/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
Expand Down Expand Up @@ -80,6 +88,7 @@ const initialState = {
postHighlightFeatures: [],

selectedCostSurface: null,
originalFeatureValues: {},

// ADJUST PLANNING UNITS
cache: Date.now(),
Expand Down Expand Up @@ -162,7 +171,12 @@ export function getScenarioEditSlice(id) {
setPostHighlightFeatures: (state, action: PayloadAction<string[]>) => {
state.postHighlightFeatures = action.payload;
},

setOriginalFeatureValues: (
state,
action: PayloadAction<ScenarioEditStateProps['originalFeatureValues']>
) => {
state.originalFeatureValues = action.payload;
},
setSelectedCostSurface: (
state,
action: PayloadAction<ScenarioEditStateProps['selectedCostSurface']>
Expand Down

0 comments on commit 02ed1fc

Please sign in to comment.