From 8ce35d23934f99fb6fe0b92671a2c1b2228455a5 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Wed, 27 Mar 2024 09:55:21 +0100 Subject: [PATCH] fix(bc): TS matrix was not sync with the constraint state --- .../AddConstraintTermDialog/index.tsx | 4 +- .../BindingConstView/BindingConstForm.tsx | 132 +++-------------- .../BindingConstView/ConstraintFields.tsx | 81 +++++++---- .../BindingConstView/index.tsx | 136 +++++++++++++++--- .../BindingConstView/style.ts | 16 --- .../BindingConstView/utils.ts | 2 +- webapp/src/services/api/studydata.ts | 4 +- 7 files changed, 197 insertions(+), 178 deletions(-) delete mode 100644 webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/style.ts diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/AddConstraintTermDialog/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/AddConstraintTermDialog/index.tsx index a10f93ed37..c1bf8216a5 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/AddConstraintTermDialog/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/AddConstraintTermDialog/index.tsx @@ -18,7 +18,7 @@ import { BaseSyntheticEvent } from "react"; interface Props extends Omit { studyId: string; constraintId: string; - append: UseFieldArrayAppend; + append: UseFieldArrayAppend; constraintTerms: ConstraintTerm[]; options: AllClustersAndLinks; } @@ -56,7 +56,7 @@ function AddConstraintTermDialog({ //////////////////////////////////////////////////////////////// const handleSubmit = async ( - { values }: SubmitHandlerPlus>, + { values }: SubmitHandlerPlus>, // TODO fix type _event?: BaseSyntheticEvent, ) => { try { diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/BindingConstForm.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/BindingConstForm.tsx index 0ea050b917..81b33b9260 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/BindingConstForm.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/BindingConstForm.tsx @@ -3,17 +3,14 @@ import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Box, Button } from "@mui/material"; import AddCircleOutlineRoundedIcon from "@mui/icons-material/AddCircleOutlineRounded"; -import DatasetIcon from "@mui/icons-material/Dataset"; -import { useFieldArray } from "react-hook-form"; import DeleteIcon from "@mui/icons-material/Delete"; +import { useFieldArray } from "react-hook-form"; import { useSnackbar } from "notistack"; -import { useNavigate } from "react-router-dom"; import useEnqueueErrorSnackbar from "../../../../../../../hooks/useEnqueueErrorSnackbar"; import { type ConstraintTerm, generateTermId, BindingConstraint, - ACTIVE_WINDOWS_DOC_PATH, } from "./utils"; import { AllClustersAndLinks, @@ -26,16 +23,9 @@ import { updateConstraintTerm, } from "../../../../../../../services/api/studydata"; import TextSeparator from "../../../../../../common/TextSeparator"; -import { TermsHeader, TermsList } from "./style"; import AddConstraintTermDialog from "./AddConstraintTermDialog"; import ConfirmationDialog from "../../../../../../common/dialogs/ConfirmationDialog"; import useDebounce from "../../../../../../../hooks/useDebounce"; -import { appendCommands } from "../../../../../../../services/api/variant"; -import { CommandEnum } from "../../../../Commands/Edition/commandTypes"; -import useAppDispatch from "../../../../../../../redux/hooks/useAppDispatch"; -import { setCurrentBindingConst } from "../../../../../../../redux/ducks/studySyntheses"; -import Matrix from "./Matrix"; -import DocLink from "../../../../../../common/DocLink"; import Fieldset from "../../../../../../common/Fieldset"; interface Props { @@ -46,23 +36,18 @@ interface Props { // TODO rename ConstraintTermsFields function BindingConstForm({ study, options, constraintId }: Props) { - const { id: studyId } = study; // TODO remove and refactor ids const [t] = useTranslation(); - const navigate = useNavigate(); - const dispatch = useAppDispatch(); const { enqueueSnackbar } = useSnackbar(); const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); - const [matrixDialogOpen, setMatrixDialogOpen] = useState(false); const [termToDelete, setTermToDelete] = useState(); - const [constraintToDelete, setConstraintToDelete] = useState(false); const [openConstraintTermDialog, setOpenConstraintTermDialog] = useState(false); - const { control, getValues } = useFormContextPlus(); + const { control } = useFormContextPlus(); const { fields, update, append, remove } = useFieldArray({ control, - name: "constraints", + name: "terms", }); const constraintTerms = useMemo( @@ -70,8 +55,6 @@ function BindingConstForm({ study, options, constraintId }: Props) { [fields], ); - const currentOperator = getValues("operator"); - //////////////////////////////////////////////////////////////// // Event Handlers //////////////////////////////////////////////////////////////// @@ -123,32 +106,6 @@ function BindingConstForm({ study, options, constraintId }: Props) { } }; - const handleDeleteConstraint = async () => { - try { - await appendCommands(study.id, [ - { - action: CommandEnum.REMOVE_BINDING_CONSTRAINT, - args: { - id: constraintId, - }, - }, - ]); - - dispatch(setCurrentBindingConst("")); - - navigate(`/studies/${study.id}/explore/modelization/bindingcontraint`); - - enqueueSnackbar(t("study.success.deleteConstraint"), { - variant: "success", - autoHideDuration: 1500, - }); - } catch (e) { - enqueueErrorSnackbar(t("study.error.deleteConstraint"), e as AxiosError); - } finally { - setConstraintToDelete(false); - } - }; - //////////////////////////////////////////////////////////////// // JSX //////////////////////////////////////////////////////////////// @@ -157,45 +114,24 @@ function BindingConstForm({ study, options, constraintId }: Props) { <>
- - - - - - - - - - - - + + + + {constraintTerms.map((term: ConstraintTerm, index: number) => ( {index > 0 && ( @@ -210,23 +146,13 @@ function BindingConstForm({ study, options, constraintId }: Props) { /> ))} - +
- {matrixDialogOpen && ( - setMatrixDialogOpen(false)} - /> - )} - {openConstraintTermDialog && ( setOpenConstraintTermDialog(false)} @@ -247,20 +173,6 @@ function BindingConstForm({ study, options, constraintId }: Props) { {t("study.modelization.bindingConst.question.deleteConstraintTerm")} )} - - {constraintToDelete && ( - setConstraintToDelete(false)} - onConfirm={() => handleDeleteConstraint()} - alert="warning" - open - > - {t( - "study.modelization.bindingConst.question.deleteBindingConstraint", - )} - - )} ); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/ConstraintFields.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/ConstraintFields.tsx index 8a5698fabb..57ee7b54dd 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/ConstraintFields.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/ConstraintFields.tsx @@ -1,26 +1,33 @@ -import Fieldset from "../../../../../../common/Fieldset"; -import SelectFE from "../../../../../../common/fieldEditors/SelectFE"; -import StringFE from "../../../../../../common/fieldEditors/StringFE"; -import { useTranslation } from "react-i18next"; import { BindingConstraint, OPERATORS, OUTPUT_FILTERS, TIME_STEPS, } from "./utils"; -import { useFormContextPlus } from "../../../../../../common/Form"; -import { useMemo } from "react"; + +import Fieldset from "../../../../../../common/Fieldset"; +import SelectFE from "../../../../../../common/fieldEditors/SelectFE"; +import StringFE from "../../../../../../common/fieldEditors/StringFE"; import { StudyMetadata } from "../../../../../../../common/types"; import SwitchFE from "../../../../../../common/fieldEditors/SwitchFE"; +import { useFormContextPlus } from "../../../../../../common/Form"; +import { useMemo } from "react"; +import { useTranslation } from "react-i18next"; import { validateString } from "../../../../../../../utils/validationUtils"; +import Matrix from "./Matrix"; +import { Box } from "@mui/material"; interface Props { study: StudyMetadata; + constraintId: string; + isMatrixOpen: boolean; + onCloseMatrix: VoidFunction; } -function Fields({ study }: Props) { +function Fields({ study, constraintId, isMatrixOpen, onCloseMatrix }: Props) { const { t } = useTranslation(); - const { control } = useFormContextPlus(); + const { control, getValues } = useFormContextPlus(); + const currentOperator = getValues("operator"); const outputFilterOptions = useMemo( () => @@ -55,7 +62,10 @@ function Fields({ study }: Props) { return ( <> -
+
+ + {study.version >= "840" && ( + + + + + )}
- {study.version >= "840" && ( -
- - -
+ {isMatrixOpen && ( + )} ); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/index.tsx b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/index.tsx index 69311e3d67..d4530773f2 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/index.tsx @@ -1,31 +1,51 @@ -import { Box, Paper } from "@mui/material"; -import { useOutletContext } from "react-router"; -import { StudyMetadata } from "../../../../../../../common/types"; -import usePromise from "../../../../../../../hooks/usePromise"; +import { ACTIVE_WINDOWS_DOC_PATH, BindingConstraint } from "./utils"; +import { Box, Button, Paper } from "@mui/material"; import Form from "../../../../../../common/Form"; -import BindingConstForm from "./BindingConstForm"; import UsePromiseCond, { mergeResponses, } from "../../../../../../common/utils/UsePromiseCond"; -import useStudySynthesis from "../../../../../../../redux/hooks/useStudySynthesis"; -import { getLinksAndClusters } from "../../../../../../../redux/selectors"; import { getBindingConstraint, updateBindingConstraint, } from "../../../../../../../services/api/studydata"; +import { useOutletContext } from "react-router"; + +import { AxiosError } from "axios"; +import BindingConstForm from "./BindingConstForm"; +import { CommandEnum } from "../../../../Commands/Edition/commandTypes"; +import ConfirmationDialog from "../../../../../../common/dialogs/ConfirmationDialog"; import ConstraintFields from "./ConstraintFields"; +import Delete from "@mui/icons-material/Delete"; +import DocLink from "../../../../../../common/DocLink"; +import { StudyMetadata } from "../../../../../../../common/types"; import { SubmitHandlerPlus } from "../../../../../../common/Form/types"; -import { BindingConstraint } from "./utils"; +import { appendCommands } from "../../../../../../../services/api/variant"; +import { getLinksAndClusters } from "../../../../../../../redux/selectors"; +import { setCurrentBindingConst } from "../../../../../../../redux/ducks/studySyntheses"; +import { t } from "i18next"; +import useAppDispatch from "../../../../../../../redux/hooks/useAppDispatch"; +import useEnqueueErrorSnackbar from "../../../../../../../hooks/useEnqueueErrorSnackbar"; +import usePromise from "../../../../../../../hooks/usePromise"; +import { useSnackbar } from "notistack"; +import { useState } from "react"; +import useStudySynthesis from "../../../../../../../redux/hooks/useStudySynthesis"; +import { Dataset } from "@mui/icons-material"; interface Props { constraintId: string; } -// TODO rename Form (its the constraint form => properties + terms forms) +// TODO rename Form (its the constraint form => properties form + terms form) function BindingConstView({ constraintId }: Props) { const { study } = useOutletContext<{ study: StudyMetadata }>(); + const dispatch = useAppDispatch(); + const { enqueueSnackbar } = useSnackbar(); + const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); + const [matrixDialogOpen, setMatrixDialogOpen] = useState(false); + const [deleteConstraintDialogOpen, setDeleteConstraintDialogOpen] = + useState(false); - const bindingConstraint = usePromise( + const constraint = usePromise( () => getBindingConstraint(study.id, constraintId), [study.id, constraintId], ); @@ -42,11 +62,44 @@ function BindingConstView({ constraintId }: Props) { const handleSubmitConstraint = ({ values, }: SubmitHandlerPlus) => { - const { id, name, constraints, ...updatedConstraint } = values; + const { id, name, ...updatedConstraint } = values; return updateBindingConstraint(study.id, constraintId, updatedConstraint); }; + /** + * @deprecated + * We should use a dedicated DELETE endpoint for removing binding constraints. This endpoint is + * not currently exposed by the API, but it is recommended to transition to it once available. + * + * This function sends a commandto remove the binding constraint by its ID. + * After successful deletion, it triggers a UI update to reflect the removal. + */ + const handleDeleteConstraint = async () => { + try { + await appendCommands(study.id, [ + { + action: CommandEnum.REMOVE_BINDING_CONSTRAINT, + args: { + id: constraintId, + }, + }, + ]); + + // Trigger a reload redirecting to the first constraint + dispatch(setCurrentBindingConst("")); + + enqueueSnackbar(t("study.success.deleteConstraint"), { + variant: "success", + autoHideDuration: 1500, + }); + } catch (e) { + enqueueErrorSnackbar(t("study.error.deleteConstraint"), e as AxiosError); + } finally { + setDeleteConstraintDialogOpen(false); + } + }; + //////////////////////////////////////////////////////////////// // JSX //////////////////////////////////////////////////////////////// @@ -54,7 +107,7 @@ function BindingConstView({ constraintId }: Props) { return ( ( <> + + + + + + +
- + setMatrixDialogOpen(false)} + />
-
represents a constraint terms list options of areas/links. options={linksAndClusters} /> @@ -90,6 +178,20 @@ function BindingConstView({ constraintId }: Props) { )} /> + + {deleteConstraintDialogOpen && ( + setDeleteConstraintDialogOpen(false)} + onConfirm={handleDeleteConstraint} + alert="warning" + open + > + {t( + "study.modelization.bindingConst.question.deleteBindingConstraint", + )} + + )}
); } diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/style.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/style.ts deleted file mode 100644 index 28eebd6a4e..0000000000 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/style.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Box, styled } from "@mui/material"; - -export const TermsList = styled(Box)(({ theme }) => ({ - display: "flex", - width: "100%", - flexDirection: "column", - marginBottom: theme.spacing(1), -})); - -export const TermsHeader = styled(Box)(({ theme }) => ({ - width: "100%", - display: "flex", - alignItems: "center", - justifyContent: "space-between", - marginBottom: theme.spacing(2), -})); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/utils.ts index 24d3666617..320284f888 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/BindingConstraints/BindingConstView/utils.ts @@ -60,7 +60,7 @@ export interface BindingConstraint { comments?: string; filterSynthesis: OutputFilter[]; filterYearByYear: OutputFilter[]; - constraints: ConstraintTerm[]; + terms: ConstraintTerm[]; } //////////////////////////////////////////////////////////////// diff --git a/webapp/src/services/api/studydata.ts b/webapp/src/services/api/studydata.ts index 54b85f256d..6558a3d15f 100644 --- a/webapp/src/services/api/studydata.ts +++ b/webapp/src/services/api/studydata.ts @@ -132,9 +132,9 @@ export const getBindingConstraintList = async ( export const updateBindingConstraint = async ( studyId: string, constraintId: string, - data: Omit, + data: Omit, ): Promise => { - const adaptedData = bindingConstraintModelAdapter(data); // TODO fix type + const adaptedData = bindingConstraintModelAdapter(data as BindingConstraint); // TODO fix type const res = await client.put( `/v1/studies/${studyId}/bindingconstraints/${encodeURIComponent(