From c920964d39b5ba3689ba424cfcc19c770bd40e47 Mon Sep 17 00:00:00 2001 From: Samir Kamal <1954121+skamril@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:12:53 +0100 Subject: [PATCH] feat(ui-studies): update validation in MoveStudyDialog ANT-2390 --- webapp/public/locales/en/main.json | 8 +- webapp/public/locales/fr/main.json | 8 +- .../App/Studies/MoveStudyDialog.tsx | 89 ++++++++++--------- webapp/src/services/api/study.ts | 11 ++- 4 files changed, 63 insertions(+), 53 deletions(-) diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json index f1b86728c2..8546537c54 100644 --- a/webapp/public/locales/en/main.json +++ b/webapp/public/locales/en/main.json @@ -78,6 +78,7 @@ "global.status": "Status", "global.semicolon": "Semicolon", "global.language": "Language", + "global.path": "Path", "global.time.hourly": "Hourly", "global.time.daily": "Daily", "global.time.weekly": "Weekly", @@ -154,6 +155,9 @@ "form.field.requireUppercase": "Must contain at least one uppercase letter.", "form.field.requireDigit": "Must contain at least one digit.", "form.field.requireSpecialChars": "Must contain at least one special character.", + "form.field.path.startWithSlashNotAllowed": "Path must not start with a '/'", + "form.field.path.endWithSlashNotAllowed": "Path must not end with a '/'", + "form.field.path.invalid": "Invalid path", "matrix.graphSelector": "Columns", "matrix.message.importHint": "Click or drag and drop a matrix here", "matrix.importNewMatrix": "Import a new matrix", @@ -620,7 +624,6 @@ "studies.error.loadStudy": "Failed to load study", "studies.error.runStudy": "Failed to run study", "studies.error.scanFolder": "Failed to start folder scan", - "studies.error.moveStudy": "Failed to move study {{study}}", "studies.error.saveData": "Failed to save data", "studies.error.copyStudy": "Failed to copy study", "studies.error.import": "Failed to import Study ({{uploadFile}})", @@ -631,11 +634,10 @@ "studies.error.createStudy": "Failed to create Study {{studyname}}", "studies.success.saveData": "Data saved with success", "studies.success.scanFolder": "Folder scan started", - "studies.success.moveStudy": "Study {{study}} was successfully moved to {{folder}}", + "studies.success.moveStudy": "Study '{{study}}' was successfully moved to '{{path}}'", "studies.success.createStudy": "Study {{studyname}} created successfully", "studies.studylaunched": "{{studyname}} launched!", "studies.copySuffix": "Copy", - "studies.folder": "Folder", "studies.filters.strictfolder": "Show only direct folder children", "studies.scanFolder": "Scan folder", "studies.moveStudy": "Move", diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json index ce4885b1cd..2538ac2f75 100644 --- a/webapp/public/locales/fr/main.json +++ b/webapp/public/locales/fr/main.json @@ -78,6 +78,7 @@ "global.status": "Statut", "global.semicolon": "Point-virgule", "global.language": "Langue", + "global.path": "Chemin", "global.time.hourly": "Horaire", "global.time.daily": "Journalier", "global.time.weekly": "Hebdomadaire", @@ -154,6 +155,9 @@ "form.field.requireUppercase": "Doit contenir au moins une lettre majuscule.", "form.field.requireDigit": "Doit contenir au moins un chiffre.", "form.field.requireSpecialChars": "Doit contenir au moins un caractère spécial.", + "form.field.path.startWithSlashNotAllowed": "Le chemin ne doit pas commencer par un '/'", + "form.field.path.endWithSlashNotAllowed": "Le chemin ne doit pas finir par un '/'", + "form.field.path.invalid": "Chemin invalide", "matrix.graphSelector": "Colonnes", "matrix.message.importHint": "Cliquer ou glisser une matrice ici", "matrix.importNewMatrix": "Import d'une nouvelle matrice", @@ -620,7 +624,6 @@ "studies.error.loadStudy": "Échec du chargement de l'étude", "studies.error.runStudy": "Échec du lancement de l'étude", "studies.error.scanFolder": "Échec du lancement du scan", - "studies.error.moveStudy": "Échec du déplacement de l'étude {{study}}", "studies.error.saveData": "Erreur lors de la sauvegarde des données", "studies.error.copyStudy": "Erreur lors de la copie de l'étude", "studies.error.import": "L'import de l'étude a échoué ({{uploadFile}})", @@ -631,11 +634,10 @@ "studies.error.createStudy": "Erreur lors de la création de l'étude {{studyname}}", "studies.success.saveData": "Donnée sauvegardée avec succès", "studies.success.scanFolder": "L'analyse du dossier a commencé", - "studies.success.moveStudy": "L'étude {{study}} a été déplacée avec succès vers {{folder}}", + "studies.success.moveStudy": "L'étude \"{{study}}\" a été déplacée avec succès vers \"{{folder}}\"", "studies.success.createStudy": "L'étude {{studyname}} a été crée avec succès", "studies.studylaunched": "{{studyname}} lancé(s) !", "studies.copySuffix": "Copie", - "studies.folder": "Dossier", "studies.filters.strictfolder": "Afficher uniquement les descendants directs", "studies.scanFolder": "Scanner le dossier", "studies.moveStudy": "Déplacer", diff --git a/webapp/src/components/App/Studies/MoveStudyDialog.tsx b/webapp/src/components/App/Studies/MoveStudyDialog.tsx index e3698438a5..d2a5882fb5 100644 --- a/webapp/src/components/App/Studies/MoveStudyDialog.tsx +++ b/webapp/src/components/App/Studies/MoveStudyDialog.tsx @@ -13,17 +13,33 @@ */ import { DialogProps } from "@mui/material"; -import TextField from "@mui/material/TextField"; import { useSnackbar } from "notistack"; -import * as R from "ramda"; import { useTranslation } from "react-i18next"; -import { usePromise } from "react-use"; import { StudyMetadata } from "../../../common/types"; -import useEnqueueErrorSnackbar from "../../../hooks/useEnqueueErrorSnackbar"; import { moveStudy } from "../../../services/api/study"; -import { isStringEmpty } from "../../../services/utils"; import FormDialog from "../../common/dialogs/FormDialog"; import { SubmitHandlerPlus } from "../../common/Form/types"; +import StringFE from "@/components/common/fieldEditors/StringFE"; +import * as R from "ramda"; +import { validatePath } from "@/utils/validation/string"; + +function formalizePath(studyId: StudyMetadata["id"], path: string | undefined) { + const pathTrimmed = path?.trim(); + + if (!pathTrimmed) { + return ""; + } + + const pathArray = pathTrimmed.split("/").filter(Boolean); + const lastFolder = R.last(pathArray); + + // Antares Web automatically add the study ID to a not empty path when moving a study (cf. `handleSubmit`) + if (lastFolder === studyId) { + return pathArray.slice(0, -1).join("/"); + } + + return pathArray.join("/"); +} interface Props extends DialogProps { study: StudyMetadata; @@ -33,36 +49,35 @@ interface Props extends DialogProps { function MoveStudyDialog(props: Props) { const { study, open, onClose } = props; const [t] = useTranslation(); - const mounted = usePromise(); const { enqueueSnackbar } = useSnackbar(); - const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); + const defaultValues = { - folder: R.join("/", R.dropLast(1, R.split("/", study.folder || ""))), + path: formalizePath(study.id, study.folder), }; //////////////////////////////////////////////////////////////// // Event Handlers //////////////////////////////////////////////////////////////// - const handleSubmit = async ( + const handleSubmit = (data: SubmitHandlerPlus) => { + const path = formalizePath(study.id, data.values.path); + return moveStudy(study.id, path && `${path}/${study.id}`); + }; + + const handleSubmitSuccessful = ( data: SubmitHandlerPlus, ) => { - const { folder } = data.values; - try { - await mounted(moveStudy(study.id, folder)); - enqueueSnackbar( - t("studies.success.moveStudy", { study: study.name, folder }), - { - variant: "success", - }, - ); - onClose(); - } catch (e) { - enqueueErrorSnackbar( - t("studies.error.moveStudy", { study: study.name }), - e as Error, - ); - } + onClose(); + + enqueueSnackbar( + t("studies.success.moveStudy", { + study: study.name, + path: data.values.path, + }), + { + variant: "success", + }, + ); }; //////////////////////////////////////////////////////////////// @@ -74,27 +89,19 @@ function MoveStudyDialog(props: Props) { open={open} config={{ defaultValues }} onSubmit={handleSubmit} + onSubmitSuccessful={handleSubmitSuccessful} onCancel={onClose} > - {(formObj) => ( - ( + { - return !isStringEmpty(value); - }, - })} /> )} diff --git a/webapp/src/services/api/study.ts b/webapp/src/services/api/study.ts index 8750b177f4..be78eda292 100644 --- a/webapp/src/services/api/study.ts +++ b/webapp/src/services/api/study.ts @@ -13,7 +13,7 @@ */ import { AxiosRequestConfig } from "axios"; -import { isBoolean, trimCharsStart } from "ramda-adjunct"; +import { isBoolean } from "ramda-adjunct"; import client from "./client"; import { FileStudyTreeConfigDTO, @@ -163,11 +163,10 @@ export const copyStudy = async ( return res.data; }; -export const moveStudy = async (sid: string, folder: string): Promise => { - const folderWithId = trimCharsStart("/", `${folder.trim()}/${sid}`); - await client.put( - `/v1/studies/${sid}/move?folder_dest=${encodeURIComponent(folderWithId)}`, - ); +export const moveStudy = async (sid: string, folder: string) => { + await client.put(`/v1/studies/${sid}/move`, null, { + params: { folder_dest: folder }, + }); }; export const archiveStudy = async (sid: string): Promise => {