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..5c7928534d --- /dev/null +++ b/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts @@ -0,0 +1,33 @@ +/** + * 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'; + +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: LccShuntCompensatorConnectionInfos[]; + mcsOnSide2: LccShuntCompensatorConnectionInfos[]; +} + +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; +} 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..9a80929436 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,43 +8,62 @@ 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, + DeepNullable, + 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 { FetchStatus } from 'services/utils.type'; +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({ - [TYPE]: yup.string().nullable().required(), [EQUIPMENT_ID]: yup.string().nullable().required(), + [TYPE]: yup.mixed().oneOf(Object.values(EquipmentType)).nullable().required(), + [DELETION_SPECIFIC_DATA]: getHvdcLccDeletionSchema(), }) .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, }; +type EquipmentDeletionDialogProps = NetworkModificationDialogProps & { + editData?: EquipmentDeletionInfos; + defaultIdValue?: UUID; + equipmentType: EquipmentType; + editDataFetchStatus?: FetchStatus; +}; + /** - * 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 onClose a callback when dialog has closed + * @param editDataFetchStatus indicates the status of fetching EditData + * @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 = ({ @@ -54,36 +73,38 @@ const EquipmentDeletionDialog = ({ editData, isUpdate, defaultIdValue, // Used to pre-select an equipmentId when calling this dialog from the SLD/map - editDataFetchStatus, equipmentType, + editDataFetchStatus, onClose, + onValidated, ...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, + [DELETION_SPECIFIC_DATA]: null, }); }, [reset] ); 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 +113,7 @@ const EquipmentDeletionDialog = ({ ); const resetFormWithEquipmentType = useCallback( - (equipmentType) => { + (equipmentType: EquipmentType) => { reset({ [TYPE]: equipmentType, }); @@ -118,15 +139,15 @@ const EquipmentDeletionDialog = ({ ]); const onSubmit = useCallback( - (formData) => { - deleteEquipment( + (formData: EquipmentDeletionFormInfos) => { + deleteEquipment({ studyUuid, - currentNodeUuid, - formData[TYPE], - formData[EQUIPMENT_ID], - editData?.uuid, - formData[DELETION_SPECIFIC_DATA] - ).catch((error) => { + 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' }); }); }, @@ -149,8 +170,9 @@ const EquipmentDeletionDialog = ({ fullWidth maxWidth="md" onClear={clear} - onSave={onSubmit} onClose={onClose} + onValidated={onValidated} + onSave={onSubmit} titleId="DeleteEquipment" open={open} isDataFetching={isUpdate && editDataFetchStatus === FetchStatus.RUNNING} @@ -167,16 +189,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 afda383e05..a133d574b4 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,13 @@ import { Grid } from '@mui/material'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; -import { useSnackMessage, AutocompleteInput, snackWithFallback, filledTextField } from '@gridsuite/commons-ui'; +import { + useSnackMessage, + AutocompleteInput, + EquipmentType, + filledTextField, + snackWithFallback, +} from '@gridsuite/commons-ui'; import { DELETION_SPECIFIC_DATA, EQUIPMENT_ID, @@ -18,16 +24,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'; + +export interface DeleteEquipmentFormProps { + studyUuid: UUID; + currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; + editData?: EquipmentDeletionInfos; +} -const DeleteEquipmentForm = ({ studyUuid, currentNode, currentRootNetworkUuid, editData }) => { +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, @@ -84,31 +107,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, @@ -149,9 +173,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} /> @@ -168,6 +192,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 83% 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..3c9e814f9e 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,26 @@ */ 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_SELECTED, 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 { 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>[]; +} -const HvdcLccDeletionSpecificForm = () => { +export default function HvdcLccDeletionSpecificForm() { const { fields: mcsRows1 } = useFieldArray({ name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_1}`, }); @@ -28,7 +33,7 @@ const HvdcLccDeletionSpecificForm = () => { name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_2}`, }); - const ShuntCompensatorSelectionForm = ({ title, arrayFormName, mcsRows }) => { + const ShuntCompensatorSelectionForm = ({ title, arrayFormName, mcsRows }: ShuntCompensatorSelectionFormProps) => { return ( @@ -38,13 +43,13 @@ const HvdcLccDeletionSpecificForm = () => { {mcsRows.map((field, index) => ( - + - + @@ -78,6 +83,4 @@ const HvdcLccDeletionSpecificForm = () => { ); -}; - -export default HvdcLccDeletionSpecificForm; +} 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/hvdc-lcc-deletion-utils.js b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts similarity index 67% 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..9ea23d48e3 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,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, + LccShuntCompensatorConnectionInfos, +} from '../equipement-deletion-dialog.type'; +import { UUID } from 'node:crypto'; const useHvdcLccDeletion = () => { const { replace: replaceMcsList1 } = useFieldArray({ @@ -28,23 +34,27 @@ const useHvdcLccDeletion = () => { const { snackError } = useSnackMessage(); const updateMcsLists = useCallback( - (hvdcLineData, editData) => { - function mergeMcsLists(dynamicList, editList) { - if (!dynamicList && !editList) { + (hvdcLineData: HvdcLccDeletionInfos, editData?: EquipmentDeletionInfos) => { + function mergeMcsLists( + dynamicList: LccShuntCompensatorConnectionInfos[], + editList: LccShuntCompensatorConnectionInfos[] + ) { + 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; } - const mergedList = dynamicList.map((obj) => { + // dynamicList and editList are not empty : let's merge them + const mergedList: LccShuntCompensatorConnectionInfos[] = dynamicList.map((obj) => { 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; @@ -60,16 +70,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 +84,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 b1df3512f7..a282a80726 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, fetchNetworkModification, IElementCreationDialog, IElementUpdateDialog, @@ -80,7 +81,6 @@ import { fetchNetworkModifications, stashModifications, } from '../../../../services/study/network-modifications'; -import { FetchStatus } from '../../../../services/utils'; import { ExcludedNetworkModifications, MenuDefinitionSubItem, @@ -119,10 +119,11 @@ 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'; +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', @@ -165,7 +166,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); @@ -234,24 +235,27 @@ const NetworkModificationNodeEditor = () => { ); } - function equipmentDeletionDialogWithDefaultParams(equipmentType: EQUIPMENT_TYPES) { - return ( - - ); + function equipmentDeletionDialogWithDefaultParams(equipmentType: EquipmentType) { + if (currentNode && studyUuid && currentRootNetworkUuid) { + return ( + + ); + } else { + return <>; + } } - 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) @@ -279,7 +283,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(SubstationModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.SUBSTATION), + equipmentDeletionSubItems(EquipmentType.SUBSTATION), ], }, { @@ -321,7 +325,7 @@ const NetworkModificationNodeEditor = () => { label: 'MOVE_VOLTAGE_LEVEL_FEEDER_BAYS', action: () => withDefaultParams(MoveVoltageLevelFeederBaysDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.VOLTAGE_LEVEL), + equipmentDeletionSubItems(EquipmentType.VOLTAGE_LEVEL), ], }, ], @@ -343,7 +347,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(LineModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.LINE), + equipmentDeletionSubItems(EquipmentType.LINE), ], }, { @@ -402,7 +406,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(TwoWindingsTransformerModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER), + equipmentDeletionSubItems(EquipmentType.TWO_WINDINGS_TRANSFORMER), ], }, { @@ -419,7 +423,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(VscModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.HVDC_LINE), + equipmentDeletionSubItems(EquipmentType.HVDC_LINE), ], }, { @@ -436,7 +440,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(LccModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.HVDC_LINE), + equipmentDeletionSubItems(EquipmentType.HVDC_LINE), ], }, ], @@ -458,7 +462,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(GeneratorModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.GENERATOR), + equipmentDeletionSubItems(EquipmentType.GENERATOR), ], }, { @@ -475,7 +479,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(BatteryModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.BATTERY), + equipmentDeletionSubItems(EquipmentType.BATTERY), ], }, { @@ -492,7 +496,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(LoadModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.LOAD), + equipmentDeletionSubItems(EquipmentType.LOAD), ], }, { @@ -509,7 +513,7 @@ const NetworkModificationNodeEditor = () => { label: 'ModifyFromMenu', action: () => withDefaultParams(ShuntCompensatorModificationDialog), }, - equipmentDeletionSubItems(EQUIPMENT_TYPES.SHUNT_COMPENSATOR), + equipmentDeletionSubItems(EquipmentType.SHUNT_COMPENSATOR), ], }, { @@ -521,7 +525,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/grid-layout/cards/diagrams/networkAreaDiagram/network-area-diagram-content.tsx b/src/components/grid-layout/cards/diagrams/networkAreaDiagram/network-area-diagram-content.tsx index 41212ff397..9a934d96bb 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, @@ -306,7 +307,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/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 dac74a25bb..616df788b4 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,17 +262,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' }); - }); + (equipmentType: EquipmentType, equipmentId: string) => { + if (equipmentType && currentNode?.id) { + if ( + equipmentType === EquipmentType.HVDC_LINE && + mapEquipments?.hvdcLinesById?.get(equipmentId)?.hvdcType === HvdcType.LCC + ) { + // only hvdc line with LCC requires a Dialog (to select MCS) + handleOpenDeletionDialog(equipmentId, EquipmentType.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] @@ -579,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); } @@ -762,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()); } @@ -802,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; @@ -876,7 +883,7 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ dispatch(setMapDataLoading(false)); }); } - setInitialized(true); + setIsInitialized(true); } }, [ handleFilteredNominalVoltagesChange, diff --git a/src/components/utils/field-constants.ts b/src/components/utils/field-constants.ts index 7b8b3cc804..416f938fb5 100644 --- a/src/components/utils/field-constants.ts +++ b/src/components/utils/field-constants.ts @@ -357,8 +357,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/hooks/use-equipment-dialogs.tsx b/src/hooks/use-equipment-dialogs.tsx index 121a2b063c..66e2ea6905 100644 --- a/src/hooks/use-equipment-dialogs.tsx +++ b/src/hooks/use-equipment-dialogs.tsx @@ -6,7 +6,13 @@ */ import { useCallback, useState } from 'react'; -import { EquipmentType, ExtendedEquipmentType, snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui'; +import { + EquipmentType, + ExtendedEquipmentType, + HvdcType, + snackWithFallback, + useSnackMessage, +} from '@gridsuite/commons-ui'; import { EQUIPMENT_INFOS_TYPES, EQUIPMENT_TYPES } from '../components/utils/equipment-types'; import { deleteEquipment } from '../services/study/network-modifications'; import { fetchNetworkElementInfos } from '../services/study/network'; @@ -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 { @@ -233,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); } 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 e3389e2e4b..3fca9741b8 100644 --- a/src/services/study/network-modifications.ts +++ b/src/services/study/network-modifications.ts @@ -69,6 +69,7 @@ import { OPERATIONAL_LIMITS_GROUPS_MODIFICATION_TYPE, } from '../../components/utils/field-constants'; import { TabularProperty } from '../../components/dialogs/network-modifications/tabular/properties/property-utils'; +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'; @@ -1130,35 +1131,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, }); @@ -1612,25 +1613,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: UUID; + nodeUuid: UUID; + uuid?: UUID; + equipmentId: UUID; + equipmentType: EquipmentType; + equipmentSpecificInfos?: EquipmentDeletionSpecificInfos; +} + +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', @@ -1639,7 +1649,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 7b098d202f..3c74ff5557 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -335,6 +335,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 21f155c63e..812e7e4b09 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -333,6 +333,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",