From b1afd692ee0bdbe1bf2cea3f74a24ddd680dcb5c Mon Sep 17 00:00:00 2001 From: basseche Date: Thu, 14 Aug 2025 16:25:55 +0200 Subject: [PATCH 01/23] Components chosen Signed-off-by: basseche --- .../contingency-list-filter-based-dialog.tsx | 86 +++++++++++++ .../contingency-list-filter-based-from.tsx | 118 ++++++++++++++++++ .../menus/directory-tree-contextual-menu.tsx | 25 +++- src/translations/en.json | 4 + src/translations/fr.json | 4 + src/utils/UIconstants.ts | 4 +- 6 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx create mode 100644 src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx new file mode 100644 index 000000000..704eb2a05 --- /dev/null +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2025, 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 { CustomMuiDialog, FieldConstants, MAX_CHAR_DESCRIPTION, yupConfig as yup } from '@gridsuite/commons-ui'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import ContingencyListFilterBasedFrom from './contingency-list-filter-based-from'; + +export interface FilterBasedContingencyListProps { + titleId: string; + open: boolean; + onClose: () => void; +} + +export type ContingencyBasedFilter = { + uuid: string; + name: string; + path: string; + equipmentType: string; +}; + +const schema: any = yup.object().shape({ + [FieldConstants.NAME]: yup.string().required(), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), + [FieldConstants.FILTERS]: yup.array().required(), +}); + +export interface ContingencyListFilterBasedFormData { + [FieldConstants.NAME]: string; + [FieldConstants.DESCRIPTION]?: string; + [FieldConstants.FILTERS]: ContingencyBasedFilter[]; +} + +const getContingencyListEmptyFormData = (name = '') => ({ + [FieldConstants.NAME]: name, + [FieldConstants.DESCRIPTION]: '', + [FieldConstants.FILTERS]: [], +}); + +const emptyFormData = (name?: string) => getContingencyListEmptyFormData(name); + +export default function FilterBasedContingencyListDialog({ + titleId, + open, + onClose, +}: Readonly) { + const onSubmit = () => { + // do something + }; + + const methods = useForm({ + defaultValues: emptyFormData(), + resolver: yupResolver(schema), + }); + const { + reset, + formState: { errors }, + } = methods; + + const closeAndClear = () => { + reset(emptyFormData()); + onClose(); + }; + + const nameError = errors[FieldConstants.NAME]; + const isValidating = errors.root?.isValidating; + + return ( + + + + ); +} diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx new file mode 100644 index 000000000..57633d08a --- /dev/null +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2025, 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 { + CustomAGGrid, + DescriptionField, + DirectoryItemSelector, + DirectoryItemsInput, + ElementType, + FieldConstants, + TreeViewFinderNodeProps, + UniqueNameInput, + unscrollableDialogStyles, +} from '@gridsuite/commons-ui'; +import { Box, Button, Typography } from '@mui/material'; +import { useSelector } from 'react-redux'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { useState } from 'react'; +import { FolderOutlined } from '@mui/icons-material'; +import { ColDef } from 'ag-grid-community'; +import { AppState } from '../../../../redux/types'; + +export interface ContingencyListFilterBasedFromProps { + studyName: string; +} + +const separator = '/'; +const defaultDef: ColDef = { + flex: 1, + resizable: false, +}; + +export default function ContingencyListFilterBasedFrom({ studyName }: Readonly) { + const activeDirectory = useSelector((state: AppState) => state.activeDirectory); + const [selectedStudy, setSelectedStudy] = useState(studyName); + const [selectedFolder, setSelectedFolder] = useState(''); + const [isOpen, setIsOpen] = useState(false); + + const intl = useIntl(); + + const colDef: ColDef[] = [ + { + headerName: intl.formatMessage({ + id: 'equipmentID', + }), + field: 'equipmentID', + }, + { + headerName: intl.formatMessage({ + id: 'type', + }), + field: 'type', + }, + ]; + + return ( + <> + + + + + + + + + + + + + + + + {selectedStudy.length > 0 ? ( + + {selectedFolder ? selectedFolder + separator + selectedStudy : selectedStudy} + + ) : ( + + )} + + + + { + if (nodes.length > 0) { + if (nodes[0].parents && nodes[0].parents.length > 0) { + setSelectedFolder(nodes[0].parents.map((entry) => entry.name).join(separator)); + } + setSelectedStudy(nodes[0].name); + } + console.log('setisOpen false'); + setIsOpen(false); + }} + multiSelect={false} + /> + + + + + ); +} diff --git a/src/components/menus/directory-tree-contextual-menu.tsx b/src/components/menus/directory-tree-contextual-menu.tsx index 5cafa5311..36826ab7f 100644 --- a/src/components/menus/directory-tree-contextual-menu.tsx +++ b/src/components/menus/directory-tree-contextual-menu.tsx @@ -66,6 +66,7 @@ import { buildPathToFromMap } from '../treeview-utils'; import { checkPermissionOnDirectory } from './menus-utils'; import DirectoryPropertiesDialog from '../dialogs/directory-properties/directory-properties-dialog'; import { FilterType } from '../../utils/elementType'; +import FilterBasedContingencyListDialog from '../dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog'; export interface DirectoryTreeContextualMenuProps extends Omit { directory: ElementAttributes | null; @@ -232,8 +233,20 @@ export default function DirectoryTreeContextualMenu(props: Readonly handleOpenDialog(DialogsId.ADD_NEW_CONTINGENCY_LIST), icon: , + subMenuItems: [ + { + messageDescriptorId: 'contingencyList.criteriaBasedOrExplicitNaming', + callback: () => + handleOpenDialog(DialogsId.ADD_NEW_EXPLICIT_NAMING_OR_CRITERIA_BASED_CONTINGENCY_LIST), + icon: null, + }, + { + messageDescriptorId: 'contingencyList.filterBased', + callback: () => handleOpenDialog(DialogsId.ADD_NEW_FILTER_BASED_CONTINGENCY_LIST), + icon: null, + }, + ], }, { messageDescriptorId: 'createNewFilter', @@ -354,7 +367,7 @@ export default function DirectoryTreeContextualMenu(props: Readonly; - case DialogsId.ADD_NEW_CONTINGENCY_LIST: + case DialogsId.ADD_NEW_EXPLICIT_NAMING_OR_CRITERIA_BASED_CONTINGENCY_LIST: return ( ); + case DialogsId.ADD_NEW_FILTER_BASED_CONTINGENCY_LIST: + return ( + + ); case DialogsId.ADD_DIRECTORY: return ( Date: Fri, 22 Aug 2025 15:52:52 +0200 Subject: [PATCH 02/23] Add new filter based contingency list - IHM Ok --- .../contingency-list-filter-based-dialog.tsx | 46 ++++++++++++++----- .../contingency-list-filter-based-from.tsx | 33 ++++++++++++- src/utils/rest-api.ts | 22 +++++++++ 3 files changed, 87 insertions(+), 14 deletions(-) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index 704eb2a05..d47694789 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -5,10 +5,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { CustomMuiDialog, FieldConstants, MAX_CHAR_DESCRIPTION, yupConfig as yup } from '@gridsuite/commons-ui'; +import { + CustomMuiDialog, + FieldConstants, + MAX_CHAR_DESCRIPTION, + useSnackMessage, + yupConfig as yup, +} from '@gridsuite/commons-ui'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; +import { useSelector } from 'react-redux'; import ContingencyListFilterBasedFrom from './contingency-list-filter-based-from'; +import { AppState } from '../../../../redux/types'; +import { createFilterBasedContingency } from '../../../../utils/rest-api'; +import { handleNotAllowedError } from '../../../utils/rest-errors'; export interface FilterBasedContingencyListProps { titleId: string; @@ -16,13 +26,6 @@ export interface FilterBasedContingencyListProps { onClose: () => void; } -export type ContingencyBasedFilter = { - uuid: string; - name: string; - path: string; - equipmentType: string; -}; - const schema: any = yup.object().shape({ [FieldConstants.NAME]: yup.string().required(), [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), @@ -32,7 +35,7 @@ const schema: any = yup.object().shape({ export interface ContingencyListFilterBasedFormData { [FieldConstants.NAME]: string; [FieldConstants.DESCRIPTION]?: string; - [FieldConstants.FILTERS]: ContingencyBasedFilter[]; + [FieldConstants.FILTERS]: string[]; } const getContingencyListEmptyFormData = (name = '') => ({ @@ -48,9 +51,8 @@ export default function FilterBasedContingencyListDialog({ open, onClose, }: Readonly) { - const onSubmit = () => { - // do something - }; + const activeDirectory = useSelector((state: AppState) => state.activeDirectory); + const { snackError } = useSnackMessage(); const methods = useForm({ defaultValues: emptyFormData(), @@ -66,6 +68,26 @@ export default function FilterBasedContingencyListDialog({ onClose(); }; + const onSubmit = (data: ContingencyListFilterBasedFormData) => { + createFilterBasedContingency( + data[FieldConstants.NAME], + data[FieldConstants.DESCRIPTION] ?? '', + data[FieldConstants.FILTERS], + activeDirectory + ) + .then(() => closeAndClear()) + .catch((error) => { + if (handleNotAllowedError(error, snackError)) { + return; + } + snackError({ + messageTxt: error.message, + headerId: 'contingencyListCreationError', + headerValues: { name: data[FieldConstants.NAME] }, + }); + }); + }; + const nameError = errors[FieldConstants.NAME]; const isValidating = errors.root?.isValidating; diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index 57633d08a..5d4f7690f 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -10,6 +10,7 @@ import { DirectoryItemSelector, DirectoryItemsInput, ElementType, + EquipmentType, FieldConstants, TreeViewFinderNodeProps, UniqueNameInput, @@ -18,9 +19,10 @@ import { import { Box, Button, Typography } from '@mui/material'; import { useSelector } from 'react-redux'; import { FormattedMessage, useIntl } from 'react-intl'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { FolderOutlined } from '@mui/icons-material'; import { ColDef } from 'ag-grid-community'; +import { blue, brown, green, indigo, lime, red, teal } from '@mui/material/colors'; import { AppState } from '../../../../redux/types'; export interface ContingencyListFilterBasedFromProps { @@ -56,6 +58,32 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly { + return [ + EquipmentType.TWO_WINDINGS_TRANSFORMER, + EquipmentType.LINE, + EquipmentType.LOAD, + EquipmentType.GENERATOR, + EquipmentType.GENERATOR, + EquipmentType.SHUNT_COMPENSATOR, + EquipmentType.STATIC_VAR_COMPENSATOR, + EquipmentType.HVDC_LINE, + ]; + }, []); + + // TODO basseche : should ask Stephane for final colors + const equipmentColorsMap: Map = useMemo(() => { + const map = new Map(); + map.set(EquipmentType.TWO_WINDINGS_TRANSFORMER, blue[700]); + map.set(EquipmentType.LINE, indigo[700]); + map.set(EquipmentType.LOAD, brown[700]); + map.set(EquipmentType.GENERATOR, green[700]); + map.set(EquipmentType.SHUNT_COMPENSATOR, red[700]); + map.set(EquipmentType.STATIC_VAR_COMPENSATOR, lime[700]); + map.set(EquipmentType.HVDC_LINE, teal[700]); + return map; + }, []); + return ( <> @@ -76,6 +104,8 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly @@ -105,7 +135,6 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly} From 4e0d846e555983bb4436e2714df08e521dd56fca Mon Sep 17 00:00:00 2001 From: basseche Date: Mon, 1 Sep 2025 17:48:12 +0200 Subject: [PATCH 03/23] Save Contingency list ok --- .../filter-based/contingency-list-filter-based-dialog.tsx | 5 +++-- src/translations/external/backend-locale-en.ts | 1 + src/translations/external/backend-locale-fr.ts | 1 + src/utils/rest-api.ts | 5 +++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index d47694789..ea727033c 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -11,6 +11,7 @@ import { MAX_CHAR_DESCRIPTION, useSnackMessage, yupConfig as yup, + TreeViewFinderNodeProps, } from '@gridsuite/commons-ui'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; @@ -35,7 +36,7 @@ const schema: any = yup.object().shape({ export interface ContingencyListFilterBasedFormData { [FieldConstants.NAME]: string; [FieldConstants.DESCRIPTION]?: string; - [FieldConstants.FILTERS]: string[]; + [FieldConstants.FILTERS]: TreeViewFinderNodeProps[]; } const getContingencyListEmptyFormData = (name = '') => ({ @@ -72,7 +73,7 @@ export default function FilterBasedContingencyListDialog({ createFilterBasedContingency( data[FieldConstants.NAME], data[FieldConstants.DESCRIPTION] ?? '', - data[FieldConstants.FILTERS], + data[FieldConstants.FILTERS]?.map((item: TreeViewFinderNodeProps) => item.id), activeDirectory ) .then(() => closeAndClear()) diff --git a/src/translations/external/backend-locale-en.ts b/src/translations/external/backend-locale-en.ts index abac72d36..07bcb7c8d 100644 --- a/src/translations/external/backend-locale-en.ts +++ b/src/translations/external/backend-locale-en.ts @@ -17,6 +17,7 @@ const BackendLocaleEn = { EXPERT_FILTER: 'Filter (Criteria based)', FORM_FILTER: 'Filter (Form)', IDENTIFIERS_CONTINGENCY_LIST: 'Contingency list (Explicit naming)', + FILTERS_CONTINGENCY_LIST: 'Contingency list (Filter based)', FORM_CONTINGENCY_LIST: 'Contingency list (Criteria based)', IDENTIFIER_LIST_FILTER: 'Filter (Explicit naming)', diff --git a/src/translations/external/backend-locale-fr.ts b/src/translations/external/backend-locale-fr.ts index 6c84aaea3..845aaf83d 100644 --- a/src/translations/external/backend-locale-fr.ts +++ b/src/translations/external/backend-locale-fr.ts @@ -18,6 +18,7 @@ const BackendLocaleFr = { FORM_FILTER: 'Filtre (Formulaire)', IDENTIFIERS_CONTINGENCY_LIST: "Liste d'aléas (Par nommage)", FORM_CONTINGENCY_LIST: "Liste d'aléas (Par critères)", + FILTERS_CONTINGENCY_LIST: "Liste d'aléas (Par filtres)", IDENTIFIER_LIST_FILTER: 'Filtre (Par nommage)', // spreadsheet config metadata diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index a12c198d7..e1337f9ea 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -549,14 +549,15 @@ export function createFilterBasedContingency( urlSearchParams.append('description', description); urlSearchParams.append('parentDirectoryUuid', parentDirectoryUuid ?? ''); - const createContingencyListUrl = `${PREFIX_EXPLORE_SERVER_QUERIES}/v1/explore/${encodeURIComponent( + console.log('filtersUuids : ', filtersUuids); + const createContingencyListUrl = `${PREFIX_EXPLORE_SERVER_QUERIES}/v1/explore/filter-based-contingency-lists/${encodeURIComponent( name )}?${urlSearchParams.toString()}`; console.debug(createContingencyListUrl); return backendFetch(createContingencyListUrl, { method: 'post', - body: JSON.stringify(filtersUuids), + body: JSON.stringify({ filterList: filtersUuids }), }); } From 6d55a168b4b92959ef9db3d75615012f6ec0b256 Mon Sep 17 00:00:00 2001 From: basseche Date: Thu, 4 Sep 2025 14:55:05 +0200 Subject: [PATCH 04/23] Edtit contingency list Ok Signed-off-by: basseche --- .../contingency-list-utils.ts | 11 ++ .../contingency-list-filter-based-dialog.tsx | 118 ++++++++++++++---- src/components/directory-content-dialog.tsx | 25 ++++ .../menus/directory-tree-contextual-menu.tsx | 4 +- src/translations/en.json | 1 + src/translations/fr.json | 1 + src/utils/UIconstants.ts | 2 +- src/utils/constants-endpoints.ts | 1 + src/utils/contingency-list-types.ts | 13 ++ src/utils/elementType.ts | 1 + src/utils/rest-api.ts | 37 +++++- 11 files changed, 185 insertions(+), 29 deletions(-) create mode 100644 src/utils/contingency-list-types.ts diff --git a/src/components/dialogs/contingency-list/contingency-list-utils.ts b/src/components/dialogs/contingency-list/contingency-list-utils.ts index 4742e1c70..f396bd386 100644 --- a/src/components/dialogs/contingency-list/contingency-list-utils.ts +++ b/src/components/dialogs/contingency-list/contingency-list-utils.ts @@ -16,6 +16,7 @@ import { import type { SetRequired } from 'type-fest'; import { prepareContingencyListForBackend } from '../contingency-list-helper'; import { ContingencyListType } from '../../../utils/elementType'; +import { FilterMetaData } from '../../../utils/contingency-list-types'; export interface Identifier { type: 'ID_BASED'; @@ -71,6 +72,16 @@ export const getCriteriaBasedFormDataFromFetchedElement = (response: any, name: ...getCriteriaBasedFormData(response), }); +// TODO basseche : try to type response +export const getFilterBasedFormDataFromFetchedElement = (response: any, name: string, description: string) => ({ + [FieldConstants.NAME]: name, + [FieldConstants.DESCRIPTION]: description, + [FieldConstants.CONTINGENCY_LIST_TYPE]: ContingencyListType.FILTERS.id, + [FieldConstants.FILTERS]: response.filters.map((filter: FilterMetaData) => { + return { id: filter.id, name: filter.name, specificMetadata: { equipmentType: filter.equipmentType } }; + }), +}); + export const getExplicitNamingFormDataFromFetchedElement = (response: any, name: string, description: string) => { let result; if (response.identifierContingencyList?.identifiers?.length) { diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index ea727033c..d4a11493a 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -16,15 +16,26 @@ import { import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { useSelector } from 'react-redux'; +import { useCallback, useEffect, useState } from 'react'; +import { UUID } from 'crypto'; import ContingencyListFilterBasedFrom from './contingency-list-filter-based-from'; import { AppState } from '../../../../redux/types'; -import { createFilterBasedContingency } from '../../../../utils/rest-api'; +import { + createFilterBasedContingency, + getContingencyList, + saveFilterBasedContingencyList, +} from '../../../../utils/rest-api'; import { handleNotAllowedError } from '../../../utils/rest-errors'; +import { ContingencyListType } from '../../../../utils/elementType'; +import { getFilterBasedFormDataFromFetchedElement } from '../contingency-list-utils'; export interface FilterBasedContingencyListProps { titleId: string; open: boolean; onClose: () => void; + name?: string; + description?: string; + id?: UUID; } const schema: any = yup.object().shape({ @@ -51,9 +62,13 @@ export default function FilterBasedContingencyListDialog({ titleId, open, onClose, + name, + description, + id, }: Readonly) { const activeDirectory = useSelector((state: AppState) => state.activeDirectory); const { snackError } = useSnackMessage(); + const [isFetching, setIsFetching] = useState(!!id); const methods = useForm({ defaultValues: emptyFormData(), @@ -64,30 +79,88 @@ export default function FilterBasedContingencyListDialog({ formState: { errors }, } = methods; - const closeAndClear = () => { + // TODO basseche : try to type response + useEffect(() => { + if (id) { + setIsFetching(true); + getContingencyList(ContingencyListType.FILTERS.id, id?.toString()) + .then((response) => { + const formData: ContingencyListFilterBasedFormData = getFilterBasedFormDataFromFetchedElement( + response, + name ?? '', + description ?? '' + ); + reset({ ...formData }); + }) + .catch((error) => { + snackError({ + messageTxt: error.message, + headerId: 'cannotRetrieveContingencyList', + }); + }) + .finally(() => setIsFetching(false)); + } + }, [id, name, reset, snackError, description]); + + const closeAndClear = useCallback(() => { reset(emptyFormData()); onClose(); - }; + }, [onClose, reset]); - const onSubmit = (data: ContingencyListFilterBasedFormData) => { - createFilterBasedContingency( - data[FieldConstants.NAME], - data[FieldConstants.DESCRIPTION] ?? '', - data[FieldConstants.FILTERS]?.map((item: TreeViewFinderNodeProps) => item.id), - activeDirectory - ) - .then(() => closeAndClear()) - .catch((error) => { - if (handleNotAllowedError(error, snackError)) { - return; - } - snackError({ - messageTxt: error.message, - headerId: 'contingencyListCreationError', - headerValues: { name: data[FieldConstants.NAME] }, - }); - }); - }; + const onSubmit = useCallback( + (data: ContingencyListFilterBasedFormData) => { + if (id) { + saveFilterBasedContingencyList( + id, + data[FieldConstants.NAME], + data[FieldConstants.DESCRIPTION] ?? '', + data[FieldConstants.FILTERS].map((item) => { + return { + id: item.id, + name: item.name, + equipmentType: item.specificMetadata?.equipmentType ?? '', + }; + }) + ) + .then(() => closeAndClear()) + .catch((error) => { + if (handleNotAllowedError(error, snackError)) { + return; + } + snackError({ + messageTxt: error.message, + headerId: 'contingencyListEditingError', + headerValues: { name: data[FieldConstants.NAME] }, + }); + }); + } else { + createFilterBasedContingency( + data[FieldConstants.NAME], + data[FieldConstants.DESCRIPTION] ?? '', + data[FieldConstants.FILTERS]?.map((item: TreeViewFinderNodeProps) => { + return { + id: item.id, + name: item.name, + equipmentType: item.specificMetadata?.equipmentType ?? '', + }; + }), + activeDirectory + ) + .then(() => closeAndClear()) + .catch((error) => { + if (handleNotAllowedError(error, snackError)) { + return; + } + snackError({ + messageTxt: error.message, + headerId: 'contingencyListCreationError', + headerValues: { name: data[FieldConstants.NAME] }, + }); + }); + } + }, + [activeDirectory, closeAndClear, id, snackError] + ); const nameError = errors[FieldConstants.NAME]; const isValidating = errors.root?.isValidating; @@ -102,6 +175,7 @@ export default function FilterBasedContingencyListDialog({ formMethods={methods} unscrollableFullHeight disabledSave={Boolean(!!nameError || isValidating)} + isDataFetching={isFetching} > diff --git a/src/components/directory-content-dialog.tsx b/src/components/directory-content-dialog.tsx index fa0271838..04919068b 100644 --- a/src/components/directory-content-dialog.tsx +++ b/src/components/directory-content-dialog.tsx @@ -44,6 +44,7 @@ import * as constants from '../utils/UIconstants'; import type { AppState } from '../redux/types'; import { useParameterState } from './dialogs/use-parameters-dialog'; import type { useDirectoryContent } from '../hooks/useDirectoryContent'; +import FilterBasedContingencyListDialog from './dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog'; export type DirectoryContentDialogApi = { handleClick: (event: CellClickedEvent) => void; @@ -122,6 +123,15 @@ function DirectoryContentDialog( setElementName(''); }, [setActiveElement, setOpenDialog]); + /* Filter based contingency list dialog: window status value for editing a filter based contingency list */ + const [currentFilterBasedContingencyListId, setcurrentFilterBasedContingencyListId] = useState(); + const handleCloseFilterBasedContingency = useCallback(() => { + setOpenDialog(constants.DialogsId.NONE); + setcurrentFilterBasedContingencyListId(undefined); + setActiveElement(undefined); + setElementName(''); + }, [setActiveElement, setOpenDialog]); + const [currentExplicitNamingFilterId, setCurrentExplicitNamingFilterId] = useState(); /* Filters dialog: window status value to edit ExplicitNaming filters */ const handleCloseExplicitNamingFilterDialog = useCallback(() => { @@ -194,6 +204,9 @@ function DirectoryContentDialog( } else if (subtype === ContingencyListType.EXPLICIT_NAMING.id) { setCurrentExplicitNamingContingencyListId(event.data.elementUuid); setOpenDialog(subtype); + } else if (subtype === ContingencyListType.FILTERS.id) { + setcurrentFilterBasedContingencyListId(event.data.elementUuid); + setOpenDialog(subtype); } break; case ElementType.FILTER: @@ -293,6 +306,18 @@ function DirectoryContentDialog( /> ); } + if (currentFilterBasedContingencyListId !== undefined && activeElement) { + return ( + + ); + } if (currentExplicitNamingFilterId !== undefined && activeElement) { return ( handleOpenDialog(DialogsId.ADD_NEW_FILTER_BASED_CONTINGENCY_LIST), + callback: () => handleOpenDialog(DialogsId.ADD_NEW_FILTERS_CONTINGENCY_LIST), icon: null, }, ], @@ -379,7 +379,7 @@ export default function DirectoryTreeContextualMenu(props: Readonly ); - case DialogsId.ADD_NEW_FILTER_BASED_CONTINGENCY_LIST: + case DialogsId.ADD_NEW_FILTERS_CONTINGENCY_LIST: return ( } + */ +export function saveFilterBasedContingencyList( + id: string, + name: string, + description: string, + filters: FilterMetaData[] +) { + console.log('--------saving contingencyList--------'); + const urlSearchParams = new URLSearchParams(); + urlSearchParams.append('name', name); + urlSearchParams.append('description', description ?? ''); + urlSearchParams.append('contingencyListType', ContingencyListType.FILTERS.id); + + const url = `${PREFIX_EXPLORE_SERVER_QUERIES}/v1/explore/contingency-lists/${id}?${urlSearchParams.toString()}`; + + return backendFetch(url, { + method: 'put', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + filters: [...filters], + }), + }); +} + /** * Saves an explicit naming contingency list * @returns {Promise} From 6c3af4ffdd6aa7f8da65d8356fcb35089e7e000d Mon Sep 17 00:00:00 2001 From: basseche Date: Tue, 9 Sep 2025 15:27:08 +0200 Subject: [PATCH 05/23] All Ok Signed-off-by: basseche --- package-lock.json | 6 +- package.json | 2 +- .../contingency-list-filter-based-from.tsx | 65 ++++++++++++++----- src/translations/en.json | 1 + src/translations/fr.json | 1 + src/utils/contingency-list-types.ts | 6 ++ src/utils/rest-api.ts | 12 ++++ 7 files changed, 73 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2c4ac858..fa54aebf1 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.120.1", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.120.1.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.120.1", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.120.1.tgz", - "integrity": "sha512-+hYfBx779mDUshFENBYJL3goAIZQkAbI2l34CNRaWm2WNSlWJ4dgYpm72fJFRwzGRKiB2TdmR1f6G4DFWEdMLw==", + "resolved": "file:../commons-ui/gridsuite-commons-ui-0.120.1.tgz", + "integrity": "sha512-0sTp4lxWTCLTM77BBG3oiTcrWBr5qu1LG7ZayWr/qOj4Afn84FytaegpyVUH+VV+NmEdBerm9FBOJKSq2GGJGg==", "license": "MPL-2.0", "dependencies": { "@ag-grid-community/locale": "^33.1.0", diff --git a/package.json b/package.json index 8af01c3b7..dfab966f7 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.120.1", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.120.1.tgz", "@hookform/resolvers": "^4.0.0", "@mui/icons-material": "^5.16.14", "@mui/lab": "5.0.0-alpha.175", diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index 5d4f7690f..e4798738c 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -12,18 +12,23 @@ import { ElementType, EquipmentType, FieldConstants, + getBasicEquipmentLabel, TreeViewFinderNodeProps, UniqueNameInput, unscrollableDialogStyles, + useSnackMessage, } from '@gridsuite/commons-ui'; import { Box, Button, Typography } from '@mui/material'; import { useSelector } from 'react-redux'; import { FormattedMessage, useIntl } from 'react-intl'; -import { useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { FolderOutlined } from '@mui/icons-material'; import { ColDef } from 'ag-grid-community'; import { blue, brown, green, indigo, lime, red, teal } from '@mui/material/colors'; +import { useFormContext } from 'react-hook-form'; import { AppState } from '../../../../redux/types'; +import { getIdentifiablesFromFitlers } from '../../../../utils/rest-api'; +import { FilterMetaData, IdentifiableAttributes } from '../../../../utils/contingency-list-types'; export interface ContingencyListFilterBasedFromProps { studyName: string; @@ -40,21 +45,24 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly(studyName); const [selectedFolder, setSelectedFolder] = useState(''); const [isOpen, setIsOpen] = useState(false); + const [rowsData, setRowsData] = useState([]); const intl = useIntl(); + const { snackError } = useSnackMessage(); + const { getValues } = useFormContext(); const colDef: ColDef[] = [ { headerName: intl.formatMessage({ - id: 'equipmentID', + id: FieldConstants.EQUIPMENT_ID, }), - field: 'equipmentID', + field: FieldConstants.ID, }, { headerName: intl.formatMessage({ - id: 'type', + id: FieldConstants.TYPE, }), - field: 'type', + field: FieldConstants.TYPE, }, ]; @@ -71,7 +79,6 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly = useMemo(() => { const map = new Map(); map.set(EquipmentType.TWO_WINDINGS_TRANSFORMER, blue[700]); @@ -84,6 +91,40 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly { + if (nodes.length > 0) { + if (nodes[0].parents && nodes[0].parents.length > 0) { + setSelectedFolder(nodes[0].parents.map((entry) => entry.name).join(separator)); + } + setSelectedStudy(nodes[0].name); + // call endpoint to update colDef + getIdentifiablesFromFitlers( + nodes[0].id, + getValues(FieldConstants.FILTERS).map((filter: FilterMetaData) => filter.id) + ) + .then((response: IdentifiableAttributes[]) => { + const attributes: IdentifiableAttributes[] = response.map((element: IdentifiableAttributes) => { + const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; + return { + id: element.id, + type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', + }; + }); + setRowsData(attributes); + }) + .catch((error) => + snackError({ + messageTxt: error.message, + headerId: 'cannotComputeContingencyList', + }) + ); + } + setIsOpen(false); + }, + [getValues, intl, snackError] + ); + return ( <> @@ -128,20 +169,12 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly { - if (nodes.length > 0) { - if (nodes[0].parents && nodes[0].parents.length > 0) { - setSelectedFolder(nodes[0].parents.map((entry) => entry.name).join(separator)); - } - setSelectedStudy(nodes[0].name); - } - setIsOpen(false); - }} + onClose={onNodeChanged} multiSelect={false} /> - + ); } diff --git a/src/translations/en.json b/src/translations/en.json index a3fb1097a..f1497ebcf 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -116,6 +116,7 @@ "Min": "Min", "Max": "Max", "cannotRetrieveContingencyList": "Could not retrieve contingency list: ", + "cannotComputeContingencyList" : "Could not compute contingency list", "retrieveCompositeModificationError": "Could not retrieve composite modification content: ", "PropertyName": "Property name", "getAppLinkError": "Error getting application link for type = {type}", diff --git a/src/translations/fr.json b/src/translations/fr.json index 24bc23503..9c819f52f 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -115,6 +115,7 @@ "Min": "Min", "Max": "Max", "cannotRetrieveContingencyList": "Erreur d'accès à la liste d'aléas : ", + "cannotComputeContingencyList" : "Erreur de calcul de la liste d'aléas", "retrieveCompositeModificationError": "Erreur d'accès au contenu de la modification composite : ", "PropertyName": "Nom de la propriété", "getAppLinkError": "Erreur lors de la récupération du lien vers l'application pour le type = {type}", diff --git a/src/utils/contingency-list-types.ts b/src/utils/contingency-list-types.ts index 66c5b91f5..4b0cf70d7 100644 --- a/src/utils/contingency-list-types.ts +++ b/src/utils/contingency-list-types.ts @@ -11,3 +11,9 @@ export interface FilterMetaData { name: string; equipmentType: string; } + +export interface IdentifiableAttributes { + id: string; + type: string; + distributionKey?: number; +} diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index fc05676e6..af5408f95 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -575,6 +575,18 @@ export function getContingencyList(type: string, id: string) { }); } +export function getIdentifiablesFromFitlers(studyUuid: UUID, filters: UUID[]) { + console.info('get identifiables resulting from application of filters list on study root network'); + + const filtersListsQueryParams = getRequestParamFromList('filtersUuid', filters); + const urlSearchParams = new URLSearchParams(filtersListsQueryParams); + + return backendFetchJson(`${PREFIX_STUDY_QUERIES}/v1/studies/${studyUuid}/filters/elements?${urlSearchParams}`, { + method: 'get', + headers: { 'Content-Type': 'application/json' }, + }); +} + export interface CriteriaBasedEditionFormData { [FieldConstants.NAME]: string; [FieldConstants.DESCRIPTION]?: string; From 8e2eeb8cfc07eba0b51e47197d51d699aa388455 Mon Sep 17 00:00:00 2001 From: basseche Date: Tue, 9 Sep 2025 16:12:11 +0200 Subject: [PATCH 06/23] remove todo --- .../dialogs/contingency-list/contingency-list-utils.ts | 1 - .../filter-based/contingency-list-filter-based-dialog.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/components/dialogs/contingency-list/contingency-list-utils.ts b/src/components/dialogs/contingency-list/contingency-list-utils.ts index f396bd386..a41ad78aa 100644 --- a/src/components/dialogs/contingency-list/contingency-list-utils.ts +++ b/src/components/dialogs/contingency-list/contingency-list-utils.ts @@ -72,7 +72,6 @@ export const getCriteriaBasedFormDataFromFetchedElement = (response: any, name: ...getCriteriaBasedFormData(response), }); -// TODO basseche : try to type response export const getFilterBasedFormDataFromFetchedElement = (response: any, name: string, description: string) => ({ [FieldConstants.NAME]: name, [FieldConstants.DESCRIPTION]: description, diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index d4a11493a..ddc9a98db 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -79,7 +79,6 @@ export default function FilterBasedContingencyListDialog({ formState: { errors }, } = methods; - // TODO basseche : try to type response useEffect(() => { if (id) { setIsFetching(true); From 9b3f7f8c194692ccbec530a3258240d6b6e0484d Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 10 Sep 2025 10:06:04 +0200 Subject: [PATCH 07/23] Improve GUI Signed-off-by: basseche --- package-lock.json | 2 +- .../contingency-list-filter-based-dialog.tsx | 2 +- .../contingency-list-filter-based-from.tsx | 88 ++++++++++--------- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index fa54aebf1..f04594bb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3138,7 +3138,7 @@ "node_modules/@gridsuite/commons-ui": { "version": "0.120.1", "resolved": "file:../commons-ui/gridsuite-commons-ui-0.120.1.tgz", - "integrity": "sha512-0sTp4lxWTCLTM77BBG3oiTcrWBr5qu1LG7ZayWr/qOj4Afn84FytaegpyVUH+VV+NmEdBerm9FBOJKSq2GGJGg==", + "integrity": "sha512-bkQzUa0cyZNU78GZuR9VvErZNNv1V1oox3qID7aiT/DvhQNotfEbbKku23oUneTuRukUC3e2ae1ASM5sbLxrcw==", "license": "MPL-2.0", "dependencies": { "@ag-grid-community/locale": "^33.1.0", diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index ddc9a98db..73aeb7c23 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -176,7 +176,7 @@ export default function FilterBasedContingencyListDialog({ disabledSave={Boolean(!!nameError || isValidating)} isDataFetching={isFetching} > - + ); } diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index e4798738c..4ae014358 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -21,35 +21,34 @@ import { import { Box, Button, Typography } from '@mui/material'; import { useSelector } from 'react-redux'; import { FormattedMessage, useIntl } from 'react-intl'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { FolderOutlined } from '@mui/icons-material'; import { ColDef } from 'ag-grid-community'; import { blue, brown, green, indigo, lime, red, teal } from '@mui/material/colors'; -import { useFormContext } from 'react-hook-form'; +import { useWatch } from 'react-hook-form'; +import { UUID } from 'crypto'; import { AppState } from '../../../../redux/types'; import { getIdentifiablesFromFitlers } from '../../../../utils/rest-api'; import { FilterMetaData, IdentifiableAttributes } from '../../../../utils/contingency-list-types'; -export interface ContingencyListFilterBasedFromProps { - studyName: string; -} - const separator = '/'; const defaultDef: ColDef = { flex: 1, resizable: false, }; -export default function ContingencyListFilterBasedFrom({ studyName }: Readonly) { +export default function ContingencyListFilterBasedFrom() { const activeDirectory = useSelector((state: AppState) => state.activeDirectory); - const [selectedStudy, setSelectedStudy] = useState(studyName); + const [selectedStudy, setSelectedStudy] = useState(''); + const [selectedStudyId, setSelectedStudyId] = useState(); const [selectedFolder, setSelectedFolder] = useState(''); const [isOpen, setIsOpen] = useState(false); + const [isFetching, setIsFetching] = useState(false); const [rowsData, setRowsData] = useState([]); const intl = useIntl(); const { snackError } = useSnackMessage(); - const { getValues } = useFormContext(); + const filters = useWatch({ name: FieldConstants.FILTERS }); const colDef: ColDef[] = [ { @@ -91,39 +90,47 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly { - if (nodes.length > 0) { - if (nodes[0].parents && nodes[0].parents.length > 0) { - setSelectedFolder(nodes[0].parents.map((entry) => entry.name).join(separator)); - } - setSelectedStudy(nodes[0].name); - // call endpoint to update colDef - getIdentifiablesFromFitlers( - nodes[0].id, - getValues(FieldConstants.FILTERS).map((filter: FilterMetaData) => filter.id) - ) - .then((response: IdentifiableAttributes[]) => { - const attributes: IdentifiableAttributes[] = response.map((element: IdentifiableAttributes) => { - const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; - return { - id: element.id, - type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', - }; - }); - setRowsData(attributes); + const onStudyChanged = useCallback(() => { + if (filters && selectedStudyId) { + setIsFetching(true); + getIdentifiablesFromFitlers( + selectedStudyId, + filters.map((filter: FilterMetaData) => filter.id) + ) + .then((response: IdentifiableAttributes[]) => { + const attributes: IdentifiableAttributes[] = response.map((element: IdentifiableAttributes) => { + const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; + return { + id: element.id, + type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', + }; + }); + setRowsData(attributes); + }) + .catch((error) => + snackError({ + messageTxt: error.message, + headerId: 'cannotComputeContingencyList', }) - .catch((error) => - snackError({ - messageTxt: error.message, - headerId: 'cannotComputeContingencyList', - }) - ); + ) + .finally(() => setIsFetching(false)); + } + }, [filters, intl, selectedStudyId, snackError]); + + useEffect(() => { + onStudyChanged(); + }, [filters, onStudyChanged, selectedStudyId]); + + const onNodeChanged = useCallback((nodes: TreeViewFinderNodeProps[]) => { + if (nodes.length > 0) { + if (nodes[0].parents && nodes[0].parents.length > 0) { + setSelectedFolder(nodes[0].parents.map((entry) => entry.name).join(separator)); } - setIsOpen(false); - }, - [getValues, intl, snackError] - ); + setSelectedStudy(nodes[0].name); + setSelectedStudyId(nodes[0].id); + } + setIsOpen(false); + }, []); return ( <> @@ -147,6 +154,7 @@ export default function ContingencyListFilterBasedFrom({ studyName }: Readonly From 21775b2b118cda236360153657a72c78bada26ec Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 10 Sep 2025 13:45:01 +0200 Subject: [PATCH 08/23] exclude filter based contingency list from types created by contingency list creation --- .../creation/contingency-list-creation-form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dialogs/contingency-list/creation/contingency-list-creation-form.tsx b/src/components/dialogs/contingency-list/creation/contingency-list-creation-form.tsx index 9351a4cc2..fc0903b8f 100644 --- a/src/components/dialogs/contingency-list/creation/contingency-list-creation-form.tsx +++ b/src/components/dialogs/contingency-list/creation/contingency-list-creation-form.tsx @@ -42,7 +42,7 @@ export default function ContingencyListCreationForm() { const contingencyListTypeField = ( ); From 999734f5b8f86dd29e96c2a9f2ede02ff8c2a075 Mon Sep 17 00:00:00 2001 From: basseche Date: Fri, 12 Sep 2025 11:41:04 +0200 Subject: [PATCH 09/23] add filtering --- .../filter-based/contingency-list-filter-based-from.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index 4ae014358..09eaa7580 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -56,12 +56,14 @@ export default function ContingencyListFilterBasedFrom() { id: FieldConstants.EQUIPMENT_ID, }), field: FieldConstants.ID, + filter: 'agTextColumnFilter', }, { headerName: intl.formatMessage({ id: FieldConstants.TYPE, }), field: FieldConstants.TYPE, + filter: 'agTextColumnFilter', }, ]; From ecc5f99ec43742bec0000c1b19e448fdb3bd0088 Mon Sep 17 00:00:00 2001 From: basseche Date: Fri, 12 Sep 2025 12:46:56 +0200 Subject: [PATCH 10/23] remove console log --- package-lock.json | 6 +++--- package.json | 2 +- src/utils/rest-api.ts | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 542b1e347..1f94e87e2 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.122.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.122.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.122.0", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.122.0.tgz", - "integrity": "sha512-bUog0r940NDYUyCfKQuRJsW2H1elylYJwu9WVpXiwSi2s5OMwjQtk8T7CHGk0FmyGI4XmviUPEWYx1WU7RzQIA==", + "resolved": "file:../commons-ui/gridsuite-commons-ui-0.122.0.tgz", + "integrity": "sha512-gUQxTvKR2fRsXAgaQwFC/Ml3wGAOtwD8O9Y57mv0en0aBRODqnjmiXxv9JMIZE+XgszYmksc2xpuX8e79lq9Rg==", "license": "MPL-2.0", "dependencies": { "@ag-grid-community/locale": "^33.1.0", diff --git a/package.json b/package.json index c9ac7d756..3cf241a4a 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.122.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.122.0.tgz", "@hookform/resolvers": "^4.0.0", "@mui/icons-material": "^5.16.14", "@mui/lab": "5.0.0-alpha.175", diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index af5408f95..a4a59ce9c 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -551,7 +551,6 @@ export function createFilterBasedContingency( urlSearchParams.append('description', description); urlSearchParams.append('parentDirectoryUuid', parentDirectoryUuid ?? ''); - console.log('filtersUuids : ', filtersUuids); const createContingencyListUrl = `${PREFIX_EXPLORE_SERVER_QUERIES}/v1/explore${CONTINGENCY_ENDPOINTS.FILTERS_CONTINGENCY_LISTS}/${encodeURIComponent( name )}?${urlSearchParams.toString()}`; @@ -653,7 +652,6 @@ export function saveFilterBasedContingencyList( description: string, filters: FilterMetaData[] ) { - console.log('--------saving contingencyList--------'); const urlSearchParams = new URLSearchParams(); urlSearchParams.append('name', name); urlSearchParams.append('description', description ?? ''); From 5bd6b63c6945c66a1a78114113deebd35d9e2303 Mon Sep 17 00:00:00 2001 From: basseche Date: Tue, 16 Sep 2025 18:05:02 +0200 Subject: [PATCH 11/23] change to adapt to server modifications Signed-off-by: basseche --- .../contingency-list-filter-based-from.tsx | 25 +++++++++++-------- src/utils/contingency-list-types.ts | 5 ++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index 09eaa7580..4ab6f2c31 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -29,7 +29,11 @@ import { useWatch } from 'react-hook-form'; import { UUID } from 'crypto'; import { AppState } from '../../../../redux/types'; import { getIdentifiablesFromFitlers } from '../../../../utils/rest-api'; -import { FilterMetaData, IdentifiableAttributes } from '../../../../utils/contingency-list-types'; +import { + FilteredIdentifiables, + FilterMetaData, + IdentifiableAttributes, +} from '../../../../utils/contingency-list-types'; const separator = '/'; const defaultDef: ColDef = { @@ -73,7 +77,6 @@ export default function ContingencyListFilterBasedFrom() { EquipmentType.LINE, EquipmentType.LOAD, EquipmentType.GENERATOR, - EquipmentType.GENERATOR, EquipmentType.SHUNT_COMPENSATOR, EquipmentType.STATIC_VAR_COMPENSATOR, EquipmentType.HVDC_LINE, @@ -99,14 +102,16 @@ export default function ContingencyListFilterBasedFrom() { selectedStudyId, filters.map((filter: FilterMetaData) => filter.id) ) - .then((response: IdentifiableAttributes[]) => { - const attributes: IdentifiableAttributes[] = response.map((element: IdentifiableAttributes) => { - const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; - return { - id: element.id, - type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', - }; - }); + .then((response: FilteredIdentifiables) => { + const attributes: IdentifiableAttributes[] = response.equipmentIds.map( + (element: IdentifiableAttributes) => { + const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; + return { + id: element.id, + type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', + }; + } + ); setRowsData(attributes); }) .catch((error) => diff --git a/src/utils/contingency-list-types.ts b/src/utils/contingency-list-types.ts index 4b0cf70d7..41eacc384 100644 --- a/src/utils/contingency-list-types.ts +++ b/src/utils/contingency-list-types.ts @@ -17,3 +17,8 @@ export interface IdentifiableAttributes { type: string; distributionKey?: number; } + +export interface FilteredIdentifiables { + equipmentIds: IdentifiableAttributes[]; + notFoundIds: IdentifiableAttributes[]; +} From 5e53494ed0fdd16048f26d6286239948641581c6 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 17 Sep 2025 11:00:13 +0200 Subject: [PATCH 12/23] add not found --- .../contingency-list-filter-based-from.tsx | 20 +++++++++++++ .../filter-based/separator-cell-renderer.tsx | 30 +++++++++++++++++++ src/translations/en.json | 1 + src/translations/fr.json | 1 + 4 files changed, 52 insertions(+) create mode 100644 src/components/dialogs/contingency-list/filter-based/separator-cell-renderer.tsx diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index 4ab6f2c31..50beef285 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -34,6 +34,7 @@ import { FilterMetaData, IdentifiableAttributes, } from '../../../../utils/contingency-list-types'; +import SeparatorCellRenderer from './separator-cell-renderer'; const separator = '/'; const defaultDef: ColDef = { @@ -61,6 +62,14 @@ export default function ContingencyListFilterBasedFrom() { }), field: FieldConstants.ID, filter: 'agTextColumnFilter', + cellRenderer: ({ data }: { data: IdentifiableAttributes }) => { + if (data.id === 'SEPARATOR') { + return SeparatorCellRenderer({ + value: intl.formatMessage({ id: 'missingFromStudy' }), + }); + } + return data.id; + }, }, { headerName: intl.formatMessage({ @@ -103,6 +112,7 @@ export default function ContingencyListFilterBasedFrom() { filters.map((filter: FilterMetaData) => filter.id) ) .then((response: FilteredIdentifiables) => { + const SEPARATOR_TYPE = 'SEPARATOR'; const attributes: IdentifiableAttributes[] = response.equipmentIds.map( (element: IdentifiableAttributes) => { const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; @@ -112,6 +122,16 @@ export default function ContingencyListFilterBasedFrom() { }; } ); + if (response.notFoundIds?.length > 0) { + attributes.push({ id: SEPARATOR_TYPE, type: '' }); + response.notFoundIds.forEach((element: IdentifiableAttributes) => { + const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; + attributes.push({ + id: element.id, + type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', + }); + }); + } setRowsData(attributes); }) .catch((error) => diff --git a/src/components/dialogs/contingency-list/filter-based/separator-cell-renderer.tsx b/src/components/dialogs/contingency-list/filter-based/separator-cell-renderer.tsx new file mode 100644 index 000000000..66e0fe243 --- /dev/null +++ b/src/components/dialogs/contingency-list/filter-based/separator-cell-renderer.tsx @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2025, 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/. + */ + +// TODO : try to see if it worth it to put this in commons-ui as it is the same as in gridstudy +import { Theme, Typography } from '@mui/material'; + +const styles = { + separator: (theme: Theme) => ({ + fontWeight: 'bold', + fontSize: '1rem', + width: '100%', + marginTop: theme.spacing(1), + }), +}; + +type SeparatorCellRendererProps = { + value: string; +}; + +export default function SeparatorCellRenderer({ value }: Readonly) { + return ( + + {value} + + ); +} diff --git a/src/translations/en.json b/src/translations/en.json index f1497ebcf..ca88c7337 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -123,6 +123,7 @@ "missingEquipmentsIdsError": "ID is required field for all equipments", "contingencyTablePartiallyDefinedError": "At least one contingency is partially defined", "contingencyTableContainAtLeastOneRowError": "Contingency list should contain at least one contingency ", + "missingFromStudy": "Missing from study", "moveItemTitle": "Move element to another folder", "moveDirectoryTitle": "Move folder ''{directoryName}'' to another folder", "moveItemValidate": "{nbElements, plural, =0 {Please select a folder} =1 {Move element} other {Move # elements}}", diff --git a/src/translations/fr.json b/src/translations/fr.json index 7db8b13d4..4e073bd1e 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -122,6 +122,7 @@ "missingEquipmentsIdsError": "L'ID est un champ obligatoire pour tous les ouvrages", "contingencyTablePartiallyDefinedError": "Au moins un des aléas est défini de manière incomplète", "contingencyTableContainAtLeastOneRowError": "La liste d'aléas doit contenir au moins un aléa", + "missingFromStudy": "Absents de l'étude", "moveItemTitle": "Déplacer élément vers un autre dossier", "moveDirectoryTitle": "Déplacer le dossier ''{directoryName}'' vers un autre dossier", "moveItemValidate": "{nbElements, plural, =0 {Veuillez sélectionner un dossier} =1 {Déplacer l'élément} other {Déplacer # éléments}}", From de8225705a2936740e1c704bcb3b42b0ae35ad7a Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 17 Sep 2025 11:27:24 +0200 Subject: [PATCH 13/23] desactivate sort and filter --- .../filter-based/contingency-list-filter-based-from.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index 50beef285..881f555db 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -40,6 +40,8 @@ const separator = '/'; const defaultDef: ColDef = { flex: 1, resizable: false, + sortable: false, + filter: }; export default function ContingencyListFilterBasedFrom() { @@ -61,7 +63,6 @@ export default function ContingencyListFilterBasedFrom() { id: FieldConstants.EQUIPMENT_ID, }), field: FieldConstants.ID, - filter: 'agTextColumnFilter', cellRenderer: ({ data }: { data: IdentifiableAttributes }) => { if (data.id === 'SEPARATOR') { return SeparatorCellRenderer({ @@ -76,7 +77,6 @@ export default function ContingencyListFilterBasedFrom() { id: FieldConstants.TYPE, }), field: FieldConstants.TYPE, - filter: 'agTextColumnFilter', }, ]; From b5af04fb3bf8970983a84bb6712486ce35b71d5b Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 17 Sep 2025 11:45:38 +0200 Subject: [PATCH 14/23] fix --- .../filter-based/contingency-list-filter-based-from.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index 881f555db..b827913d9 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -41,7 +41,6 @@ const defaultDef: ColDef = { flex: 1, resizable: false, sortable: false, - filter: }; export default function ContingencyListFilterBasedFrom() { From cf5011b0a7eab2fabd09b41db75b552eadd4d34c Mon Sep 17 00:00:00 2001 From: basseche Date: Fri, 19 Sep 2025 16:18:22 +0200 Subject: [PATCH 15/23] modification to fit back --- .../dialogs/contingency-list/contingency-list-utils.ts | 4 ++-- .../filter-based/contingency-list-filter-based-dialog.tsx | 4 ---- .../filter-based/contingency-list-filter-based-from.tsx | 4 ++-- src/utils/contingency-list-types.ts | 7 ++++--- src/utils/rest-api.ts | 6 +++--- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/components/dialogs/contingency-list/contingency-list-utils.ts b/src/components/dialogs/contingency-list/contingency-list-utils.ts index a41ad78aa..deda6becf 100644 --- a/src/components/dialogs/contingency-list/contingency-list-utils.ts +++ b/src/components/dialogs/contingency-list/contingency-list-utils.ts @@ -16,7 +16,7 @@ import { import type { SetRequired } from 'type-fest'; import { prepareContingencyListForBackend } from '../contingency-list-helper'; import { ContingencyListType } from '../../../utils/elementType'; -import { FilterMetaData } from '../../../utils/contingency-list-types'; +import { FilterAttributes } from '../../../utils/contingency-list-types'; export interface Identifier { type: 'ID_BASED'; @@ -76,7 +76,7 @@ export const getFilterBasedFormDataFromFetchedElement = (response: any, name: st [FieldConstants.NAME]: name, [FieldConstants.DESCRIPTION]: description, [FieldConstants.CONTINGENCY_LIST_TYPE]: ContingencyListType.FILTERS.id, - [FieldConstants.FILTERS]: response.filters.map((filter: FilterMetaData) => { + [FieldConstants.FILTERS]: response.filters.map((filter: FilterAttributes) => { return { id: filter.id, name: filter.name, specificMetadata: { equipmentType: filter.equipmentType } }; }), }); diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index 73aeb7c23..3b188fe20 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -116,8 +116,6 @@ export default function FilterBasedContingencyListDialog({ data[FieldConstants.FILTERS].map((item) => { return { id: item.id, - name: item.name, - equipmentType: item.specificMetadata?.equipmentType ?? '', }; }) ) @@ -139,8 +137,6 @@ export default function FilterBasedContingencyListDialog({ data[FieldConstants.FILTERS]?.map((item: TreeViewFinderNodeProps) => { return { id: item.id, - name: item.name, - equipmentType: item.specificMetadata?.equipmentType ?? '', }; }), activeDirectory diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx index b827913d9..6799653ba 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx @@ -31,7 +31,7 @@ import { AppState } from '../../../../redux/types'; import { getIdentifiablesFromFitlers } from '../../../../utils/rest-api'; import { FilteredIdentifiables, - FilterMetaData, + FilterAttributes, IdentifiableAttributes, } from '../../../../utils/contingency-list-types'; import SeparatorCellRenderer from './separator-cell-renderer'; @@ -108,7 +108,7 @@ export default function ContingencyListFilterBasedFrom() { setIsFetching(true); getIdentifiablesFromFitlers( selectedStudyId, - filters.map((filter: FilterMetaData) => filter.id) + filters.map((filter: FilterAttributes) => filter.id) ) .then((response: FilteredIdentifiables) => { const SEPARATOR_TYPE = 'SEPARATOR'; diff --git a/src/utils/contingency-list-types.ts b/src/utils/contingency-list-types.ts index 41eacc384..1febcc87f 100644 --- a/src/utils/contingency-list-types.ts +++ b/src/utils/contingency-list-types.ts @@ -6,10 +6,11 @@ */ import { UUID } from 'crypto'; -export interface FilterMetaData { +// reproduce partially dto from Filter-server +export interface FilterAttributes { id: UUID; - name: string; - equipmentType: string; + name?: string; + equipmentType?: string; } export interface IdentifiableAttributes { diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index a4a59ce9c..29e33f3d7 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -31,7 +31,7 @@ import { AppState } from '../redux/types'; import { PrepareContingencyListForBackend } from '../components/dialogs/contingency-list-helper'; import { UsersIdentities } from './user-identities.type'; import { HTTP_OK } from './UIconstants'; -import { FilterMetaData } from './contingency-list-types'; +import { FilterAttributes } from './contingency-list-types'; const PREFIX_USER_ADMIN_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/user-admin`; const PREFIX_EXPLORE_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/explore`; @@ -543,7 +543,7 @@ export function createContingencyList( export function createFilterBasedContingency( name: string, description: string, - filtersUuids: FilterMetaData[], + filtersUuids: FilterAttributes[], parentDirectoryUuid: UUID | undefined ) { console.info('Creating a new filter based contingency list...'); @@ -650,7 +650,7 @@ export function saveFilterBasedContingencyList( id: string, name: string, description: string, - filters: FilterMetaData[] + filters: FilterAttributes[] ) { const urlSearchParams = new URLSearchParams(); urlSearchParams.append('name', name); From a346a7914b7d4f4a40ea5c2a4dfbd7c927008028 Mon Sep 17 00:00:00 2001 From: basseche Date: Mon, 22 Sep 2025 16:01:10 +0200 Subject: [PATCH 16/23] review --- .../contingency-list-filter-based-dialog.tsx | 7 ++++--- ...om.tsx => contingency-list-filter-based-form.tsx} | 12 ++++-------- 2 files changed, 8 insertions(+), 11 deletions(-) rename src/components/dialogs/contingency-list/filter-based/{contingency-list-filter-based-from.tsx => contingency-list-filter-based-form.tsx} (96%) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index 3b188fe20..349b03b9c 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -18,7 +18,8 @@ import { yupResolver } from '@hookform/resolvers/yup'; import { useSelector } from 'react-redux'; import { useCallback, useEffect, useState } from 'react'; import { UUID } from 'crypto'; -import ContingencyListFilterBasedFrom from './contingency-list-filter-based-from'; +import { ObjectSchema } from 'yup'; +import ContingencyListFilterBasedForm from './contingency-list-filter-based-form'; import { AppState } from '../../../../redux/types'; import { createFilterBasedContingency, @@ -38,7 +39,7 @@ export interface FilterBasedContingencyListProps { id?: UUID; } -const schema: any = yup.object().shape({ +const schema: ObjectSchema = yup.object().shape({ [FieldConstants.NAME]: yup.string().required(), [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), [FieldConstants.FILTERS]: yup.array().required(), @@ -172,7 +173,7 @@ export default function FilterBasedContingencyListDialog({ disabledSave={Boolean(!!nameError || isValidating)} isDataFetching={isFetching} > - + ); } diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx similarity index 96% rename from src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx rename to src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx index 6799653ba..b17eeb34c 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-from.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx @@ -43,7 +43,7 @@ const defaultDef: ColDef = { sortable: false, }; -export default function ContingencyListFilterBasedFrom() { +export default function ContingencyListFilterBasedForm() { const activeDirectory = useSelector((state: AppState) => state.activeDirectory); const [selectedStudy, setSelectedStudy] = useState(''); const [selectedStudyId, setSelectedStudyId] = useState(); @@ -103,7 +103,7 @@ export default function ContingencyListFilterBasedFrom() { return map; }, []); - const onStudyChanged = useCallback(() => { + useEffect(() => { if (filters && selectedStudyId) { setIsFetching(true); getIdentifiablesFromFitlers( @@ -114,7 +114,7 @@ export default function ContingencyListFilterBasedFrom() { const SEPARATOR_TYPE = 'SEPARATOR'; const attributes: IdentifiableAttributes[] = response.equipmentIds.map( (element: IdentifiableAttributes) => { - const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; + const equipmentType: string = getBasicEquipmentLabel(element?.type); return { id: element.id, type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', @@ -124,7 +124,7 @@ export default function ContingencyListFilterBasedFrom() { if (response.notFoundIds?.length > 0) { attributes.push({ id: SEPARATOR_TYPE, type: '' }); response.notFoundIds.forEach((element: IdentifiableAttributes) => { - const equipmentType: string = getBasicEquipmentLabel(element?.type) ?? null; + const equipmentType: string = getBasicEquipmentLabel(element?.type); attributes.push({ id: element.id, type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', @@ -143,10 +143,6 @@ export default function ContingencyListFilterBasedFrom() { } }, [filters, intl, selectedStudyId, snackError]); - useEffect(() => { - onStudyChanged(); - }, [filters, onStudyChanged, selectedStudyId]); - const onNodeChanged = useCallback((nodes: TreeViewFinderNodeProps[]) => { if (nodes.length > 0) { if (nodes[0].parents && nodes[0].parents.length > 0) { From 3ae3628c0ae6dd3342d6ea720f8f4944ac4c48f5 Mon Sep 17 00:00:00 2001 From: basseche Date: Tue, 23 Sep 2025 14:09:57 +0200 Subject: [PATCH 17/23] change function name commun-ui --- .../filter-based/contingency-list-filter-based-form.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx index b17eeb34c..382cf34c0 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx @@ -12,7 +12,7 @@ import { ElementType, EquipmentType, FieldConstants, - getBasicEquipmentLabel, + getFilterEquipmentTypeLabel, TreeViewFinderNodeProps, UniqueNameInput, unscrollableDialogStyles, @@ -114,7 +114,7 @@ export default function ContingencyListFilterBasedForm() { const SEPARATOR_TYPE = 'SEPARATOR'; const attributes: IdentifiableAttributes[] = response.equipmentIds.map( (element: IdentifiableAttributes) => { - const equipmentType: string = getBasicEquipmentLabel(element?.type); + const equipmentType: string = getFilterEquipmentTypeLabel(element?.type); return { id: element.id, type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', @@ -124,7 +124,7 @@ export default function ContingencyListFilterBasedForm() { if (response.notFoundIds?.length > 0) { attributes.push({ id: SEPARATOR_TYPE, type: '' }); response.notFoundIds.forEach((element: IdentifiableAttributes) => { - const equipmentType: string = getBasicEquipmentLabel(element?.type); + const equipmentType: string = getFilterEquipmentTypeLabel(element?.type); attributes.push({ id: element.id, type: equipmentType ? intl.formatMessage({ id: equipmentType }) : '', From 47ed523bcacae2b5133cfd13e089babeec50757c Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 24 Sep 2025 12:06:05 +0200 Subject: [PATCH 18/23] Update src/utils/rest-api.ts Co-authored-by: Thang PHAM <117309322+thangqp@users.noreply.github.com> --- src/utils/rest-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index 29e33f3d7..6e2b0e91b 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -558,7 +558,7 @@ export function createFilterBasedContingency( return backendFetch(createContingencyListUrl, { method: 'post', - body: JSON.stringify({ filters: filtersUuids }), + body: JSON.stringify(contingency), }); } From 5eff01930722a0c4fb6e517762d55a63dc9bc87b Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 24 Sep 2025 12:06:23 +0200 Subject: [PATCH 19/23] Update src/utils/rest-api.ts Co-authored-by: Thang PHAM <117309322+thangqp@users.noreply.github.com> --- src/utils/rest-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index 6e2b0e91b..a810700e9 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -543,7 +543,7 @@ export function createContingencyList( export function createFilterBasedContingency( name: string, description: string, - filtersUuids: FilterAttributes[], + contingency: FilterBasedContingencyList, parentDirectoryUuid: UUID | undefined ) { console.info('Creating a new filter based contingency list...'); From a250861581056b3c649fe0d2662e88535acad5e5 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 24 Sep 2025 12:35:24 +0200 Subject: [PATCH 20/23] review --- .../contingency-list-utils.ts | 2 +- .../contingency-list-filter-based-dialog.tsx | 18 +++---- .../contingency-list-filter-based-form.tsx | 50 +++++++++---------- .../filter-based/separator-cell-renderer.tsx | 30 ----------- ...list-types.ts => contingency-list-type.ts} | 0 src/utils/rest-api.ts | 2 +- 6 files changed, 34 insertions(+), 68 deletions(-) delete mode 100644 src/components/dialogs/contingency-list/filter-based/separator-cell-renderer.tsx rename src/utils/{contingency-list-types.ts => contingency-list-type.ts} (100%) diff --git a/src/components/dialogs/contingency-list/contingency-list-utils.ts b/src/components/dialogs/contingency-list/contingency-list-utils.ts index deda6becf..9c5ee2882 100644 --- a/src/components/dialogs/contingency-list/contingency-list-utils.ts +++ b/src/components/dialogs/contingency-list/contingency-list-utils.ts @@ -16,7 +16,7 @@ import { import type { SetRequired } from 'type-fest'; import { prepareContingencyListForBackend } from '../contingency-list-helper'; import { ContingencyListType } from '../../../utils/elementType'; -import { FilterAttributes } from '../../../utils/contingency-list-types'; +import { FilterAttributes } from '../../../utils/contingency-list-type'; export interface Identifier { type: 'ID_BASED'; diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index 349b03b9c..ff14a46b0 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -30,15 +30,6 @@ import { handleNotAllowedError } from '../../../utils/rest-errors'; import { ContingencyListType } from '../../../../utils/elementType'; import { getFilterBasedFormDataFromFetchedElement } from '../contingency-list-utils'; -export interface FilterBasedContingencyListProps { - titleId: string; - open: boolean; - onClose: () => void; - name?: string; - description?: string; - id?: UUID; -} - const schema: ObjectSchema = yup.object().shape({ [FieldConstants.NAME]: yup.string().required(), [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION), @@ -59,6 +50,15 @@ const getContingencyListEmptyFormData = (name = '') => ({ const emptyFormData = (name?: string) => getContingencyListEmptyFormData(name); +export interface FilterBasedContingencyListProps { + titleId: string; + open: boolean; + onClose: () => void; + name?: string; + description?: string; + id?: UUID; +} + export default function FilterBasedContingencyListDialog({ titleId, open, diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx index 382cf34c0..bafe620b0 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx @@ -13,6 +13,7 @@ import { EquipmentType, FieldConstants, getFilterEquipmentTypeLabel, + SeparatorCellRenderer, TreeViewFinderNodeProps, UniqueNameInput, unscrollableDialogStyles, @@ -21,7 +22,7 @@ import { import { Box, Button, Typography } from '@mui/material'; import { useSelector } from 'react-redux'; import { FormattedMessage, useIntl } from 'react-intl'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { FolderOutlined } from '@mui/icons-material'; import { ColDef } from 'ag-grid-community'; import { blue, brown, green, indigo, lime, red, teal } from '@mui/material/colors'; @@ -33,8 +34,7 @@ import { FilteredIdentifiables, FilterAttributes, IdentifiableAttributes, -} from '../../../../utils/contingency-list-types'; -import SeparatorCellRenderer from './separator-cell-renderer'; +} from '../../../../utils/contingency-list-type'; const separator = '/'; const defaultDef: ColDef = { @@ -43,6 +43,26 @@ const defaultDef: ColDef = { sortable: false, }; +const equipmentTypes: string[] = [ + EquipmentType.TWO_WINDINGS_TRANSFORMER, + EquipmentType.LINE, + EquipmentType.LOAD, + EquipmentType.GENERATOR, + EquipmentType.SHUNT_COMPENSATOR, + EquipmentType.STATIC_VAR_COMPENSATOR, + EquipmentType.HVDC_LINE, +]; + +const equipmentColorsMap: Map = new Map([ + [EquipmentType.TWO_WINDINGS_TRANSFORMER, blue[700]], + [EquipmentType.LINE, indigo[700]], + [EquipmentType.LOAD, brown[700]], + [EquipmentType.GENERATOR, green[700]], + [EquipmentType.SHUNT_COMPENSATOR, red[700]], + [EquipmentType.STATIC_VAR_COMPENSATOR, lime[700]], + [EquipmentType.HVDC_LINE, teal[700]], +]); + export default function ContingencyListFilterBasedForm() { const activeDirectory = useSelector((state: AppState) => state.activeDirectory); const [selectedStudy, setSelectedStudy] = useState(''); @@ -79,30 +99,6 @@ export default function ContingencyListFilterBasedForm() { }, ]; - const equipmentTypes: string[] = useMemo(() => { - return [ - EquipmentType.TWO_WINDINGS_TRANSFORMER, - EquipmentType.LINE, - EquipmentType.LOAD, - EquipmentType.GENERATOR, - EquipmentType.SHUNT_COMPENSATOR, - EquipmentType.STATIC_VAR_COMPENSATOR, - EquipmentType.HVDC_LINE, - ]; - }, []); - - const equipmentColorsMap: Map = useMemo(() => { - const map = new Map(); - map.set(EquipmentType.TWO_WINDINGS_TRANSFORMER, blue[700]); - map.set(EquipmentType.LINE, indigo[700]); - map.set(EquipmentType.LOAD, brown[700]); - map.set(EquipmentType.GENERATOR, green[700]); - map.set(EquipmentType.SHUNT_COMPENSATOR, red[700]); - map.set(EquipmentType.STATIC_VAR_COMPENSATOR, lime[700]); - map.set(EquipmentType.HVDC_LINE, teal[700]); - return map; - }, []); - useEffect(() => { if (filters && selectedStudyId) { setIsFetching(true); diff --git a/src/components/dialogs/contingency-list/filter-based/separator-cell-renderer.tsx b/src/components/dialogs/contingency-list/filter-based/separator-cell-renderer.tsx deleted file mode 100644 index 66e0fe243..000000000 --- a/src/components/dialogs/contingency-list/filter-based/separator-cell-renderer.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2025, 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/. - */ - -// TODO : try to see if it worth it to put this in commons-ui as it is the same as in gridstudy -import { Theme, Typography } from '@mui/material'; - -const styles = { - separator: (theme: Theme) => ({ - fontWeight: 'bold', - fontSize: '1rem', - width: '100%', - marginTop: theme.spacing(1), - }), -}; - -type SeparatorCellRendererProps = { - value: string; -}; - -export default function SeparatorCellRenderer({ value }: Readonly) { - return ( - - {value} - - ); -} diff --git a/src/utils/contingency-list-types.ts b/src/utils/contingency-list-type.ts similarity index 100% rename from src/utils/contingency-list-types.ts rename to src/utils/contingency-list-type.ts diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index a810700e9..d7d3abd44 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -31,7 +31,7 @@ import { AppState } from '../redux/types'; import { PrepareContingencyListForBackend } from '../components/dialogs/contingency-list-helper'; import { UsersIdentities } from './user-identities.type'; import { HTTP_OK } from './UIconstants'; -import { FilterAttributes } from './contingency-list-types'; +import { FilterAttributes } from './contingency-list-type'; const PREFIX_USER_ADMIN_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/user-admin`; const PREFIX_EXPLORE_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/explore`; From dfedccab07b169002084bad8e45074c595b67a14 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 24 Sep 2025 13:13:56 +0200 Subject: [PATCH 21/23] review --- .../contingency-list-filter-based-dialog.tsx | 21 ++++++++++--------- src/utils/rest-api.ts | 11 ++++++---- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index ff14a46b0..e0a00e6f6 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -23,6 +23,7 @@ import ContingencyListFilterBasedForm from './contingency-list-filter-based-form import { AppState } from '../../../../redux/types'; import { createFilterBasedContingency, + FilterBasedContingencyList, getContingencyList, saveFilterBasedContingencyList, } from '../../../../utils/rest-api'; @@ -109,16 +110,20 @@ export default function FilterBasedContingencyListDialog({ const onSubmit = useCallback( (data: ContingencyListFilterBasedFormData) => { + const filterBaseContingencyList: FilterBasedContingencyList = { + filters: data[FieldConstants.FILTERS]?.map((item: TreeViewFinderNodeProps) => { + return { + id: item.id, + }; + }), + }; + if (id) { saveFilterBasedContingencyList( id, data[FieldConstants.NAME], data[FieldConstants.DESCRIPTION] ?? '', - data[FieldConstants.FILTERS].map((item) => { - return { - id: item.id, - }; - }) + filterBaseContingencyList ) .then(() => closeAndClear()) .catch((error) => { @@ -135,11 +140,7 @@ export default function FilterBasedContingencyListDialog({ createFilterBasedContingency( data[FieldConstants.NAME], data[FieldConstants.DESCRIPTION] ?? '', - data[FieldConstants.FILTERS]?.map((item: TreeViewFinderNodeProps) => { - return { - id: item.id, - }; - }), + filterBaseContingencyList, activeDirectory ) .then(() => closeAndClear()) diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index d7d3abd44..44377aacb 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -540,6 +540,11 @@ export function createContingencyList( body: JSON.stringify(formContent), }); } + +export interface FilterBasedContingencyList { + filters: FilterAttributes[]; +} + export function createFilterBasedContingency( name: string, description: string, @@ -650,7 +655,7 @@ export function saveFilterBasedContingencyList( id: string, name: string, description: string, - filters: FilterAttributes[] + contingencyList: FilterBasedContingencyList ) { const urlSearchParams = new URLSearchParams(); urlSearchParams.append('name', name); @@ -662,9 +667,7 @@ export function saveFilterBasedContingencyList( return backendFetch(url, { method: 'put', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - filters: [...filters], - }), + body: JSON.stringify(contingencyList), }); } From b63f5f77bce551b62ea07675e285ab973beb09f4 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Wed, 24 Sep 2025 14:54:28 +0200 Subject: [PATCH 22/23] move type Signed-off-by: Thang PHAM --- .../dialogs/contingency-list/contingency-list-utils.ts | 2 +- .../filter-based/contingency-list-filter-based-dialog.tsx | 4 ++-- .../filter-based/contingency-list-filter-based-form.tsx | 2 +- ...{contingency-list-type.ts => contingency-list.type.ts} | 5 +++++ src/utils/rest-api.ts | 8 ++------ 5 files changed, 11 insertions(+), 10 deletions(-) rename src/utils/{contingency-list-type.ts => contingency-list.type.ts} (85%) diff --git a/src/components/dialogs/contingency-list/contingency-list-utils.ts b/src/components/dialogs/contingency-list/contingency-list-utils.ts index 9c5ee2882..59b010b88 100644 --- a/src/components/dialogs/contingency-list/contingency-list-utils.ts +++ b/src/components/dialogs/contingency-list/contingency-list-utils.ts @@ -16,7 +16,7 @@ import { import type { SetRequired } from 'type-fest'; import { prepareContingencyListForBackend } from '../contingency-list-helper'; import { ContingencyListType } from '../../../utils/elementType'; -import { FilterAttributes } from '../../../utils/contingency-list-type'; +import { FilterAttributes } from '../../../utils/contingency-list.type'; export interface Identifier { type: 'ID_BASED'; diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index e0a00e6f6..a4ecaac55 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -9,9 +9,9 @@ import { CustomMuiDialog, FieldConstants, MAX_CHAR_DESCRIPTION, + TreeViewFinderNodeProps, useSnackMessage, yupConfig as yup, - TreeViewFinderNodeProps, } from '@gridsuite/commons-ui'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; @@ -23,13 +23,13 @@ import ContingencyListFilterBasedForm from './contingency-list-filter-based-form import { AppState } from '../../../../redux/types'; import { createFilterBasedContingency, - FilterBasedContingencyList, getContingencyList, saveFilterBasedContingencyList, } from '../../../../utils/rest-api'; import { handleNotAllowedError } from '../../../utils/rest-errors'; import { ContingencyListType } from '../../../../utils/elementType'; import { getFilterBasedFormDataFromFetchedElement } from '../contingency-list-utils'; +import { FilterBasedContingencyList } from '../../../../utils/contingency-list.type'; const schema: ObjectSchema = yup.object().shape({ [FieldConstants.NAME]: yup.string().required(), diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx index bafe620b0..3278a462b 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx @@ -34,7 +34,7 @@ import { FilteredIdentifiables, FilterAttributes, IdentifiableAttributes, -} from '../../../../utils/contingency-list-type'; +} from '../../../../utils/contingency-list.type'; const separator = '/'; const defaultDef: ColDef = { diff --git a/src/utils/contingency-list-type.ts b/src/utils/contingency-list.type.ts similarity index 85% rename from src/utils/contingency-list-type.ts rename to src/utils/contingency-list.type.ts index 1febcc87f..4b9b7ea80 100644 --- a/src/utils/contingency-list-type.ts +++ b/src/utils/contingency-list.type.ts @@ -23,3 +23,8 @@ export interface FilteredIdentifiables { equipmentIds: IdentifiableAttributes[]; notFoundIds: IdentifiableAttributes[]; } + +// type taken from actions-server +export interface FilterBasedContingencyList { + filters: FilterAttributes[]; +} diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts index 44377aacb..cd43cf00c 100644 --- a/src/utils/rest-api.ts +++ b/src/utils/rest-api.ts @@ -31,7 +31,7 @@ import { AppState } from '../redux/types'; import { PrepareContingencyListForBackend } from '../components/dialogs/contingency-list-helper'; import { UsersIdentities } from './user-identities.type'; import { HTTP_OK } from './UIconstants'; -import { FilterAttributes } from './contingency-list-type'; +import { FilterBasedContingencyList, FilteredIdentifiables } from './contingency-list.type'; const PREFIX_USER_ADMIN_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/user-admin`; const PREFIX_EXPLORE_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/explore`; @@ -541,10 +541,6 @@ export function createContingencyList( }); } -export interface FilterBasedContingencyList { - filters: FilterAttributes[]; -} - export function createFilterBasedContingency( name: string, description: string, @@ -579,7 +575,7 @@ export function getContingencyList(type: string, id: string) { }); } -export function getIdentifiablesFromFitlers(studyUuid: UUID, filters: UUID[]) { +export function getIdentifiablesFromFitlers(studyUuid: UUID, filters: UUID[]): Promise { console.info('get identifiables resulting from application of filters list on study root network'); const filtersListsQueryParams = getRequestParamFromList('filtersUuid', filters); From c4339dd1d660e9452df9365c458b6e5a012366f6 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 24 Sep 2025 15:16:26 +0200 Subject: [PATCH 23/23] up version commons-ui --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index df9128e99..78c688fd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@gridsuite/commons-ui": "0.126.0", + "@gridsuite/commons-ui": "0.127.0", "@hookform/resolvers": "^4.1.3", "@mui/icons-material": "^5.18.0", "@mui/lab": "5.0.0-alpha.175", @@ -3121,9 +3121,9 @@ } }, "node_modules/@gridsuite/commons-ui": { - "version": "0.126.0", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.126.0.tgz", - "integrity": "sha512-PQKNxVOYeygiRMLsKyBdjF+u1HZKG3yZONDc4YHGpaV8rOqmtHC7721qUL6dg5EP7C8b+Wi7nlSxeMd3n/UrBg==", + "version": "0.127.0", + "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.127.0.tgz", + "integrity": "sha512-TZd6Hx/zOE2zxJSdgd4hnIcSN+PXYpwZvwsJI/3s1oic75rO90GzhP43EpgUlM++txYS5Gbxc2fok1SifK0DLg==", "license": "MPL-2.0", "dependencies": { "@ag-grid-community/locale": "^33.3.2", diff --git a/package.json b/package.json index 0a509f387..a0d82a293 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@gridsuite/commons-ui": "0.126.0", + "@gridsuite/commons-ui": "0.127.0", "@hookform/resolvers": "^4.1.3", "@mui/icons-material": "^5.18.0", "@mui/lab": "5.0.0-alpha.175",