From 883cd44a56583fe4d0c64102f0eedf1f7f983b90 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Fri, 16 Jan 2026 10:28:18 +0100 Subject: [PATCH 01/10] wip Signed-off-by: David BRAQUART --- .../equipement-deletion-dialog.type.ts | 34 +++++++ ...alog.jsx => equipment-deletion-dialog.tsx} | 93 ++++++++++--------- ...n-form.jsx => equipment-deletion-form.tsx} | 83 ++++++++++------- ...sx => hvdc-lcc-deletion-specific-form.tsx} | 37 +------- .../shunt-compensator-selection-form.tsx | 47 ++++++++++ ...tion-utils.js => use-hvdc-lcc-deletion.ts} | 31 ++++--- .../network-modification-node-editor.tsx | 56 +++++------ src/components/network/network-map-panel.tsx | 28 ++++-- src/hooks/use-equipment-dialogs.tsx | 74 ++++++++------- src/services/study/network-map.ts | 5 +- src/services/study/network-modifications.ts | 53 ++++++----- src/translations/en.json | 1 + src/translations/fr.json | 1 + 13 files changed, 326 insertions(+), 217 deletions(-) create mode 100644 src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts rename src/components/dialogs/network-modifications/equipment-deletion/{equipment-deletion-dialog.jsx => equipment-deletion-dialog.tsx} (67%) rename src/components/dialogs/network-modifications/equipment-deletion/{equipment-deletion-form.jsx => equipment-deletion-form.tsx} (67%) rename src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/{hvdc-lcc-deletion-specific-form.jsx => hvdc-lcc-deletion-specific-form.tsx} (54%) create mode 100644 src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx rename src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/{hvdc-lcc-deletion-utils.js => use-hvdc-lcc-deletion.ts} (75%) diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts b/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts new file mode 100644 index 0000000000..333fc0775b --- /dev/null +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts @@ -0,0 +1,34 @@ +/** + * 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 { EquipmentType, ModificationType } from '@gridsuite/commons-ui'; +import { UUID } from 'node:crypto'; +import { LccShuntCompensatorInfos } from '../../../../services/network-modification-types'; + +// TODO DBR move to mod types ? + +// Specific deletion: 1 use-case with LCC deletion (specificType = HVDC_LINE_LCC_DELETION_SPECIFIC_TYPE) +export interface EquipmentDeletionSpecificInfos { + specificType: string; + // below is specific + mcsOnSide1: LccShuntCompensatorInfos[]; + mcsOnSide2: LccShuntCompensatorInfos[]; +} + +export type EquipmentDeletionInfos = { + type: ModificationType; + uuid?: UUID; + equipmentId: UUID; + equipmentType: EquipmentType; + equipmentInfos?: EquipmentDeletionSpecificInfos; +}; + +// Maps HvdcLccDeletionInfos from modification-server +export interface HvdcLccDeletionInfos extends EquipmentDeletionSpecificInfos { + id?: UUID; + mcsOnSide1: LccShuntCompensatorInfos[]; + mcsOnSide2: LccShuntCompensatorInfos[]; +} diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.jsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx similarity index 67% rename from src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.jsx rename to src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx index 31fbec45b0..88db20ec3c 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.jsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx @@ -8,42 +8,59 @@ import { yupResolver } from '@hookform/resolvers/yup'; import yup from 'components/utils/yup-config'; import { DELETION_SPECIFIC_DATA, EQUIPMENT_ID, TYPE } from '../../../utils/field-constants'; -import { CustomFormProvider, snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui'; +import { CustomFormProvider, EquipmentType, snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui'; import { useForm } from 'react-hook-form'; import { useCallback, useEffect } from 'react'; import { ModificationDialog } from '../../commons/modificationDialog'; -import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; import DeleteEquipmentForm from './equipment-deletion-form'; -import PropTypes from 'prop-types'; import { useOpenShortWaitFetching } from 'components/dialogs/commons/handle-modification-form'; import { FORM_LOADING_DELAY } from 'components/network/constants'; import { deleteEquipment } from '../../../../services/study/network-modifications'; -import { FetchStatus } from '../../../../services/utils'; +import { UUID } from 'node:crypto'; +import { CurrentTreeNode } from 'components/graph/tree-node.type'; +import { FetchStatus } from 'services/utils.type'; +import { DeepNullable } from 'components/utils/ts-utils'; +import { EquipmentDeletionInfos } from './equipement-deletion-dialog.type'; const formSchema = yup .object() .shape({ - [TYPE]: yup.string().nullable().required(), [EQUIPMENT_ID]: yup.string().nullable().required(), + [TYPE]: yup.mixed().oneOf(Object.values(EquipmentType)).nullable().required(), + [DELETION_SPECIFIC_DATA]: yup.string().nullable(), }) .required(); -const emptyFormData = { - [TYPE]: null, - [EQUIPMENT_ID]: null, +export type EquipmentDeletionFormInfos = yup.InferType; + +const emptyFormData: EquipmentDeletionFormInfos = { + [EQUIPMENT_ID]: '', + [TYPE]: EquipmentType.SUBSTATION, [DELETION_SPECIFIC_DATA]: null, }; +interface EquipmentDeletionDialogProps { + studyUuid: UUID; + currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; + editData?: EquipmentDeletionInfos; + isUpdate: boolean; + defaultIdValue?: UUID; + equipmentType: EquipmentType; + editDataFetchStatus?: FetchStatus; + onClose?: () => void; +} + /** - * Dialog to delete an equipment from its type and ID. + * Dialog to delete equipment from its type and ID. * @param studyUuid the study we are currently working on * @param currentNode the node we are currently working on * @param currentRootNetworkUuid * @param editData the data to edit * @param isUpdate check if edition form * @param defaultIdValue the default equipment id - * @param editDataFetchStatus indicates the status of fetching EditData * @param equipmentType + * @param editDataFetchStatus indicates the status of fetching EditData * @param onClose a callback when dialog has closed * @param dialogProps props that are forwarded to the generic ModificationDialog component */ @@ -54,26 +71,26 @@ const EquipmentDeletionDialog = ({ editData, isUpdate, defaultIdValue, // Used to pre-select an equipmentId when calling this dialog from the SLD/map - editDataFetchStatus, equipmentType, + editDataFetchStatus, onClose, ...dialogProps -}) => { +}: EquipmentDeletionDialogProps) => { const currentNodeUuid = currentNode?.id; const { snackError } = useSnackMessage(); - const formMethods = useForm({ + const formMethods = useForm>({ defaultValues: emptyFormData, - resolver: yupResolver(formSchema), + resolver: yupResolver>(formSchema), }); const { reset } = formMethods; const fromEditDataToFormValues = useCallback( - (editData) => { + (editData: EquipmentDeletionInfos) => { reset({ - [TYPE]: EQUIPMENT_TYPES[editData.equipmentType], + [TYPE]: editData.equipmentType, [EQUIPMENT_ID]: editData.equipmentId, }); }, @@ -81,9 +98,9 @@ const EquipmentDeletionDialog = ({ ); const fromMenuDataValues = useCallback( - (menuSelectId) => { + (menuSelectId: UUID) => { reset({ - [TYPE]: EQUIPMENT_TYPES.HVDC_LINE, + [TYPE]: EquipmentType.HVDC_LINE, [EQUIPMENT_ID]: menuSelectId, [DELETION_SPECIFIC_DATA]: null, }); @@ -92,7 +109,7 @@ const EquipmentDeletionDialog = ({ ); const resetFormWithEquipmentType = useCallback( - (equipmentType) => { + (equipmentType: EquipmentType) => { reset({ [TYPE]: equipmentType, }); @@ -118,17 +135,19 @@ const EquipmentDeletionDialog = ({ ]); const onSubmit = useCallback( - (formData) => { - deleteEquipment( - studyUuid, - currentNodeUuid, - formData[TYPE], - formData[EQUIPMENT_ID], - editData?.uuid, - formData[DELETION_SPECIFIC_DATA] - ).catch((error) => { - snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); - }); + (formData: EquipmentDeletionFormInfos) => { + if (formData[EQUIPMENT_ID]) { + deleteEquipment({ + studyUuid, + nodeUuid: currentNodeUuid, + uuid: editData?.uuid, + equipmentId: formData[EQUIPMENT_ID] as UUID, + equipmentType: formData[TYPE], + equipmentSpecificInfos: formData[DELETION_SPECIFIC_DATA], + }).catch((error) => { + snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); + }); + } }, [currentNodeUuid, editData, snackError, studyUuid] ); @@ -149,8 +168,8 @@ const EquipmentDeletionDialog = ({ fullWidth maxWidth="md" onClear={clear} - onSave={onSubmit} onClose={onClose} + onSave={onSubmit} titleId="DeleteEquipment" open={open} isDataFetching={isUpdate && editDataFetchStatus === FetchStatus.RUNNING} @@ -167,16 +186,4 @@ const EquipmentDeletionDialog = ({ ); }; -EquipmentDeletionDialog.propTypes = { - studyUuid: PropTypes.string, - currentNode: PropTypes.object, - currentRootNetworkUuid: PropTypes.string, - editData: PropTypes.object, - isUpdate: PropTypes.bool, - defaultIdValue: PropTypes.string, - editDataFetchStatus: PropTypes.string, - equipmentType: PropTypes.string, - onClose: PropTypes.func, -}; - export default EquipmentDeletionDialog; diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.jsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx similarity index 67% rename from src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.jsx rename to src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx index 56cb62b07e..09cfbfe0e0 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.jsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx @@ -8,7 +8,7 @@ import { Grid } from '@mui/material'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; -import { useSnackMessage, AutocompleteInput, snackWithFallback } from '@gridsuite/commons-ui'; +import { useSnackMessage, AutocompleteInput, snackWithFallback, EquipmentType } from '@gridsuite/commons-ui'; import { filledTextField } from 'components/dialogs/dialog-utils'; import { DELETION_SPECIFIC_DATA, @@ -19,16 +19,33 @@ import { import { areIdsEqual, getObjectId, richTypeEquals } from 'components/utils/utils'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; import HvdcLccDeletionSpecificForm from './hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form'; -import useHvdcLccDeletion from './hvdc-lcc-deletion/hvdc-lcc-deletion-utils'; import { fetchEquipmentsIds } from '../../../../services/study/network-map'; import useGetLabelEquipmentTypes from '../../../../hooks/use-get-label-equipment-types'; import GridItem from '../../commons/grid-item'; +import type { UUID } from 'node:crypto'; +import { CurrentTreeNode } from '../../../graph/tree-node.type'; +import { EquipmentDeletionInfos } from './equipement-deletion-dialog.type'; +import useHvdcLccDeletion from './hvdc-lcc-deletion/use-hvdc-lcc-deletion'; -const DeleteEquipmentForm = ({ studyUuid, currentNode, currentRootNetworkUuid, editData }) => { +export interface DeleteEquipmentFormProps { + studyUuid: UUID; + currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; + editData?: EquipmentDeletionInfos; +} + +const NULL_UUID: UUID = '00000000-0000-0000-0000-000000000000'; + +export default function DeleteEquipmentForm({ + studyUuid, + currentNode, + currentRootNetworkUuid, + editData, +}: Readonly) { const { snackError } = useSnackMessage(); - const editedIdRef = useRef(null); - const currentTypeRef = useRef(null); + const editedIdRef = useRef(null); + const currentTypeRef = useRef(null); const watchType = useWatch({ name: TYPE, @@ -71,6 +88,7 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, currentRootNetworkUuid, e fetchEquipmentsIds(studyUuid, currentNode?.id, currentRootNetworkUuid, undefined, watchType, true) .then((vals) => { // check race condition here + console.log('DBG DBR fetchEquipmentsIds', vals); if (!ignore) { setEquipmentsOptions(vals.sort()); } @@ -85,31 +103,32 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, currentRootNetworkUuid, e }, [studyUuid, currentNode?.id, currentRootNetworkUuid, watchType, snackError]); useEffect(() => { - if (studyUuid && currentNode?.id && currentRootNetworkUuid) { - if (editData?.equipmentId) { - if (editedIdRef.current === null) { - // The first time we render an edition, we want to merge the - // dynamic data with the edition data coming from the database - editedIdRef.current = editData.equipmentId; - } else if (watchEquipmentId !== editedIdRef.current && editedIdRef.current !== '') { - // we have changed eqptId, leave the "first edit" mode (then if we circle back - // to editData?.equipmentId, we wont make the merge anymore). - editedIdRef.current = ''; - } + if (!studyUuid || !currentNode?.id || !currentRootNetworkUuid) { + return; + } + if (editData?.equipmentId) { + if (editedIdRef.current === null) { + // The first time we render an edition, we want to merge the + // dynamic data with the edition data coming from the database + editedIdRef.current = editData.equipmentId; + } else if (watchEquipmentId !== editedIdRef.current && editedIdRef.current !== NULL_UUID) { + // we have changed eqptId, leave the "first edit" mode (then if we circle back + // to editData?.equipmentId, we won't make the merge anymore). + editedIdRef.current = NULL_UUID; } + } - if (watchEquipmentId && currentTypeRef.current === EQUIPMENT_TYPES.HVDC_LINE) { - // need specific update related to HVDC LCC deletion (for MCS lists) - hvdcLccSpecificUpdate( - studyUuid, - currentNode?.id, - currentRootNetworkUuid, - watchEquipmentId, - watchEquipmentId === editedIdRef.current ? editData : null - ); - } else { - setValue(DELETION_SPECIFIC_DATA, null); - } + if (watchEquipmentId && currentTypeRef.current === EquipmentType.HVDC_LINE) { + // need specific update related to HVDC LCC deletion (for MCS lists) + hvdcLccSpecificUpdate( + studyUuid, + currentNode?.id, + currentRootNetworkUuid, + watchEquipmentId, + watchEquipmentId === editedIdRef.current ? editData : undefined + ); + } else { + setValue(DELETION_SPECIFIC_DATA, null); } }, [ studyUuid, @@ -150,9 +169,9 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, currentRootNetworkUuid, e options={equipmentsOptions} getOptionLabel={getObjectId} //hack to work with freesolo autocomplete - //setting null programatically when freesolo is enable wont empty the field + //setting null programmatically when freesolo is enable won't empty the field inputTransform={(value) => value ?? ''} - outputTransform={(value) => (value === '' ? null : getObjectId(value))} + outputTransform={(value: any) => (value === '' ? null : getObjectId(value))} size={'small'} formProps={filledTextField} /> @@ -169,6 +188,4 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, currentRootNetworkUuid, e )} ); -}; - -export default DeleteEquipmentForm; +} diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.jsx b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.tsx similarity index 54% rename from src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.jsx rename to src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.tsx index 69212f24bb..c27e896229 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.jsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.tsx @@ -6,21 +6,17 @@ */ import { Grid } from '@mui/material'; -import { CheckboxInput } from '@gridsuite/commons-ui'; -import ReadOnlyInput from 'components/utils/rhf-inputs/read-only/read-only-input'; import { - SHUNT_COMPENSATOR_SELECTED, - ID, DELETION_SPECIFIC_DATA, SHUNT_COMPENSATOR_SIDE_1, SHUNT_COMPENSATOR_SIDE_2, } from 'components/utils/field-constants'; -import { FormattedMessage } from 'react-intl'; import { useFieldArray } from 'react-hook-form'; import GridSection from '../../../commons/grid-section'; import GridItem from '../../../commons/grid-item'; +import ShuntCompensatorSelectionForm from './shunt-compensator-selection-form'; -const HvdcLccDeletionSpecificForm = () => { +export default function HvdcLccDeletionSpecificForm() { const { fields: mcsRows1 } = useFieldArray({ name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_1}`, }); @@ -28,31 +24,6 @@ const HvdcLccDeletionSpecificForm = () => { name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_2}`, }); - const ShuntCompensatorSelectionForm = ({ title, arrayFormName, mcsRows }) => { - return ( - - -

- -

-
- {mcsRows.map((field, index) => ( - - - - - - - - - ))} -
- ); - }; - const mcsOnsideOne = ( { ); -}; - -export default HvdcLccDeletionSpecificForm; +} diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx new file mode 100644 index 0000000000..ebaadc401e --- /dev/null +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2023, 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 { Grid } from '@mui/material'; +import { CheckboxInput } from '@gridsuite/commons-ui'; +import ReadOnlyInput from 'components/utils/rhf-inputs/read-only/read-only-input'; +import { SHUNT_COMPENSATOR_SELECTED, ID } from 'components/utils/field-constants'; +import { FormattedMessage } from 'react-intl'; + +export interface ShuntCompensatorSelectionFormProps { + title: string; + arrayFormName: string; + mcsRows: Record<'id', string>[]; +} + +export default function ShuntCompensatorSelectionForm({ + title, + arrayFormName, + mcsRows, +}: Readonly) { + return ( + + +

+ +

+
+ {mcsRows.map((field, index) => ( + + + + + + + + + ))} +
+ ); +} diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.js b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts similarity index 75% rename from src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.js rename to src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts index 29e932ea66..9a050b07c3 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.js +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts @@ -16,6 +16,9 @@ import { useCallback } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui'; import { fetchHvdcLineWithShuntCompensators } from '../../../../../services/study/network-map'; +import { EquipmentDeletionInfos, HvdcLccDeletionInfos } from '../equipement-deletion-dialog.type'; +import { UUID } from 'node:crypto'; +import { LccShuntCompensatorInfos } from '../../../../../services/network-modification-types'; const useHvdcLccDeletion = () => { const { replace: replaceMcsList1 } = useFieldArray({ @@ -28,8 +31,8 @@ const useHvdcLccDeletion = () => { const { snackError } = useSnackMessage(); const updateMcsLists = useCallback( - (hvdcLineData, editData) => { - function mergeMcsLists(dynamicList, editList) { + (hvdcLineData: HvdcLccDeletionInfos, editData?: EquipmentDeletionInfos) => { + function mergeMcsLists(dynamicList: LccShuntCompensatorInfos[], editList: LccShuntCompensatorInfos[]) { if (!dynamicList && !editList) { return []; } else if (!dynamicList) { @@ -38,7 +41,7 @@ const useHvdcLccDeletion = () => { } else if (!editList) { return dynamicList; } - const mergedList = dynamicList.map((obj) => { + const mergedList: LccShuntCompensatorInfos[] = dynamicList.map((obj) => { return { ...obj, connectedToHvdc: false }; }); // now overwrite dynamic values with edited modification values @@ -60,16 +63,12 @@ const useHvdcLccDeletion = () => { if ( hvdcLineData?.mcsOnSide1 || hvdcLineData?.mcsOnSide2 || - editData?.[DELETION_SPECIFIC_DATA]?.mcsOnSide1 || - editData?.[DELETION_SPECIFIC_DATA]?.mcsOnSide2 + editData?.equipmentInfos?.mcsOnSide1 || + editData?.equipmentInfos?.mcsOnSide2 ) { setValue(`${DELETION_SPECIFIC_DATA}.${DELETION_SPECIFIC_TYPE}`, HVDC_LINE_LCC_DELETION_SPECIFIC_TYPE); - replaceMcsList1( - mergeMcsLists(hvdcLineData?.mcsOnSide1, editData?.[DELETION_SPECIFIC_DATA]?.mcsOnSide1) - ); - replaceMcsList2( - mergeMcsLists(hvdcLineData?.mcsOnSide2, editData?.[DELETION_SPECIFIC_DATA]?.mcsOnSide2) - ); + replaceMcsList1(mergeMcsLists(hvdcLineData?.mcsOnSide1, editData?.equipmentInfos?.mcsOnSide1 ?? [])); + replaceMcsList2(mergeMcsLists(hvdcLineData?.mcsOnSide2, editData?.equipmentInfos?.mcsOnSide2 ?? [])); } else { setValue(DELETION_SPECIFIC_DATA, null); } @@ -78,14 +77,20 @@ const useHvdcLccDeletion = () => { ); const specificUpdate = useCallback( - (studyUuid, nodeId, currentRootNetworkUuid, equipmentId, editData) => { + ( + studyUuid: UUID, + nodeId: UUID, + currentRootNetworkUuid: UUID, + equipmentId: UUID, + editData?: EquipmentDeletionInfos + ) => { fetchHvdcLineWithShuntCompensators(studyUuid, nodeId, currentRootNetworkUuid, equipmentId) .then((hvdcLineData) => { updateMcsLists(hvdcLineData, editData); }) .catch((error) => { setValue(DELETION_SPECIFIC_DATA, null); - snackWithFallback(snackError, error, { headerId: 'HVDCLineConverterStationError' }); + snackWithFallback(snackError, error, { headerId: 'FetchHvdcLineWithShuntCompensatorsError' }); }); }, [setValue, updateMcsLists, snackError] diff --git a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx index a5b389d82f..8cc255493f 100644 --- a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx +++ b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx @@ -8,6 +8,7 @@ import { ElementSaveDialog, ElementType, + EquipmentType, IElementCreationDialog, IElementUpdateDialog, MODIFICATION_TYPES, @@ -114,7 +115,6 @@ import { BalancesAdjustmentDialog } from '../../../dialogs/network-modifications import CreateVoltageLevelTopologyDialog from '../../../dialogs/network-modifications/voltage-level/topology-creation/create-voltage-level-topology-dialog'; import { NodeType } from 'components/graph/tree-node.type'; import { LimitSetsModificationDialog } from '../../../dialogs/network-modifications/limit-sets/limit-sets-modification-dialog'; -import { EQUIPMENT_TYPES } from '../../../utils/equipment-types'; import CreateVoltageLevelSectionDialog from '../../../dialogs/network-modifications/voltage-level/section/create-voltage-level-section-dialog'; import MoveVoltageLevelFeederBaysDialog from '../../../dialogs/network-modifications/voltage-level/move-feeder-bays/move-voltage-level-feeder-bays-dialog'; import { useCopiedNetworkModifications } from 'hooks/copy-paste/use-copied-network-modifications'; @@ -230,21 +230,23 @@ const NetworkModificationNodeEditor = () => { ); } - function equipmentDeletionDialogWithDefaultParams(equipmentType: EQUIPMENT_TYPES) { - return ( - - ); + function equipmentDeletionDialogWithDefaultParams(equipmentType: EquipmentType) { + if (currentNode && studyUuid && currentRootNetworkUuid && editData) { + return ( + + ); + } } const equipmentDeletionSubItems = (equipmentType: EQUIPMENT_TYPES) => { @@ -275,7 +277,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(SubstationModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.SUBSTATION), + equipmentDeletionSubItems(EquipmentType.SUBSTATION), ], }, { @@ -317,7 +319,7 @@ const NetworkModificationNodeEditor = () => { label: 'MOVE_VOLTAGE_LEVEL_FEEDER_BAYS', action: () => withDefaultParams(MoveVoltageLevelFeederBaysDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.VOLTAGE_LEVEL), + equipmentDeletionSubItems(EquipmentType.VOLTAGE_LEVEL), ], }, ], @@ -339,7 +341,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(LineModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.LINE), + equipmentDeletionSubItems(EquipmentType.LINE), ], }, { @@ -398,7 +400,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(TwoWindingsTransformerModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER), + equipmentDeletionSubItems(EquipmentType.TWO_WINDINGS_TRANSFORMER), ], }, { @@ -415,7 +417,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(VscModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.HVDC_LINE), + equipmentDeletionSubItems(EquipmentType.HVDC_LINE), ], }, { @@ -432,7 +434,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(LccModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.HVDC_LINE), + equipmentDeletionSubItems(EquipmentType.HVDC_LINE), ], }, ], @@ -454,7 +456,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(GeneratorModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.GENERATOR), + equipmentDeletionSubItems(EquipmentType.GENERATOR), ], }, { @@ -471,7 +473,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(BatteryModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.BATTERY), + equipmentDeletionSubItems(EquipmentType.BATTERY), ], }, { @@ -488,7 +490,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(LoadModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.LOAD), + equipmentDeletionSubItems(EquipmentType.LOAD), ], }, { @@ -505,7 +507,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(ShuntCompensatorModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.SHUNT_COMPENSATOR), + equipmentDeletionSubItems(EquipmentType.SHUNT_COMPENSATOR), ], }, { @@ -517,7 +519,7 @@ const NetworkModificationNodeEditor = () => { label: 'menu.create', action: () => withDefaultParams(StaticVarCompensatorCreationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.STATIC_VAR_COMPENSATOR), + equipmentDeletionSubItems(EquipmentType.STATIC_VAR_COMPENSATOR), ], }, ], diff --git a/src/components/network/network-map-panel.tsx b/src/components/network/network-map-panel.tsx index 537d5177b9..206fb4b5b5 100644 --- a/src/components/network/network-map-panel.tsx +++ b/src/components/network/network-map-panel.tsx @@ -263,16 +263,24 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ const handleDeleteEquipment = useCallback( (equipmentType: EquipmentType | null, equipmentId: string) => { - if ( - equipmentType === EquipmentType.HVDC_LINE && - mapEquipments?.hvdcLinesById?.get(equipmentId)?.hvdcType === 'LCC' - ) { - // only hvdc line with LCC requires a Dialog (to select MCS) - handleOpenDeletionDialog(equipmentId, EQUIPMENT_TYPES.HVDC_LINE); - } else { - deleteEquipment(studyUuid, currentNode?.id, equipmentType, equipmentId, undefined).catch((error) => { - snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); - }); + // TODO DBR change type -= null / +UUID + if (equipmentType && currentNode?.id) { + if ( + equipmentType === EquipmentType.HVDC_LINE && + mapEquipments?.hvdcLinesById?.get(equipmentId)?.hvdcType === 'LCC' + ) { + // only hvdc line with LCC requires a Dialog (to select MCS) + handleOpenDeletionDialog(equipmentId, EQUIPMENT_TYPES.HVDC_LINE); + } else { + deleteEquipment({ + studyUuid, + nodeUuid: currentNode?.id, + equipmentId: equipmentId as UUID, + equipmentType, + }).catch((error) => { + snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); + }); + } } }, [studyUuid, currentNode?.id, snackError, handleOpenDeletionDialog, mapEquipments?.hvdcLinesById] diff --git a/src/hooks/use-equipment-dialogs.tsx b/src/hooks/use-equipment-dialogs.tsx index 121a2b063c..a9296d2faa 100644 --- a/src/hooks/use-equipment-dialogs.tsx +++ b/src/hooks/use-equipment-dialogs.tsx @@ -6,8 +6,14 @@ */ import { useCallback, useState } from 'react'; -import { EquipmentType, ExtendedEquipmentType, snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui'; -import { EQUIPMENT_INFOS_TYPES, EQUIPMENT_TYPES } from '../components/utils/equipment-types'; +import { + EquipmentType, + ExtendedEquipmentType, + HvdcType, + snackWithFallback, + useSnackMessage, +} from '@gridsuite/commons-ui'; +import { EQUIPMENT_INFOS_TYPES } from '../components/utils/equipment-types'; import { deleteEquipment } from '../services/study/network-modifications'; import { fetchNetworkElementInfos } from '../services/study/network'; import { CurrentTreeNode } from '../components/graph/tree-node.type'; @@ -28,7 +34,7 @@ import { LccModificationDialog } from '../components/dialogs/network-modificatio type EquipmentToModify = { equipmentId: string; - equipmentType: EQUIPMENT_TYPES; + equipmentType: EquipmentType; equipmentSubtype?: ExtendedEquipmentType | null; }; @@ -50,12 +56,11 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork // Handlers const handleOpenModificationDialog = useCallback( - (id: string, type: EquipmentType | null, subtype: ExtendedEquipmentType | null) => { + (id: string, type: EquipmentType, subtype: ExtendedEquipmentType | null) => { if (type) { - const equipmentEnumType = EQUIPMENT_TYPES[type as keyof typeof EQUIPMENT_TYPES]; setEquipmentToModify({ equipmentId: id, - equipmentType: equipmentEnumType, + equipmentType: type, equipmentSubtype: subtype, }); } @@ -63,16 +68,16 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork [] ); - const handleOpenDeletionDialog = useCallback((equipmentId: string, equipmentType: EQUIPMENT_TYPES) => { + const handleOpenDeletionDialog = useCallback((equipmentId: string, equipmentType: EquipmentType) => { setEquipmentToDelete({ equipmentId, equipmentType }); }, []); const handleOpenDynamicSimulationEventDialog = useCallback( - (equipmentId: string, equipmentType: EquipmentType | null, dialogTitle: string) => { + (equipmentId: string, equipmentType: EquipmentType, dialogTitle: string) => { setDynamicSimulationEventDialogTitle(dialogTitle); setEquipmentToConfigDynamicSimulationEvent({ equipmentId, - equipmentType: EQUIPMENT_TYPES[equipmentType as keyof typeof EQUIPMENT_TYPES], + equipmentType: equipmentType, }); }, [] @@ -92,9 +97,14 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork }, []); const removeEquipment = useCallback( - (equipmentType: string, equipmentId: string) => { + (equipmentType: EquipmentType, equipmentId: string) => { if (studyUuid) { - deleteEquipment(studyUuid, currentNode?.id, equipmentType, equipmentId, undefined).catch((error) => { + deleteEquipment({ + studyUuid, + nodeUuid: currentNode?.id, + equipmentId: equipmentId as UUID, + equipmentType, + }).catch((error) => { snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); }); } @@ -103,27 +113,24 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork ); const handleDeleteEquipment = useCallback( - (equipmentType: EquipmentType | null, equipmentId: string) => { - const equipmentEnumType = EQUIPMENT_TYPES[equipmentType as keyof typeof EQUIPMENT_TYPES]; - if (equipmentEnumType !== EQUIPMENT_TYPES.HVDC_LINE) { - removeEquipment(equipmentEnumType, equipmentId); - } else { + (equipmentType: EquipmentType, equipmentId: string) => { + if (equipmentType === EquipmentType.HVDC_LINE) { // need a query to know the HVDC converters type (LCC vs VSC) fetchNetworkElementInfos( studyUuid, currentNode?.id, currentRootNetworkUuid, - EQUIPMENT_TYPES.HVDC_LINE, + EquipmentType.HVDC_LINE, EQUIPMENT_INFOS_TYPES.MAP.type, equipmentId, false ) .then((hvdcInfos) => { - if (hvdcInfos?.hvdcType === 'LCC') { + if (hvdcInfos?.hvdcType === HvdcType.LCC) { // only hvdc line with LCC requires a Dialog (to select MCS) - handleOpenDeletionDialog(equipmentId, EQUIPMENT_TYPES.HVDC_LINE); + handleOpenDeletionDialog(equipmentId, EquipmentType.HVDC_LINE); } else { - removeEquipment(equipmentEnumType, equipmentId); + removeEquipment(equipmentType, equipmentId); } }) .catch(() => { @@ -132,6 +139,8 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork messageValues: { equipmentId: equipmentId }, }); }); + } else { + removeEquipment(equipmentType, equipmentId); } }, [studyUuid, currentNode?.id, currentRootNetworkUuid, snackError, handleOpenDeletionDialog, removeEquipment] @@ -145,28 +154,28 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork let CurrentModificationDialog; switch (equipmentToModify.equipmentType) { - case EQUIPMENT_TYPES.SUBSTATION: + case EquipmentType.SUBSTATION: CurrentModificationDialog = SubstationModificationDialog; break; - case EQUIPMENT_TYPES.VOLTAGE_LEVEL: + case EquipmentType.VOLTAGE_LEVEL: CurrentModificationDialog = VoltageLevelModificationDialog; break; - case EQUIPMENT_TYPES.BATTERY: + case EquipmentType.BATTERY: CurrentModificationDialog = BatteryModificationDialog; break; - case EQUIPMENT_TYPES.GENERATOR: + case EquipmentType.GENERATOR: CurrentModificationDialog = GeneratorModificationDialog; break; - case EQUIPMENT_TYPES.LOAD: + case EquipmentType.LOAD: CurrentModificationDialog = LoadModificationDialog; break; - case EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER: + case EquipmentType.TWO_WINDINGS_TRANSFORMER: CurrentModificationDialog = TwoWindingsTransformerModificationDialog; break; - case EQUIPMENT_TYPES.LINE: + case EquipmentType.LINE: CurrentModificationDialog = LineModificationDialog; break; - case EQUIPMENT_TYPES.HVDC_LINE: + case EquipmentType.HVDC_LINE: if (equipmentToModify?.equipmentSubtype === ExtendedEquipmentType.HVDC_LINE_LCC) { CurrentModificationDialog = LccModificationDialog; } else if (equipmentToModify.equipmentSubtype === ExtendedEquipmentType.HVDC_LINE_VSC) { @@ -175,7 +184,7 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork return null; } break; - case EQUIPMENT_TYPES.SHUNT_COMPENSATOR: + case EquipmentType.SHUNT_COMPENSATOR: CurrentModificationDialog = ShuntCompensatorModificationDialog; break; default: @@ -205,19 +214,18 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork return null; } - if (equipmentToDelete.equipmentType === EQUIPMENT_TYPES.HVDC_LINE) { + if (equipmentToDelete.equipmentType === EquipmentType.HVDC_LINE) { return ( ); } else { diff --git a/src/services/study/network-map.ts b/src/services/study/network-map.ts index 4034589501..89c47e1df7 100644 --- a/src/services/study/network-map.ts +++ b/src/services/study/network-map.ts @@ -22,13 +22,14 @@ import { fetchNetworkElementsInfos } from './network'; import { createContingencyList } from 'services/explore'; import { ContingencyList, createIdentifierContingencyList } from './contingency-list'; import type { UUID } from 'node:crypto'; +import { HvdcLccDeletionInfos } from '../../components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type'; export function fetchHvdcLineWithShuntCompensators( studyUuid: UUID, currentNodeUuid: UUID, currentRootNetworkUuid: UUID, - hvdcLineId: string -) { + hvdcLineId: UUID +): Promise { console.info( `Fetching HVDC Line '${hvdcLineId}' with Shunt Compensators of study '${studyUuid}' on root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}'...` ); diff --git a/src/services/study/network-modifications.ts b/src/services/study/network-modifications.ts index 5e5afa67e4..2ba3249a97 100644 --- a/src/services/study/network-modifications.ts +++ b/src/services/study/network-modifications.ts @@ -1174,35 +1174,35 @@ export function formatPropertiesForBackend(previousProperties: any, newPropertie const newPropertiesArray = Object.entries(newProperties).map(([name, value]) => ({ name, value })); const propertiesModifications: any = []; - previousPropertiesArray.forEach((previousPropertiePair) => { - const updatedProperty = newPropertiesArray.find((updatedObj) => updatedObj.name === previousPropertiePair.name); + previousPropertiesArray.forEach((previousPropertyPair) => { + const updatedProperty = newPropertiesArray.find((updatedObj) => updatedObj.name === previousPropertyPair.name); if (!updatedProperty) { // The property has been deleted (does not exist in the new properties array) propertiesModifications.push({ - ...previousPropertiePair, - previousValue: previousPropertiePair.value, + ...previousPropertyPair, + previousValue: previousPropertyPair.value, value: null, deletionMark: true, }); - } else if (updatedProperty.value !== previousPropertiePair.value) { + } else if (updatedProperty.value !== previousPropertyPair.value) { // the property exist in both the previous and the new properties array but has been modified propertiesModifications.push({ ...updatedProperty, added: false, deletionMark: false, - previousValue: previousPropertiePair.value, + previousValue: previousPropertyPair.value, }); } }); - newPropertiesArray.forEach((newPropertie) => { + newPropertiesArray.forEach((newProperty) => { // The property has been added - const previousPropertie = previousPropertiesArray.find((oldObj) => oldObj.name === newPropertie.name); + const previousProperty = previousPropertiesArray.find((oldObj) => oldObj.name === newProperty.name); //the propertie is new ( does not exist in the previous properties array) - if (!previousPropertie) { + if (!previousProperty) { propertiesModifications.push({ - ...newPropertie, + ...newProperty, added: true, deletionMark: false, }); @@ -1656,25 +1656,34 @@ export function deleteAttachingLine({ }); } -export function deleteEquipment( - studyUuid: string, - nodeUuid: UUID | undefined, - equipmentType: EquipmentType | string | null, - equipmentId: string, - modificationUuid: UUID | undefined, - equipmentInfos: any = undefined -) { +export interface DeleteEquipmentInfo { + studyUuid: string; + nodeUuid: UUID; + uuid?: UUID; + equipmentId: UUID; + equipmentType: EquipmentType; + equipmentSpecificInfos?: any; +} + +export function deleteEquipment({ + studyUuid, + nodeUuid, + uuid, + equipmentId, + equipmentType, + equipmentSpecificInfos, +}: DeleteEquipmentInfo) { let deleteEquipmentUrl = getNetworkModificationUrl(studyUuid, nodeUuid); - if (modificationUuid) { - deleteEquipmentUrl += '/' + encodeURIComponent(modificationUuid); + if (uuid) { + deleteEquipmentUrl += '/' + encodeURIComponent(uuid); console.info('Updating equipment deletion'); } else { console.info('Creating equipment deletion'); } return backendFetch(deleteEquipmentUrl, { - method: modificationUuid ? 'PUT' : 'POST', + method: uuid ? 'PUT' : 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', @@ -1683,7 +1692,7 @@ export function deleteEquipment( type: MODIFICATION_TYPES.EQUIPMENT_DELETION.type, equipmentId: equipmentId, equipmentType: equipmentType, - equipmentInfos: equipmentInfos, + equipmentInfos: equipmentSpecificInfos, }), }); } diff --git a/src/translations/en.json b/src/translations/en.json index 395bfe8645..74a08b8f17 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -338,6 +338,7 @@ "FetchCountryError": "An error occurred while fetching countries", "FetchNominalVoltagesError": "An error occurred while fetching nominal voltages", "FetchVoltageLevelsError": "An error occurred while fetching voltage levels", + "FetchHvdcLineWithShuntCompensatorsError": "An error occurred while fetching HVDC lines and related shunt compensators", "DynamicSimulationEventPropertyTEvent": "Event Time", "DynamicSimulationEventPropertySide": "Only Side", "DynamicSimulationEventPropertyNodeFaultDuration": "Node Fault Duration", diff --git a/src/translations/fr.json b/src/translations/fr.json index a6c30e3f1a..41813a6cec 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -336,6 +336,7 @@ "FetchCountryError": "Une erreur est survenue lors du chargement des pays", "FetchNominalVoltagesError": "Une erreur est survenue lors du chargement des tensions nominales", "FetchVoltageLevelsError": "Une erreur est survenue lors du chargement des postes", + "FetchHvdcLineWithShuntCompensatorsError": "Une erreur est survenue lors du chargement des lignes HVDC et des MCS associés", "DynamicSimulationEventPropertyTEvent": "Temps de l'événement", "DynamicSimulationEventPropertySide": "Uniquement Côté", "DynamicSimulationEventPropertyNodeFaultDuration": "Durée du court-circuit", From 9b45bafc719f4eb538b45d7ea9a1a5ff45a4d654 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Mon, 19 Jan 2026 18:35:01 +0100 Subject: [PATCH 02/10] wip 2 / compile Signed-off-by: David BRAQUART --- .../network-modification-node-editor.tsx | 12 ++++++----- src/components/menus/base-equipment-menu.tsx | 20 +++++++++++++------ src/components/menus/equipment-menu.tsx | 2 +- .../menus/operating-status-menu.tsx | 17 ++++++++-------- src/components/network/network-map-panel.tsx | 2 +- src/hooks/use-equipment-dialogs.tsx | 4 ++-- src/hooks/use-equipment-menu.tsx | 12 +++++------ 7 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx index 8cc255493f..0f869ed2a4 100644 --- a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx +++ b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx @@ -78,7 +78,6 @@ import { fetchNetworkModifications, stashModifications, } from '../../../../services/study/network-modifications'; -import { FetchStatus } from '../../../../services/utils'; import { ExcludedNetworkModifications, MenuDefinitionSubItem, @@ -118,6 +117,8 @@ import { LimitSetsModificationDialog } from '../../../dialogs/network-modificati import CreateVoltageLevelSectionDialog from '../../../dialogs/network-modifications/voltage-level/section/create-voltage-level-section-dialog'; import MoveVoltageLevelFeederBaysDialog from '../../../dialogs/network-modifications/voltage-level/move-feeder-bays/move-voltage-level-feeder-bays-dialog'; import { useCopiedNetworkModifications } from 'hooks/copy-paste/use-copied-network-modifications'; +import { FetchStatus } from '../../../../services/utils.type'; +import { EquipmentDeletionInfos } from '../../../dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type'; const nonEditableModificationTypes = new Set([ 'EQUIPMENT_ATTRIBUTE_MODIFICATION', @@ -160,7 +161,7 @@ const NetworkModificationNodeEditor = () => { const [editDialogOpen, setEditDialogOpen] = useState(undefined); const [editData, setEditData] = useState(undefined); - const [editDataFetchStatus, setEditDataFetchStatus] = useState(FetchStatus.IDLE); + const [editDataFetchStatus, setEditDataFetchStatus] = useState(FetchStatus.IDLE); const [restoreDialogOpen, setRestoreDialogOpen] = useState(false); const [importDialogOpen, setImportDialogOpen] = useState(false); const [createCompositeModificationDialogOpen, setCreateCompositeModificationDialogOpen] = useState(false); @@ -232,20 +233,21 @@ const NetworkModificationNodeEditor = () => { function equipmentDeletionDialogWithDefaultParams(equipmentType: EquipmentType) { if (currentNode && studyUuid && currentRootNetworkUuid && editData) { + // TODO DBR onValidated={handleValidatedDialog} return ( ); + } else { + return <>; } } diff --git a/src/components/menus/base-equipment-menu.tsx b/src/components/menus/base-equipment-menu.tsx index 9fccb922d7..e31068818a 100644 --- a/src/components/menus/base-equipment-menu.tsx +++ b/src/components/menus/base-equipment-menu.tsx @@ -36,10 +36,10 @@ const styles = { } as const satisfies MuiStyles; type HandleViewInSpreadsheet = (equipmentType: EquipmentType, equipmentId: string) => void; -type HandleDeleteEquipment = (equipmentType: EquipmentType | null, equipmentId: string) => void; +type HandleDeleteEquipment = (equipmentType: EquipmentType, equipmentId: string) => void; type HandleOpenModificationDialog = ( equipmentId: string, - equipmentType: EquipmentType | null, + equipmentType: EquipmentType, equipmentSubtype: ExtendedEquipmentType | null ) => void; @@ -85,7 +85,12 @@ const DeleteEquipmentItem = ({ return ( handleDeleteEquipment(getCommonEquipmentType(equipmentType), equipmentId)} + onClick={() => { + const commonType = getCommonEquipmentType(equipmentType); + if (commonType) { + handleDeleteEquipment(commonType, equipmentId); + } + }} selected={false} disabled={isNodeReadOnly(currentNode)} > @@ -115,9 +120,12 @@ const ModifyEquipmentItem = ({ return ( - handleOpenModificationDialog(equipmentId, getCommonEquipmentType(equipmentType), equipmentSubtype) - } + onClick={() => { + const commonType = getCommonEquipmentType(equipmentType); + if (commonType) { + handleOpenModificationDialog(equipmentId, commonType, equipmentSubtype); + } + }} selected={false} disabled={isNodeReadOnly(currentNode)} > diff --git a/src/components/menus/equipment-menu.tsx b/src/components/menus/equipment-menu.tsx index a219957bf8..5792c77bb3 100644 --- a/src/components/menus/equipment-menu.tsx +++ b/src/components/menus/equipment-menu.tsx @@ -58,7 +58,7 @@ const withEquipmentMenu = ); const handleOpenDynamicSimulationEventDialog = useCallback( - (equipmentId: string, equipmentType: EquipmentType | null, dialogTitle: string) => { + (equipmentId: string, equipmentType: EquipmentType, dialogTitle: string) => { if (onOpenDynamicSimulationEventDialog) { handleClose(); onOpenDynamicSimulationEventDialog(equipmentId, equipmentType, dialogTitle); diff --git a/src/components/menus/operating-status-menu.tsx b/src/components/menus/operating-status-menu.tsx index 4803e3df66..e761a2683a 100644 --- a/src/components/menus/operating-status-menu.tsx +++ b/src/components/menus/operating-status-menu.tsx @@ -66,13 +66,9 @@ export type MenuBranchProps = { position: [number, number] | null; handleClose: () => void; handleViewInSpreadsheet: (type: EquipmentType, id: string) => void; - handleDeleteEquipment: (type: EquipmentType | null, id: string) => void; - handleOpenModificationDialog: ( - id: string, - type: EquipmentType | null, - subtype: ExtendedEquipmentType | null - ) => void; - onOpenDynamicSimulationEventDialog?: (id: string, type: EquipmentType | null, dialogTitle: string) => void; + handleDeleteEquipment: (type: EquipmentType, id: string) => void; + handleOpenModificationDialog: (id: string, type: EquipmentType, subtype: ExtendedEquipmentType | null) => void; + onOpenDynamicSimulationEventDialog?: (id: string, type: EquipmentType, dialogTitle: string) => void; currentNode?: CurrentTreeNode; studyUuid?: UUID; currentRootNetworkUuid?: UUID | null; @@ -362,7 +358,12 @@ const withOperatingStatusMenu = )} handleDeleteEquipment(getCommonEquipmentType(equipmentType), equipment.id)} + onClick={() => { + const commonType = getCommonEquipmentType(equipmentType); + if (commonType) { + handleDeleteEquipment(commonType, equipment.id); + } + }} disabled={!isNodeEditable} > diff --git a/src/components/network/network-map-panel.tsx b/src/components/network/network-map-panel.tsx index 206fb4b5b5..2422aee443 100644 --- a/src/components/network/network-map-panel.tsx +++ b/src/components/network/network-map-panel.tsx @@ -270,7 +270,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ mapEquipments?.hvdcLinesById?.get(equipmentId)?.hvdcType === 'LCC' ) { // only hvdc line with LCC requires a Dialog (to select MCS) - handleOpenDeletionDialog(equipmentId, EQUIPMENT_TYPES.HVDC_LINE); + handleOpenDeletionDialog(equipmentId, EquipmentType.HVDC_LINE); } else { deleteEquipment({ studyUuid, diff --git a/src/hooks/use-equipment-dialogs.tsx b/src/hooks/use-equipment-dialogs.tsx index a9296d2faa..66e2ea6905 100644 --- a/src/hooks/use-equipment-dialogs.tsx +++ b/src/hooks/use-equipment-dialogs.tsx @@ -13,7 +13,7 @@ import { snackWithFallback, useSnackMessage, } from '@gridsuite/commons-ui'; -import { EQUIPMENT_INFOS_TYPES } from '../components/utils/equipment-types'; +import { EQUIPMENT_INFOS_TYPES, EQUIPMENT_TYPES } from '../components/utils/equipment-types'; import { deleteEquipment } from '../services/study/network-modifications'; import { fetchNetworkElementInfos } from '../services/study/network'; import { CurrentTreeNode } from '../components/graph/tree-node.type'; @@ -241,7 +241,7 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork return ( diff --git a/src/hooks/use-equipment-menu.tsx b/src/hooks/use-equipment-menu.tsx index 664c8decd1..1dbca44bb5 100644 --- a/src/hooks/use-equipment-menu.tsx +++ b/src/hooks/use-equipment-menu.tsx @@ -36,11 +36,11 @@ interface UseEquipmentMenuProps { studyUuid: UUID; disabled: boolean; onViewInSpreadsheet: (equipmentType: EquipmentType, equipmentId: string) => void; - onDeleteEquipment: (equipmentType: EquipmentType | null, equipmentId: string) => void; - onOpenModificationDialog: (id: string, type: EquipmentType | null, subtype: ExtendedEquipmentType | null) => void; + onDeleteEquipment: (equipmentType: EquipmentType, equipmentId: string) => void; + onOpenModificationDialog: (id: string, type: EquipmentType, subtype: ExtendedEquipmentType | null) => void; onOpenDynamicSimulationEventDialog?: ( equipmentId: string, - equipmentType: EquipmentType | null, + equipmentType: EquipmentType, dialogTitle: string ) => void; modificationInProgress?: boolean; @@ -152,7 +152,7 @@ export const useEquipmentMenu = ({ ); const handleDeleteEquipment = useCallback( - (equipmentType: EquipmentType | null, equipmentId: string) => { + (equipmentType: EquipmentType, equipmentId: string) => { onDeleteEquipment(equipmentType, equipmentId); closeEquipmentMenu(); }, @@ -160,7 +160,7 @@ export const useEquipmentMenu = ({ ); const handleOpenModificationDialog = useCallback( - (id: string, type: EquipmentType | null, subtype: ExtendedEquipmentType | null) => { + (id: string, type: EquipmentType, subtype: ExtendedEquipmentType | null) => { onOpenModificationDialog(id, type, subtype); closeEquipmentMenu(); }, @@ -168,7 +168,7 @@ export const useEquipmentMenu = ({ ); const handleOpenDynamicSimulationEventDialog = useCallback( - (equipmentId: string, equipmentType: EquipmentType | null, dialogTitle: string) => { + (equipmentId: string, equipmentType: EquipmentType, dialogTitle: string) => { if (onOpenDynamicSimulationEventDialog) { onOpenDynamicSimulationEventDialog(equipmentId, equipmentType, dialogTitle); } From 5e9f43352eab4edc4675b258ad3872f2b30ae776 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Mon, 19 Jan 2026 19:10:56 +0100 Subject: [PATCH 03/10] fixes Signed-off-by: David BRAQUART --- .../equipment-deletion/equipment-deletion-dialog.tsx | 6 +++++- .../equipment-deletion/equipment-deletion-form.tsx | 1 - .../network-modification-node-editor.tsx | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx index 88db20ec3c..5db8afbb2e 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx @@ -49,6 +49,7 @@ interface EquipmentDeletionDialogProps { equipmentType: EquipmentType; editDataFetchStatus?: FetchStatus; onClose?: () => void; + onValidated?: () => void; } /** @@ -61,7 +62,8 @@ interface EquipmentDeletionDialogProps { * @param defaultIdValue the default equipment id * @param equipmentType * @param editDataFetchStatus indicates the status of fetching EditData - * @param onClose a callback when dialog has closed + * @param onClose a callback when dialog has been closed + * @param onValidated a callback when dialog has been validated * @param dialogProps props that are forwarded to the generic ModificationDialog component */ const EquipmentDeletionDialog = ({ @@ -74,6 +76,7 @@ const EquipmentDeletionDialog = ({ equipmentType, editDataFetchStatus, onClose, + onValidated, ...dialogProps }: EquipmentDeletionDialogProps) => { const currentNodeUuid = currentNode?.id; @@ -169,6 +172,7 @@ const EquipmentDeletionDialog = ({ maxWidth="md" onClear={clear} onClose={onClose} + onValidated={onValidated} onSave={onSubmit} titleId="DeleteEquipment" open={open} diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx index 09cfbfe0e0..e7932248c4 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx @@ -88,7 +88,6 @@ export default function DeleteEquipmentForm({ fetchEquipmentsIds(studyUuid, currentNode?.id, currentRootNetworkUuid, undefined, watchType, true) .then((vals) => { // check race condition here - console.log('DBG DBR fetchEquipmentsIds', vals); if (!ignore) { setEquipmentsOptions(vals.sort()); } diff --git a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx index 0f869ed2a4..d6762f4e21 100644 --- a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx +++ b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx @@ -232,11 +232,11 @@ const NetworkModificationNodeEditor = () => { } function equipmentDeletionDialogWithDefaultParams(equipmentType: EquipmentType) { - if (currentNode && studyUuid && currentRootNetworkUuid && editData) { - // TODO DBR onValidated={handleValidatedDialog} + if (currentNode && studyUuid && currentRootNetworkUuid) { return ( Date: Tue, 20 Jan 2026 10:50:17 +0100 Subject: [PATCH 04/10] sonar issues Signed-off-by: David BRAQUART --- .../equipement-deletion-dialog.type.ts | 7 +------ .../hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts | 4 ++-- src/components/network/network-map-panel.tsx | 13 ++++++------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts b/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts index 333fc0775b..f51f9d7001 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts @@ -8,12 +8,9 @@ import { EquipmentType, ModificationType } from '@gridsuite/commons-ui'; import { UUID } from 'node:crypto'; import { LccShuntCompensatorInfos } from '../../../../services/network-modification-types'; -// TODO DBR move to mod types ? - -// Specific deletion: 1 use-case with LCC deletion (specificType = HVDC_LINE_LCC_DELETION_SPECIFIC_TYPE) export interface EquipmentDeletionSpecificInfos { specificType: string; - // below is specific + // below is specific to HVDC-LCC deletion (then specificType = HVDC_LINE_LCC_DELETION_SPECIFIC_TYPE) mcsOnSide1: LccShuntCompensatorInfos[]; mcsOnSide2: LccShuntCompensatorInfos[]; } @@ -29,6 +26,4 @@ export type EquipmentDeletionInfos = { // Maps HvdcLccDeletionInfos from modification-server export interface HvdcLccDeletionInfos extends EquipmentDeletionSpecificInfos { id?: UUID; - mcsOnSide1: LccShuntCompensatorInfos[]; - mcsOnSide2: LccShuntCompensatorInfos[]; } diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts index 9a050b07c3..f7d99aea0f 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts @@ -45,9 +45,9 @@ const useHvdcLccDeletion = () => { return { ...obj, connectedToHvdc: false }; }); // now overwrite dynamic values with edited modification values - const dynamicIds = dynamicList.map((obj) => obj.id); + const dynamicIds = new Set(dynamicList.map((obj) => obj.id)); for (let editObj of editList.values()) { - if (dynamicIds.includes(editObj.id)) { + if (dynamicIds.has(editObj.id)) { const mergedObj = mergedList.find((obj) => obj.id === editObj.id); if (mergedObj) { mergedObj.connectedToHvdc = editObj.connectedToHvdc; diff --git a/src/components/network/network-map-panel.tsx b/src/components/network/network-map-panel.tsx index 2422aee443..02b7da24a8 100644 --- a/src/components/network/network-map-panel.tsx +++ b/src/components/network/network-map-panel.tsx @@ -160,7 +160,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ const { showInSpreadsheet, openSLD } = useWorkspacePanelActions(); const [isRootNodeGeoDataLoaded, setIsRootNodeGeoDataLoaded] = useState(false); - const [isInitialized, setInitialized] = useState(false); + const [isInitialized, setIsInitialized] = useState(false); const mapBoxToken = useMapBoxToken(); const { snackError } = useSnackMessage(); @@ -262,8 +262,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ }); const handleDeleteEquipment = useCallback( - (equipmentType: EquipmentType | null, equipmentId: string) => { - // TODO DBR change type -= null / +UUID + (equipmentType: EquipmentType, equipmentId: string) => { if (equipmentType && currentNode?.id) { if ( equipmentType === EquipmentType.HVDC_LINE && @@ -587,7 +586,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ // trigger root node geodata fetching loadRootNodeGeoData(); // set initialized to false to trigger "missing geo-data fetching" - setInitialized(false); + setIsInitialized(false); // set isRootNodeGeoDataLoaded to false so "missing geo-data fetching" waits for root node geo-data to be fully fetched before triggering setIsRootNodeGeoDataLoaded(false); } @@ -770,7 +769,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ if (isRootNetworksUpdatedNotification(eventData)) { const rootNetworkUuidsFromNotification = eventData.headers.rootNetworkUuids; if (rootNetworkUuidsFromNotification.includes(currentRootNetworkUuid)) { - setInitialized(false); + setIsInitialized(false); setIsRootNodeGeoDataLoaded(false); dispatch(resetMapEquipment()); } @@ -810,7 +809,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ // when root network has just been changed, we reset map equipment and geo data, they will be loaded as if we were opening a new study // DO NOT BREAK AT FIRST LOADING (previousCurrentRootNetworkUuid=null) if (previousCurrentRootNetworkUuid && previousCurrentRootNetworkUuid !== currentRootNetworkUuid) { - setInitialized(false); + setIsInitialized(false); setIsRootNodeGeoDataLoaded(false); dispatch(resetMapEquipment()); return; @@ -884,7 +883,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ dispatch(setMapDataLoading(false)); }); } - setInitialized(true); + setIsInitialized(true); } }, [ handleFilteredNominalVoltagesChange, From c2cd606f3d698670f88b9a5c6c6ccfee5f4c5e59 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Tue, 20 Jan 2026 11:25:49 +0100 Subject: [PATCH 05/10] use Enum rather then 'LCC' Signed-off-by: David BRAQUART --- .../networkAreaDiagram/network-area-diagram-content.tsx | 3 ++- src/components/network/network-map-panel.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/grid-layout/cards/diagrams/networkAreaDiagram/network-area-diagram-content.tsx b/src/components/grid-layout/cards/diagrams/networkAreaDiagram/network-area-diagram-content.tsx index 9fbe520e3b..45a2168209 100644 --- a/src/components/grid-layout/cards/diagrams/networkAreaDiagram/network-area-diagram-content.tsx +++ b/src/components/grid-layout/cards/diagrams/networkAreaDiagram/network-area-diagram-content.tsx @@ -37,6 +37,7 @@ import { ElementType, EquipmentType, ExtendedEquipmentType, + HvdcType, IElementCreationDialog, IElementUpdateDialog, mergeSx, @@ -296,7 +297,7 @@ const NetworkAreaDiagramContent = memo(function NetworkAreaDiagramContent(props: ) .then((hvdcInfos) => { const equipmentSubtype = - hvdcInfos?.hvdcType === 'LCC' + hvdcInfos?.hvdcType === HvdcType.LCC ? ExtendedEquipmentType.HVDC_LINE_LCC : ExtendedEquipmentType.HVDC_LINE_VSC; diff --git a/src/components/network/network-map-panel.tsx b/src/components/network/network-map-panel.tsx index 02b7da24a8..776d7e7f70 100644 --- a/src/components/network/network-map-panel.tsx +++ b/src/components/network/network-map-panel.tsx @@ -266,7 +266,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ if (equipmentType && currentNode?.id) { if ( equipmentType === EquipmentType.HVDC_LINE && - mapEquipments?.hvdcLinesById?.get(equipmentId)?.hvdcType === 'LCC' + mapEquipments?.hvdcLinesById?.get(equipmentId)?.hvdcType === HvdcType.LCC ) { // only hvdc line with LCC requires a Dialog (to select MCS) handleOpenDeletionDialog(equipmentId, EquipmentType.HVDC_LINE); From e318cd542a8b76ed1af6655c95e1efd24118b4fe Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Tue, 20 Jan 2026 13:22:02 +0100 Subject: [PATCH 06/10] more typing Signed-off-by: David BRAQUART --- .../equipement-deletion-dialog.type.ts | 12 ++++-- .../equipment-deletion-dialog.tsx | 38 ++++++++----------- .../hvdc-lcc-deletion-utils.ts | 37 ++++++++++++++++++ .../shunt-compensator-selection-form.tsx | 2 +- .../use-hvdc-lcc-deletion.ts | 14 +++++-- src/components/utils/field-constants.ts | 2 - src/services/study/network-modifications.ts | 5 ++- 7 files changed, 75 insertions(+), 35 deletions(-) create mode 100644 src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.ts diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts b/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts index f51f9d7001..5c7928534d 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts @@ -1,18 +1,22 @@ /** - * Copyright (c) 2025, RTE (http://www.rte-france.com) + * Copyright (c) 2026, 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 { EquipmentType, ModificationType } from '@gridsuite/commons-ui'; import { UUID } from 'node:crypto'; -import { LccShuntCompensatorInfos } from '../../../../services/network-modification-types'; + +export interface LccShuntCompensatorConnectionInfos { + id: string; + connectedToHvdc: boolean; +} export interface EquipmentDeletionSpecificInfos { specificType: string; // below is specific to HVDC-LCC deletion (then specificType = HVDC_LINE_LCC_DELETION_SPECIFIC_TYPE) - mcsOnSide1: LccShuntCompensatorInfos[]; - mcsOnSide2: LccShuntCompensatorInfos[]; + mcsOnSide1: LccShuntCompensatorConnectionInfos[]; + mcsOnSide2: LccShuntCompensatorConnectionInfos[]; } export type EquipmentDeletionInfos = { diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx index 5db8afbb2e..7b91126617 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx @@ -17,17 +17,18 @@ import { useOpenShortWaitFetching } from 'components/dialogs/commons/handle-modi import { FORM_LOADING_DELAY } from 'components/network/constants'; import { deleteEquipment } from '../../../../services/study/network-modifications'; import { UUID } from 'node:crypto'; -import { CurrentTreeNode } from 'components/graph/tree-node.type'; import { FetchStatus } from 'services/utils.type'; import { DeepNullable } from 'components/utils/ts-utils'; import { EquipmentDeletionInfos } from './equipement-deletion-dialog.type'; +import { NetworkModificationDialogProps } from '../../../graph/menus/network-modifications/network-modification-menu.type'; +import { getHvdcLccDeletionSchema } from './hvdc-lcc-deletion/hvdc-lcc-deletion-utils'; const formSchema = yup .object() .shape({ [EQUIPMENT_ID]: yup.string().nullable().required(), [TYPE]: yup.mixed().oneOf(Object.values(EquipmentType)).nullable().required(), - [DELETION_SPECIFIC_DATA]: yup.string().nullable(), + [DELETION_SPECIFIC_DATA]: getHvdcLccDeletionSchema(), }) .required(); @@ -39,18 +40,12 @@ const emptyFormData: EquipmentDeletionFormInfos = { [DELETION_SPECIFIC_DATA]: null, }; -interface EquipmentDeletionDialogProps { - studyUuid: UUID; - currentNode: CurrentTreeNode; - currentRootNetworkUuid: UUID; +type EquipmentDeletionDialogProps = NetworkModificationDialogProps & { editData?: EquipmentDeletionInfos; - isUpdate: boolean; defaultIdValue?: UUID; equipmentType: EquipmentType; editDataFetchStatus?: FetchStatus; - onClose?: () => void; - onValidated?: () => void; -} +}; /** * Dialog to delete equipment from its type and ID. @@ -95,6 +90,7 @@ const EquipmentDeletionDialog = ({ reset({ [TYPE]: editData.equipmentType, [EQUIPMENT_ID]: editData.equipmentId, + [DELETION_SPECIFIC_DATA]: null, }); }, [reset] @@ -139,18 +135,16 @@ const EquipmentDeletionDialog = ({ const onSubmit = useCallback( (formData: EquipmentDeletionFormInfos) => { - if (formData[EQUIPMENT_ID]) { - deleteEquipment({ - studyUuid, - nodeUuid: currentNodeUuid, - uuid: editData?.uuid, - equipmentId: formData[EQUIPMENT_ID] as UUID, - equipmentType: formData[TYPE], - equipmentSpecificInfos: formData[DELETION_SPECIFIC_DATA], - }).catch((error) => { - snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); - }); - } + deleteEquipment({ + studyUuid, + nodeUuid: currentNodeUuid, + uuid: editData?.uuid, + equipmentId: formData[EQUIPMENT_ID] as UUID, + equipmentType: formData[TYPE], + equipmentSpecificInfos: formData[DELETION_SPECIFIC_DATA] ?? undefined, + }).catch((error) => { + snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); + }); }, [currentNodeUuid, editData, snackError, studyUuid] ); diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.ts b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.ts new file mode 100644 index 0000000000..4055b9b2f0 --- /dev/null +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2026, 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 yup from '../../../../utils/yup-config'; +import { + DELETION_SPECIFIC_TYPE, + ID, + SHUNT_COMPENSATOR_SELECTED, + SHUNT_COMPENSATOR_SIDE_1, + SHUNT_COMPENSATOR_SIDE_2, +} from '../../../../utils/field-constants'; + +const getMscConnectionsSchema = () => + yup + .array() + .of( + yup.object().shape({ + [ID]: yup.string().required(), + [SHUNT_COMPENSATOR_SELECTED]: yup.boolean().required(), + }) + ) + .required(); + +export const getHvdcLccDeletionSchema = () => + yup + .object() + .shape({ + [DELETION_SPECIFIC_TYPE]: yup.string().required(), + [SHUNT_COMPENSATOR_SIDE_1]: getMscConnectionsSchema(), + [SHUNT_COMPENSATOR_SIDE_2]: getMscConnectionsSchema(), + }) + .optional() + .nullable(); diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx index ebaadc401e..7b3aa92082 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx @@ -1,5 +1,5 @@ /** - * Copyright (c) 2023, RTE (http://www.rte-france.com) + * Copyright (c) 2026, 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/. diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts index f7d99aea0f..cb6effb3e2 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts @@ -16,9 +16,12 @@ import { useCallback } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui'; import { fetchHvdcLineWithShuntCompensators } from '../../../../../services/study/network-map'; -import { EquipmentDeletionInfos, HvdcLccDeletionInfos } from '../equipement-deletion-dialog.type'; +import { + EquipmentDeletionInfos, + HvdcLccDeletionInfos, + LccShuntCompensatorConnectionInfos, +} from '../equipement-deletion-dialog.type'; import { UUID } from 'node:crypto'; -import { LccShuntCompensatorInfos } from '../../../../../services/network-modification-types'; const useHvdcLccDeletion = () => { const { replace: replaceMcsList1 } = useFieldArray({ @@ -32,7 +35,10 @@ const useHvdcLccDeletion = () => { const updateMcsLists = useCallback( (hvdcLineData: HvdcLccDeletionInfos, editData?: EquipmentDeletionInfos) => { - function mergeMcsLists(dynamicList: LccShuntCompensatorInfos[], editList: LccShuntCompensatorInfos[]) { + function mergeMcsLists( + dynamicList: LccShuntCompensatorConnectionInfos[], + editList: LccShuntCompensatorConnectionInfos[] + ) { if (!dynamicList && !editList) { return []; } else if (!dynamicList) { @@ -41,7 +47,7 @@ const useHvdcLccDeletion = () => { } else if (!editList) { return dynamicList; } - const mergedList: LccShuntCompensatorInfos[] = dynamicList.map((obj) => { + const mergedList: LccShuntCompensatorConnectionInfos[] = dynamicList.map((obj) => { return { ...obj, connectedToHvdc: false }; }); // now overwrite dynamic values with edited modification values diff --git a/src/components/utils/field-constants.ts b/src/components/utils/field-constants.ts index 32b0d3a41b..7c573bbb5a 100644 --- a/src/components/utils/field-constants.ts +++ b/src/components/utils/field-constants.ts @@ -365,8 +365,6 @@ export const SHUNT_COMPENSATOR_SIDE_2 = 'mcsOnSide2'; export const SHUNT_COMPENSATOR_SELECTED = 'connectedToHvdc'; export const PREVIOUS_SHUNT_COMPENSATOR_SELECTED = 'previousConnectedToHvdc'; export const PROVIDER = 'provider'; -export const CONTINGENCIES = 'contingencies'; -export const MONITORED_BRANCHES = 'monitoredBranches'; // VSC export const ACTIVE_POWER_SETPOINT = 'activePowerSetpoint'; diff --git a/src/services/study/network-modifications.ts b/src/services/study/network-modifications.ts index 2ba3249a97..abc1e73350 100644 --- a/src/services/study/network-modifications.ts +++ b/src/services/study/network-modifications.ts @@ -67,6 +67,7 @@ import { OLGS_MODIFICATION_TYPE, OPERATIONAL_LIMITS_GROUPS_MODIFICATION_TYPE, } from '../../components/utils/field-constants'; +import { EquipmentDeletionSpecificInfos } from '../../components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type'; function getNetworkModificationUrl(studyUuid: string | null | undefined, nodeUuid: string | undefined) { return getStudyUrlWithNodeUuid(studyUuid, nodeUuid) + '/network-modifications'; @@ -1657,12 +1658,12 @@ export function deleteAttachingLine({ } export interface DeleteEquipmentInfo { - studyUuid: string; + studyUuid: UUID; nodeUuid: UUID; uuid?: UUID; equipmentId: UUID; equipmentType: EquipmentType; - equipmentSpecificInfos?: any; + equipmentSpecificInfos?: EquipmentDeletionSpecificInfos; } export function deleteEquipment({ From d8c06235bd043fdabfd4efadd66346c606037c07 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Tue, 20 Jan 2026 16:42:38 +0100 Subject: [PATCH 07/10] fix connected mcs display Signed-off-by: David BRAQUART --- .../hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts index cb6effb3e2..9ea23d48e3 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts @@ -39,14 +39,15 @@ const useHvdcLccDeletion = () => { dynamicList: LccShuntCompensatorConnectionInfos[], editList: LccShuntCompensatorConnectionInfos[] ) { - if (!dynamicList && !editList) { + if (!dynamicList?.length && !editList?.length) { return []; - } else if (!dynamicList) { + } else if (!dynamicList?.length) { // TODO: we should refactor modification-server to store only selected MCS return editList.filter((item) => item.connectedToHvdc); - } else if (!editList) { + } else if (!editList?.length) { return dynamicList; } + // dynamicList and editList are not empty : let's merge them const mergedList: LccShuntCompensatorConnectionInfos[] = dynamicList.map((obj) => { return { ...obj, connectedToHvdc: false }; }); From 35482afeb1a3c889c4607451db654c2192730761 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Wed, 28 Jan 2026 11:36:17 +0100 Subject: [PATCH 08/10] fix after rebase Signed-off-by: David BRAQUART --- .../network-modifications/network-modification-node-editor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx index d6762f4e21..2a67399e28 100644 --- a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx +++ b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx @@ -251,7 +251,7 @@ const NetworkModificationNodeEditor = () => { } } - const equipmentDeletionSubItems = (equipmentType: EQUIPMENT_TYPES) => { + const equipmentDeletionSubItems = (equipmentType: EquipmentType) => { return { // We have a single deletion modification type, but we have a deletion menu item ID per equipment type // (because we want to preset the equipment type in creation case) From 365970f10dee47e107fed6e291e9e5d26cc4c876 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Wed, 28 Jan 2026 16:05:44 +0100 Subject: [PATCH 09/10] fix update issue when we switch hvdc-lcc in the ID combobox Signed-off-by: David BRAQUART --- .../hvdc-lcc-deletion-specific-form.tsx | 36 +++++++++++++- .../shunt-compensator-selection-form.tsx | 47 ------------------- 2 files changed, 35 insertions(+), 48 deletions(-) delete mode 100644 src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/shunt-compensator-selection-form.tsx diff --git a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.tsx b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.tsx index c27e896229..3c9e814f9e 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.tsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.tsx @@ -8,13 +8,22 @@ import { Grid } from '@mui/material'; import { DELETION_SPECIFIC_DATA, + SHUNT_COMPENSATOR_SELECTED, SHUNT_COMPENSATOR_SIDE_1, SHUNT_COMPENSATOR_SIDE_2, } from 'components/utils/field-constants'; import { useFieldArray } from 'react-hook-form'; import GridSection from '../../../commons/grid-section'; import GridItem from '../../../commons/grid-item'; -import ShuntCompensatorSelectionForm from './shunt-compensator-selection-form'; +import { CheckboxInput, ID } from '@gridsuite/commons-ui'; +import ReadOnlyInput from '../../../../utils/rhf-inputs/read-only/read-only-input'; +import { FormattedMessage } from 'react-intl'; + +interface ShuntCompensatorSelectionFormProps { + title: string; + arrayFormName: string; + mcsRows: Record<'id', string>[]; +} export default function HvdcLccDeletionSpecificForm() { const { fields: mcsRows1 } = useFieldArray({ @@ -24,6 +33,31 @@ export default function HvdcLccDeletionSpecificForm() { name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_2}`, }); + const ShuntCompensatorSelectionForm = ({ title, arrayFormName, mcsRows }: ShuntCompensatorSelectionFormProps) => { + return ( + + +

+ +

+
+ {mcsRows.map((field, index) => ( + + + + + + + + + ))} +
+ ); + }; + const mcsOnsideOne = ( []; -} - -export default function ShuntCompensatorSelectionForm({ - title, - arrayFormName, - mcsRows, -}: Readonly) { - return ( - - -

- -

-
- {mcsRows.map((field, index) => ( - - - - - - - - - ))} -
- ); -} From f74710ced7256e0b520779ef6889b5b96cfcb93a Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Tue, 10 Feb 2026 18:21:27 +0100 Subject: [PATCH 10/10] lint after merge Signed-off-by: David BRAQUART --- .../equipment-deletion/equipment-deletion-dialog.tsx | 9 +++++++-- .../equipment-deletion/equipment-deletion-form.tsx | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx index 7b91126617..9a80929436 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx @@ -8,7 +8,13 @@ import { yupResolver } from '@hookform/resolvers/yup'; import yup from 'components/utils/yup-config'; import { DELETION_SPECIFIC_DATA, EQUIPMENT_ID, TYPE } from '../../../utils/field-constants'; -import { CustomFormProvider, EquipmentType, snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui'; +import { + CustomFormProvider, + DeepNullable, + EquipmentType, + snackWithFallback, + useSnackMessage, +} from '@gridsuite/commons-ui'; import { useForm } from 'react-hook-form'; import { useCallback, useEffect } from 'react'; import { ModificationDialog } from '../../commons/modificationDialog'; @@ -18,7 +24,6 @@ import { FORM_LOADING_DELAY } from 'components/network/constants'; import { deleteEquipment } from '../../../../services/study/network-modifications'; import { UUID } from 'node:crypto'; import { FetchStatus } from 'services/utils.type'; -import { DeepNullable } from 'components/utils/ts-utils'; import { EquipmentDeletionInfos } from './equipement-deletion-dialog.type'; import { NetworkModificationDialogProps } from '../../../graph/menus/network-modifications/network-modification-menu.type'; import { getHvdcLccDeletionSchema } from './hvdc-lcc-deletion/hvdc-lcc-deletion-utils'; diff --git a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx index 56f74ff50f..a133d574b4 100644 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx @@ -8,7 +8,13 @@ import { Grid } from '@mui/material'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; -import { useSnackMessage, AutocompleteInput, EquipmentType, filledTextField, snackWithFallback } from '@gridsuite/commons-ui'; +import { + useSnackMessage, + AutocompleteInput, + EquipmentType, + filledTextField, + snackWithFallback, +} from '@gridsuite/commons-ui'; import { DELETION_SPECIFIC_DATA, EQUIPMENT_ID,