diff --git a/src/components/dialogs/connectivity/connectivity-form-utils.ts b/src/components/dialogs/connectivity/connectivity-form-utils.ts index aa6249a42a..a505c50636 100644 --- a/src/components/dialogs/connectivity/connectivity-form-utils.ts +++ b/src/components/dialogs/connectivity/connectivity-form-utils.ts @@ -176,7 +176,7 @@ export const getConnectivityData = ( }; }; -export const getConnectivityFormData = ( +export const getConnectivityFormData = ( { voltageLevelId, busbarSectionId, @@ -194,20 +194,21 @@ export const getConnectivityFormData = ( terminalConnected?: boolean | null; isEquipmentModification?: boolean; }, - id = CONNECTIVITY + id: T = CONNECTIVITY as T ) => { - return { - [id]: { - ...getConnectivityPropertiesData({ - voltageLevelId, - busbarSectionId, - }), - [CONNECTION_DIRECTION]: connectionDirection ?? null, - [CONNECTION_NAME]: connectionName ?? '', - [CONNECTION_POSITION]: connectionPosition ?? null, - [CONNECTED]: terminalConnected ?? (isEquipmentModification ? null : true), - }, + const connectivityData = { + ...getConnectivityPropertiesData({ + voltageLevelId, + busbarSectionId, + }), + [CONNECTION_DIRECTION]: connectionDirection ?? null, + [CONNECTION_NAME]: connectionName ?? '', + [CONNECTION_POSITION]: connectionPosition ?? null, + [CONNECTED]: terminalConnected ?? (isEquipmentModification ? null : true), }; + return { + [id]: connectivityData, + } as Record; }; export const createConnectivityData = (equipmentToModify: any, index: number) => ({ diff --git a/src/components/dialogs/limits/limits-pane-utils.ts b/src/components/dialogs/limits/limits-pane-utils.ts index b550923a1e..fdb0410ee0 100644 --- a/src/components/dialogs/limits/limits-pane-utils.ts +++ b/src/components/dialogs/limits/limits-pane-utils.ts @@ -133,17 +133,21 @@ function hasDuplicateOperationalLimitsGroups(context: TestContext) { return !isDuplicate; } -const limitsValidationSchemaCreation = (id: string) => { - const completeLimitsGroupSchema = { +const limitsValidationSchemaCreation = (id: T) => { + const completeLimitsGroupSchemaShape = { [OPERATIONAL_LIMITS_GROUPS]: yup.array(yup.object().shape(limitsGroupValidationSchema())).required(), [SELECTED_OPERATIONAL_LIMITS_GROUP_ID1]: yup.string().nullable(), [SELECTED_OPERATIONAL_LIMITS_GROUP_ID2]: yup.string().nullable(), [ENABLE_OLG_MODIFICATION]: yup.boolean(), }; - return { [id]: yup.object().shape(completeLimitsGroupSchema) }; + const completeLimitsGroupsSchema = yup.object().shape(completeLimitsGroupSchemaShape); + return { [id]: completeLimitsGroupsSchema } as Record< + T, + yup.ObjectSchema> + >; }; -export const getLimitsValidationSchema = (id: string = LIMITS) => { +export const getLimitsValidationSchema = (id: T = LIMITS as T) => { return limitsValidationSchemaCreation(id); }; @@ -190,21 +194,22 @@ export const formatOpLimitGroupsToFormInfos = ( }); }; -export const getAllLimitsFormData = ( +export const getAllLimitsFormData = ( operationalLimitsGroups: OperationalLimitsGroupFormSchema[] = [], selectedOperationalLimitsGroupId1: string | null = null, selectedOperationalLimitsGroupId2: string | null = null, enableOLGModification: boolean | null = true, - id = LIMITS + id: T = LIMITS as T ) => { - return { - [id]: { - [OPERATIONAL_LIMITS_GROUPS]: operationalLimitsGroups, - [SELECTED_OPERATIONAL_LIMITS_GROUP_ID1]: selectedOperationalLimitsGroupId1, - [SELECTED_OPERATIONAL_LIMITS_GROUP_ID2]: selectedOperationalLimitsGroupId2, - [ENABLE_OLG_MODIFICATION]: !!enableOLGModification, - }, + const allLimitsFormData = { + [OPERATIONAL_LIMITS_GROUPS]: operationalLimitsGroups, + [SELECTED_OPERATIONAL_LIMITS_GROUP_ID1]: selectedOperationalLimitsGroupId1, + [SELECTED_OPERATIONAL_LIMITS_GROUP_ID2]: selectedOperationalLimitsGroupId2, + [ENABLE_OLG_MODIFICATION]: !!enableOLGModification, }; + return { + [id]: allLimitsFormData, + } as Record; }; /** @@ -236,7 +241,9 @@ export const sanitizeLimitsGroups = ( }, })); -export const sanitizeLimitNames = (temporaryLimitList: TemporaryLimitFormSchema[]): TemporaryLimitFormSchema[] => +export const sanitizeLimitNames = ( + temporaryLimitList: TemporaryLimitFormSchema[] | undefined +): TemporaryLimitFormSchema[] => temporaryLimitList ?.filter((limit: TemporaryLimitFormSchema) => limit?.name?.trim()) .map(({ name, ...temporaryLimit }) => ({ diff --git a/src/components/dialogs/limits/operational-limits-groups-types.ts b/src/components/dialogs/limits/operational-limits-groups-types.ts index 3e2bbc93a1..1bcc9c84db 100644 --- a/src/components/dialogs/limits/operational-limits-groups-types.ts +++ b/src/components/dialogs/limits/operational-limits-groups-types.ts @@ -46,7 +46,7 @@ export interface OperationalLimitsGroupFormSchema { export interface CurrentLimitsFormSchema { [PERMANENT_LIMIT]: number | null; - [TEMPORARY_LIMITS]: TemporaryLimitFormSchema[]; + [TEMPORARY_LIMITS]?: TemporaryLimitFormSchema[]; } interface LimitsPropertyFormSchema { diff --git a/src/components/dialogs/line-types-catalog/line-catalog.type.ts b/src/components/dialogs/line-types-catalog/line-catalog.type.ts index 32384bc19c..2fd2a4c5cc 100644 --- a/src/components/dialogs/line-types-catalog/line-catalog.type.ts +++ b/src/components/dialogs/line-types-catalog/line-catalog.type.ts @@ -34,6 +34,7 @@ export type ComputedLineCharacteristics = { totalResistance: number; totalReactance: number; totalSusceptance: number; + finalCurrentLimits: CurrentLimitsInfo[]; }; export type CurrentLimitsInfo = { diff --git a/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-dialog.jsx b/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-dialog.jsx index 6029d847f7..41c350596b 100644 --- a/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-dialog.jsx +++ b/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-dialog.jsx @@ -208,22 +208,22 @@ const LineAttachToVoltageLevelDialog = ({ }, [reset]); const onLineCreationDo = useCallback( - (lineCreationInfo) => { + ({ lineCreationInfos }) => { return new Promise(() => { const preparedLine = { type: MODIFICATION_TYPES.LINE_CREATION.type, - equipmentId: lineCreationInfo.equipmentId, - equipmentName: lineCreationInfo.equipmentName, - r: lineCreationInfo.r, - x: lineCreationInfo.x, - g1: lineCreationInfo.g1, - b1: lineCreationInfo.b1, - g2: lineCreationInfo.g2, - b2: lineCreationInfo.b2, - operationalLimitsGroups: lineCreationInfo.operationalLimitsGroups, - selectedOperationalLimitsGroupId1: lineCreationInfo.selectedOperationalLimitsGroupId1, - selectedOperationalLimitsGroupId2: lineCreationInfo.selectedOperationalLimitsGroupId2, - properties: lineCreationInfo.properties, + equipmentId: lineCreationInfos.equipmentId, + equipmentName: lineCreationInfos.equipmentName, + r: lineCreationInfos.r, + x: lineCreationInfos.x, + g1: lineCreationInfos.g1, + b1: lineCreationInfos.b1, + g2: lineCreationInfos.g2, + b2: lineCreationInfos.b2, + operationalLimitsGroups: lineCreationInfos.operationalLimitsGroups, + selectedOperationalLimitsGroupId1: lineCreationInfos.selectedOperationalLimitsGroupId1, + selectedOperationalLimitsGroupId2: lineCreationInfos.selectedOperationalLimitsGroupId2, + properties: lineCreationInfos.properties, }; setAttachmentLine(preparedLine); setValue(`${ATTACHMENT_LINE_ID}`, preparedLine.equipmentId, { diff --git a/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.js b/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.ts similarity index 57% rename from src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.js rename to src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.ts index 639c46d96c..55fe065897 100644 --- a/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.js +++ b/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.ts @@ -6,14 +6,14 @@ */ import { + B1, + B2, CHARACTERISTICS, CONNECTIVITY_1, CONNECTIVITY_2, - R, G1, G2, - B1, - B2, + R, X, } from 'components/utils/field-constants'; import yup from 'components/utils/yup-config'; @@ -21,9 +21,16 @@ import { getConnectivityWithPositionEmptyFormData, getConnectivityWithPositionValidationSchema, } from '../../../connectivity/connectivity-form-utils'; +import { LineCharacteristics } from '../modification/line-modification-type'; +import { Connectivity } from 'components/dialogs/connectivity/connectivity.type'; +import { DeepNullable } from '../../../../utils/ts-utils'; -const characteristicsValidationSchema = (id, displayConnectivity, modification) => ({ - [id]: yup.object().shape({ +const characteristicsValidationSchema = ( + id: T, + displayConnectivity: boolean, + modification: boolean +) => { + const characteristicsSchema = yup.object().shape({ [R]: modification ? yup.number().nullable().min(0, 'mustBeGreaterOrEqualToZero') : yup.number().nullable().min(0, 'mustBeGreaterOrEqualToZero').required(), @@ -34,14 +41,21 @@ const characteristicsValidationSchema = (id, displayConnectivity, modification) [G2]: yup.number().nullable().min(0, 'mustBeGreaterOrEqualToZero'), ...(displayConnectivity && getConnectivityWithPositionValidationSchema(false, CONNECTIVITY_1)), ...(displayConnectivity && getConnectivityWithPositionValidationSchema(false, CONNECTIVITY_2)), - }), -}); + }); + return { + [id]: characteristicsSchema, + } as Record>>; +}; -export const getCharacteristicsValidationSchema = (id, displayConnectivity, modification = false) => { +export const getCharacteristicsValidationSchema = ( + id: T, + displayConnectivity: boolean, + modification: boolean = false +) => { return characteristicsValidationSchema(id, displayConnectivity, modification); }; -const characteristicsEmptyFormData = (id, displayConnectivity = true) => ({ +const characteristicsEmptyFormData = (id: string, displayConnectivity: boolean = true) => ({ [id]: { [R]: null, [X]: null, @@ -54,15 +68,33 @@ const characteristicsEmptyFormData = (id, displayConnectivity = true) => ({ }, }); -export const getCharacteristicsEmptyFormData = (id = CHARACTERISTICS, displayConnectivity = true) => { +export const getCharacteristicsEmptyFormData = (id: string = CHARACTERISTICS, displayConnectivity: boolean = true) => { return characteristicsEmptyFormData(id, displayConnectivity); }; -export const getCharacteristicsFormData = ( - { r = null, x = null, g1 = null, b1 = null, g2 = null, b2 = null, connectivity1 = null, connectivity2 = null }, - id = CHARACTERISTICS -) => ({ - [id]: { +export const getCharacteristicsFormData = ( + { + r, + x, + g1 = null, + b1 = null, + g2 = null, + b2 = null, + connectivity1 = null, + connectivity2 = null, + }: { + r: number; + x: number; + g1: number | null; + b1: number | null; + g2: number | null; + b2: number | null; + connectivity1: DeepNullable | null; + connectivity2: DeepNullable | null; + }, + id: T = CHARACTERISTICS as T +) => { + const characteristicsFormData = { [R]: r, [X]: x, [G1]: g1, @@ -71,10 +103,16 @@ export const getCharacteristicsFormData = ( [B2]: b2, [CONNECTIVITY_1]: connectivity1, [CONNECTIVITY_2]: connectivity2, - }, -}); + }; + return { + [id]: characteristicsFormData, + } as Record; +}; -export const getCharacteristicsWithOutConnectivityFormData = ({ r, x, g1, b1, g2, b2 }, id = CHARACTERISTICS) => ({ +export const getCharacteristicsWithOutConnectivityFormData = ( + { r, x, g1, b1, g2, b2 }: LineCharacteristics, + id = CHARACTERISTICS +) => ({ [id]: { [R]: r, [X]: x, diff --git a/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.jsx b/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.tsx similarity index 93% rename from src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.jsx rename to src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.tsx index b744e37b53..3ee8b049f6 100644 --- a/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.jsx +++ b/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.tsx @@ -24,6 +24,8 @@ import PropertiesForm from '../../common/properties/properties-form'; import useVoltageLevelsListInfos from '../../../../../hooks/use-voltage-levels-list-infos'; import GridSection from '../../../commons/grid-section'; import GridItem from '../../../commons/grid-item'; +import { UUID } from 'node:crypto'; +import { CurrentTreeNode } from '../../../../graph/tree-node.type'; const styles = { h3: { @@ -32,6 +34,16 @@ const styles = { }, }; +interface LineCharacteristicsPaneProps { + id?: string; + studyUuid: UUID; + currentNode: CurrentTreeNode; + currentRootNetworkUuid: UUID; + displayConnectivity: boolean; + lineToModify?: any; + clearableFields?: boolean; + isModification?: boolean; +} const LineCharacteristicsPane = ({ id = CHARACTERISTICS, studyUuid, @@ -41,7 +53,7 @@ const LineCharacteristicsPane = ({ lineToModify, clearableFields = false, isModification = false, -}) => { +}: LineCharacteristicsPaneProps) => { const currentNodeUuid = currentNode.id; const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNodeUuid, currentRootNetworkUuid); diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.js b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.ts similarity index 54% rename from src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.js rename to src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.ts index 1b9e3d15d9..4f4ac0c5a4 100644 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.js +++ b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.ts @@ -7,19 +7,22 @@ import { EQUIPMENT_ID, EQUIPMENT_NAME, TAB_HEADER } from 'components/utils/field-constants'; import yup from 'components/utils/yup-config'; +import { ObjectSchema } from 'yup'; -const headerValidationSchema = (id) => ({ - [id]: yup.object().shape({ +const headerValidationSchema = (id: T) => { + const headerSchema = yup.object().shape({ [EQUIPMENT_ID]: yup.string().required(), [EQUIPMENT_NAME]: yup.string().nullable(), - }), -}); - -export const getHeaderValidationSchema = (id = TAB_HEADER) => { + }); + return { + [id]: headerSchema, + } as Record>>; +}; +export const getHeaderValidationSchema = (id: T = TAB_HEADER as T) => { return headerValidationSchema(id); }; -const headerEmptyFormData = (id) => ({ +const headerEmptyFormData = (id: string) => ({ [id]: { [EQUIPMENT_ID]: '', [EQUIPMENT_NAME]: '', @@ -30,12 +33,18 @@ export const getHeaderEmptyFormData = (id = TAB_HEADER) => { return headerEmptyFormData(id); }; -export const getHeaderFormData = ({ equipmentId, equipmentName = '' }, id = TAB_HEADER) => ({ - [id]: { +export const getHeaderFormData = ( + { equipmentId, equipmentName = '' }: { equipmentId: string; equipmentName: string | null }, + id: T = TAB_HEADER as T +) => { + const headerFormData = { [EQUIPMENT_ID]: equipmentId, [EQUIPMENT_NAME]: equipmentName, - }, -}); + }; + return { + [id]: headerFormData, + } as Record; +}; export const LineCreationDialogTab = { CHARACTERISTICS_TAB: 0, diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.jsx b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx similarity index 64% rename from src/components/dialogs/network-modifications/line/creation/line-creation-dialog.jsx rename to src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx index b7305fede7..6bfa5cf84c 100644 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.jsx +++ b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx @@ -9,7 +9,9 @@ import { convertInputValue, convertOutputValue, CustomFormProvider, + EquipmentType, FieldType, + ModificationType, snackWithFallback, TextInput, useSnackMessage, @@ -45,9 +47,8 @@ import { X, } from 'components/utils/field-constants'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; -import PropTypes from 'prop-types'; -import { useCallback, useEffect, useState } from 'react'; -import { useForm } from 'react-hook-form'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { FieldErrors, useForm } from 'react-hook-form'; import { FetchStatus } from '../../../../../services/utils'; import { APPLICABILITY, FORM_LOADING_DELAY, UNDEFINED_CONNECTION_DIRECTION } from 'components/network/constants'; import yup from 'components/utils/yup-config'; @@ -87,15 +88,29 @@ import { } from '../../common/properties/property-utils'; import GridItem from '../../../commons/grid-item'; import { formatCompleteCurrentLimit } from '../../../../utils/utils'; -import { LimitsPane } from '../../../limits/limits-pane.tsx'; +import { LimitsPane } from '../../../limits/limits-pane'; +import { LineCreationInfos } from '../../../../../services/network-modification-types'; +import { LineModificationFormInfos } from '../modification/line-modification-type'; +import { ComputedLineCharacteristics, CurrentLimitsInfo } from '../../../line-types-catalog/line-catalog.type'; +import { DeepNullable } from '../../../../utils/ts-utils'; +import { OperationalLimitsGroupFormSchema } from '../../../limits/operational-limits-groups-types'; +import { Limit, LineCreationFormData, LineCreationFormInfos, LineFormInfos } from './line-creation-type'; +import { ObjectSchema } from 'yup'; +import { NetworkModificationDialogProps } from '../../../../graph/menus/network-modifications/network-modification-menu.type'; -const emptyFormData = { +const emptyFormData: Partial = { ...getHeaderEmptyFormData(), ...getCharacteristicsEmptyFormData(), ...getLimitsEmptyFormData(false), ...emptyProperties, }; +type LineCreationDialogProps = NetworkModificationDialogProps & { + editData?: LineCreationInfos; // contains data when we try to edit an existing hypothesis + onCreateLine: typeof createLine; + displayConnectivity?: boolean; +}; + /** * Dialog to create a line in the network * @param studyUuid the study we are currently working on @@ -108,7 +123,7 @@ const emptyFormData = { * @param dialogProps props that are forwarded to the generic ModificationDialog component * @param editDataFetchStatus indicates the status of fetching EditData */ -const LineCreationDialog = ({ +export default function LineCreationDialog({ editData, studyUuid, currentNode, @@ -118,12 +133,12 @@ const LineCreationDialog = ({ isUpdate, editDataFetchStatus, ...dialogProps -}) => { +}: Readonly) { const currentNodeUuid = currentNode?.id; const { snackError } = useSnackMessage(); const [tabIndex, setTabIndex] = useState(LineCreationDialogTab.CHARACTERISTICS_TAB); - const [tabIndexesWithError, setTabIndexesWithError] = useState([]); + const [tabIndexesWithError, setTabIndexesWithError] = useState([]); const [isOpenLineTypesCatalogDialog, setIsOpenLineTypesCatalogDialog] = useState(false); @@ -131,117 +146,134 @@ const LineCreationDialog = ({ setIsOpenLineTypesCatalogDialog(false); }; - const formSchema = yup - .object() - .shape({ - ...getHeaderValidationSchema(), - ...getCharacteristicsValidationSchema(CHARACTERISTICS, displayConnectivity), - ...getLimitsValidationSchema(), - }) - .concat(creationPropertiesSchema) - .required(); + const formSchema: ObjectSchema> = useMemo( + () => + yup + .object() + .shape({ + ...getHeaderValidationSchema(), + ...getCharacteristicsValidationSchema(CHARACTERISTICS, displayConnectivity), + ...getLimitsValidationSchema(), + }) + .concat(creationPropertiesSchema) + .required(), + [displayConnectivity] + ); - const formMethods = useForm({ + const formMethods = useForm>({ defaultValues: emptyFormData, - resolver: yupResolver(formSchema), + resolver: yupResolver>(formSchema), }); - const { reset, setValue } = formMethods; - const fromSearchCopyToFormValues = (line) => { - reset( - { - ...getHeaderFormData({ - equipmentId: line.id + '(1)', - equipmentName: line.name ?? '', - }), - ...getCharacteristicsFormData({ - r: line.r, - x: line.x, - g1: convertInputValue(FieldType.G1, line.g1), // this form uses and displays microSiemens - b1: convertInputValue(FieldType.B1, line.b1), - g2: convertInputValue(FieldType.G2, line.g2), - b2: convertInputValue(FieldType.B2, line.b2), - ...(displayConnectivity && - getConnectivityFormData( - { - voltageLevelId: line.voltageLevelId1, - busbarSectionId: line.busOrBusbarSectionId1, - connectionDirection: line.connectablePosition1.connectionDirection, - connectionName: line.connectablePosition1.connectionName, - }, - CONNECTIVITY_1 - )), - ...(displayConnectivity && - getConnectivityFormData( - { - voltageLevelId: line.voltageLevelId2, - busbarSectionId: line.busOrBusbarSectionId2, - connectionDirection: line.connectablePosition2.connectionDirection, - connectionName: line.connectablePosition2.connectionName, - }, - CONNECTIVITY_2 - )), - }), - ...getAllLimitsFormData( - formatCompleteCurrentLimit(line.currentLimits), - line.selectedOperationalLimitsGroupId1 ?? null, - line.selectedOperationalLimitsGroupId2 ?? null - ), - ...copyEquipmentPropertiesForCreation(line), - }, - { keepDefaultValues: true } + const fromSearchCopyToFormValues = (line: LineFormInfos) => { + const headerFormData = getHeaderFormData({ + equipmentId: line.id + '(1)', + equipmentName: line.name ?? '', + }); + const characteristicsFormData = getCharacteristicsFormData({ + r: line.r, + x: line.x, + g1: convertInputValue(FieldType.G1, line.g1), // this form uses and displays microSiemens + b1: convertInputValue(FieldType.B1, line.b1), + g2: convertInputValue(FieldType.G2, line.g2), + b2: convertInputValue(FieldType.B2, line.b2), + ...getConnectivityFormData( + { + voltageLevelId: line.voltageLevelId1, + busbarSectionId: line.busOrBusbarSectionId1, + connectionDirection: line.connectablePosition1.connectionDirection, + connectionName: line.connectablePosition1.connectionName, + connectionPosition: line.connectablePosition1.connectionPosition, + terminalConnected: line.terminal1Connected, + }, + CONNECTIVITY_1 + ), + ...getConnectivityFormData( + { + voltageLevelId: line.voltageLevelId2, + busbarSectionId: line.busOrBusbarSectionId2, + connectionDirection: line.connectablePosition2.connectionDirection, + connectionName: line.connectablePosition2.connectionName, + connectionPosition: line.connectablePosition2.connectionPosition, + terminalConnected: line.terminal2Connected, + }, + CONNECTIVITY_2 + ), + }); + const allLimitsFormData = getAllLimitsFormData( + formatCompleteCurrentLimit(line.currentLimits), + line.selectedOperationalLimitsGroupId1 ?? null, + line.selectedOperationalLimitsGroupId2 ?? null ); + const properties = copyEquipmentPropertiesForCreation(line); + + const formData = { + ...headerFormData, + ...characteristicsFormData, + ...allLimitsFormData, + ...properties, + } satisfies DeepNullable; + + reset(formData, { keepDefaultValues: true }); }; const fromEditDataToFormValues = useCallback( - (line) => { - reset({ - ...getHeaderFormData({ - equipmentId: line.equipmentId, - equipmentName: line.equipmentName, - }), - ...getCharacteristicsFormData({ - r: line.r, - x: line.x, - g1: convertInputValue(FieldType.G1, line.g1), - b1: convertInputValue(FieldType.B1, line.b1), - g2: convertInputValue(FieldType.G2, line.g2), - b2: convertInputValue(FieldType.B2, line.b2), - ...getConnectivityFormData( - { - busbarSectionId: line.busOrBusbarSectionId1, - connectionDirection: line.connectionDirection1, - connectionName: line.connectionName1, - connectionPosition: line.connectionPosition1, - voltageLevelId: line.voltageLevelId1, - terminalConnected: line.connected1, - }, - CONNECTIVITY_1 - ), - ...getConnectivityFormData( - { - busbarSectionId: line.busOrBusbarSectionId2, - connectionDirection: line.connectionDirection2, - connectionName: line.connectionName2, - connectionPosition: line.connectionPosition2, - voltageLevelId: line.voltageLevelId2, - terminalConnected: line.connected2, - }, - CONNECTIVITY_2 - ), - }), - ...getAllLimitsFormData( - line?.operationalLimitsGroups?.map(({ id, ...baseData }) => ({ - ...baseData, - name: id, - id: id + baseData.applicability, - })), - line?.selectedOperationalLimitsGroupId1 ?? null, - line?.selectedOperationalLimitsGroupId2 ?? null + (line: LineCreationInfos) => { + const headerFormData = getHeaderFormData({ + equipmentId: line.equipmentId, + equipmentName: line.equipmentName, + }); + const characteristicsFormData = getCharacteristicsFormData({ + r: line.r, + x: line.x, + g1: convertInputValue(FieldType.G1, line.g1), + b1: convertInputValue(FieldType.B1, line.b1), + g2: convertInputValue(FieldType.G2, line.g2), + b2: convertInputValue(FieldType.B2, line.b2), + ...getConnectivityFormData( + { + busbarSectionId: line.busOrBusbarSectionId1, + connectionDirection: line.connectionDirection1, + connectionName: line.connectionName1, + connectionPosition: line.connectionPosition1, + voltageLevelId: line.voltageLevelId1, + terminalConnected: line.connected1, + }, + CONNECTIVITY_1 + ), + ...getConnectivityFormData( + { + busbarSectionId: line.busOrBusbarSectionId2, + connectionDirection: line.connectionDirection2, + connectionName: line.connectionName2, + connectionPosition: line.connectionPosition2, + voltageLevelId: line.voltageLevelId2, + terminalConnected: line.connected2, + }, + CONNECTIVITY_2 ), - ...getPropertiesFromModification(line.properties), }); + const allLimitsFormData = getAllLimitsFormData( + line?.operationalLimitsGroups?.map( + ({ id, ...baseData }) => + ({ + ...baseData, + name: id, + id: id + baseData.applicability, + }) satisfies OperationalLimitsGroupFormSchema + ), + line?.selectedOperationalLimitsGroupId1 ?? null, + line?.selectedOperationalLimitsGroupId2 ?? null + ); + const formData = { + ...headerFormData, + ...characteristicsFormData, + ...allLimitsFormData, + ...getPropertiesFromModification(line.properties), + } satisfies DeepNullable; + + reset(formData, { keepDefaultValues: true }); }, [reset] ); @@ -254,7 +286,7 @@ const LineCreationDialog = ({ } }, [fromEditDataToFormValues, editData]); - const handleLineSegmentsBuildSubmit = (data) => { + const handleLineSegmentsBuildSubmit = (data: ComputedLineCharacteristics) => { setValue(`${CHARACTERISTICS}.${R}`, data[TOTAL_RESISTANCE], { shouldDirty: true, }); @@ -267,8 +299,8 @@ const LineCreationDialog = ({ setValue(`${CHARACTERISTICS}.${B2}`, data[TOTAL_SUSCEPTANCE] / 2, { shouldDirty: true, }); - const finalLimits = []; - data[FINAL_CURRENT_LIMITS].forEach((item) => { + const finalLimits: Limit[] = []; + data[FINAL_CURRENT_LIMITS].forEach((item: CurrentLimitsInfo) => { const temporaryLimitsList = []; if (item.temporaryLimitValue) { temporaryLimitsList.push({ @@ -292,13 +324,12 @@ const LineCreationDialog = ({ }; const onSubmit = useCallback( - (line) => { + (line: LineCreationFormInfos) => { const header = line[TAB_HEADER]; const characteristics = line[CHARACTERISTICS]; const limits = line[LIMITS]; - onCreateLine({ - studyUuid: studyUuid, - nodeUuid: currentNodeUuid, + const lineCreationInfos: LineCreationInfos = { + type: ModificationType.LINE_CREATION, equipmentId: header[EQUIPMENT_ID], equipmentName: sanitizeString(header[EQUIPMENT_NAME]), r: characteristics[R], @@ -314,7 +345,6 @@ const LineCreationDialog = ({ operationalLimitsGroups: sanitizeLimitsGroups(limits[OPERATIONAL_LIMITS_GROUPS]), selectedOperationalLimitsGroupId1: limits[SELECTED_OPERATIONAL_LIMITS_GROUP_ID1], selectedOperationalLimitsGroupId2: limits[SELECTED_OPERATIONAL_LIMITS_GROUP_ID2], - isUpdate: !!editData, modificationUuid: editData ? editData.uuid : undefined, connectionName1: sanitizeString(characteristics[CONNECTIVITY_1]?.[CONNECTION_NAME]), connectionDirection1: @@ -327,6 +357,14 @@ const LineCreationDialog = ({ connected1: characteristics[CONNECTIVITY_1]?.[CONNECTED] ?? null, connected2: characteristics[CONNECTIVITY_2]?.[CONNECTED] ?? null, properties: toModificationProperties(line), + } satisfies LineCreationInfos; + + onCreateLine({ + lineCreationInfos, + studyUuid, + nodeUuid: currentNodeUuid, + modificationUuid: editData ? editData.uuid : undefined, + isUpdate: !!editData, }).catch((error) => { snackWithFallback(snackError, error, { headerId: 'LineCreationError' }); }); @@ -334,7 +372,7 @@ const LineCreationDialog = ({ [editData, studyUuid, currentNodeUuid, snackError, onCreateLine] ); - const onValidationError = (errors) => { + const onValidationError = (errors: FieldErrors) => { let tabsInError = []; if (errors?.[CHARACTERISTICS] !== undefined) { tabsInError.push(LineCreationDialogTab.CHARACTERISTICS_TAB); @@ -429,7 +467,7 @@ const LineCreationDialog = ({ ); -}; - -LineCreationDialog.propTypes = { - editData: PropTypes.object, - studyUuid: PropTypes.string, - currentNode: PropTypes.object, - currentRootNetworkUuid: PropTypes.string, - isUpdate: PropTypes.bool, - editDataFetchStatus: PropTypes.string, -}; - -export default LineCreationDialog; +} diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-type.ts b/src/components/dialogs/network-modifications/line/creation/line-creation-type.ts new file mode 100644 index 0000000000..249e42079e --- /dev/null +++ b/src/components/dialogs/network-modifications/line/creation/line-creation-type.ts @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { OperationalLimitsGroup } from '../../../../../services/network-modification-types'; +import { Property } from '../../common/properties/property-utils'; +import { + ADDITIONAL_PROPERTIES, + CHARACTERISTICS, + CONNECTIVITY_1, + CONNECTIVITY_2, + LIMITS, + OPERATIONAL_LIMITS_GROUPS, + SELECTED_OPERATIONAL_LIMITS_GROUP_ID1, + SELECTED_OPERATIONAL_LIMITS_GROUP_ID2, + TAB_HEADER, +} from 'components/utils/field-constants'; +import { Connectivity } from 'components/dialogs/connectivity/connectivity.type'; +import { CurrentLimitsData } from '../../../../../services/study/network-map.type'; +import { LineModificationFormInfos } from '../modification/line-modification-type'; +import { OperationalLimitsGroupFormSchema } from '../../../limits/operational-limits-groups-types'; + +export interface LineCreationFormData { + [TAB_HEADER]: { + equipmentId: string; + equipmentName?: string | null; + }; + [CHARACTERISTICS]: { + r?: number | null; + x?: number | null; + b1?: number | null; + g1?: number | null; + b2?: number | null; + g2?: number | null; + [CONNECTIVITY_1]?: Connectivity; + [CONNECTIVITY_2]?: Connectivity; + }; + [LIMITS]: { + [OPERATIONAL_LIMITS_GROUPS]?: OperationalLimitsGroupFormSchema[]; + [SELECTED_OPERATIONAL_LIMITS_GROUP_ID1]?: string | null; + [SELECTED_OPERATIONAL_LIMITS_GROUP_ID2]?: string | null; + }; + [ADDITIONAL_PROPERTIES]?: Property[]; + [key: string]: any; +} + +export interface ConnectablePosition { + connectionName: string | null; + connectionDirection: string | null; + connectionPosition: number | null; +} + +export interface Limit { + id: string; + name: string; + applicability: string; + currentLimits: { + id: string; + permanentLimit: number; + temporaryLimits: { + name: string; + acceptableDuration: number; + value: number; + }[]; + }; +} + +export interface LineFormInfos { + id: string; + name: string | null; + voltageLevelId1: string; + voltageLevelId2: string; + terminal1Connected: boolean; + terminal2Connected: boolean; + p1: number; + q1: number; + p2: number; + q2: number; + i1: number; + i2: number; + r: number; + x: number; + g1?: number; + b1?: number; + g2?: number; + b2?: number; + busOrBusbarSectionId1: string; + busOrBusbarSectionId2: string; + selectedOperationalLimitsGroupId1: string; + selectedOperationalLimitsGroupId2: string; + connectablePosition1: ConnectablePosition; + connectablePosition2: ConnectablePosition; + currentLimits: CurrentLimitsData[]; + properties: Record; +} + +export interface LineCreationFormInfos extends LineModificationFormInfos { + tabHeader: { + equipmentId: string; + equipmentName?: string; + }; +} diff --git a/src/components/dialogs/network-modifications/line/line-dialog-tabs.jsx b/src/components/dialogs/network-modifications/line/line-dialog-tabs.tsx similarity index 65% rename from src/components/dialogs/network-modifications/line/line-dialog-tabs.jsx rename to src/components/dialogs/network-modifications/line/line-dialog-tabs.tsx index b90b368211..4b64479b65 100644 --- a/src/components/dialogs/network-modifications/line/line-dialog-tabs.jsx +++ b/src/components/dialogs/network-modifications/line/line-dialog-tabs.tsx @@ -11,8 +11,19 @@ import { getTabIndicatorStyle, getTabStyle } from '../../../utils/tab-utils'; import { LineCreationDialogTab } from './creation/line-creation-dialog-utils'; import { LineModificationDialogTab } from './line-utils'; -const LineDialogTabs = ({ tabIndex, tabIndexesWithError, setTabIndex, isModification = false }) => { - const LineDialogTab = isModification ? LineModificationDialogTab : LineCreationDialogTab; +interface LineDialogTabsProps { + tabIndex: number; + tabIndexesWithError: number[]; + setTabIndex: (index: number) => void; + isModification?: boolean; +} + +const LineDialogTabs = ({ + tabIndex, + tabIndexesWithError, + setTabIndex, + isModification = false, +}: LineDialogTabsProps) => { return ( } - sx={getTabStyle(tabIndexesWithError, LineDialogTab.CONNECTIVITY_TAB)} + sx={getTabStyle(tabIndexesWithError, LineModificationDialogTab.CONNECTIVITY_TAB)} /> )} } - sx={getTabStyle(tabIndexesWithError, LineDialogTab.CHARACTERISTICS_TAB)} + sx={getTabStyle( + tabIndexesWithError, + isModification + ? LineModificationDialogTab.CHARACTERISTICS_TAB + : LineCreationDialogTab.CHARACTERISTICS_TAB + )} /> } - sx={getTabStyle(tabIndexesWithError, LineDialogTab.LIMITS_TAB)} + sx={getTabStyle( + tabIndexesWithError, + isModification ? LineModificationDialogTab.LIMITS_TAB : LineCreationDialogTab.LIMITS_TAB + )} /> {isModification && ( } - sx={getTabStyle(tabIndexesWithError, LineDialogTab.STATE_ESTIMATION_TAB)} + sx={getTabStyle(tabIndexesWithError, LineModificationDialogTab.STATE_ESTIMATION_TAB)} /> )} diff --git a/src/components/dialogs/network-modifications/line/modification/line-modification-dialog.tsx b/src/components/dialogs/network-modifications/line/modification/line-modification-dialog.tsx index 5769f4f545..e20a29aeca 100644 --- a/src/components/dialogs/network-modifications/line/modification/line-modification-dialog.tsx +++ b/src/components/dialogs/network-modifications/line/modification/line-modification-dialog.tsx @@ -109,6 +109,7 @@ import { LineModificationInfos } from '../../../../../services/network-modificat import { toModificationOperation } from '../../../../utils/utils'; import { useFormWithDirtyTracking } from 'components/dialogs/commons/use-form-with-dirty-tracking'; import { OperationalLimitsGroupsFormSchema } from '../../../limits/operational-limits-groups-types'; +import { ComputedLineCharacteristics } from '../../../line-types-catalog/line-catalog.type'; export interface LineModificationDialogProps { // contains data when we try to edit an existing hypothesis from the current node's list @@ -392,7 +393,7 @@ const LineModificationDialog = ({ setIsOpenLineTypesCatalogDialog(false); }; - const handleLineSegmentsBuildSubmit = (data: any) => { + const handleLineSegmentsBuildSubmit = (data: ComputedLineCharacteristics) => { setValue(`${CHARACTERISTICS}.${R}`, data[TOTAL_RESISTANCE], { shouldDirty: true, }); diff --git a/src/components/dialogs/network-modifications/line/modification/line-modification-type.ts b/src/components/dialogs/network-modifications/line/modification/line-modification-type.ts index 95e2a7c69f..8baf4eaa81 100644 --- a/src/components/dialogs/network-modifications/line/modification/line-modification-type.ts +++ b/src/components/dialogs/network-modifications/line/modification/line-modification-type.ts @@ -10,6 +10,15 @@ import type { UUID } from 'node:crypto'; import { Property } from '../../common/properties/property-utils'; import { OperationalLimitsGroupsFormSchema } from '../../../limits/operational-limits-groups-types'; +export interface LineCharacteristics { + r: number | null; + x: number | null; + g1: number | null; + b1: number | null; + g2: number | null; + b2: number | null; +} + export interface LineModificationFormInfos { equipmentId?: string; equipmentName?: string; @@ -18,12 +27,12 @@ export interface LineModificationFormInfos { modificationUuid: string; lineId: string; lineName: string | null; - r: number; - x: number; - g1: number; - b1: number; - g2: number; - b2: number; + r: number | null; + x: number | null; + g1: number | null; + b1: number | null; + g2: number | null; + b2: number | null; operationalLimitsGroups: OperationalLimitsGroup[]; selectedOperationalLimitsGroupId1: string | null; selectedOperationalLimitsGroupId2: string | null; diff --git a/src/services/network-modification-types.ts b/src/services/network-modification-types.ts index d20f548932..4bf9868655 100644 --- a/src/services/network-modification-types.ts +++ b/src/services/network-modification-types.ts @@ -17,6 +17,7 @@ import { ReactiveCapabilityCurvePoints } from '../components/dialogs/reactive-li import { ModificationType } from '@gridsuite/commons-ui'; import { ENABLE_OLG_MODIFICATION } from '../components/utils/field-constants'; import { VARIATION_TYPES } from '../components/network/constants'; +import { OperationalLimitsGroupFormSchema } from '../components/dialogs/limits/operational-limits-groups-types'; export enum OperationType { SET = 'SET', @@ -532,9 +533,9 @@ export interface ShuntCompensatorCreationInfo { properties: Property[] | null; } -export interface LineCreationInfo { - studyUuid: string; - nodeUuid: UUID; +export interface LineCreationInfos { + type: ModificationType; + uuid?: string | null; equipmentId: string; equipmentName: string | null; r: number; @@ -547,17 +548,16 @@ export interface LineCreationInfo { busOrBusbarSectionId1: string; voltageLevelId2: string; busOrBusbarSectionId2: string; - operationalLimitsGroups: OperationalLimitsGroup[]; - selectedOperationalLimitsGroupId1: string; - selectedOperationalLimitsGroupId2: string; - isUpdate: boolean; - modificationUuid: string; + operationalLimitsGroups: OperationalLimitsGroupFormSchema[]; + selectedOperationalLimitsGroupId1?: string; + selectedOperationalLimitsGroupId2?: string; + modificationUuid?: string | null; connectionName1: string | null; connectionDirection1: string | null; connectionName2: string | null; connectionDirection2: string | null; - connectionPosition1: string | null; - connectionPosition2: string | null; + connectionPosition1: number | null; + connectionPosition2: number | null; connected1: boolean; connected2: boolean; properties: Property[] | null; diff --git a/src/services/study/network-modifications.ts b/src/services/study/network-modifications.ts index 52a87af87f..df5ac2d86e 100644 --- a/src/services/study/network-modifications.ts +++ b/src/services/study/network-modifications.ts @@ -36,7 +36,7 @@ import { GeneratorModificationInfos, LCCCreationInfo, LccModificationInfos, - LineCreationInfo, + LineCreationInfos, LineModificationInfos, LinesAttachToSplitLinesInfo, LoadCreationInfo, @@ -760,38 +760,21 @@ export function createStaticVarCompensator(staticVarCompensatorCreationParameter } export function createLine({ + lineCreationInfos, studyUuid, nodeUuid, - equipmentId, - equipmentName, - r, - x, - g1, - b1, - g2, - b2, - voltageLevelId1, - busOrBusbarSectionId1, - voltageLevelId2, - busOrBusbarSectionId2, - operationalLimitsGroups, - selectedOperationalLimitsGroupId1, - selectedOperationalLimitsGroupId2, - isUpdate = false, modificationUuid, - connectionName1, - connectionDirection1, - connectionName2, - connectionDirection2, - connectionPosition1, - connectionPosition2, - connected1, - connected2, - properties, -}: LineCreationInfo) { + isUpdate = false, +}: { + lineCreationInfos: LineCreationInfos; + studyUuid: UUID; + nodeUuid: UUID; + modificationUuid?: string | null; + isUpdate: boolean; +}) { let createLineUrl = getNetworkModificationUrl(studyUuid, nodeUuid); - if (isUpdate) { + if (modificationUuid) { createLineUrl += '/' + encodeURIComponent(modificationUuid); console.info('Updating line creation'); } else { @@ -804,33 +787,7 @@ export function createLine({ Accept: 'application/json', 'Content-Type': 'application/json', }, - body: JSON.stringify({ - type: MODIFICATION_TYPES.LINE_CREATION.type, - equipmentId: equipmentId, - equipmentName: equipmentName, - r: r, - x: x, - g1: g1, - b1: b1, - g2: g2, - b2: b2, - voltageLevelId1: voltageLevelId1, - busOrBusbarSectionId1: busOrBusbarSectionId1, - voltageLevelId2: voltageLevelId2, - busOrBusbarSectionId2: busOrBusbarSectionId2, - operationalLimitsGroups: operationalLimitsGroups, - selectedOperationalLimitsGroupId1: selectedOperationalLimitsGroupId1, - selectedOperationalLimitsGroupId2: selectedOperationalLimitsGroupId2, - connectionName1: connectionName1, - connectionDirection1: connectionDirection1, - connectionName2: connectionName2, - connectionDirection2: connectionDirection2, - connectionPosition1: connectionPosition1, - connectionPosition2: connectionPosition2, - connected1: connected1, - connected2: connected2, - properties, - }), + body: JSON.stringify(lineCreationInfos), }); }