From 003e7d308c10683e0d1a4bce32d784192addfc32 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Thu, 22 May 2025 13:08:41 +0200 Subject: [PATCH 1/3] replace `yupConfig` --- package-lock.json | 15 +++++++++++---- package.json | 2 +- src/components/app.tsx | 2 ++ .../creation/contingency-list-creation-dialog.tsx | 8 ++++---- .../criteria-based-edition-dialog.tsx | 6 +++--- .../explicit-naming-edition-dialog.tsx | 4 ++-- .../explicit-naming/explicit-naming-utils.tsx | 3 ++- src/components/dialogs/copy-to-script-dialog.tsx | 14 ++++---------- .../create-case-dialog-utils.ts | 3 ++- .../create-study-dialog-utils.ts | 11 +++-------- .../dialogs/directory-properties/utils.ts | 5 +++-- .../composite-modification-dialog.tsx | 12 +++++------- 12 files changed, 42 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01e5c9689..f84277560 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@gridsuite/commons-ui": "0.102.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.102.0.tgz", "@hookform/resolvers": "^4.0.0", "@mui/icons-material": "^5.16.14", "@mui/lab": "5.0.0-alpha.175", @@ -3137,8 +3137,8 @@ }, "node_modules/@gridsuite/commons-ui": { "version": "0.102.0", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.102.0.tgz", - "integrity": "sha512-Sg2dMZ/dHQjQdBDARPI4BuUEJVN5P+nbKgT9KtQmVRMP3eepiwzWTWcCtw6h6ATLcCNk1A193fr7Te5OnsqLgw==", + "resolved": "file:../commons-ui/gridsuite-commons-ui-0.102.0.tgz", + "integrity": "sha512-MjT4hNRzkQs6T3NrmXjunVNEjZAPQntAwvWa35eGHGhsxTCj6ORVDxQviX68GelKTpP5IaZr11wbz9U6LdZU7A==", "license": "MPL-2.0", "dependencies": { "@ag-grid-community/locale": "^33.1.0", @@ -3160,7 +3160,8 @@ "react-querybuilder": "^8.2.0", "reconnecting-websocket": "^4.4.0", "type-fest": "^4.34.1", - "uuid": "^11.0.5" + "uuid": "^11.0.5", + "yup-locales": "^1.2.28" }, "engines": { "node": ">=22", @@ -15453,6 +15454,12 @@ "type-fest": "^2.19.0" } }, + "node_modules/yup-locales": { + "version": "1.2.28", + "resolved": "https://registry.npmjs.org/yup-locales/-/yup-locales-1.2.28.tgz", + "integrity": "sha512-q2p5XDVThFXTLOOV1DgMbpMOfrxfZTjYSkS1WRWco6YZJOeHrbuM3ISytrLaYa8TiX/YPtE9tuQmMiUyTBZDIw==", + "license": "MIT" + }, "node_modules/yup/node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", diff --git a/package.json b/package.json index ca2163933..717851fbe 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@gridsuite/commons-ui": "0.102.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.102.0.tgz", "@hookform/resolvers": "^4.0.0", "@mui/icons-material": "^5.16.14", "@mui/lab": "5.0.0-alpha.175", diff --git a/src/components/app.tsx b/src/components/app.tsx index 7e49fa67e..14b80a6cf 100644 --- a/src/components/app.tsx +++ b/src/components/app.tsx @@ -20,6 +20,7 @@ import { useNotificationsListener, UserManagerState, useSnackMessage, + useYupIntl, } from '@gridsuite/commons-ui'; import { FormattedMessage } from 'react-intl'; import { Grid } from '@mui/material'; @@ -35,6 +36,7 @@ import { AppState } from '../redux/types'; export default function App() { const { snackError } = useSnackMessage(); + useYupIntl(); const user = useSelector((state: AppState) => state.user); diff --git a/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx b/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx index b91a2d45f..615a06cce 100644 --- a/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx +++ b/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx @@ -12,22 +12,22 @@ import { getCriteriaBasedSchema, MAX_CHAR_DESCRIPTION, useSnackMessage, - yupConfig as yup, } from '@gridsuite/commons-ui'; +import * as yup from 'yup'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { createContingencyList } from '../../../../utils/rest-api'; import ContingencyListCreationForm from './contingency-list-creation-form'; import { - ContingencyListFormData, - ContingencyListFormDataWithRequiredCriteria, + type ContingencyListFormData, + type ContingencyListFormDataWithRequiredCriteria, getContingencyListEmptyFormData, getFormContent, } from '../contingency-list-utils'; import { ContingencyListType } from '../../../../utils/elementType'; import { useParameterState } from '../../use-parameters-dialog'; import { PARAM_LANGUAGE } from '../../../../utils/config-params'; -import { AppState } from '../../../../redux/types'; +import type { AppState } from '../../../../redux/types'; import { getExplicitNamingSchema } from '../explicit-naming/explicit-naming-utils'; import { handleNotAllowedError } from '../../../utils/rest-errors'; diff --git a/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx b/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx index 825a1f5f9..b4eb6e186 100644 --- a/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx +++ b/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx @@ -12,14 +12,14 @@ import { MAX_CHAR_DESCRIPTION, NO_ITEM_SELECTION_FOR_COPY, useSnackMessage, - yupConfig as yup, } from '@gridsuite/commons-ui'; +import * as yup from 'yup'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { useEffect, useState } from 'react'; import { getContingencyList, saveCriteriaBasedContingencyList } from 'utils/rest-api'; import { useDispatch, useSelector } from 'react-redux'; -import { AppState } from '../../../../../redux/types'; +import type { AppState } from '../../../../../redux/types'; import { getContingencyListEmptyFormData, getCriteriaBasedFormDataFromFetchedElement, @@ -27,7 +27,7 @@ import { import CriteriaBasedEditionForm from './criteria-based-edition-form'; import { setItemSelectionForCopy } from '../../../../../redux/actions'; import { useParameterState } from '../../../use-parameters-dialog'; -import { CriteriaBasedEditionFormData } from '../../../../../utils/rest-api'; +import { type CriteriaBasedEditionFormData } from '../../../../../utils/rest-api'; import { PARAM_LANGUAGE } from '../../../../../utils/config-params'; const schema = yup.object().shape({ diff --git a/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx b/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx index a628eb499..953a161f8 100644 --- a/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx +++ b/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx @@ -11,8 +11,8 @@ import { MAX_CHAR_DESCRIPTION, NO_ITEM_SELECTION_FOR_COPY, useSnackMessage, - yupConfig as yup, } from '@gridsuite/commons-ui'; +import * as yup from 'yup'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { useEffect, useState } from 'react'; @@ -25,7 +25,7 @@ import { } from '../../contingency-list-utils'; import ExplicitNamingEditionForm from './explicit-naming-edition-form'; import { setItemSelectionForCopy } from '../../../../../redux/actions'; -import { AppState } from '../../../../../redux/types'; +import type { AppState } from '../../../../../redux/types'; import { getExplicitNamingEditSchema } from '../../explicit-naming/explicit-naming-utils'; interface ExplicitNamingEditionFormData { diff --git a/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx b/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx index 909c695e2..a411441d8 100644 --- a/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx +++ b/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx @@ -5,7 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { FieldConstants, yupConfig as yup } from '@gridsuite/commons-ui'; +import * as yup from 'yup'; +import { FieldConstants } from '@gridsuite/commons-ui'; import { ContingencyListType } from '../../../../utils/elementType'; const getExplicitNamingConditionSchema = (schema: yup.ArraySchema) => diff --git a/src/components/dialogs/copy-to-script-dialog.tsx b/src/components/dialogs/copy-to-script-dialog.tsx index d2dab511f..976789de5 100644 --- a/src/components/dialogs/copy-to-script-dialog.tsx +++ b/src/components/dialogs/copy-to-script-dialog.tsx @@ -9,18 +9,12 @@ import { useIntl } from 'react-intl'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { CircularProgress, Grid } from '@mui/material'; -import { - CustomMuiDialog, - ElementType, - FieldConstants, - UniqueNameInput, - useSnackMessage, - yupConfig as yup, -} from '@gridsuite/commons-ui'; -import { UUID } from 'crypto'; +import { CustomMuiDialog, ElementType, FieldConstants, UniqueNameInput, useSnackMessage } from '@gridsuite/commons-ui'; +import type { UUID } from 'crypto'; import { useSelector } from 'react-redux'; +import * as yup from 'yup'; import { getNameCandidate } from '../../utils/rest-api'; -import { AppState } from '../../redux/types'; +import type { AppState } from '../../redux/types'; import { handleGenericTxtError } from '../utils/rest-errors'; const schema = yup.object().shape({ diff --git a/src/components/dialogs/create-case-dialog/create-case-dialog-utils.ts b/src/components/dialogs/create-case-dialog/create-case-dialog-utils.ts index 58463c950..c8f1d038a 100644 --- a/src/components/dialogs/create-case-dialog/create-case-dialog-utils.ts +++ b/src/components/dialogs/create-case-dialog/create-case-dialog-utils.ts @@ -5,7 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { FieldConstants, MAX_CHAR_DESCRIPTION, yupConfig as yup } from '@gridsuite/commons-ui'; +import * as yup from 'yup'; +import { FieldConstants, MAX_CHAR_DESCRIPTION } from '@gridsuite/commons-ui'; export const getCreateCaseDialogFormValidationDefaultValues = () => ({ [FieldConstants.CASE_NAME]: '', diff --git a/src/components/dialogs/create-study-dialog/create-study-dialog-utils.ts b/src/components/dialogs/create-study-dialog/create-study-dialog-utils.ts index 217610c9a..3ae64ab08 100644 --- a/src/components/dialogs/create-study-dialog/create-study-dialog-utils.ts +++ b/src/components/dialogs/create-study-dialog/create-study-dialog-utils.ts @@ -5,14 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { - ElementAttributes, - FieldConstants, - MAX_CHAR_DESCRIPTION, - Parameter, - yupConfig as yup, -} from '@gridsuite/commons-ui'; -import { UUID } from 'crypto'; +import { type ElementAttributes, FieldConstants, MAX_CHAR_DESCRIPTION, type Parameter } from '@gridsuite/commons-ui'; +import * as yup from 'yup'; +import type { UUID } from 'crypto'; export const getCreateStudyDialogFormDefaultValues = ({ // @ts-expect-error how react-hook-form manage strings like UUIDs? diff --git a/src/components/dialogs/directory-properties/utils.ts b/src/components/dialogs/directory-properties/utils.ts index c6fe8d88f..aed5a12c0 100644 --- a/src/components/dialogs/directory-properties/utils.ts +++ b/src/components/dialogs/directory-properties/utils.ts @@ -5,8 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Option, yupConfig as yup } from '@gridsuite/commons-ui'; -import { UUID } from 'crypto'; +import * as yup from 'yup'; +import { type Option } from '@gridsuite/commons-ui'; +import type { UUID } from 'crypto'; export const READ_ALL_USERS = 'readAllUsers'; export const READ_GROUPS = 'readGroups'; diff --git a/src/components/dialogs/network-modification/composite-modification/composite-modification-dialog.tsx b/src/components/dialogs/network-modification/composite-modification/composite-modification-dialog.tsx index d2536c87b..aec2f2a5b 100644 --- a/src/components/dialogs/network-modification/composite-modification/composite-modification-dialog.tsx +++ b/src/components/dialogs/network-modification/composite-modification/composite-modification-dialog.tsx @@ -4,25 +4,23 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { SyntheticEvent, useEffect, useState } from 'react'; +import { type SyntheticEvent, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import Box from '@mui/material/Box'; -import Divider from '@mui/material/Divider'; +import { Box, Divider, List, ListItem } from '@mui/material'; import { useForm } from 'react-hook-form'; import { useIntl } from 'react-intl'; -import { List, ListItem } from '@mui/material'; import { CustomMuiDialog, FieldConstants, - NetworkModificationMetadata, + type NetworkModificationMetadata, NO_ITEM_SELECTION_FOR_COPY, unscrollableDialogStyles, useModificationLabelComputer, useSnackMessage, - yupConfig as yup, } from '@gridsuite/commons-ui'; +import * as yup from 'yup'; import { yupResolver } from '@hookform/resolvers/yup'; -import { AppState } from '../../../../redux/types'; +import type { AppState } from '../../../../redux/types'; import { useParameterState } from '../../use-parameters-dialog'; import { PARAM_LANGUAGE } from '../../../../utils/config-params'; import { fetchCompositeModificationContent, saveCompositeModification } from '../../../../utils/rest-api'; From fd0a968da536db1b699b1c0fc2d2b8952d470efe Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Thu, 22 May 2025 13:10:14 +0200 Subject: [PATCH 2/3] import type --- .../contingency-list/explicit-naming/explicit-naming-utils.tsx | 3 ++- src/components/dialogs/directory-properties/utils.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx b/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx index a411441d8..61c0d72f0 100644 --- a/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx +++ b/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx @@ -6,10 +6,11 @@ */ import * as yup from 'yup'; +import type { ArraySchema } from 'yup'; import { FieldConstants } from '@gridsuite/commons-ui'; import { ContingencyListType } from '../../../../utils/elementType'; -const getExplicitNamingConditionSchema = (schema: yup.ArraySchema) => +const getExplicitNamingConditionSchema = (schema: ArraySchema) => schema .min(1, 'contingencyTableContainAtLeastOneRowError') .test( diff --git a/src/components/dialogs/directory-properties/utils.ts b/src/components/dialogs/directory-properties/utils.ts index aed5a12c0..cb7812926 100644 --- a/src/components/dialogs/directory-properties/utils.ts +++ b/src/components/dialogs/directory-properties/utils.ts @@ -6,6 +6,7 @@ */ import * as yup from 'yup'; +import type { InferType } from 'yup'; import { type Option } from '@gridsuite/commons-ui'; import type { UUID } from 'crypto'; @@ -38,7 +39,7 @@ export const schema = yup.object().shape({ [WRITE_GROUPS]: yup.array().of(groupSchema), }); -export type PermissionForm = yup.InferType; +export type PermissionForm = InferType; export const emptyForm: PermissionForm = { [READ_ALL_USERS]: false, From 0d4ac0a1ea124359598c4ebc4b2cdc6f1e1a260e Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Thu, 22 May 2025 13:38:30 +0200 Subject: [PATCH 3/3] yup i18n --- .../contingency-list-creation-dialog.tsx | 37 ++++++++++------ .../criteria-based-edition-dialog.tsx | 26 +++++++---- .../explicit-naming-edition-dialog.tsx | 26 +++++++---- .../explicit-naming/explicit-naming-utils.tsx | 30 +++++++------ .../dialogs/copy-to-script-dialog.tsx | 26 ++++++----- .../create-case-dialog.tsx | 44 +++++++++++++------ .../create-case-dialog-utils.ts | 21 --------- .../create-study-dialog-utils.ts | 27 +++++++----- .../create-study-dialog.tsx | 15 ++++--- .../composite-modification-dialog.tsx | 25 ++++++----- .../menus/directory-tree-contextual-menu.tsx | 2 +- 11 files changed, 163 insertions(+), 116 deletions(-) rename src/components/dialogs/{create-case-dialog => }/create-case-dialog.tsx (78%) delete mode 100644 src/components/dialogs/create-case-dialog/create-case-dialog-utils.ts diff --git a/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx b/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx index 615a06cce..3bddef3be 100644 --- a/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx +++ b/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { useMemo } from 'react'; +import { useIntl } from 'react-intl'; import { useSelector } from 'react-redux'; import { CustomMuiDialog, @@ -31,19 +33,6 @@ import type { AppState } from '../../../../redux/types'; import { getExplicitNamingSchema } from '../explicit-naming/explicit-naming-utils'; import { handleNotAllowedError } from '../../../utils/rest-errors'; -const schema = yup.object().shape({ - [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), - [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), - [FieldConstants.CONTINGENCY_LIST_TYPE]: yup.string().nullable(), - [FieldConstants.EQUIPMENT_TYPE]: yup.string().when([FieldConstants.CONTINGENCY_LIST_TYPE], { - is: ContingencyListType.CRITERIA_BASED.id, - then: (schemaThen) => schemaThen.required(), - otherwise: (schemaOtherwise) => schemaOtherwise.nullable(), - }), - ...getExplicitNamingSchema(), - ...getCriteriaBasedSchema(), -}); - const emptyFormData = getContingencyListEmptyFormData(); export interface ContingencyListCreationDialogProps { @@ -59,9 +48,29 @@ export default function ContingencyListCreationDialog({ }: Readonly) { const activeDirectory = useSelector((state: AppState) => state.activeDirectory); const { snackError } = useSnackMessage(); - + const intl = useIntl(); const [languageLocal] = useParameterState(PARAM_LANGUAGE); + const schema = useMemo( + () => + yup.object().shape({ + [FieldConstants.NAME]: yup + .string() + .trim() + .required(intl.formatMessage({ id: 'nameEmpty' })), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), + [FieldConstants.CONTINGENCY_LIST_TYPE]: yup.string().nullable(), + [FieldConstants.EQUIPMENT_TYPE]: yup.string().when([FieldConstants.CONTINGENCY_LIST_TYPE], { + is: ContingencyListType.CRITERIA_BASED.id, + then: (schemaThen) => schemaThen.required(), + otherwise: (schemaOtherwise) => schemaOtherwise.nullable(), + }), + ...getExplicitNamingSchema(intl), + ...getCriteriaBasedSchema(), + }), + [intl] + ); + const methods = useForm({ defaultValues: emptyFormData, resolver: yupResolver(schema), diff --git a/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx b/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx index b4eb6e186..a5f9d4e30 100644 --- a/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx +++ b/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { useIntl } from 'react-intl'; import { CustomMuiDialog, FieldConstants, @@ -16,7 +17,7 @@ import { import * as yup from 'yup'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { getContingencyList, saveCriteriaBasedContingencyList } from 'utils/rest-api'; import { useDispatch, useSelector } from 'react-redux'; import type { AppState } from '../../../../../redux/types'; @@ -30,13 +31,6 @@ import { useParameterState } from '../../../use-parameters-dialog'; import { type CriteriaBasedEditionFormData } from '../../../../../utils/rest-api'; import { PARAM_LANGUAGE } from '../../../../../utils/config-params'; -const schema = yup.object().shape({ - [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), - [FieldConstants.EQUIPMENT_TYPE]: yup.string().required(), - [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), - ...getCriteriaBasedSchema(), -}); - export interface CriteriaBasedEditionDialogProps { contingencyListId: string; contingencyListType: string; @@ -63,6 +57,22 @@ export default function CriteriaBasedEditionDialog({ const { snackError } = useSnackMessage(); const itemSelectionForCopy = useSelector((state: AppState) => state.itemSelectionForCopy); const dispatch = useDispatch(); + const intl = useIntl(); + + const schema = useMemo( + () => + yup.object().shape({ + [FieldConstants.NAME]: yup + .string() + .trim() + .required(intl.formatMessage({ id: 'nameEmpty' })), + [FieldConstants.EQUIPMENT_TYPE]: yup.string().required(), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), + ...getCriteriaBasedSchema(), + }), + [intl] + ); + const methods = useForm({ defaultValues: getContingencyListEmptyFormData(name), resolver: yupResolver(schema), diff --git a/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx b/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx index 953a161f8..566a775be 100644 --- a/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx +++ b/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { useIntl } from 'react-intl'; import { CustomMuiDialog, FieldConstants, @@ -15,7 +16,7 @@ import { import * as yup from 'yup'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { getContingencyList, saveExplicitNamingContingencyList } from 'utils/rest-api'; import { prepareContingencyListForBackend } from 'components/dialogs/contingency-list-helper'; import { useDispatch, useSelector } from 'react-redux'; @@ -38,13 +39,6 @@ interface ExplicitNamingEditionFormData { }[]; } -const schema = yup.object().shape({ - [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), - [FieldConstants.EQUIPMENT_TYPE]: yup.string().nullable(), - [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), - ...getExplicitNamingEditSchema(), -}); - const emptyFormData = (name?: string) => getContingencyListEmptyFormData(name); export interface ExplicitNamingEditionDialogProps { @@ -72,6 +66,22 @@ export default function ExplicitNamingEditionDialog({ const { snackError } = useSnackMessage(); const itemSelectionForCopy = useSelector((state: AppState) => state.itemSelectionForCopy); const dispatch = useDispatch(); + const intl = useIntl(); + + const schema = useMemo( + () => + yup.object().shape({ + [FieldConstants.NAME]: yup + .string() + .trim() + .required(intl.formatMessage({ id: 'nameEmpty' })), + [FieldConstants.EQUIPMENT_TYPE]: yup.string().nullable(), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), + ...getExplicitNamingEditSchema(intl), + }), + [intl] + ); + const methods = useForm({ defaultValues: emptyFormData(name), resolver: yupResolver(schema), diff --git a/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx b/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx index 61c0d72f0..a18dd5b85 100644 --- a/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx +++ b/src/components/dialogs/contingency-list/explicit-naming/explicit-naming-utils.tsx @@ -5,24 +5,26 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { type IntlShape } from 'react-intl'; import * as yup from 'yup'; import type { ArraySchema } from 'yup'; import { FieldConstants } from '@gridsuite/commons-ui'; import { ContingencyListType } from '../../../../utils/elementType'; -const getExplicitNamingConditionSchema = (schema: ArraySchema) => - schema +function getExplicitNamingConditionSchema(intl: IntlShape, schema: ArraySchema) { + return schema .min(1, 'contingencyTableContainAtLeastOneRowError') .test( 'rowWithoutName', - 'contingencyTablePartiallyDefinedError', + intl.formatMessage({ id: 'contingencyTablePartiallyDefinedError' }), (array) => !array.some((row: any) => !row[FieldConstants.CONTINGENCY_NAME]?.trim()) ) .test( 'rowWithoutEquipments', - 'contingencyTablePartiallyDefinedError', + intl.formatMessage({ id: 'contingencyTablePartiallyDefinedError' }), (array) => !array.some((row: any) => !row[FieldConstants.EQUIPMENT_IDS]?.length) ); +} const getSchema = () => yup @@ -35,15 +37,17 @@ const getSchema = () => ) // we remove empty lines .compact((row) => !row[FieldConstants.CONTINGENCY_NAME] && !row[FieldConstants.EQUIPMENT_IDS]?.length); -export const getExplicitNamingSchema = () => ({ - [FieldConstants.EQUIPMENT_TABLE]: getSchema().when([FieldConstants.CONTINGENCY_LIST_TYPE], { - is: ContingencyListType.EXPLICIT_NAMING.id, - then: (schema) => getExplicitNamingConditionSchema(schema), - }), -}); +export function getExplicitNamingSchema(intl: IntlShape) { + return { + [FieldConstants.EQUIPMENT_TABLE]: getSchema().when([FieldConstants.CONTINGENCY_LIST_TYPE], { + is: ContingencyListType.EXPLICIT_NAMING.id, + then: (schema) => getExplicitNamingConditionSchema(intl, schema), + }), + }; +} -export const getExplicitNamingEditSchema = () => { +export function getExplicitNamingEditSchema(intl: IntlShape) { return { - [FieldConstants.EQUIPMENT_TABLE]: getExplicitNamingConditionSchema(getSchema()), + [FieldConstants.EQUIPMENT_TABLE]: getExplicitNamingConditionSchema(intl, getSchema()), }; -}; +} diff --git a/src/components/dialogs/copy-to-script-dialog.tsx b/src/components/dialogs/copy-to-script-dialog.tsx index 976789de5..4c515332e 100644 --- a/src/components/dialogs/copy-to-script-dialog.tsx +++ b/src/components/dialogs/copy-to-script-dialog.tsx @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useIntl } from 'react-intl'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; @@ -17,14 +17,6 @@ import { getNameCandidate } from '../../utils/rest-api'; import type { AppState } from '../../redux/types'; import { handleGenericTxtError } from '../utils/rest-errors'; -const schema = yup.object().shape({ - [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), -}); - -const emptyFormData = { - [FieldConstants.NAME]: '', -}; - export interface CopyToScriptDialogProps { id: string; open: boolean; @@ -64,8 +56,22 @@ export default function CopyToScriptDialog({ const [loading, setLoading] = useState(false); const { snackError } = useSnackMessage(); const intl = useIntl(); + + const schema = useMemo( + () => + yup.object().shape({ + [FieldConstants.NAME]: yup + .string() + .trim() + .required(intl.formatMessage({ id: 'nameEmpty' })), + }), + [intl] + ); + const methods = useForm({ - defaultValues: emptyFormData, + defaultValues: { + [FieldConstants.NAME]: '', + }, resolver: yupResolver(schema), }); diff --git a/src/components/dialogs/create-case-dialog/create-case-dialog.tsx b/src/components/dialogs/create-case-dialog.tsx similarity index 78% rename from src/components/dialogs/create-case-dialog/create-case-dialog.tsx rename to src/components/dialogs/create-case-dialog.tsx index eefdf8f9f..b1d1472d9 100644 --- a/src/components/dialogs/create-case-dialog/create-case-dialog.tsx +++ b/src/components/dialogs/create-case-dialog.tsx @@ -5,8 +5,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { useIntl } from 'react-intl'; import { Grid } from '@mui/material'; +import * as yup from 'yup'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { @@ -18,21 +21,18 @@ import { FieldErrorAlert, isObjectEmpty, keyGenerator, + MAX_CHAR_DESCRIPTION, useConfidentialityWarning, useSnackMessage, } from '@gridsuite/commons-ui'; -import { createCase } from '../../../utils/rest-api'; -import { HTTP_UNPROCESSABLE_ENTITY_STATUS } from '../../../utils/UIconstants'; -import { addUploadingElement, removeUploadingElement } from '../../../redux/actions'; -import UploadNewCase from '../commons/upload-new-case'; -import { - createCaseDialogFormValidationSchema, - getCreateCaseDialogFormValidationDefaultValues, -} from './create-case-dialog-utils'; -import PrefilledNameInput from '../commons/prefilled-name-input'; -import { handleMaxElementsExceededError, handleNotAllowedError } from '../../utils/rest-errors'; -import { AppDispatch } from '../../../redux/store'; -import { AppState, UploadingElement } from '../../../redux/types'; +import { createCase } from '../../utils/rest-api'; +import { HTTP_UNPROCESSABLE_ENTITY_STATUS } from '../../utils/UIconstants'; +import { addUploadingElement, removeUploadingElement } from '../../redux/actions'; +import UploadNewCase from './commons/upload-new-case'; +import PrefilledNameInput from './commons/prefilled-name-input'; +import { handleMaxElementsExceededError, handleNotAllowedError } from '../utils/rest-errors'; +import type { AppDispatch } from '../../redux/store'; +import type { AppState, UploadingElement } from '../../redux/types'; interface IFormData { [FieldConstants.CASE_NAME]: string; @@ -49,9 +49,27 @@ export default function CreateCaseDialog({ onClose, open }: Readonly(); const { snackError } = useSnackMessage(); const confidentialityWarningKey = useConfidentialityWarning(); + const intl = useIntl(); + + const createCaseDialogFormValidationSchema = useMemo( + () => + yup.object().shape({ + [FieldConstants.CASE_NAME]: yup + .string() + .trim() + .required(intl.formatMessage({ id: 'nameEmpty' })), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), + [FieldConstants.CASE_FILE]: yup.mixed().nullable().required(), + }), + [intl] + ); const createCaseFormMethods = useForm({ - defaultValues: getCreateCaseDialogFormValidationDefaultValues(), + defaultValues: { + [FieldConstants.CASE_NAME]: '', + [FieldConstants.DESCRIPTION]: '', + [FieldConstants.CASE_FILE]: null, + }, resolver: yupResolver(createCaseDialogFormValidationSchema), }); diff --git a/src/components/dialogs/create-case-dialog/create-case-dialog-utils.ts b/src/components/dialogs/create-case-dialog/create-case-dialog-utils.ts deleted file mode 100644 index c8f1d038a..000000000 --- a/src/components/dialogs/create-case-dialog/create-case-dialog-utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2024, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import * as yup from 'yup'; -import { FieldConstants, MAX_CHAR_DESCRIPTION } from '@gridsuite/commons-ui'; - -export const getCreateCaseDialogFormValidationDefaultValues = () => ({ - [FieldConstants.CASE_NAME]: '', - [FieldConstants.DESCRIPTION]: '', - [FieldConstants.CASE_FILE]: null, -}); - -export const createCaseDialogFormValidationSchema = yup.object().shape({ - [FieldConstants.CASE_NAME]: yup.string().trim().required('nameEmpty'), - [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), - [FieldConstants.CASE_FILE]: yup.mixed().nullable().required(), -}); diff --git a/src/components/dialogs/create-study-dialog/create-study-dialog-utils.ts b/src/components/dialogs/create-study-dialog/create-study-dialog-utils.ts index 3ae64ab08..791fdc948 100644 --- a/src/components/dialogs/create-study-dialog/create-study-dialog-utils.ts +++ b/src/components/dialogs/create-study-dialog/create-study-dialog-utils.ts @@ -8,6 +8,7 @@ import { type ElementAttributes, FieldConstants, MAX_CHAR_DESCRIPTION, type Parameter } from '@gridsuite/commons-ui'; import * as yup from 'yup'; import type { UUID } from 'crypto'; +import { IntlShape } from 'react-intl'; export const getCreateStudyDialogFormDefaultValues = ({ // @ts-expect-error how react-hook-form manage strings like UUIDs? @@ -33,17 +34,21 @@ export const getCreateStudyDialogFormDefaultValues = ({ [FieldConstants.CASE_NAME]: '', }); -export const createStudyDialogFormValidationSchema = yup.object().shape({ - [FieldConstants.STUDY_NAME]: yup.string().trim().required('nameEmpty'), - [FieldConstants.FORMATTED_CASE_PARAMETERS]: yup.mixed().required(), - [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), - [FieldConstants.CURRENT_PARAMETERS]: yup.mixed>().required(), - [FieldConstants.CASE_UUID]: yup.string().nullable().uuid().required(), - [FieldConstants.CASE_FILE]: yup.mixed().nullable().required(), - [FieldConstants.DIRECTORY]: yup.string().uuid().required(), - [FieldConstants.CASE_FORMAT]: yup.string().optional(), - [FieldConstants.CASE_NAME]: yup.string().optional(), -}); +export const getCreateStudyDialogFormValidationSchema = (intl: IntlShape) => + yup.object().shape({ + [FieldConstants.STUDY_NAME]: yup + .string() + .trim() + .required(intl.formatMessage({ id: 'nameEmpty' })), + [FieldConstants.FORMATTED_CASE_PARAMETERS]: yup.mixed().required(), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), + [FieldConstants.CURRENT_PARAMETERS]: yup.mixed>().required(), + [FieldConstants.CASE_UUID]: yup.string().nullable().uuid().required(), + [FieldConstants.CASE_FILE]: yup.mixed().nullable().required(), + [FieldConstants.DIRECTORY]: yup.string().uuid().required(), + [FieldConstants.CASE_FORMAT]: yup.string().optional(), + [FieldConstants.CASE_NAME]: yup.string().optional(), + }); export interface CreateStudyDialogFormValues { [FieldConstants.STUDY_NAME]: string; diff --git a/src/components/dialogs/create-study-dialog/create-study-dialog.tsx b/src/components/dialogs/create-study-dialog/create-study-dialog.tsx index cb6a5c442..36706fb14 100644 --- a/src/components/dialogs/create-study-dialog/create-study-dialog.tsx +++ b/src/components/dialogs/create-study-dialog/create-study-dialog.tsx @@ -7,11 +7,11 @@ import { useForm } from 'react-hook-form'; import { Grid } from '@mui/material'; import { useIntl } from 'react-intl'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { CustomMuiDialog, DescriptionField, - ElementAttributes, + type ElementAttributes, ElementType, ErrorInput, FieldConstants, @@ -19,26 +19,26 @@ import { isObjectEmpty, keyGenerator, ModifyElementSelection, - Parameter, + type Parameter, useConfidentialityWarning, useSnackMessage, } from '@gridsuite/commons-ui'; import { useDispatch, useSelector } from 'react-redux'; import { yupResolver } from '@hookform/resolvers/yup'; -import { UUID } from 'crypto'; +import type { UUID } from 'crypto'; import UploadNewCase from '../commons/upload-new-case'; import { createStudy, deleteCase, getCaseImportParameters } from '../../../utils/rest-api'; import { HTTP_CONNECTION_FAILED_MESSAGE, HTTP_UNPROCESSABLE_ENTITY_STATUS } from '../../../utils/UIconstants'; import ImportParametersSection from './importParametersSection'; import { addUploadingElement, removeUploadingElement, setActiveDirectory } from '../../../redux/actions'; import { - createStudyDialogFormValidationSchema, - CreateStudyDialogFormValues, + type CreateStudyDialogFormValues, getCreateStudyDialogFormDefaultValues, + getCreateStudyDialogFormValidationSchema, } from './create-study-dialog-utils'; import PrefilledNameInput from '../commons/prefilled-name-input'; import { handleMaxElementsExceededError, handleNotAllowedError } from '../../utils/rest-errors'; -import { AppState, UploadingElement } from '../../../redux/types'; +import type { AppState, UploadingElement } from '../../../redux/types'; const STRING_LIST = 'STRING_LIST'; @@ -83,6 +83,7 @@ export default function CreateStudyDialog({ open, onClose, providedExistingCase const { elementUuid, elementName } = providedExistingCase || {}; + const createStudyDialogFormValidationSchema = useMemo(() => getCreateStudyDialogFormValidationSchema(intl), [intl]); const createStudyFormMethods = useForm({ defaultValues: getCreateStudyDialogFormDefaultValues({ directory: activeDirectory, diff --git a/src/components/dialogs/network-modification/composite-modification/composite-modification-dialog.tsx b/src/components/dialogs/network-modification/composite-modification/composite-modification-dialog.tsx index aec2f2a5b..4dfca3e2d 100644 --- a/src/components/dialogs/network-modification/composite-modification/composite-modification-dialog.tsx +++ b/src/components/dialogs/network-modification/composite-modification/composite-modification-dialog.tsx @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { type SyntheticEvent, useEffect, useState } from 'react'; +import { type SyntheticEvent, useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Box, Divider, List, ListItem } from '@mui/material'; import { useForm } from 'react-hook-form'; @@ -27,14 +27,6 @@ import { fetchCompositeModificationContent, saveCompositeModification } from '.. import CompositeModificationForm from './composite-modification-form'; import { setItemSelectionForCopy } from '../../../../redux/actions'; -const schema = yup.object().shape({ - [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), -}); - -const emptyFormData = (name?: string) => ({ - [FieldConstants.NAME]: name, -}); - interface FormData { [FieldConstants.NAME]: string; } @@ -64,8 +56,21 @@ export default function CompositeModificationDialog({ const [modifications, setModifications] = useState([]); const dispatch = useDispatch(); + const schema = useMemo( + () => + yup.object().shape({ + [FieldConstants.NAME]: yup + .string() + .trim() + .required(intl.formatMessage({ id: 'nameEmpty' })), + }), + [intl] + ); + const methods = useForm({ - defaultValues: emptyFormData(name), + defaultValues: { + [FieldConstants.NAME]: name, + }, resolver: yupResolver(schema), }); diff --git a/src/components/menus/directory-tree-contextual-menu.tsx b/src/components/menus/directory-tree-contextual-menu.tsx index e39ce18eb..bb2cb7e9a 100644 --- a/src/components/menus/directory-tree-contextual-menu.tsx +++ b/src/components/menus/directory-tree-contextual-menu.tsx @@ -46,7 +46,7 @@ import { import CommonContextualMenu, { CommonContextualMenuProps, MenuItemType } from './common-contextual-menu'; import { useDeferredFetch } from '../../utils/custom-hooks'; import ContingencyListCreationDialog from '../dialogs/contingency-list/creation/contingency-list-creation-dialog'; -import CreateCaseDialog from '../dialogs/create-case-dialog/create-case-dialog'; +import CreateCaseDialog from '../dialogs/create-case-dialog'; import { useParameterState } from '../dialogs/use-parameters-dialog'; import { PARAM_LANGUAGE } from '../../utils/config-params'; import {