diff --git a/src/components/app-wrapper.jsx b/src/components/app-wrapper.jsx index 8f3ec6bc7a..7a4c31b376 100644 --- a/src/components/app-wrapper.jsx +++ b/src/components/app-wrapper.jsx @@ -40,6 +40,8 @@ import { equipmentShortFr, equipmentTagEn, equipmentTagFr, + equipmentTypesEn, + equipmentTypesFr, errorsEn, errorsFr, exportParamsEn, @@ -405,6 +407,7 @@ const messages = { ...equipmentSearchEn, ...equipmentShortEn, ...equipmentTagEn, + ...equipmentTypesEn, ...directoryItemsInputEn, ...cardErrorBoundaryEn, ...flatParametersEn, @@ -447,6 +450,7 @@ const messages = { ...equipmentSearchFr, ...equipmentShortFr, ...equipmentTagFr, + ...equipmentTypesFr, ...directoryItemsInputFr, ...cardErrorBoundaryFr, ...flatParametersFr, diff --git a/src/components/dialogs/line-types-catalog/line-type-segment-creation.tsx b/src/components/dialogs/line-types-catalog/line-type-segment-creation.tsx index c0cb2ee3ce..8a1c042aed 100644 --- a/src/components/dialogs/line-types-catalog/line-type-segment-creation.tsx +++ b/src/components/dialogs/line-types-catalog/line-type-segment-creation.tsx @@ -8,7 +8,7 @@ import { useCallback, useEffect } from 'react'; import { useWatch } from 'react-hook-form'; import EditIcon from '@mui/icons-material/Edit'; -import { FloatInput, KilometerAdornment } from '@gridsuite/commons-ui'; +import { ButtonReadOnlyInput, FloatInput, KilometerAdornment, ReadOnlyInput } from '@gridsuite/commons-ui'; import { IconButton } from '@mui/material'; import { SEGMENT_DISTANCE_VALUE, @@ -17,8 +17,6 @@ import { SEGMENT_SUSCEPTANCE, SEGMENT_TYPE_VALUE, } from '../../utils/field-constants'; -import { ReadOnlyInput } from '../../utils/rhf-inputs/read-only/read-only-input'; -import { ButtonReadOnlyInput } from '../../utils/rhf-inputs/read-only/button-read-only-input'; import GridItem from '../commons/grid-item'; export interface LineTypeSegmentCreationProps { diff --git a/src/components/dialogs/line-types-catalog/line-type-segment-form.tsx b/src/components/dialogs/line-types-catalog/line-type-segment-form.tsx index 160c2e528f..7a555bc964 100644 --- a/src/components/dialogs/line-types-catalog/line-type-segment-form.tsx +++ b/src/components/dialogs/line-types-catalog/line-type-segment-form.tsx @@ -9,7 +9,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useFormContext } from 'react-hook-form'; import { Box, Grid } from '@mui/material'; import { FormattedMessage, useIntl } from 'react-intl'; -import { ReadOnlyInput } from '../../utils/rhf-inputs/read-only/read-only-input'; import { FINAL_CURRENT_LIMITS, SEGMENT_CURRENT_LIMITS, @@ -34,6 +33,7 @@ import { ExpandableInput, fetchStudyMetadata, type MuiStyles, + ReadOnlyInput, snackWithFallback, useSnackMessage, } from '@gridsuite/commons-ui'; diff --git a/src/components/dialogs/network-modifications/by-filter/by-assignment/modification-by-assignment-form.tsx b/src/components/dialogs/network-modifications/by-filter/by-assignment/modification-by-assignment-form.tsx index 6694404694..f066034571 100644 --- a/src/components/dialogs/network-modifications/by-filter/by-assignment/modification-by-assignment-form.tsx +++ b/src/components/dialogs/network-modifications/by-filter/by-assignment/modification-by-assignment-form.tsx @@ -12,9 +12,8 @@ import { Box, Grid } from '@mui/material'; import { getAssignmentInitialValue } from './assignment/assignment-utils'; import { useFormContext } from 'react-hook-form'; import SelectWithConfirmationInput from '../../../commons/select-with-confirmation-input'; -import { ExpandableInput, mergeSx, unscrollableDialogStyles } from '@gridsuite/commons-ui'; +import { ExpandableInput, mergeSx, unscrollableDialogStyles, useGetLabelEquipmentTypes } from '@gridsuite/commons-ui'; import { EQUIPMENTS_FIELDS, EquipmentTypeOptionType } from './assignment/assignment-constants'; -import useGetLabelEquipmentTypes from '../../../../../hooks/use-get-label-equipment-types'; import GridItem from '../../../commons/grid-item'; interface ModificationByAssignmentFormProps {} diff --git a/src/components/dialogs/network-modifications/by-filter/by-filter-deletion/by-filter-deletion-form.tsx b/src/components/dialogs/network-modifications/by-filter/by-filter-deletion/by-filter-deletion-form.tsx index c83c8b1b6a..f2b5cee1aa 100644 --- a/src/components/dialogs/network-modifications/by-filter/by-filter-deletion/by-filter-deletion-form.tsx +++ b/src/components/dialogs/network-modifications/by-filter/by-filter-deletion/by-filter-deletion-form.tsx @@ -8,11 +8,10 @@ import { Grid } from '@mui/material'; import { useCallback, useMemo } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; -import { AutocompleteInput, DirectoryItemsInput, ElementType } from '@gridsuite/commons-ui'; +import { AutocompleteInput, DirectoryItemsInput, ElementType, useGetLabelEquipmentTypes } from '@gridsuite/commons-ui'; import { FILTERS, TYPE } from 'components/utils/field-constants'; import { richTypeEquals } from 'components/utils/utils'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; -import useGetLabelEquipmentTypes from '../../../../../hooks/use-get-label-equipment-types'; import GridItem from '../../../commons/grid-item'; const ByFilterDeletionForm = () => { 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 deleted file mode 100644 index 5c7928534d..0000000000 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipement-deletion-dialog.type.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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.tsx b/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog.tsx index 50ca45392d..42bb4f7b35 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 @@ -6,50 +6,38 @@ */ 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, DeepNullable, + equipmentDeletionEmptyFormData, + EquipmentDeletionDto, EquipmentType, snackWithFallback, useSnackMessage, + equipmentDeletionFormSchema, + EquipmentDeletionFormData, + EquipmentDeletionForm, + equipmentDeletionDtoToForm, + equipmentDeletionFormToDto, } from '@gridsuite/commons-ui'; import { useForm } from 'react-hook-form'; import { useCallback, useEffect, useMemo } from 'react'; import { ModificationDialog } from '../../commons/modificationDialog'; -import DeleteEquipmentForm from './equipment-deletion-form'; 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 { 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({ - [EQUIPMENT_ID]: yup.string().nullable().required(), - [TYPE]: yup.mixed().oneOf(Object.values(EquipmentType)).nullable().required(), - [DELETION_SPECIFIC_DATA]: getHvdcLccDeletionSchema(), - }) - .required(); - -export type EquipmentDeletionFormInfos = yup.InferType; - -const emptyFormData: DeepNullable = { - [EQUIPMENT_ID]: '', - [TYPE]: null, - [DELETION_SPECIFIC_DATA]: null, -}; +import { WithModificationId } from 'services/network-modification-types'; +import { fetchEquipmentsIds, fetchHvdcLineWithShuntCompensators } from 'services/study/network-map'; + +export interface EquipmentDeletionDtoWithId extends EquipmentDeletionDto, WithModificationId {} type EquipmentDeletionDialogProps = NetworkModificationDialogProps & { - editData?: EquipmentDeletionInfos; + editData?: EquipmentDeletionDtoWithId; defaultIdValue?: UUID; - equipmentType: EquipmentType; - editDataFetchStatus?: FetchStatus; + equipmentType?: EquipmentType; }; /** @@ -59,8 +47,8 @@ type EquipmentDeletionDialogProps = NetworkModificationDialogProps & { * @param currentRootNetworkUuid * @param editData the data to edit * @param isUpdate check if edition form - * @param defaultIdValue the default equipment id - * @param equipmentType + * @param defaultIdValue in creation mode, we can specify the equipment ID we want to delete + * @param equipmentType in creation mode, we can specify the equipment type we want to delete * @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 @@ -83,39 +71,39 @@ const EquipmentDeletionDialog = ({ const { snackError } = useSnackMessage(); - const formMethods = useForm>({ - defaultValues: emptyFormData, - resolver: yupResolver>(formSchema), + const formMethods = useForm>({ + defaultValues: equipmentDeletionEmptyFormData, + resolver: yupResolver>(equipmentDeletionFormSchema), }); const { reset } = formMethods; const fromEditDataToFormValues = useCallback( - (editData: EquipmentDeletionInfos) => { + (editData: EquipmentDeletionDto) => { + const formData = equipmentDeletionDtoToForm(editData); reset({ - [TYPE]: editData.equipmentType, - [EQUIPMENT_ID]: editData.equipmentId, - [DELETION_SPECIFIC_DATA]: null, + ...formData, + equipmentInfos: null, // this prop will be computed (cf LCC specific part) }); }, [reset] ); - const fromMenuDataValues = useCallback( - (menuSelectId: UUID) => { + const presetTypeAndId = useCallback( + (equipmentType: EquipmentType, equipmentId: UUID) => { reset({ - [TYPE]: EquipmentType.HVDC_LINE, - [EQUIPMENT_ID]: menuSelectId, - [DELETION_SPECIFIC_DATA]: null, + equipmentID: equipmentId, + type: equipmentType, + equipmentInfos: null, }); }, [reset] ); - const resetFormWithEquipmentType = useCallback( + const presetType = useCallback( (equipmentType: EquipmentType) => { reset({ - [TYPE]: equipmentType, + type: equipmentType, }); }, [reset] @@ -123,31 +111,21 @@ const EquipmentDeletionDialog = ({ useEffect(() => { if (editData) { + // edition mode fromEditDataToFormValues(editData); - } else if (defaultIdValue) { - fromMenuDataValues(defaultIdValue); + } else if (equipmentType && defaultIdValue) { + // creation mode with both Id and Type (ex: from diagram) + presetTypeAndId(equipmentType, defaultIdValue); } else if (equipmentType) { - resetFormWithEquipmentType(equipmentType); + // creation mode with only Type (ex: from node modifications menu) + presetType(equipmentType); } - }, [ - fromEditDataToFormValues, - editData, - fromMenuDataValues, - defaultIdValue, - equipmentType, - resetFormWithEquipmentType, - ]); + }, [defaultIdValue, editData, equipmentType, fromEditDataToFormValues, presetType, presetTypeAndId]); const onSubmit = useCallback( - (formData: EquipmentDeletionFormInfos) => { - deleteEquipment({ - studyUuid, - nodeUuid: currentNodeUuid, - uuid: editData?.uuid, - equipmentId: formData[EQUIPMENT_ID] as UUID, - equipmentType: formData[TYPE], - equipmentSpecificInfos: formData[DELETION_SPECIFIC_DATA] ?? undefined, - }).catch((error) => { + (formData: EquipmentDeletionFormData) => { + const dto = equipmentDeletionFormToDto(formData); + deleteEquipment(studyUuid, currentNodeUuid, editData?.uuid, dto).catch((error: unknown) => { snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); }); }, @@ -155,7 +133,7 @@ const EquipmentDeletionDialog = ({ ); const clear = useCallback(() => { - reset(emptyFormData); + reset(equipmentDeletionEmptyFormData); }, [reset]); const open = useOpenShortWaitFetching({ @@ -169,8 +147,20 @@ const EquipmentDeletionDialog = ({ [editDataFetchStatus, isUpdate] ); + const fetchEquipmentIds = useCallback( + (equipmentType: EquipmentType) => + fetchEquipmentsIds(studyUuid, currentNodeUuid, currentRootNetworkUuid, undefined, equipmentType, true), + [studyUuid, currentNodeUuid, currentRootNetworkUuid] + ); + + const fetchHvdcWithShuntCompensators = useCallback( + (hvdcLineId: UUID) => + fetchHvdcLineWithShuntCompensators(studyUuid, currentNodeUuid, currentRootNetworkUuid, hvdcLineId), + [studyUuid, currentNodeUuid, currentRootNetworkUuid] + ); + return ( - + - 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 deleted file mode 100644 index 5099c75157..0000000000 --- a/src/components/dialogs/network-modifications/equipment-deletion/equipment-deletion-form.tsx +++ /dev/null @@ -1,197 +0,0 @@ -/** - * 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 { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useFormContext, useWatch } from 'react-hook-form'; -import { - useSnackMessage, - AutocompleteInput, - EquipmentType, - filledTextField, - snackWithFallback, - getObjectId, - areIdsEqual, -} from '@gridsuite/commons-ui'; -import { - DELETION_SPECIFIC_DATA, - EQUIPMENT_ID, - HVDC_LINE_LCC_DELETION_SPECIFIC_TYPE, - TYPE, -} from 'components/utils/field-constants'; -import { 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 { 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 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 watchType = useWatch({ - name: TYPE, - }); - const watchEquipmentId = useWatch({ - name: EQUIPMENT_ID, - }); - const watchSpecificData = useWatch({ - name: DELETION_SPECIFIC_DATA, - }); - const { specificUpdate: hvdcLccSpecificUpdate } = useHvdcLccDeletion(); - const { setValue } = useFormContext(); - - const getOptionLabel = useGetLabelEquipmentTypes(); - - const [equipmentsOptions, setEquipmentsOptions] = useState([]); - - const typesOptions = useMemo(() => { - const equipmentTypesToExclude = new Set([ - EQUIPMENT_TYPES.SWITCH, - EQUIPMENT_TYPES.LCC_CONVERTER_STATION, - EQUIPMENT_TYPES.VSC_CONVERTER_STATION, - EQUIPMENT_TYPES.HVDC_CONVERTER_STATION, - EQUIPMENT_TYPES.BUS, - EQUIPMENT_TYPES.BUSBAR_SECTION, - EQUIPMENT_TYPES.TIE_LINE, - EQUIPMENT_TYPES.BREAKER, - EQUIPMENT_TYPES.DISCONNECTOR, - ]); - return Object.values(EQUIPMENT_TYPES).filter((equipmentType) => !equipmentTypesToExclude.has(equipmentType)); - }, []); - - useEffect(() => { - setEquipmentsOptions([]); - if (watchType) { - if (watchType !== currentTypeRef.current) { - currentTypeRef.current = watchType; - } - let ignore = false; - fetchEquipmentsIds(studyUuid, currentNode?.id, currentRootNetworkUuid, undefined, watchType, true) - .then((vals) => { - // check race condition here - if (!ignore) { - setEquipmentsOptions(vals.sort()); - } - }) - .catch((error) => { - snackWithFallback(snackError, error, { headerId: 'equipmentsLoadingError' }); - }); - return () => { - ignore = true; - }; - } - }, [studyUuid, currentNode?.id, currentRootNetworkUuid, watchType, snackError]); - - useEffect(() => { - 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 === 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, - currentNode?.id, - currentRootNetworkUuid, - watchEquipmentId, - snackError, - setValue, - hvdcLccSpecificUpdate, - editData, - ]); - - const handleChange = useCallback(() => { - setValue(EQUIPMENT_ID, null); - }, [setValue]); - - const equipmentTypeField = ( - typesOptions.find((option) => option === value) || value} - formProps={filledTextField} - /> - ); - - const equipmentField = ( - value ?? ''} - outputTransform={(value: any) => (value === '' ? null : getObjectId(value))} - size={'small'} - formProps={filledTextField} - /> - ); - - return ( - <> - - {equipmentTypeField} - {equipmentField} - - {watchSpecificData?.specificType === HVDC_LINE_LCC_DELETION_SPECIFIC_TYPE && ( - - )} - - ); -} 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 deleted file mode 100644 index 3c9e814f9e..0000000000 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-specific-form.tsx +++ /dev/null @@ -1,86 +0,0 @@ -/** - * 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 { - 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 { 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({ - name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_1}`, - }); - const { fields: mcsRows2 } = useFieldArray({ - name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_2}`, - }); - - const ShuntCompensatorSelectionForm = ({ title, arrayFormName, mcsRows }: ShuntCompensatorSelectionFormProps) => { - return ( - - -

- -

-
- {mcsRows.map((field, index) => ( - - - - - - - - - ))} -
- ); - }; - - const mcsOnsideOne = ( - - ); - - const mcsOnsideTwo = ( - - ); - - return ( - - - - {mcsOnsideOne} - {mcsOnsideTwo} - - - ); -} 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 deleted file mode 100644 index 4055b9b2f0..0000000000 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/hvdc-lcc-deletion-utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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/use-hvdc-lcc-deletion.ts b/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts deleted file mode 100644 index 9ea23d48e3..0000000000 --- a/src/components/dialogs/network-modifications/equipment-deletion/hvdc-lcc-deletion/use-hvdc-lcc-deletion.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * 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 { - DELETION_SPECIFIC_DATA, - DELETION_SPECIFIC_TYPE, - HVDC_LINE_LCC_DELETION_SPECIFIC_TYPE, - SHUNT_COMPENSATOR_SIDE_1, - SHUNT_COMPENSATOR_SIDE_2, -} from 'components/utils/field-constants'; -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({ - name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_1}`, - }); - const { replace: replaceMcsList2 } = useFieldArray({ - name: `${DELETION_SPECIFIC_DATA}.${SHUNT_COMPENSATOR_SIDE_2}`, - }); - const { setValue } = useFormContext(); - const { snackError } = useSnackMessage(); - - const updateMcsLists = useCallback( - (hvdcLineData: HvdcLccDeletionInfos, editData?: EquipmentDeletionInfos) => { - function mergeMcsLists( - dynamicList: LccShuntCompensatorConnectionInfos[], - editList: LccShuntCompensatorConnectionInfos[] - ) { - if (!dynamicList?.length && !editList?.length) { - return []; - } else if (!dynamicList?.length) { - // TODO: we should refactor modification-server to store only selected MCS - return editList.filter((item) => item.connectedToHvdc); - } 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 }; - }); - // now overwrite dynamic values with edited modification values - const dynamicIds = new Set(dynamicList.map((obj) => obj.id)); - for (let editObj of editList.values()) { - if (dynamicIds.has(editObj.id)) { - const mergedObj = mergedList.find((obj) => obj.id === editObj.id); - if (mergedObj) { - mergedObj.connectedToHvdc = editObj.connectedToHvdc; - } - } else if (editObj.connectedToHvdc) { - // if a selected edit data does not exist at this time, we add/display it anyway - mergedList.push(editObj); - } - } - return mergedList; - } - - if ( - hvdcLineData?.mcsOnSide1 || - hvdcLineData?.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?.equipmentInfos?.mcsOnSide1 ?? [])); - replaceMcsList2(mergeMcsLists(hvdcLineData?.mcsOnSide2, editData?.equipmentInfos?.mcsOnSide2 ?? [])); - } else { - setValue(DELETION_SPECIFIC_DATA, null); - } - }, - [replaceMcsList1, replaceMcsList2, setValue] - ); - - const specificUpdate = useCallback( - ( - 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: 'FetchHvdcLineWithShuntCompensatorsError' }); - }); - }, - [setValue, updateMcsLists, snackError] - ); - - return { specificUpdate }; -}; - -export default useHvdcLccDeletion; 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 a1fea8ff9f..e61a51149e 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 @@ -35,7 +35,9 @@ import BatteryCreationDialog from 'components/dialogs/network-modifications/batt import BatteryModificationDialog from 'components/dialogs/network-modifications/battery/modification/battery-modification-dialog'; import DeleteAttachingLineDialog from 'components/dialogs/network-modifications/delete-attaching-line/delete-attaching-line-dialog'; import DeleteVoltageLevelOnLineDialog from 'components/dialogs/network-modifications/delete-voltage-level-on-line/delete-voltage-level-on-line-dialog'; -import EquipmentDeletionDialog from 'components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog'; +import EquipmentDeletionDialog, { + EquipmentDeletionDtoWithId, +} from 'components/dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog'; import GenerationDispatchDialog from 'components/dialogs/network-modifications/generation-dispatch/generation-dispatch-dialog'; import GeneratorScalingDialog from 'components/dialogs/network-modifications/generator-scaling/generator-scaling-dialog'; import GeneratorCreationDialog from 'components/dialogs/network-modifications/generator/creation/generator-creation-dialog'; @@ -123,7 +125,6 @@ import MoveVoltageLevelFeederBaysDialog from '../../../dialogs/network-modificat import { useCopiedNetworkModifications } from 'hooks/copy-paste/use-copied-network-modifications'; import { DragStart, DropResult } from '@hello-pangea/dnd'; 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', @@ -244,7 +245,7 @@ const NetworkModificationNodeEditor = () => { currentNode={currentNode} studyUuid={studyUuid} currentRootNetworkUuid={currentRootNetworkUuid} - editData={editData as EquipmentDeletionInfos} + editData={editData as EquipmentDeletionDtoWithId} isUpdate={isUpdate} editDataFetchStatus={editDataFetchStatus} equipmentType={equipmentType} diff --git a/src/components/network/network-map-panel.tsx b/src/components/network/network-map-panel.tsx index a8829ad5d7..36958e4c20 100644 --- a/src/components/network/network-map-panel.tsx +++ b/src/components/network/network-map-panel.tsx @@ -39,6 +39,7 @@ import { ExtendedEquipmentType, HvdcType, type MuiStyles, + newEquipmentDeletionDto, NotificationsUrlKeys, snackWithFallback, useNotificationsListener, @@ -271,12 +272,12 @@ export const NetworkMapPanel = memo(function NetworkMapPanel({ // only hvdc line with LCC requires a Dialog (to select MCS) handleOpenDeletionDialog(equipmentId, EquipmentType.HVDC_LINE); } else { - deleteEquipment({ + deleteEquipment( studyUuid, - nodeUuid: currentNode.id, - equipmentId: equipmentId as UUID, - equipmentType, - }).catch((error) => { + currentNode.id, + undefined, + newEquipmentDeletionDto(equipmentType, equipmentId as UUID) + ).catch((error) => { snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); }); } diff --git a/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx b/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx deleted file mode 100644 index 0aeec9e17a..0000000000 --- a/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/** - * 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 { useController } from 'react-hook-form'; -import { InputAdornment, TextField, useTheme } from '@mui/material'; -import { genHelperError } from '@gridsuite/commons-ui'; -import { PropsWithChildren } from 'react'; - -interface ButtonReadOnlyInputProps extends PropsWithChildren { - name: string; - isNumerical?: boolean; -} - -export function ButtonReadOnlyInput({ name, isNumerical = false, children }: ButtonReadOnlyInputProps) { - const theme = useTheme(); - - const { - field: { value }, - fieldState: { error }, - } = useController({ name }); - - return ( - - {children} - - ), - }} - size="small" - fullWidth - value={value} - {...genHelperError(error?.message)} - /> - ); -} diff --git a/src/components/utils/rhf-inputs/read-only/read-only-input.tsx b/src/components/utils/rhf-inputs/read-only/read-only-input.tsx deleted file mode 100644 index 11d3ae41c5..0000000000 --- a/src/components/utils/rhf-inputs/read-only/read-only-input.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 { useController } from 'react-hook-form'; -import { TextField } from '@mui/material'; -import { genHelperError } from '@gridsuite/commons-ui'; - -interface ReadOnlyInputProps { - name: string; - isNumerical?: boolean; -} - -export function ReadOnlyInput({ name, isNumerical = false }: ReadOnlyInputProps) { - const { - field: { value }, - fieldState: { error }, - } = useController({ name }); - - return ( - - ); -} - -export default ReadOnlyInput; diff --git a/src/hooks/use-equipment-dialogs.tsx b/src/hooks/use-equipment-dialogs.tsx index 66e2ea6905..a96ab55cbb 100644 --- a/src/hooks/use-equipment-dialogs.tsx +++ b/src/hooks/use-equipment-dialogs.tsx @@ -10,6 +10,7 @@ import { EquipmentType, ExtendedEquipmentType, HvdcType, + newEquipmentDeletionDto, snackWithFallback, useSnackMessage, } from '@gridsuite/commons-ui'; @@ -98,13 +99,13 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork const removeEquipment = useCallback( (equipmentType: EquipmentType, equipmentId: string) => { - if (studyUuid) { - deleteEquipment({ + if (studyUuid && currentNode?.id) { + deleteEquipment( studyUuid, - nodeUuid: currentNode?.id, - equipmentId: equipmentId as UUID, - equipmentType, - }).catch((error) => { + currentNode.id, + undefined, + newEquipmentDeletionDto(equipmentType, equipmentId as UUID) + ).catch((error) => { snackWithFallback(snackError, error, { headerId: 'UnableToDeleteEquipment' }); }); } @@ -220,11 +221,11 @@ export const useEquipmentDialogs = ({ studyUuid, currentNode, currentRootNetwork studyUuid={studyUuid} currentNode={currentNode} currentRootNetworkUuid={currentRootNetworkUuid} - defaultIdValue={equipmentToDelete.equipmentId as UUID} isUpdate={true} onClose={closeDeletionDialog} editData={undefined} editDataFetchStatus={undefined} + defaultIdValue={equipmentToDelete.equipmentId as UUID} equipmentType={equipmentToDelete.equipmentType} /> ); diff --git a/src/hooks/use-get-label-equipment-types.ts b/src/hooks/use-get-label-equipment-types.ts deleted file mode 100644 index 1d7933648b..0000000000 --- a/src/hooks/use-get-label-equipment-types.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2024, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -import { useMemo } from 'react'; -import { useIntl } from 'react-intl'; -import { EQUIPMENT_TYPES } from '../components/utils/equipment-types'; -import { getIdOrValue } from '../components/dialogs/commons/utils'; -import { Option } from '@gridsuite/commons-ui'; - -export default function useGetLabelEquipmentTypes() { - const intl = useIntl(); - return useMemo( - () => (equipmentType: Option) => - intl.formatMessage({ - id: equipmentType === EQUIPMENT_TYPES.HVDC_LINE ? 'Hvdc' : getIdOrValue(equipmentType), - }), - [intl] - ); -} diff --git a/src/services/study/network-map.ts b/src/services/study/network-map.ts index 89c47e1df7..626a99e607 100644 --- a/src/services/study/network-map.ts +++ b/src/services/study/network-map.ts @@ -15,6 +15,7 @@ import { EquipmentInfos, EquipmentType, ExtendedEquipmentType, + LccDeletionDto, Identifiable, NewFilterType, } from '@gridsuite/commons-ui'; @@ -22,14 +23,13 @@ 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: UUID -): Promise { +): 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 c4aca43476..8a0452d964 100644 --- a/src/services/study/network-modifications.ts +++ b/src/services/study/network-modifications.ts @@ -9,8 +9,8 @@ import { backendFetch, backendFetchJson, backendFetchText, + EquipmentDeletionDto, EquipmentInfos, - EquipmentType, LoadCreationDto, LoadModificationDto, MODIFICATION_TYPES, @@ -69,7 +69,6 @@ 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'; @@ -1525,44 +1524,27 @@ export function deleteAttachingLine({ }); } -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) { +export function deleteEquipment( + studyUuid: UUID, + nodeUuid: UUID, + modificationUuid: UUID | undefined, + dto: EquipmentDeletionDto +) { let deleteEquipmentUrl = getNetworkModificationUrl(studyUuid, nodeUuid); - - if (uuid) { - deleteEquipmentUrl += '/' + encodeURIComponent(uuid); + if (modificationUuid) { + deleteEquipmentUrl += '/' + encodeURIComponent(modificationUuid); console.info('Updating equipment deletion'); } else { console.info('Creating equipment deletion'); } - return backendFetch(deleteEquipmentUrl, { - method: uuid ? 'PUT' : 'POST', + return backendFetchText(deleteEquipmentUrl, { + method: modificationUuid ? 'PUT' : 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, - body: JSON.stringify({ - type: MODIFICATION_TYPES.EQUIPMENT_DELETION.type, - equipmentId: equipmentId, - equipmentType: equipmentType, - equipmentInfos: equipmentSpecificInfos, - }), + body: JSON.stringify(dto), }); } diff --git a/src/translations/en.json b/src/translations/en.json index 969be33741..24bb989193 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -602,33 +602,13 @@ "warnDuplicateModificationMsg": "Number of modifications not copied :", "errDeleteModificationMsg": "Network modifications deletion error", "errRestoreModificationMsg": "Network modifications restore error", - "DeleteEquipment": "Delete equipment", - "LCCConverterStationShuntCompensators": "Shunt compensators related to the LCC", "Type": "Type", - "SUBSTATION": "Substation", "BRANCH": "Branch", - "LINE": "Line", - "TIE_LINE": "Tie line", - "SWITCH": "Switch", - "TWO_WINDINGS_TRANSFORMER": "Two Windings Transformer", - "THREE_WINDINGS_TRANSFORMER": "Three Windings Transformer", - "GENERATOR": "Generator", - "LOAD": "Load", - "BATTERY": "Battery", - "DANGLING_LINE": "Dangling Line", "Hvdc": "HVDC", - "HVDC_LINE": "HVDC Line", - "HVDC_CONVERTER_STATION": "HVDC Converter Station", - "LCC_CONVERTER_STATION": "Lcc Converter Station", - "VSC_CONVERTER_STATION": "Vsc Converter Station", "VSC": "HVDC (VSC)", "LCC": "HVDC (LCC)", - "BUS": "Bus", - "BUSBAR_SECTION": "Bus bar section", "BY_FORMULA": "Modify by formula", "BY_FILTER": "Modify by filter", - "SHUNT_COMPENSATOR": "Shunt Compensator", - "STATIC_VAR_COMPENSATOR": "Static Var Compensator", "BY_TABLE": "Modify by table", "TabularModification": "Tabular modification", "ImportModifications": "Import modifications", @@ -647,7 +627,6 @@ "voltageRegulationOn": "Voltage regulation", "ModificationsRequiredTabError": "At least one modification must be imported", - "UnableToDeleteEquipment": "Unable to delete the equipment", "LoadCreationError": "Error while creating load", "LoadModificationError": "Error while modifying load", "LineCreationError": "Error while creating line", @@ -771,8 +750,6 @@ "Characteristics": "Characteristics", "Filters": "Filters", "Side": "Side", - "Side1": "Side 1", - "Side2": "Side 2", "BothSides": "Both Sides", "SeriesResistanceText": "Series resistance", "SeriesReactanceText": "Series reactance", @@ -873,8 +850,6 @@ "VoltageLevelSectionCreationError": "Error while creating a section", "Breaker": "Breaker", "Disconnector": "Disconnector", - "BREAKER": "Breaker", - "DISCONNECTOR": "Disconnector", "SameHorizAndVertPos": "At least two bus bar sections have same horizontal and vertical position", "DuplicateId": "Duplicate id", "VoltageLevelModificationError": "Error while modifying a voltage level", diff --git a/src/translations/fr.json b/src/translations/fr.json index eb39fc506c..ced5331e58 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -598,33 +598,13 @@ "warnDuplicateModificationMsg": "Nombre de modifications non copiées :", "errDeleteModificationMsg": "Une erreur est survenue lors de la suppression des modifications", "errRestoreModificationMsg": "Une erreur est survenue lors de la restauration des modifications", - "DeleteEquipment": "Supprimer un ouvrage", - "LCCConverterStationShuntCompensators": "Moyens de compensation liés à la LCC", "Type": "Type", - "SUBSTATION": "Site", "BRANCH": "Quadripôle", - "LINE": "Ligne", - "TIE_LINE": "Ligne d'interconnexion", - "SWITCH": "disjoncteur", - "TWO_WINDINGS_TRANSFORMER": "Transformateur à 2 enroulements", - "THREE_WINDINGS_TRANSFORMER": "Transformateur à 3 enroulements", - "GENERATOR": "Groupe", - "LOAD": "Consommation", - "BATTERY": "Batterie", - "DANGLING_LINE": "Ligne frontière", "Hvdc": "HVDC", - "HVDC_LINE": "Ligne HVDC", - "HVDC_CONVERTER_STATION": "Station de conversion HVDC", - "LCC_CONVERTER_STATION": "Station de conversion LCC", - "VSC_CONVERTER_STATION": "Station de conversion VSC", "VSC": "HVDC (VSC)", "LCC": "HVDC (LCC)", - "BUS": "Noeud électrique", - "BUSBAR_SECTION": "Section de jeux de barres", "BY_FORMULA": "Modifier par formule", "BY_FILTER": "Modifier par filtre", - "SHUNT_COMPENSATOR": "MCS", - "STATIC_VAR_COMPENSATOR": "CSPR", "BY_TABLE": "Modifier par tableau", "TabularModification": "Modification tabulaire", "ImportModifications": "Importer des modifications", @@ -643,7 +623,6 @@ "voltageRegulationOn": "Réglage de tension", "ModificationsRequiredTabError": "Au moins une modification doit être importée", - "UnableToDeleteEquipment": "Impossible de supprimer l'ouvrage", "LoadCreationError": "Erreur lors de la création d'une consommation", "LoadModificationError": "Erreur lors de la modification d'une consommation", "LineCreationError": "Erreur lors de la création d'une ligne", @@ -765,8 +744,6 @@ "CreateLcc": "Création d'une HVDC (LCC)", "Characteristics": "Caractéristiques", "Filters": "Filtres", - "Side1": "Côté 1", - "Side2": "Côté 2", "BothSides": "Ouvrage", "Side": "Côté", "SeriesResistanceText": "Résistance série", @@ -865,8 +842,6 @@ "VoltageLevelSectionCreationError": "Erreur lors de la création d'une section", "Breaker": "Disjoncteur", "Disconnector": "Sectionneur", - "BREAKER": "Disjoncteur", - "DISCONNECTOR": "Sectionneur", "SameHorizAndVertPos": "Au moins deux SJBs ont les mêmes positions horizontale et verticale", "DuplicateId": "Identifiant non unique", "VoltageLevelModificationError": "Erreur lors de la modification d'un poste",