From 20329a4080ea83ed6b37a82630b450ea42c95f93 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Mon, 22 Jun 2026 14:58:43 +0200 Subject: [PATCH 01/11] wip Signed-off-by: David BRAQUART --- .../dialogs/limits/limits-constants.ts | 20 - .../limits/limits-groups-contextual-menu.tsx | 144 ------- .../dialogs/limits/limits-pane-utils.ts | 373 ------------------ src/components/dialogs/limits/limits-pane.tsx | 233 ----------- .../limits/limits-properties-side-stack.tsx | 144 ------- .../limits/limits-properties-stack.tsx | 41 -- .../dialogs/limits/limits-side-pane.tsx | 276 ------------- .../dialogs/limits/limits-tag-chip.tsx | 34 -- src/components/dialogs/limits/limitsChart.tsx | 262 ------------ .../operational-limits-group-tab-label.tsx | 103 ----- .../operational-limits-groups-styles.ts | 52 --- .../limits/operational-limits-groups-tabs.tsx | 146 ------- .../limits/operational-limits-groups-types.ts | 62 --- .../limits/operational-limits-groups-utils.ts | 61 --- .../selected-operational-limit-group.tsx | 71 ---- .../dialogs/limits/temporary-limits-table.tsx | 173 -------- .../line-types-catalog/segment-utils.ts | 11 +- .../limit-sets-modification-dialog.tsx | 9 +- .../limit-sets-tabular-modification-utils.ts | 3 +- .../line-attach-to-voltage-level-dialog.tsx | 14 +- .../line-attach-to-voltage-level-form.tsx | 6 +- .../line-characteristics-pane-utils.ts | 108 ----- .../line-characteristics-pane.tsx | 193 --------- .../creation/line-creation-dialog-utils.ts | 46 --- .../line/creation/line-creation-dialog.tsx | 329 ++++----------- .../line/creation/line-creation-type.ts | 72 ---- .../line/line-dialog-tabs.tsx | 70 ---- .../line/modification/line-dialog-tabs.tsx | 51 +++ .../line-modification-dialog-header.tsx | 12 +- .../line-modification-dialog-tabs.tsx | 25 +- .../modification/line-modification-dialog.tsx | 67 ++-- .../modification/line-modification-type.ts | 13 +- .../line/{ => modification}/line-utils.ts | 0 .../tabular/tabular-modification-utils.ts | 6 +- ...nsformer-creation-characteristics-pane.tsx | 3 +- ...o-windings-transformer-creation-dialog.tsx | 40 +- ...ndings-transformer-modification-dialog.tsx | 53 ++- .../two-windings-transformer.types.ts | 4 +- src/components/network/constants.ts | 6 - .../tooltips/branch/branch-utils.tsx | 2 +- .../tooltips/equipment-popover-type.ts | 3 +- src/components/utils/field-constants.ts | 18 +- src/components/utils/utils.ts | 44 +-- src/services/network-modification-types.ts | 106 +---- src/services/study/network-map.type.ts | 34 +- src/services/study/network-modifications.ts | 11 +- tsconfig.json | 4 + vite.config.ts | 31 +- 48 files changed, 275 insertions(+), 3314 deletions(-) delete mode 100644 src/components/dialogs/limits/limits-constants.ts delete mode 100644 src/components/dialogs/limits/limits-groups-contextual-menu.tsx delete mode 100644 src/components/dialogs/limits/limits-pane-utils.ts delete mode 100644 src/components/dialogs/limits/limits-pane.tsx delete mode 100644 src/components/dialogs/limits/limits-properties-side-stack.tsx delete mode 100644 src/components/dialogs/limits/limits-properties-stack.tsx delete mode 100644 src/components/dialogs/limits/limits-side-pane.tsx delete mode 100644 src/components/dialogs/limits/limits-tag-chip.tsx delete mode 100644 src/components/dialogs/limits/limitsChart.tsx delete mode 100644 src/components/dialogs/limits/operational-limits-group-tab-label.tsx delete mode 100644 src/components/dialogs/limits/operational-limits-groups-styles.ts delete mode 100644 src/components/dialogs/limits/operational-limits-groups-tabs.tsx delete mode 100644 src/components/dialogs/limits/operational-limits-groups-types.ts delete mode 100644 src/components/dialogs/limits/operational-limits-groups-utils.ts delete mode 100644 src/components/dialogs/limits/selected-operational-limit-group.tsx delete mode 100644 src/components/dialogs/limits/temporary-limits-table.tsx delete mode 100644 src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.ts delete mode 100644 src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.tsx delete mode 100644 src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.ts delete mode 100644 src/components/dialogs/network-modifications/line/creation/line-creation-type.ts delete mode 100644 src/components/dialogs/network-modifications/line/line-dialog-tabs.tsx create mode 100644 src/components/dialogs/network-modifications/line/modification/line-dialog-tabs.tsx rename src/components/dialogs/network-modifications/line/{ => modification}/line-utils.ts (100%) diff --git a/src/components/dialogs/limits/limits-constants.ts b/src/components/dialogs/limits/limits-constants.ts deleted file mode 100644 index 16dd921df9..0000000000 --- a/src/components/dialogs/limits/limits-constants.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -export enum LimitsPropertyName { - LIMITS_TYPE = 'Limit type', -} - -export function getPropertyAvatar(type: string): string { - const transformedType: LimitsPropertyName = type as LimitsPropertyName; - - const descriptions: Record = { - [LimitsPropertyName.LIMITS_TYPE]: 'Ty', - }; - - return descriptions[transformedType] ?? transformedType.substring(0, 2); -} diff --git a/src/components/dialogs/limits/limits-groups-contextual-menu.tsx b/src/components/dialogs/limits/limits-groups-contextual-menu.tsx deleted file mode 100644 index 683914713d..0000000000 --- a/src/components/dialogs/limits/limits-groups-contextual-menu.tsx +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { - ID, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID1, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID2, -} from '../../utils/field-constants'; -import { FieldValues, UseFieldArrayAppend, UseFieldArrayRemove, useFormContext } from 'react-hook-form'; -import Menu from '@mui/material/Menu'; -import MenuItem from '@mui/material/MenuItem'; -import ListItemIcon from '@mui/material/ListItemIcon'; -import { ContentCopy, Delete } from '@mui/icons-material'; -import ListItemText from '@mui/material/ListItemText'; -import { useIntl } from 'react-intl'; -import { APPLICABILITY } from '../../network/constants'; -import { useCallback } from 'react'; -import { CurrentLimitsData } from '../../../services/study/network-map.type'; -import { OperationalLimitsGroupFormSchema } from './operational-limits-groups-types'; - -export interface ContextMenuCoordinates { - x: null | number; - y: null | number; - tabIndex: null | number; -} - -export interface LimitsGroupsContextualMenuProps { - parentFormName: string; - indexSelectedLimitSet: number | null; - setIndexSelectedLimitSet: React.Dispatch>; - handleCloseMenu: () => void; - contextMenuCoordinates: ContextMenuCoordinates; - selectedLimitsGroups1: string; - selectedLimitsGroups2: string; - currentLimitsToModify: CurrentLimitsData[]; - operationalLimitsGroups: FieldValues; - appendToLimitsGroups: UseFieldArrayAppend< - { - [p: string]: OperationalLimitsGroupFormSchema[]; - }, - string - >; - removeLimitsGroups: UseFieldArrayRemove; -} - -export function LimitsGroupsContextualMenu({ - parentFormName, - indexSelectedLimitSet, - setIndexSelectedLimitSet, - handleCloseMenu, - contextMenuCoordinates, - selectedLimitsGroups1, - selectedLimitsGroups2, - operationalLimitsGroups, - appendToLimitsGroups, - removeLimitsGroups, -}: Readonly) { - const intl = useIntl(); - const { setValue } = useFormContext(); - - const handleDeleteTab = useCallback(() => { - if (indexSelectedLimitSet !== null) { - const tabName: string = operationalLimitsGroups[indexSelectedLimitSet]?.name; - const applicability: string = operationalLimitsGroups[indexSelectedLimitSet]?.applicability ?? ''; - // if this operational limit was selected, deselect it - if ( - selectedLimitsGroups1 === tabName && - (applicability === APPLICABILITY.SIDE1.id || applicability === APPLICABILITY.EQUIPMENT.id) - ) { - setValue(`${parentFormName}.${SELECTED_OPERATIONAL_LIMITS_GROUP_ID1}`, null); - } - if ( - selectedLimitsGroups2 === tabName && - (applicability === APPLICABILITY.SIDE2.id || applicability === APPLICABILITY.EQUIPMENT.id) - ) { - setValue(`${parentFormName}.${SELECTED_OPERATIONAL_LIMITS_GROUP_ID2}`, null); - } - removeLimitsGroups(indexSelectedLimitSet); - setIndexSelectedLimitSet(null); - } - handleCloseMenu(); - }, [ - handleCloseMenu, - indexSelectedLimitSet, - operationalLimitsGroups, - parentFormName, - removeLimitsGroups, - selectedLimitsGroups1, - selectedLimitsGroups2, - setIndexSelectedLimitSet, - setValue, - ]); - - const handleDuplicateTab = useCallback(() => { - let newName: string = ''; - if (indexSelectedLimitSet !== null) { - const duplicatedLimits1: OperationalLimitsGroupFormSchema = operationalLimitsGroups[indexSelectedLimitSet]; - newName = duplicatedLimits1.name + '_COPY'; - const newLimitsGroup1: OperationalLimitsGroupFormSchema = { - ...duplicatedLimits1, - [ID]: newName, - }; - appendToLimitsGroups(newLimitsGroup1); - handleCloseMenu(); - setIndexSelectedLimitSet(operationalLimitsGroups.length); - } - }, [ - indexSelectedLimitSet, - operationalLimitsGroups, - appendToLimitsGroups, - setIndexSelectedLimitSet, - handleCloseMenu, - ]); - - return ( - - - - - - {intl.formatMessage({ id: 'DeleteFromMenu' })} - - - - - - {intl.formatMessage({ id: 'Duplicate' })} - - - ); -} diff --git a/src/components/dialogs/limits/limits-pane-utils.ts b/src/components/dialogs/limits/limits-pane-utils.ts deleted file mode 100644 index 6bb6f83ce9..0000000000 --- a/src/components/dialogs/limits/limits-pane-utils.ts +++ /dev/null @@ -1,373 +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 { - APPLICABILITY_FIELD, - CURRENT_LIMITS, - ENABLE_OLG_MODIFICATION, - ID, - LIMITS, - LIMITS_PROPERTIES, - NAME, - OLG_IS_DUPLICATE, - OPERATIONAL_LIMITS_GROUPS, - OPERATIONAL_LIMITS_GROUPS_MODIFICATION_TYPE, - PERMANENT_LIMIT, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID1, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID2, - TEMPORARY_LIMIT_DURATION, - TEMPORARY_LIMIT_MODIFICATION_TYPE, - TEMPORARY_LIMIT_NAME, - TEMPORARY_LIMIT_VALUE, - TEMPORARY_LIMITS, -} from 'components/utils/field-constants'; -import { - areArrayElementsUnique, - formatMapInfosToTemporaryLimitsFormSchema, - formatTemporaryLimitsModificationToFormSchema, -} from 'components/utils/utils'; -import * as yup from 'yup'; -import { - CurrentLimits, - OperationalLimitsGroup, - OperationalLimitsGroupModificationInfos, - TemporaryLimit, -} from '../../../services/network-modification-types'; -import { CurrentLimitsData } from '../../../services/study/network-map.type'; -import { LineModificationFormSchema } from '../network-modifications/line/modification/line-modification-type'; -import { OperationalLimitsGroupFormSchema, TemporaryLimitFormSchema } from './operational-limits-groups-types'; -import { TestContext } from 'yup'; -import { APPLICABILITY } from 'components/network/constants'; -import { - AttributeModification, - FieldConstants, - OperationType, - sanitizeString, - toModificationOperation, -} from '@gridsuite/commons-ui'; - -const limitsGroupValidationSchema = () => ({ - [ID]: yup.string().nonNullable().required(), - [NAME]: yup.string().nonNullable().required(), - [APPLICABILITY_FIELD]: yup.string().nonNullable().required(), - [OLG_IS_DUPLICATE]: yup.boolean().nullable().test('testDistincts', 'LimitSetApplicabilityError', hasDuplicate), - [CURRENT_LIMITS]: yup.object().shape(currentLimitsValidationSchema()), - [LIMITS_PROPERTIES]: yup.array().of(limitsPropertyValidationSchema()), -}); - -const temporaryLimitsValidationSchema = () => { - return yup.object().shape( - { - [TEMPORARY_LIMIT_DURATION]: yup - .number() - .nullable() - .min(0, 'mustBeGreaterOrEqualToZero') - .when([TEMPORARY_LIMIT_VALUE, TEMPORARY_LIMIT_NAME], { - is: (value: number | null, name: string | null) => value != null || !!name, - then: (schema) => schema.required(), - }), - [TEMPORARY_LIMIT_VALUE]: yup.number().nullable().positive(), - [TEMPORARY_LIMIT_NAME]: yup - .string() - .nullable() - .trim() - .when([TEMPORARY_LIMIT_VALUE, TEMPORARY_LIMIT_DURATION], { - is: (value: number | null, duration: number | null) => value != null || duration != null, - then: (schema) => schema.required(), - }), - }, - [[TEMPORARY_LIMIT_DURATION, TEMPORARY_LIMIT_NAME]] - ); -}; -const limitsPropertyValidationSchema = () => { - return yup.object().shape({ - [NAME]: yup.string().required(), - [FieldConstants.VALUE]: yup.string().required(), - }); -}; - -const currentLimitsValidationSchema = () => ({ - [PERMANENT_LIMIT]: yup.number().positive('permanentCurrentLimitMustBeGreaterThanZero').required(), - [TEMPORARY_LIMITS]: yup - .array() - .of(temporaryLimitsValidationSchema()) - .test('distinctNames', 'TemporaryLimitNameUnicityError', (array) => { - const namesArray = !array - ? [] - : array.filter((l) => !!l[TEMPORARY_LIMIT_NAME]).map((l) => sanitizeString(l[TEMPORARY_LIMIT_NAME])); - return areArrayElementsUnique(namesArray); - }) - .test('distinctDurations', 'TemporaryLimitDurationUnicityError', (array) => { - const durationsArray = !array ? [] : array.map((l) => l[TEMPORARY_LIMIT_DURATION]).filter((d) => d); // empty lines are ignored - return areArrayElementsUnique(durationsArray); - }), -}); - -interface OperationalLimitsGroupFormSchemaWithPath extends OperationalLimitsGroupFormSchema { - rhfPath: string; -} - -function hasDuplicate(field: boolean | null | undefined, context: TestContext) { - return hasDuplicateOperationalLimitsGroups(context); -} - -function hasDuplicateOperationalLimitsGroups(context: TestContext) { - const limitsGroup: OperationalLimitsGroupFormSchema = context.parent; - const operationalLimitsGroups: OperationalLimitsGroupFormSchema[] = - context.from?.[1]?.value?.[OPERATIONAL_LIMITS_GROUPS]; - const operationalLimitsGroupsWithPath: OperationalLimitsGroupFormSchemaWithPath[] = operationalLimitsGroups.map( - (item, index) => { - return { ...item, rhfPath: `${LIMITS}.${OPERATIONAL_LIMITS_GROUPS}[${index}]` }; - } - ); - - const limitsGroupName = sanitizeString(limitsGroup[NAME]); - const filtered = operationalLimitsGroupsWithPath.filter( - (item: OperationalLimitsGroupFormSchemaWithPath) => sanitizeString(item[NAME]) === limitsGroupName - ); - - if (filtered.length <= 1) { - return true; - } - - const applicabilityEquipment: number = filtered.filter( - (item) => item[APPLICABILITY_FIELD] === APPLICABILITY.EQUIPMENT.id - ).length; - const applicabilitySide1: number = filtered.filter( - (item) => item[APPLICABILITY_FIELD] === APPLICABILITY.SIDE1.id - ).length; - - const isDuplicate = - filtered.length > 2 || applicabilityEquipment > 0 || applicabilitySide1 === 0 || applicabilitySide1 > 1; - - return !isDuplicate; -} - -const limitsValidationSchemaCreation = (id: string, isModification: boolean) => { - const completeLimitsGroupSchema = { - [OPERATIONAL_LIMITS_GROUPS]: isModification - ? yup.array(yup.object().shape(limitsGroupValidationSchema())).when([ENABLE_OLG_MODIFICATION], { - is: true, - then: (schema) => schema.required(), - otherwise: (schema) => schema.strip(), - }) - : 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) }; -}; - -export type LimitsFormSchema = yup.InferType[typeof LIMITS]>; - -export const getLimitsValidationSchema = (id: string = LIMITS, isModification: boolean = false) => { - return limitsValidationSchemaCreation(id, isModification); -}; - -const limitsEmptyFormData = (isModification: boolean, id: string) => { - const limitsGroup = { - [OPERATIONAL_LIMITS_GROUPS]: [], - [SELECTED_OPERATIONAL_LIMITS_GROUP_ID1]: null, - [SELECTED_OPERATIONAL_LIMITS_GROUP_ID2]: null, - [ENABLE_OLG_MODIFICATION]: !isModification, - }; - - return { [id]: limitsGroup }; -}; - -export const getLimitsEmptyFormData = (isModification = true, id = LIMITS) => { - return limitsEmptyFormData(isModification, id); -}; - -export const formatOpLimitGroupsToFormInfos = ( - limitGroups?: OperationalLimitsGroup[] | OperationalLimitsGroupModificationInfos[] | null -): OperationalLimitsGroupFormSchema[] => { - if (!limitGroups) { - return []; - } - - return limitGroups - .filter( - (opLimitGroup: OperationalLimitsGroup | OperationalLimitsGroupModificationInfos) => - opLimitGroup.modificationType !== OPERATIONAL_LIMITS_GROUPS_MODIFICATION_TYPE.DELETE - ) - .map((opLimitGroup: OperationalLimitsGroup | OperationalLimitsGroupModificationInfos) => { - return { - id: opLimitGroup.id + opLimitGroup.applicability, - name: opLimitGroup.id, - applicability: opLimitGroup.applicability, - limitsProperties: opLimitGroup.limitsProperties, - currentLimits: { - permanentLimit: opLimitGroup?.currentLimits?.permanentLimit, - temporaryLimits: formatTemporaryLimitsModificationToFormSchema( - opLimitGroup?.currentLimits?.temporaryLimits as TemporaryLimit[] - ), - }, - }; - }) as OperationalLimitsGroupFormSchema[]; -}; - -export const getAllLimitsFormData = ( - operationalLimitsGroups: OperationalLimitsGroupFormSchema[] = [], - selectedOperationalLimitsGroupId1: string | null = null, - selectedOperationalLimitsGroupId2: string | null = null, - enableOLGModification: boolean | null = true, - id = LIMITS -) => { - return { - [id]: { - [OPERATIONAL_LIMITS_GROUPS]: operationalLimitsGroups, - [SELECTED_OPERATIONAL_LIMITS_GROUP_ID1]: selectedOperationalLimitsGroupId1, - [SELECTED_OPERATIONAL_LIMITS_GROUP_ID2]: selectedOperationalLimitsGroupId2, - [ENABLE_OLG_MODIFICATION]: !!enableOLGModification, - }, - }; -}; - -/** - * sanitizes limit names and filters out the empty temporary limits lines - */ -export const sanitizeLimitsGroups = ( - limitsGroups: OperationalLimitsGroupFormSchema[] -): OperationalLimitsGroupFormSchema[] => - limitsGroups.map(({ currentLimits, ...baseData }) => ({ - ...baseData, - id: baseData.name, - currentLimits: !currentLimits - ? { - id: '', - permanentLimit: null, - temporaryLimits: [], - } - : { - permanentLimit: currentLimits.permanentLimit, - temporaryLimits: !currentLimits.temporaryLimits - ? [] - : currentLimits.temporaryLimits - // completely empty lines should be filtered out (the interface always displays some lines even if empty) - .filter(({ name }) => name?.trim()) - .map(({ name, ...temporaryLimit }) => ({ - ...temporaryLimit, - name: sanitizeString(name) ?? '', - })), - }, - })); - -export const sanitizeLimitNames = (temporaryLimitList: TemporaryLimitFormSchema[]): TemporaryLimitFormSchema[] => - temporaryLimitList - ?.filter((limit: TemporaryLimitFormSchema) => limit?.name?.trim()) - .map(({ name, ...temporaryLimit }) => ({ - ...temporaryLimit, - name: sanitizeString(name) ?? '', - })) || []; - -export const mapServerLimitsGroupsToFormInfos = (currentLimits: CurrentLimitsData[]) => { - return currentLimits?.map((currentLimit: CurrentLimitsData) => { - return { - id: currentLimit.id + currentLimit.applicability, - name: currentLimit.id, - applicability: currentLimit.applicability, - limitsProperties: currentLimit.limitsProperties, - currentLimits: { - id: currentLimit.id, - permanentLimit: currentLimit.permanentLimit, - temporaryLimits: formatMapInfosToTemporaryLimitsFormSchema(currentLimit.temporaryLimits), - }, - }; - }); -}; - -export const convertToOperationalLimitsGroupFormSchema = ( - currentLimits: CurrentLimitsData[] -): OperationalLimitsGroupFormSchema[] => { - let updatedOpLG: OperationalLimitsGroupFormSchema[] = []; - - for (const currentLimit of currentLimits) { - const equivalentFromNetMod = updatedOpLG.find( - (opLG: OperationalLimitsGroupFormSchema) => - currentLimit.id === opLG.name && currentLimit.applicability === opLG[APPLICABILITY_FIELD] - ); - if (equivalentFromNetMod === undefined) { - updatedOpLG.push({ - id: currentLimit.id + currentLimit.applicability, - name: currentLimit.id, - applicability: currentLimit.applicability, - limitsProperties: currentLimit.limitsProperties, - currentLimits: { - permanentLimit: currentLimit.permanentLimit, - temporaryLimits: formatMapInfosToTemporaryLimitsFormSchema(currentLimit.temporaryLimits), - }, - }); - } - } - - return updatedOpLG; -}; - -export const getOpLimitsGroupInfosFromBranchModification = ( - formBranchModification: LineModificationFormSchema -): OperationalLimitsGroupFormSchema[] => { - return formBranchModification?.limits?.operationalLimitsGroups ?? []; -}; -export const addModificationTypeToTemporaryLimits = ( - formTemporaryLimits: TemporaryLimitFormSchema[] -): TemporaryLimit[] => { - return formTemporaryLimits.map((limit: TemporaryLimitFormSchema) => { - return { - name: toModificationOperation(limit?.name), - acceptableDuration: toModificationOperation(limit?.acceptableDuration), - value: toModificationOperation(limit?.value), - modificationType: TEMPORARY_LIMIT_MODIFICATION_TYPE.MODIFY_OR_ADD, - }; - }); -}; - -export function addOperationTypeToSelectedOpLG( - selectedOpLG: string | null | undefined, - noSelectionString: string -): AttributeModification | null { - return selectedOpLG === noSelectionString - ? { - value: selectedOpLG, - op: OperationType.UNSET, - } - : toModificationOperation(selectedOpLG); -} - -/** - * converts the limits groups into a modification limits group - * ie mostly add the ADD, MODIFY, MODIFY_OR_ADD, DELETE and REPLACE tags to the data using a delta between the form and the network values - * note : for now only MODIFY_OR_ADD is handled, the others have been disabled for various reasons - * - * @param limitsGroupsForm current data from the form - */ -export const addModificationTypeToOpLimitsGroups = ( - limitsGroupsForm: OperationalLimitsGroupFormSchema[] -): OperationalLimitsGroup[] => { - let modificationLimitsGroupsForm: OperationalLimitsGroupFormSchema[] = sanitizeLimitsGroups(limitsGroupsForm); - - return modificationLimitsGroupsForm.map((limitsGroupForm: OperationalLimitsGroupFormSchema) => { - const temporaryLimits: TemporaryLimit[] = addModificationTypeToTemporaryLimits( - sanitizeLimitNames(limitsGroupForm[CURRENT_LIMITS]?.[TEMPORARY_LIMITS]) - ); - const currentLimits: CurrentLimits = { - permanentLimit: limitsGroupForm[CURRENT_LIMITS]?.[PERMANENT_LIMIT] ?? null, - temporaryLimits: temporaryLimits ?? [], - }; - - return { - id: limitsGroupForm.id, - name: limitsGroupForm.name, - applicability: limitsGroupForm.applicability, - limitsProperties: limitsGroupForm.limitsProperties, - currentLimits: currentLimits, - modificationType: OPERATIONAL_LIMITS_GROUPS_MODIFICATION_TYPE.MODIFY_OR_ADD, - temporaryLimitsModificationType: TEMPORARY_LIMIT_MODIFICATION_TYPE.REPLACE, - }; - }); -}; diff --git a/src/components/dialogs/limits/limits-pane.tsx b/src/components/dialogs/limits/limits-pane.tsx deleted file mode 100644 index b6936c18b2..0000000000 --- a/src/components/dialogs/limits/limits-pane.tsx +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { Box, Grid2 as Grid } from '@mui/material'; -import { - ENABLE_OLG_MODIFICATION, - LIMITS, - OPERATIONAL_LIMITS_GROUPS, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID1, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID2, -} from 'components/utils/field-constants'; -import { LimitsSidePane } from './limits-side-pane'; -import { SelectedOperationalLimitGroup } from './selected-operational-limit-group.js'; -import { useCallback, useMemo, useState } from 'react'; -import { useFieldArray, useFormContext, useWatch } from 'react-hook-form'; -import { CurrentLimits } from '../../../services/network-modification-types'; -import { OperationalLimitsGroupsTabs } from './operational-limits-groups-tabs'; -import { tabStyles } from 'components/utils/tab-utils'; -import IconButton from '@mui/material/IconButton'; -import { CurrentTreeNode } from '../../graph/tree-node.type'; -import { Grid2Section as GridSection } from '../commons/grid2-section'; -import AddIcon from '@mui/icons-material/ControlPoint'; -import { APPLICABILITY } from '../../network/constants'; -import { InputWithPopupConfirmation, SwitchInput } from '@gridsuite/commons-ui'; -import { mapServerLimitsGroupsToFormInfos } from './limits-pane-utils'; -import { BranchInfos, CurrentLimitsData } from '../../../services/study/network-map.type'; -import { OperationalLimitsGroupFormSchema } from './operational-limits-groups-types'; -import { generateEmptyOperationalLimitsGroup, generateUniqueId } from './operational-limits-groups-utils'; - -export interface LimitsPaneProps { - id?: string; - currentNode?: CurrentTreeNode; - equipmentToModify?: BranchInfos | null; - clearableFields?: boolean; -} - -export function LimitsPane({ - id = LIMITS, - currentNode, - equipmentToModify, - clearableFields, -}: Readonly) { - const [indexSelectedLimitSet, setIndexSelectedLimitSet] = useState(null); - const { getValues, reset } = useFormContext(); - - const olgEditable: boolean = useWatch({ - name: `${id}.${ENABLE_OLG_MODIFICATION}`, - }); - - const operationalLimitsGroupsFormName: string = `${id}.${OPERATIONAL_LIMITS_GROUPS}`; - const { - fields: operationalLimitsGroups, - append: appendToLimitsGroups, - prepend: prependToLimitsGroups, - remove: removeLimitsGroups, - } = useFieldArray<{ - [key: string]: OperationalLimitsGroupFormSchema[]; - }>({ - name: operationalLimitsGroupsFormName, - }); - - const watchedOperationalLimitsGroups: OperationalLimitsGroupFormSchema[] = useWatch({ - name: operationalLimitsGroupsFormName, - }); - - const isAModification: boolean = useMemo(() => !!equipmentToModify, [equipmentToModify]); - - const getCurrentLimits = (equipmentToModify: any, operationalLimitsGroupId: string): CurrentLimitsData | null => { - if (equipmentToModify?.currentLimits) { - return equipmentToModify.currentLimits.find( - (currentLimit: CurrentLimitsData) => - currentLimit.id + currentLimit.applicability === operationalLimitsGroupId - ); - } - return null; - }; - - const getCurrentLimitsIgnoreApplicability = ( - equipmentToModify: any, - operationalLimitsGroupName: string - ): CurrentLimits | null => { - if (equipmentToModify?.currentLimits) { - return equipmentToModify.currentLimits.find( - (currentLimit: CurrentLimitsData) => currentLimit.id === operationalLimitsGroupName - ); - } - return null; - }; - - const handlePopupConfirmation = () => { - const resetOLGs: OperationalLimitsGroupFormSchema[] = mapServerLimitsGroupsToFormInfos( - equipmentToModify?.currentLimits ?? [] - ); - const currentValues = getValues(); - reset( - { - ...currentValues, - [LIMITS]: { - [OPERATIONAL_LIMITS_GROUPS]: resetOLGs, - [ENABLE_OLG_MODIFICATION]: false, - }, - }, - { keepDefaultValues: true } - ); - }; - - const prependEmptyOperationalLimitsGroup = useCallback( - (name: string) => { - prependToLimitsGroups(generateEmptyOperationalLimitsGroup(name)); - }, - [prependToLimitsGroups] - ); - - const addNewLimitSet = useCallback(() => { - let name = 'DEFAULT'; - - // Try to generate unique name (we relie on watched table because name can be changed without using useFieldArray functions) - if (watchedOperationalLimitsGroups?.length > 0) { - const ids: string[] = watchedOperationalLimitsGroups.map((l) => l.name); - name = generateUniqueId('DEFAULT', ids); - } - prependEmptyOperationalLimitsGroup(name); - setIndexSelectedLimitSet(0); - }, [watchedOperationalLimitsGroups, prependEmptyOperationalLimitsGroup]); - - return ( - <> - {/* active limit sets */} - - - - - - {/* if the user wants to switch off the modification a modal asks him to confirm */} - {isAModification && ( - olgEditable} - resetOnConfirmation={handlePopupConfirmation} - message="disableOLGedition" - validateButtonLabel="validate" - /> - )} - - - - - - - - - - {/* limits */} - - - - - - - - - - - - {indexSelectedLimitSet !== null && - operationalLimitsGroups.map( - (operationalLimitsGroup: OperationalLimitsGroupFormSchema, index: number) => - index === indexSelectedLimitSet && ( - - ) - )} - - - - ); -} diff --git a/src/components/dialogs/limits/limits-properties-side-stack.tsx b/src/components/dialogs/limits/limits-properties-side-stack.tsx deleted file mode 100644 index 3737bf64cf..0000000000 --- a/src/components/dialogs/limits/limits-properties-side-stack.tsx +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -import { LimitsTagChip } from './limits-tag-chip'; -import { Autocomplete, AutocompleteRenderInputParams, Box, Stack, TextField, IconButton } from '@mui/material'; -import { useCallback, useMemo, useState } from 'react'; -import { Delete } from '@mui/icons-material'; -import { useIntl } from 'react-intl'; -import { LimitsProperty } from '../../../services/network-modification-types'; -import { useFieldArray } from 'react-hook-form'; -import { AddButton, usePredefinedProperties } from '@gridsuite/commons-ui'; - -export interface LimitsPropertiesSideStackProps { - name: string; - disabled?: boolean; -} -export function LimitsPropertiesSideStack({ name, disabled }: Readonly) { - const { - fields: limitsProperties, - append, - remove, - } = useFieldArray<{ [key: string]: LimitsProperty[] }>({ name: name }); - - const [isEditing, setIsEditing] = useState(false); - const [hovered, setHovered] = useState(false); - const [propertyName, setPropertyName] = useState(''); - const [propertyValue, setPropertyValue] = useState(''); - const [nameEditorError, setNameEditorError] = useState(''); - const [valueEditorError, setValueEditorError] = useState(''); - const intl = useIntl(); - - const [predefinedProperties] = usePredefinedProperties('limitsGroup'); - const predefinedPropertiesNames = useMemo(() => { - return Object.keys(predefinedProperties ?? {}).sort((a, b) => a.localeCompare(b)); - }, [predefinedProperties]); - - const handleKeyPress = useCallback( - (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - setNameEditorError(''); - setValueEditorError(''); - - let error = false; - if (propertyName.trim() === '') { - setNameEditorError(intl.formatMessage({ id: 'FieldNotEmpty' })); - error = true; - } - if (propertyValue.trim() === '') { - setValueEditorError(intl.formatMessage({ id: 'FieldNotEmpty' })); - error = true; - } - - if (error) { - return; - } - - if (limitsProperties.some((l) => l.name === propertyName)) { - setNameEditorError(intl.formatMessage({ id: 'UniqueName' })); - return; - } else { - append({ name: propertyName, value: propertyValue }); - setPropertyName(''); - setPropertyValue(''); - } - setIsEditing(false); - } - }, - [append, intl, limitsProperties, propertyName, propertyValue] - ); - - const handleOnChange = useCallback((value: string) => { - setPropertyName(value); - setNameEditorError(''); - }, []); - - return ( - - - {limitsProperties?.map((property: LimitsProperty, index: number) => ( - remove(index)} - disabled={disabled} - showTooltip - /> - ))} - {!isEditing && setIsEditing(true)} />} - - {isEditing && !disabled ? ( - setHovered(true)} - onMouseLeave={() => setHovered(false)} - > - handleOnChange(value ?? '')} - renderInput={(params: AutocompleteRenderInputParams) => ( - handleOnChange(event.target.value)} - fullWidth - error={nameEditorError !== ''} - helperText={nameEditorError} - onKeyDown={handleKeyPress} - /> - )} - sx={{ flex: 1 }} - freeSolo={true} - /> - setPropertyValue(event.target.value)} - error={valueEditorError !== ''} - helperText={valueEditorError} - /> - { - setIsEditing(false); - setNameEditorError(''); - }} - > - - - - ) : ( - '' - )} - - ); -} diff --git a/src/components/dialogs/limits/limits-properties-stack.tsx b/src/components/dialogs/limits/limits-properties-stack.tsx deleted file mode 100644 index 3806301d9d..0000000000 --- a/src/components/dialogs/limits/limits-properties-stack.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { Avatar, Stack } from '@mui/material'; -import { LimitsProperty } from '../../../services/network-modification-types'; -import { LimitsTagChip } from './limits-tag-chip'; -import { useWatch } from 'react-hook-form'; - -const MAX_PROPERTIES_TO_RENDER: number = 2; - -export interface LimitsPropertiesStackProps { - name: string; -} - -function getLimitsPropertiesToRender(limitsProperties: LimitsProperty[]) { - return limitsProperties.length < MAX_PROPERTIES_TO_RENDER ? limitsProperties : limitsProperties?.slice(0, 2); -} - -export function LimitsPropertiesStack({ name }: Readonly) { - const limitsProperties: LimitsProperty[] | undefined = useWatch({ name: name }); - const propertiesToRender: LimitsProperty[] = getLimitsPropertiesToRender(limitsProperties ?? []); - - return ( - - {propertiesToRender.map((property: LimitsProperty) => ( - - ))} - {limitsProperties && propertiesToRender.length !== limitsProperties.length ? ( - {`+${limitsProperties.length - propertiesToRender.length}`} - ) : ( - '' - )} - - ); -} diff --git a/src/components/dialogs/limits/limits-side-pane.tsx b/src/components/dialogs/limits/limits-side-pane.tsx deleted file mode 100644 index 2815da24ac..0000000000 --- a/src/components/dialogs/limits/limits-side-pane.tsx +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -import { Box, Grid } from '@mui/material'; -import { FormattedMessage, useIntl } from 'react-intl'; -import { - AmpereAdornment, - ColumnNumeric, - ColumnText, - DndColumn, - DndColumnType, - ErrorInput, - FieldErrorAlert, - FloatInput, - SelectInput, - TextInput, -} from '@gridsuite/commons-ui'; -import { - APPLICABILITY_FIELD, - CURRENT_LIMITS, - LIMITS, - LIMITS_PROPERTIES, - NAME, - OLG_IS_DUPLICATE, - OPERATIONAL_LIMITS_GROUPS, - PERMANENT_LIMIT, - TEMPORARY_LIMIT_DURATION, - TEMPORARY_LIMIT_MODIFICATION_TYPE, - TEMPORARY_LIMIT_NAME, - TEMPORARY_LIMIT_VALUE, - TEMPORARY_LIMITS, -} from 'components/utils/field-constants'; -import { useCallback, useEffect, useMemo } from 'react'; -import { useController, useFormContext } from 'react-hook-form'; -import { isNodeBuilt } from '../../graph/util/model-functions'; -import TemporaryLimitsTable from './temporary-limits-table'; -import LimitsChart from './limitsChart'; -import { CurrentTreeNode } from '../../graph/tree-node.type'; -import { APPLICABILITY } from '../../network/constants'; -import { LimitsPropertiesSideStack } from './limits-properties-side-stack'; -import { TemporaryLimitsData } from '../../../services/study/network-map.type'; - -export interface LimitsSidePaneProps { - name?: string; - permanentCurrentLimitPreviousValue: number | null | undefined; - temporaryLimitsPreviousValues: TemporaryLimitsData[]; - applicabilityPreviousValue?: string; - clearableFields: boolean | undefined; - currentNode?: CurrentTreeNode; - disabled: boolean; -} - -export function LimitsSidePane({ - name, - permanentCurrentLimitPreviousValue, - temporaryLimitsPreviousValues, - applicabilityPreviousValue, - clearableFields, - currentNode, - disabled, -}: Readonly) { - const intl = useIntl(); - const { getValues, subscribe, trigger } = useFormContext(); - const limitsGroupFormName = `${name}.${CURRENT_LIMITS}`; - const columnsDefinition: ((ColumnText | ColumnNumeric) & { initialValue: string | null })[] = useMemo(() => { - return [ - { - label: 'TemporaryLimitName', - dataKey: TEMPORARY_LIMIT_NAME, - initialValue: '', - editable: true, - type: DndColumnType.TEXT as const, - maxWidth: 200, - }, - { - label: 'TemporaryLimitDuration', - dataKey: TEMPORARY_LIMIT_DURATION, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC as const, - maxWidth: 100, - }, - { - label: 'TemporaryLimitValue', - dataKey: TEMPORARY_LIMIT_VALUE, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC as const, - maxWidth: 100, - }, - ].map((column) => ({ - ...column, - label: intl.formatMessage({ id: column.label }), - })); - }, [intl]); - - const newRowData = useMemo(() => { - let newRowData: any = {}; - columnsDefinition.forEach((column) => (newRowData[column.dataKey] = column.initialValue)); - return newRowData; - }, [columnsDefinition]); - const createRows = () => [newRowData]; - - const temporaryLimitHasPreviousValue = useCallback( - (rowIndex: number, arrayFormName: string, temporaryLimits?: TemporaryLimitsData[]) => { - if (!temporaryLimits) { - return false; - } - return ( - temporaryLimits?.filter( - (l: TemporaryLimitsData) => - l.name === getValues(arrayFormName)[rowIndex]?.name && - l.acceptableDuration === getValues(arrayFormName)[rowIndex]?.acceptableDuration - )?.length > 0 - ); - }, - [getValues] - ); - - const shouldReturnPreviousValue = useCallback( - (rowIndex: number, column: DndColumn, arrayFormName: string, temporaryLimits: TemporaryLimitsData[]) => { - return ( - (temporaryLimitHasPreviousValue(rowIndex, arrayFormName, temporaryLimits) && - column.dataKey === TEMPORARY_LIMIT_VALUE) || - getValues(arrayFormName)[rowIndex]?.modificationType === TEMPORARY_LIMIT_MODIFICATION_TYPE.ADD - ); - }, - [getValues, temporaryLimitHasPreviousValue] - ); - - const findTemporaryLimit = useCallback( - (rowIndex: number, arrayFormName: string, temporaryLimits: TemporaryLimitsData[]) => { - return temporaryLimits?.find( - (e: TemporaryLimitsData) => - e.name === getValues(arrayFormName)[rowIndex]?.name && - e.acceptableDuration === getValues(arrayFormName)[rowIndex]?.acceptableDuration - ); - }, - [getValues] - ); - - const getPreviousValue = useCallback( - (rowIndex: number, column: DndColumn, arrayFormName: string, temporaryLimits?: TemporaryLimitsData[]) => { - if (!temporaryLimits) { - return undefined; - } - if (!temporaryLimits?.length) { - return undefined; - } - if (!shouldReturnPreviousValue(rowIndex, column, arrayFormName, temporaryLimits)) { - return undefined; - } - const temporaryLimit = findTemporaryLimit(rowIndex, arrayFormName, temporaryLimits); - if (temporaryLimit === undefined) { - return undefined; - } - if (column.dataKey === TEMPORARY_LIMIT_VALUE) { - return temporaryLimit?.value ?? Number.MAX_VALUE; - } else if (column.dataKey === TEMPORARY_LIMIT_DURATION) { - return temporaryLimit?.acceptableDuration ?? Number.MAX_VALUE; - } - }, - [findTemporaryLimit, shouldReturnPreviousValue] - ); - - const isValueModified = useCallback( - (rowIndex: number, arrayFormName: string) => { - const temporaryLimits = getValues(arrayFormName); - const temporaryLimit = temporaryLimits ? temporaryLimits[rowIndex] : null; - if ( - temporaryLimit?.modificationType === TEMPORARY_LIMIT_MODIFICATION_TYPE.MODIFY && - !isNodeBuilt(currentNode ?? null) - ) { - return false; - } else { - return temporaryLimit?.modificationType !== null; - } - }, - [currentNode, getValues] - ); - - // Trigger all OLG_IS_DUPLICATE fields when change on applicability or name field - useEffect(() => { - const unsubscribeCallBack = subscribe({ - name: [`${name}.${APPLICABILITY_FIELD}`, `${name}.${NAME}`], - formState: { - values: true, - }, - callback: ({ isSubmitted }) => { - if (isSubmitted) { - const operationalLimitsGroups = getValues(`${LIMITS}.${OPERATIONAL_LIMITS_GROUPS}`); - for (let index = 0; index < operationalLimitsGroups?.length; index++) { - trigger(`${LIMITS}.${OPERATIONAL_LIMITS_GROUPS}[${index}].${OLG_IS_DUPLICATE}`).then(); - } - } - }, - }); - return () => unsubscribeCallBack(); - }, [trigger, subscribe, name, getValues]); - - const { - fieldState: { error }, - } = useController({ - name: `${name}.${OLG_IS_DUPLICATE}`, - }); - - return ( - - - {name && ( - - - - - - - - - - - - - - - )} - - - - - - - - ); -} diff --git a/src/components/dialogs/limits/limits-tag-chip.tsx b/src/components/dialogs/limits/limits-tag-chip.tsx deleted file mode 100644 index ee2a92bab8..0000000000 --- a/src/components/dialogs/limits/limits-tag-chip.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { Avatar, Chip, ChipProps, Tooltip } from '@mui/material'; -import { getPropertyAvatar } from './limits-constants'; -import * as React from 'react'; -import { LimitsProperty } from '../../../services/network-modification-types'; - -export interface LimitTagChipProps extends Omit { - limitsProperty: LimitsProperty; - showTooltip?: boolean; -} - -export function LimitsTagChip({ limitsProperty, showTooltip, onDelete, ...props }: Readonly) { - const chipContent = ( - {getPropertyAvatar(limitsProperty.name)}} - label={limitsProperty.value} - sx={{ maxWidth: onDelete ? '200px' : '180px', margin: 0.5, borderRadius: '4px' }} - onDelete={onDelete} - {...props} - /> - ); - - return showTooltip ? ( - {chipContent} - ) : ( - chipContent - ); -} diff --git a/src/components/dialogs/limits/limitsChart.tsx b/src/components/dialogs/limits/limitsChart.tsx deleted file mode 100644 index 32dccf9995..0000000000 --- a/src/components/dialogs/limits/limitsChart.tsx +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { BarChart } from '@mui/x-charts/BarChart'; -import { useCallback, useMemo } from 'react'; -import { useWatch } from 'react-hook-form'; -import { useIntl } from 'react-intl'; -import { TemporaryLimitsData } from '../../../services/study/network-map.type'; -import { AxisValueFormatterContext, BarSeriesType } from '@mui/x-charts/models'; - -export interface LimitsGraphProps { - limitsGroupFormName: string; - previousPermanentLimit?: number | null; // back value when there are no permanent value in the current form -} -const colorPermanentLimit = '#58d058'; -const colors: string[] = ['#ffc019', '#e47400', '#cc5500', '#ff5757', '#ff0000']; -const colorForbidden: string = '#b10303'; - -interface Ticks { - label: string; - position: number | null; - incoherent: boolean; -} - -const formatTempo = (tempo: number | null) => { - if (!tempo) { - return ''; - } - const min = Math.floor(tempo / 60); - const sec = tempo % 60; - if (min > 0 && sec === 0) { - return `${min}min`; - } - if (min === 0 && sec > 0) { - return `${sec}s`; - } - return `${min}min${sec}s`; -}; - -export default function LimitsChart({ limitsGroupFormName, previousPermanentLimit }: Readonly) { - const currentLimits = useWatch({ name: `${limitsGroupFormName}` }); - const intl = useIntl(); - const permanentLimit: number | null = currentLimits.permanentLimit ?? previousPermanentLimit ?? null; - - const isIncoherent = useCallback( - (maxValuePermanentLimit: number, item: TemporaryLimitsData, previousItem?: TemporaryLimitsData) => { - // Incoherent cases : - // threshold without tempo that is not permanent limit when permanent limit exists - // threshold with biggest tempo and biggest value than the previous threshold - // more than one threshold without value - const isPermanentLimit = - (item.name === intl.formatMessage({ id: 'permanentLimit' }) && permanentLimit) || - (!permanentLimit && !item.acceptableDuration); - - const permanentLimitValue = permanentLimit || maxValuePermanentLimit; - - const itemTempoGreaterThanPrevious: boolean = - (item?.acceptableDuration && - previousItem?.acceptableDuration && - item.acceptableDuration > previousItem.acceptableDuration) || - false; - - const atLeastTwoItemsWithNotempo: boolean = - (previousItem?.acceptableDuration && !item.acceptableDuration) || false; - - return ( - (!item.acceptableDuration && permanentLimit && !isPermanentLimit) || - (!isPermanentLimit && item.value && item.value < permanentLimitValue) || - itemTempoGreaterThanPrevious || - atLeastTwoItemsWithNotempo || - false - ); - }, - [intl, permanentLimit] - ); - - const { series, ticks, max } = useMemo(() => { - const thresholds: TemporaryLimitsData[] = []; - let noValueThresholdFound = false; - let maxValuePermanentLimit: number = 0; - - if (permanentLimit) { - thresholds.push({ - name: intl.formatMessage({ id: 'permanentLimit' }), - value: permanentLimit ? +permanentLimit : permanentLimit, - acceptableDuration: null, - }); - maxValuePermanentLimit = permanentLimit ?? 0; - } - - if (currentLimits?.temporaryLimits) { - currentLimits.temporaryLimits - .filter((field: TemporaryLimitsData) => field.name && (field.acceptableDuration || field.value)) - .forEach((field: TemporaryLimitsData) => { - if (!field.value) { - noValueThresholdFound = true; - } - if (!field.acceptableDuration && field.value) { - maxValuePermanentLimit = Math.max(maxValuePermanentLimit, field.value); - } - thresholds.push({ - name: field.name, - value: field.value ? +field.value : field.value, - acceptableDuration: field.acceptableDuration, - }); - }); - } - - // Sort by value, if no value put at the end - thresholds.sort((a, b) => { - if (a.value && !b.value) { - return -1; - } - if (!a.value && !b.value && a.acceptableDuration && b.acceptableDuration) { - return a.acceptableDuration - b.acceptableDuration; - } - if (!a.value && b.value) { - return 1; - } - if (a.value && b.value) { - return a.value - b.value; - } - return 0; - }); - - const maxValue = Math.max(...thresholds.map((item) => item.value ?? 0)); - let colorIndex = 0; - let previousSum = 0; - - return thresholds.reduce<{ series: BarSeriesType[]; ticks: Ticks[]; max: number }>( - (acc, item, index) => { - const isPermanentLimit = - (item.name === intl.formatMessage({ id: 'permanentLimit' }) && permanentLimit) || - (!permanentLimit && !item.acceptableDuration && item.value === maxValuePermanentLimit); - const difference = item.value ? item.value - previousSum : undefined; - - const color = - isPermanentLimit || - !item.acceptableDuration || - (item.acceptableDuration && item.value && maxValuePermanentLimit >= item.value) - ? colorPermanentLimit - : (colors?.[colorIndex] ?? colors[colors.length - 1]); - - const incoherent = isIncoherent( - maxValuePermanentLimit, - item, - index > 0 ? thresholds[index - 1] : undefined - ); - - if (item.value && item.value >= maxValuePermanentLimit) { - previousSum = item.value; - } - if (item.value && item.value > maxValuePermanentLimit) { - colorIndex++; - } - - const updatedSeries: BarSeriesType[] = - (item.acceptableDuration && !item.value) || (item.value && item.value >= maxValuePermanentLimit) - ? [ - ...acc.series, - { - type: 'bar', - label: - isPermanentLimit || item.value === maxValuePermanentLimit - ? intl.formatMessage({ id: 'unlimited' }) - : formatTempo(item.acceptableDuration), - data: [difference ?? maxValue * 0.15], - color: item.value ? color : colorForbidden, - stack: 'total', - }, - ] - : [...acc.series]; - const updatedTicks: Ticks[] = [ - ...acc.ticks, - { position: item.value, label: item.name, incoherent: incoherent }, - ]; - - if ( - index === thresholds.length - 1 && - updatedTicks[updatedTicks.length - 1].position && - !noValueThresholdFound - ) { - updatedSeries.push({ - type: 'bar', - label: intl.formatMessage({ id: 'forbidden' }), - data: [(updatedTicks?.[updatedTicks?.length - 1]?.position ?? 0.0) * 0.15], - color: colorForbidden, - stack: 'total', - }); - } - return { - series: updatedSeries, - ticks: updatedTicks, - max: - updatedSeries - .flatMap((serie) => serie.data) // Only one number by array. - .filter(Number.isFinite) - // @ts-ignore : We filtered values above. - .reduce((a, b) => a + b, 0) ?? 100, - }; - }, - { series: [], ticks: [], max: 0 } - ); - }, [currentLimits.temporaryLimits, intl, isIncoherent, permanentLimit]); - - const tickPositions = ticks.map((t) => t.position); - - return ( - 0 - ? series - : [{ label: intl.formatMessage({ id: 'unlimited' }), data: [100], color: colorPermanentLimit }] - } - layout="horizontal" - yAxis={[ - // We can't totally disable the yAxis, so we have to give empty data - { - id: 'leftAxis', - position: 'none', - data: [''], - }, - ]} - xAxis={[ - { - id: 'bottomAxis', - position: 'bottom', - tickInterval: tickPositions, - tickLabelStyle: { fontSize: 10 }, - disableLine: true, - min: 0, - max: max, - }, - { - id: 'topAxis', - position: 'top', - tickInterval: tickPositions, - tickLabelStyle: { fontSize: 10 }, - valueFormatter: (value: number, _context: AxisValueFormatterContext) => - ticks.find((item: Ticks) => item.position === value)?.label ?? '', - disableLine: true, - min: 0, - max: max, - }, - ]} - sx={{ pointerEvents: 'none' }} - /> - ); -} diff --git a/src/components/dialogs/limits/operational-limits-group-tab-label.tsx b/src/components/dialogs/limits/operational-limits-group-tab-label.tsx deleted file mode 100644 index 26becb5496..0000000000 --- a/src/components/dialogs/limits/operational-limits-group-tab-label.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { Box, FormHelperText, Stack, Typography, Tooltip, IconButton } from '@mui/material'; -import { FormattedMessage } from 'react-intl'; -import { APPLICABILITY } from '../../network/constants'; -import { LimitsPropertiesStack } from './limits-properties-stack'; -import { grey, red } from '@mui/material/colors'; -import { useFormState } from 'react-hook-form'; -import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined'; -import { LIMITS, OLG_IS_DUPLICATE, OPERATIONAL_LIMITS_GROUPS } from '../../utils/field-constants'; -import { LimitsFormSchema, OperationalLimitsGroupFormSchema } from './operational-limits-groups-types'; -import MoreVertIcon from '@mui/icons-material/MoreVert'; - -interface OperationalLimitsGroupTabLabelProps { - operationalLimitsGroup: OperationalLimitsGroupFormSchema; - showIconButton: boolean; - limitsPropertiesName: string; - handleOpenMenu: (event: React.MouseEvent, index: number) => void; - index: number; -} - -export function OperationalLimitsGroupTabLabel({ - operationalLimitsGroup, - showIconButton, - limitsPropertiesName, - handleOpenMenu, - index, -}: Readonly) { - const { errors } = useFormState({ name: `${LIMITS}.${OPERATIONAL_LIMITS_GROUPS}` }); - - const temporaryLimitsErrors = errors?.limits?.operationalLimitsGroups?.[index]?.currentLimits?.temporaryLimits; - const hasTemporaryLimitError = - Array.isArray(temporaryLimitsErrors) && - temporaryLimitsErrors.some((tl) => tl?.name?.message || tl?.acceptableDuration?.message); - - const hasError = - errors?.limits?.operationalLimitsGroups?.[index]?.name?.message || - errors?.limits?.operationalLimitsGroups?.[index]?.currentLimits?.permanentLimit?.message || - errors?.limits?.operationalLimitsGroups?.[index]?.[OLG_IS_DUPLICATE]?.message || - hasTemporaryLimitError; - - return ( - - - - - - - {operationalLimitsGroup.name} - - {hasError && ( - - - - )} - - - {operationalLimitsGroup?.applicability ? ( - - item.id === operationalLimitsGroup.applicability - )?.label - } - /> - - ) : ( - '' - )} - - - - {showIconButton && ( - handleOpenMenu(e, index)}> - - - )} - - ); -} diff --git a/src/components/dialogs/limits/operational-limits-groups-styles.ts b/src/components/dialogs/limits/operational-limits-groups-styles.ts deleted file mode 100644 index 39ab02ef4b..0000000000 --- a/src/components/dialogs/limits/operational-limits-groups-styles.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import type { MuiStyles } from '@gridsuite/commons-ui'; -import { stylesLayout } from '../../utils/tab-utils'; -import { blue } from '@mui/material/colors'; - -export const limitsStyles = { - tabs: () => ({ - ...stylesLayout.listDisplay, - maxHeight: '45vh', - height: '100%', - borderRight: 1, - borderColor: 'divider', - transition: 'transform 0.3s ease-in-out', - '& .MuiTabs-indicator': { - borderRight: `3px solid ${blue[700]}`, - }, - '.MuiTab-root.MuiButtonBase-root': { - textTransform: 'none', - textAlign: 'left', - alignItems: 'stretch', - p: 0, - }, - }), - tabBackground: { - '&:hover': { - backgroundColor: 'rgba(25, 118, 210, 0.05)', // blue[700] - }, - maxWidth: 600, - width: '100%', - p: 1, - minHeight: 60, - }, - copyLimitsToRightBackground: { - height: 200, - display: 'flex', - }, - copyLimitsToLeftBackground: { - height: '50%', - }, - copyLimitsButtons: { - alignSelf: 'flex-end', - minWidth: '0px', - height: 'auto', - padding: '1', - }, -} as const satisfies MuiStyles; diff --git a/src/components/dialogs/limits/operational-limits-groups-tabs.tsx b/src/components/dialogs/limits/operational-limits-groups-tabs.tsx deleted file mode 100644 index 1d0a0c1f55..0000000000 --- a/src/components/dialogs/limits/operational-limits-groups-tabs.tsx +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { Grid, Tab, Tabs } from '@mui/material'; -import React, { useCallback, useEffect, useState } from 'react'; -import { - LIMITS_PROPERTIES, - OPERATIONAL_LIMITS_GROUPS, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID1, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID2, -} from '../../utils/field-constants'; -import { UseFieldArrayAppend, useWatch } from 'react-hook-form'; -import { ContextMenuCoordinates, LimitsGroupsContextualMenu } from './limits-groups-contextual-menu'; -import { OperationalLimitsGroupTabLabel } from './operational-limits-group-tab-label'; -import { OperationalLimitsGroupFormSchema } from './operational-limits-groups-types'; -import { CurrentLimitsData } from 'services/study/network-map.type'; -import { limitsStyles } from './operational-limits-groups-styles'; - -export interface OperationalLimitsGroupsTabsProps { - parentFormName: string; - indexSelectedLimitSet: number | null; - setIndexSelectedLimitSet: React.Dispatch>; - currentLimitsToModify: CurrentLimitsData[]; - editable: boolean; - appendToLimitsGroups: UseFieldArrayAppend< - { - [p: string]: OperationalLimitsGroupFormSchema[]; - }, - string - >; - removeLimitsGroups: () => void; -} - -export const OperationalLimitsGroupsTabs = ({ - parentFormName, - setIndexSelectedLimitSet, - indexSelectedLimitSet, - editable, - appendToLimitsGroups, - removeLimitsGroups, - currentLimitsToModify, -}: Readonly) => { - const [hoveredRowIndex, setHoveredRowIndex] = useState(-1); - const [contextMenuCoordinates, setContextMenuCoordinates] = useState({ - x: null, - y: null, - tabIndex: null, - }); - const selectedLimitsGroups1: string = useWatch({ - name: `${parentFormName}.${SELECTED_OPERATIONAL_LIMITS_GROUP_ID1}`, - }); - const selectedLimitsGroups2: string = useWatch({ - name: `${parentFormName}.${SELECTED_OPERATIONAL_LIMITS_GROUP_ID2}`, - }); - - const operationalLimitsGroups: OperationalLimitsGroupFormSchema[] = useWatch({ - name: `${parentFormName}.${OPERATIONAL_LIMITS_GROUPS}`, - }); - - const handleOpenMenu = useCallback( - (event: React.MouseEvent, index: number): void => { - if (!editable) { - return; - } - event.preventDefault(); - event.stopPropagation(); - setIndexSelectedLimitSet(index); - setContextMenuCoordinates({ - x: event.clientX, - y: event.clientY, - tabIndex: index, - }); - }, - [editable, setIndexSelectedLimitSet] - ); - - const handleCloseMenu = useCallback(() => { - setContextMenuCoordinates({ - x: null, - y: null, - tabIndex: null, - }); - }, [setContextMenuCoordinates]); - - useEffect(() => { - // as long as there are limit sets, one should be selected - if (indexSelectedLimitSet === null && operationalLimitsGroups.length > 0) { - setIndexSelectedLimitSet(0); - } - }, [indexSelectedLimitSet, setIndexSelectedLimitSet, operationalLimitsGroups]); - - const handleTabChange = useCallback( - (_event: React.SyntheticEvent, newValue: number): void => { - setIndexSelectedLimitSet(newValue); - }, - [setIndexSelectedLimitSet] - ); - - return ( - - - {operationalLimitsGroups.map((opLg: OperationalLimitsGroupFormSchema, index: number) => ( - setHoveredRowIndex(index)} - onContextMenu={(e) => handleOpenMenu(e, index)} - key={opLg.id + index} - disableRipple - sx={limitsStyles.tabBackground} - label={ - - } - /> - ))} - - - - ); -}; diff --git a/src/components/dialogs/limits/operational-limits-groups-types.ts b/src/components/dialogs/limits/operational-limits-groups-types.ts deleted file mode 100644 index d929a8d61d..0000000000 --- a/src/components/dialogs/limits/operational-limits-groups-types.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -import { - APPLICABILITY_FIELD, - CURRENT_LIMITS, - ENABLE_OLG_MODIFICATION, - ID, - LIMITS, - LIMITS_PROPERTIES, - NAME, - OLG_IS_DUPLICATE, - OPERATIONAL_LIMITS_GROUPS, - PERMANENT_LIMIT, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID1, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID2, - TEMPORARY_LIMIT_DURATION, - TEMPORARY_LIMIT_NAME, - TEMPORARY_LIMIT_VALUE, - TEMPORARY_LIMITS, -} from '../../utils/field-constants'; -import { FieldConstants } from '@gridsuite/commons-ui'; - -export interface LimitsFormSchema { - [LIMITS]: OperationalLimitsGroupsFormSchema; -} - -export interface OperationalLimitsGroupsFormSchema { - [OPERATIONAL_LIMITS_GROUPS]: OperationalLimitsGroupFormSchema[]; - [SELECTED_OPERATIONAL_LIMITS_GROUP_ID1]?: string; - [SELECTED_OPERATIONAL_LIMITS_GROUP_ID2]?: string; - [ENABLE_OLG_MODIFICATION]: boolean; -} - -export interface OperationalLimitsGroupFormSchema { - [ID]: string; - [NAME]: string; - [APPLICABILITY_FIELD]?: string; - [OLG_IS_DUPLICATE]?: boolean; - [CURRENT_LIMITS]: CurrentLimitsFormSchema; - [LIMITS_PROPERTIES]?: LimitsPropertyFormSchema[]; -} - -export interface CurrentLimitsFormSchema { - [ID]?: string; - [PERMANENT_LIMIT]: number | null; - [TEMPORARY_LIMITS]: TemporaryLimitFormSchema[]; -} - -interface LimitsPropertyFormSchema { - [NAME]: string; - [FieldConstants.VALUE]: string; -} - -export interface TemporaryLimitFormSchema { - [TEMPORARY_LIMIT_DURATION]?: number | null; - [TEMPORARY_LIMIT_VALUE]?: number | null; - [TEMPORARY_LIMIT_NAME]?: string | null; -} diff --git a/src/components/dialogs/limits/operational-limits-groups-utils.ts b/src/components/dialogs/limits/operational-limits-groups-utils.ts deleted file mode 100644 index 925915c03c..0000000000 --- a/src/components/dialogs/limits/operational-limits-groups-utils.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { - APPLICABILITY_FIELD, - CURRENT_LIMITS, - ID, - LIMITS_PROPERTIES, - NAME, - PERMANENT_LIMIT, - TEMPORARY_LIMIT_DURATION, - TEMPORARY_LIMIT_NAME, - TEMPORARY_LIMIT_VALUE, - TEMPORARY_LIMITS, -} from 'components/utils/field-constants'; -import { APPLICABILITY } from 'components/network/constants'; -import { OperationalLimitsGroupFormSchema, TemporaryLimitFormSchema } from './operational-limits-groups-types'; - -function generateEmptyTemporaryLimitArray(): TemporaryLimitFormSchema[] { - return [ - { - [TEMPORARY_LIMIT_NAME]: '', - [TEMPORARY_LIMIT_DURATION]: null, - [TEMPORARY_LIMIT_VALUE]: null, - }, - ]; -} - -export function generateEmptyOperationalLimitsGroup(name: string): OperationalLimitsGroupFormSchema { - return { - [ID]: crypto.randomUUID(), - [NAME]: name, - [APPLICABILITY_FIELD]: APPLICABILITY.EQUIPMENT.id, - [LIMITS_PROPERTIES]: [], - [CURRENT_LIMITS]: { - [TEMPORARY_LIMITS]: generateEmptyTemporaryLimitArray(), - [PERMANENT_LIMIT]: null, - }, - }; -} - -export function generateUniqueId(baseName: string, names: string[]): string { - let finalId = baseName; - let found = false; - let increment = 1; - let suffix = ''; - do { - found = names.includes(baseName + suffix, 0); - if (found) { - increment++; - suffix = '(' + increment + ')'; - finalId = baseName + suffix; - } - } while (found); - - return finalId; -} diff --git a/src/components/dialogs/limits/selected-operational-limit-group.tsx b/src/components/dialogs/limits/selected-operational-limit-group.tsx deleted file mode 100644 index 8c255204f2..0000000000 --- a/src/components/dialogs/limits/selected-operational-limit-group.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -import { useMemo } from 'react'; -import { useWatch } from 'react-hook-form'; -import { Box } from '@mui/material'; -import { AutocompleteInput } from '@gridsuite/commons-ui'; -import { APPLICABILITY } from '../../network/constants'; -import { useIntl } from 'react-intl'; -import { OperationalLimitsGroupFormSchema } from './operational-limits-groups-types'; - -export interface SelectedOperationalLimitGroupProps { - selectedFormName: string; - optionsFormName: string; - label?: string; - filteredApplicability?: string; - previousValue?: string; - isABranchModif: boolean; // if false, this is a branch creation -} - -export const SelectedOperationalLimitGroup = ({ - selectedFormName, - optionsFormName, - label, - filteredApplicability, - previousValue, - isABranchModif, -}: Readonly) => { - const optionsValues = useWatch({ - name: optionsFormName, - }); - const intl = useIntl(); - - const opLimitsGroupsNames: string[] = useMemo((): string[] => { - const finalOptions: string[] = optionsValues - ? optionsValues - .filter( - (optionObj: OperationalLimitsGroupFormSchema) => - optionObj.applicability && - (optionObj.applicability === filteredApplicability || - optionObj.applicability === APPLICABILITY.EQUIPMENT.id) - ) - .map((filteredoptionObj: OperationalLimitsGroupFormSchema) => filteredoptionObj.name) - .filter((id: string) => id != null) - : []; - if (isABranchModif) { - finalOptions.push( - intl.formatMessage({ - id: 'None', - }) - ); - } - return finalOptions; - }, [filteredApplicability, intl, isABranchModif, optionsValues]); - - return ( - - - - ); -}; diff --git a/src/components/dialogs/limits/temporary-limits-table.tsx b/src/components/dialogs/limits/temporary-limits-table.tsx deleted file mode 100644 index 0cdc476512..0000000000 --- a/src/components/dialogs/limits/temporary-limits-table.tsx +++ /dev/null @@ -1,173 +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 { useState } from 'react'; -import { useFieldArray } from 'react-hook-form'; -import { Box, Grid, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material'; -import { - type ColumnNumeric, - type ColumnText, - DndColumnType, - ErrorInput, - FieldErrorAlert, - type MuiStyles, - TableNumericalInput, - TableTextInput, -} from '@gridsuite/commons-ui'; -import IconButton from '@mui/material/IconButton'; -import AddCircleIcon from '@mui/icons-material/AddCircle'; -import DeleteIcon from '@mui/icons-material/Delete'; -import { SELECTED } from '../../utils/field-constants'; -import { TemporaryLimitsData } from '../../../services/study/network-map.type'; - -const styles = { - columnsStyle: { - display: 'inline-flex', - justifyContent: 'space-between', - alignItems: 'center', - margin: 1, - textTransform: 'none', - }, -} as const satisfies MuiStyles; - -interface CustomTableCellProps { - name: string; - column: ColumnText | ColumnNumeric; - disabled: boolean; - previousValue: number | undefined; - valueModified: boolean; -} - -function EditableTableCell({ name, column, previousValue, valueModified, ...props }: Readonly) { - return ( - - {column.type === DndColumnType.NUMERIC ? ( - - ) : ( - - )} - - ); -} - -interface TemporaryLimitsTableProps { - arrayFormName: string; - columnsDefinition: (ColumnText | ColumnNumeric)[]; - createRow: () => any[]; - disabled?: boolean; - previousValues: TemporaryLimitsData[]; - getPreviousValue: ( - rowIndex: number, - column: ColumnText | ColumnNumeric, - arrayFormName: string, - temporaryLimits: TemporaryLimitsData[] - ) => number | undefined; - isValueModified: (rowIndex: number, arrayFormName: string) => boolean; - disableAddingRows?: boolean; -} - -function TemporaryLimitsTable({ - arrayFormName, - columnsDefinition, - createRow, - disabled = false, - previousValues, - getPreviousValue, - isValueModified, - disableAddingRows = false, -}: Readonly) { - const { fields, append, remove } = useFieldArray({ name: arrayFormName }); - const [hoveredRowIndex, setHoveredRowIndex] = useState(-1); - - function renderTableCell(rowId: string, rowIndex: number, column: ColumnText | ColumnNumeric) { - const name = `${arrayFormName}[${rowIndex}].${column.dataKey}`; - return ( - - ); - } - - function handleAddRowButton() { - const rowsToAdd = createRow().map((row) => { - return { ...row, [SELECTED]: false }; - }); - - append(rowsToAdd); - } - - function renderTableHead() { - return ( - - - {columnsDefinition.map((column) => ( - - {column.label} - - ))} - - - - - - - - ); - } - - const renderTableRow = (rowId: string, index: number) => ( - setHoveredRowIndex(index)} onMouseLeave={() => setHoveredRowIndex(-1)}> - {columnsDefinition.map((column) => renderTableCell(rowId, index, column))} - - remove(index)}> - - - - - ); - - function renderTableBody() { - return ( - - {fields.map((value: Record<'id', string>, index: number) => renderTableRow(value.id, index))} - - ); - } - - return ( - - - - - {renderTableHead()} - {renderTableBody()} -
-
- -
-
- ); -} - -export default TemporaryLimitsTable; diff --git a/src/components/dialogs/line-types-catalog/segment-utils.ts b/src/components/dialogs/line-types-catalog/segment-utils.ts index dcde9f1c4b..ea2f83ecce 100644 --- a/src/components/dialogs/line-types-catalog/segment-utils.ts +++ b/src/components/dialogs/line-types-catalog/segment-utils.ts @@ -27,11 +27,14 @@ import { } from 'components/utils/field-constants'; import * as yup from 'yup'; import { InferType } from 'yup'; -import { LineSegmentInfos } from '../../../services/network-modification-types'; -import { DeepNullable } from '@gridsuite/commons-ui'; +import { + APPLICABILITY, + DeepNullable, + LineSegmentInfos, + OperationalLimitsGroupFormSchema, + TemporaryLimitFormSchema, +} from '@gridsuite/commons-ui'; import { CurrentLimitsInfo } from './line-catalog.type'; -import { OperationalLimitsGroupFormSchema, TemporaryLimitFormSchema } from '../limits/operational-limits-groups-types'; -import { APPLICABILITY } from '../../network/constants'; export const SegmentTemporaryLimitSchema = yup.object().shape({ [LIMIT_VALUE]: yup.number().required(), diff --git a/src/components/dialogs/network-modifications/limit-sets/limit-sets-modification-dialog.tsx b/src/components/dialogs/network-modifications/limit-sets/limit-sets-modification-dialog.tsx index 88f22a7b00..3dfed0ec56 100644 --- a/src/components/dialogs/network-modifications/limit-sets/limit-sets-modification-dialog.tsx +++ b/src/components/dialogs/network-modifications/limit-sets/limit-sets-modification-dialog.tsx @@ -9,11 +9,16 @@ import { CSV_FILENAME, MODIFICATIONS_TABLE, OLGS_MODIFICATION_TYPE, - OPERATIONAL_LIMITS_GROUPS_MODIFICATION_TYPE, TYPE, } from '../../../utils/field-constants'; import { useIntl } from 'react-intl'; -import { CustomFormProvider, ModificationType, snackWithFallback, useSnackMessage } from '@gridsuite/commons-ui'; +import { + CustomFormProvider, + ModificationType, + OPERATIONAL_LIMITS_GROUPS_MODIFICATION_TYPE, + snackWithFallback, + useSnackMessage, +} from '@gridsuite/commons-ui'; import { SubmitHandler, useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { useCallback, useEffect, useMemo } from 'react'; diff --git a/src/components/dialogs/network-modifications/limit-sets/limit-sets-tabular-modification-utils.ts b/src/components/dialogs/network-modifications/limit-sets/limit-sets-tabular-modification-utils.ts index 851333a732..8860827f1b 100644 --- a/src/components/dialogs/network-modifications/limit-sets/limit-sets-tabular-modification-utils.ts +++ b/src/components/dialogs/network-modifications/limit-sets/limit-sets-tabular-modification-utils.ts @@ -26,8 +26,7 @@ import { import * as yup from 'yup'; import type { UUID } from 'node:crypto'; import { LIMIT_SETS_TABULAR_MODIFICATION_EQUIPMENTS } from '../tabular/tabular-modification-utils'; -import { APPLICABILITY } from '../../../network/constants'; -import { AttributeModification, EquipmentType, toModificationOperation } from '@gridsuite/commons-ui'; +import { APPLICABILITY, AttributeModification, EquipmentType, toModificationOperation } from '@gridsuite/commons-ui'; type TemporaryLimit = { name: AttributeModification; diff --git a/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-dialog.tsx b/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-dialog.tsx index 6bbec38029..6534ff2a42 100644 --- a/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-dialog.tsx +++ b/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-dialog.tsx @@ -12,6 +12,7 @@ import { getConnectivityPropertiesValidationSchema, getConnectivityWithoutPositionEmptyFormData, getNewVoltageLevelData, + LineCreationDto, ModificationType, sanitizeString, snackWithFallback, @@ -56,7 +57,6 @@ import { FetchStatus } from '../../../../services/utils.type'; import { AttachLineInfo, ExtendedVoltageLevelCreationInfo, - LineCreationInfos, VoltageLevelCreationInfo, } from '../../../../services/network-modification-types'; @@ -124,7 +124,7 @@ const LineAttachToVoltageLevelDialog = ({ }: LineAttachToVoltageLevelDialogProps) => { const currentNodeUuid = currentNode?.id; - const [attachmentLine, setAttachmentLine] = useState(); + const [attachmentLine, setAttachmentLine] = useState(); const [newVoltageLevel, setNewVoltageLevel] = useState(); const [attachmentPoint, setAttachmentPoint] = useState({ type: ModificationType.VOLTAGE_LEVEL_CREATION, @@ -254,7 +254,7 @@ const LineAttachToVoltageLevelDialog = ({ }, [reset]); const onLineCreationDo = useCallback( - ({ lineCreationInfos }: { lineCreationInfos: LineCreationInfos }) => { + ({ lineCreationInfos }: { lineCreationInfos: LineCreationDto }) => { return new Promise(() => { // clean unused (required) fields by a simple copy with casting const { @@ -273,7 +273,7 @@ const LineAttachToVoltageLevelDialog = ({ properties, } = lineCreationInfos; - const preparedLine: LineCreationInfos = { + const preparedLine: LineCreationDto = { type, equipmentId, equipmentName, @@ -287,7 +287,7 @@ const LineAttachToVoltageLevelDialog = ({ selectedOperationalLimitsGroupId1, selectedOperationalLimitsGroupId2, properties, - } as LineCreationInfos; + } as LineCreationDto; setAttachmentLine(preparedLine); setValue(`${ATTACHMENT_LINE_ID}`, preparedLine.equipmentId, { @@ -368,7 +368,7 @@ const LineAttachToVoltageLevelDialog = ({ setNewVoltageLevel(preparedVoltageLevel); setValue( - `${CONNECTIVITY}.${VOLTAGE_LEVEL}`, + `${CONNECTIVITY}.${VOLTAGE_LEVEL}` as any, { [ID]: preparedVoltageLevel.equipmentId, }, @@ -377,7 +377,7 @@ const LineAttachToVoltageLevelDialog = ({ shouldDirty: true, } ); - setValue(`${CONNECTIVITY}.${BUS_OR_BUSBAR_SECTION}`, null); + setValue(`${CONNECTIVITY}.${BUS_OR_BUSBAR_SECTION}` as any, null); }); }, [currentNodeUuid, studyUuid, newVoltageLevel?.equipmentId, voltageLevelOptions, setValue] diff --git a/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-form.tsx b/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-form.tsx index 8638025894..150aaa0211 100644 --- a/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-form.tsx +++ b/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-form.tsx @@ -26,6 +26,7 @@ import { VoltageLevelConnectivityForm, TextInput, VoltageLevelOption, + LineCreationDto, } from '@gridsuite/commons-ui'; import { Box } from '@mui/material'; import LineCreationDialog from '../line/creation/line-creation-dialog'; @@ -38,7 +39,6 @@ import { UUID } from 'node:crypto'; import { CurrentTreeNode } from '../../../graph/tree-node.type'; import { ExtendedVoltageLevelCreationInfo, - LineCreationInfos, VoltageLevelCreationInfo, } from '../../../../services/network-modification-types'; import { FetchStatus } from '../../../../services/utils.type'; @@ -48,8 +48,8 @@ interface LineAttachToVoltageLevelFormProps { studyUuid: UUID; currentNode: CurrentTreeNode; currentRootNetworkUuid: UUID; - onLineCreationDo: ({ lineCreationInfos }: { lineCreationInfos: LineCreationInfos }) => Promise; - lineToEdit?: LineCreationInfos; + onLineCreationDo: ({ lineCreationInfos }: { lineCreationInfos: LineCreationDto }) => Promise; + lineToEdit?: LineCreationDto; onVoltageLevelCreationDo: (voltageLevel: VoltageLevelCreationInfo) => Promise; voltageLevelToEdit?: ExtendedVoltageLevelCreationInfo; onAttachmentPointModificationDo: (voltageLevel: VoltageLevelCreationInfo) => Promise; diff --git a/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.ts b/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.ts deleted file mode 100644 index db88e5e53a..0000000000 --- a/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane-utils.ts +++ /dev/null @@ -1,108 +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 { - B1, - B2, - CHARACTERISTICS, - CONNECTIVITY_1, - CONNECTIVITY_2, - G1, - G2, - R, - X, -} from 'components/utils/field-constants'; -import * as yup from 'yup'; -import { LineCharacteristics } from '../modification/line-modification-type'; -import { - Connectivity, - FieldConstants, - getConnectivityWithPositionEmptyFormData, - getConnectivityWithPositionValidationSchema, -} from '@gridsuite/commons-ui'; - -const characteristicsValidationSchema = (id: string, displayConnectivity: boolean, modification: boolean) => ({ - [id]: yup.object().shape({ - [R]: modification - ? yup.number().nullable().min(0, 'mustBeGreaterOrEqualToZero') - : yup.number().nullable().min(0, 'mustBeGreaterOrEqualToZero').required(), - [X]: modification ? yup.number().nullable() : yup.number().nullable().required(), - [B1]: yup.number().nullable(), - [G1]: yup.number().nullable().min(0, 'mustBeGreaterOrEqualToZero'), - [B2]: yup.number().nullable(), - [G2]: yup.number().nullable().min(0, 'mustBeGreaterOrEqualToZero'), - ...(displayConnectivity && getConnectivityWithPositionValidationSchema(false, FieldConstants.CONNECTIVITY_1)), - ...(displayConnectivity && getConnectivityWithPositionValidationSchema(false, FieldConstants.CONNECTIVITY_2)), - }), -}); - -export const getCharacteristicsValidationSchema = ( - id: string, - displayConnectivity: boolean, - modification: boolean = false -) => { - return characteristicsValidationSchema(id, displayConnectivity, modification); -}; - -const characteristicsEmptyFormData = (id: string, displayConnectivity: boolean = true) => ({ - [id]: { - [R]: null, - [X]: null, - [B1]: null, - [G1]: null, - [B2]: null, - [G2]: null, - ...(displayConnectivity && getConnectivityWithPositionEmptyFormData(false, FieldConstants.CONNECTIVITY_1)), - ...(displayConnectivity && getConnectivityWithPositionEmptyFormData(false, FieldConstants.CONNECTIVITY_2)), - }, -}); - -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, - }: LineCharacteristics & { - connectivity1?: Connectivity | null; - connectivity2?: Connectivity | null; - }, - id = CHARACTERISTICS -) => ({ - [id]: { - [R]: r, - [X]: x, - [G1]: g1, - [B1]: b1, - [G2]: g2, - [B2]: b2, - [CONNECTIVITY_1]: connectivity1, - [CONNECTIVITY_2]: connectivity2, - }, -}); - -export const getCharacteristicsWithOutConnectivityFormData = ( - { r, x, g1, b1, g2, b2 }: LineCharacteristics, - id = CHARACTERISTICS -) => ({ - [id]: { - [R]: r, - [X]: x, - [G1]: g1, - [B1]: b1, - [G2]: g2, - [B2]: b2, - }, -}); diff --git a/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.tsx b/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.tsx deleted file mode 100644 index 88160a0579..0000000000 --- a/src/components/dialogs/network-modifications/line/characteristics-pane/line-characteristics-pane.tsx +++ /dev/null @@ -1,193 +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 { - ConnectivityForm, - convertInputValue, - FieldType, - FloatInput, - MicroSusceptanceAdornment, - OhmAdornment, - PropertiesForm, -} from '@gridsuite/commons-ui'; -import { - B1, - B2, - CHARACTERISTICS, - CONNECTIVITY_1, - CONNECTIVITY_2, - G1, - G2, - R, - X, -} from 'components/utils/field-constants'; -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'; -import { BranchInfos } from '../../../../../services/study/network-map.type'; -import PositionDiagramPane from '../../../../grid-layout/cards/diagrams/singleLineDiagram/positionDiagram/position-diagram-pane'; -import { useCallback } from 'react'; -import { fetchBusesOrBusbarSectionsForVoltageLevel } from '../../../../../services/study/network'; - -const styles = { - h3: { - marginTop: 0, - marginBottom: 0, - }, -}; - -interface LineCharacteristicsPaneProps { - id?: string; - studyUuid: UUID; - currentNode: CurrentTreeNode; - currentRootNetworkUuid: UUID; - displayConnectivity: boolean; - lineToModify?: BranchInfos | null; - clearableFields?: boolean; - isModification?: boolean; -} -const LineCharacteristicsPane = ({ - id = CHARACTERISTICS, - studyUuid, - currentNode, - currentRootNetworkUuid, - displayConnectivity, - lineToModify, - clearableFields = false, - isModification = false, -}: LineCharacteristicsPaneProps) => { - const currentNodeUuid = currentNode.id; - const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNodeUuid, currentRootNetworkUuid); - - const fetchBusesOrBusbarSections = useCallback( - (voltageLevelId: string) => - fetchBusesOrBusbarSectionsForVoltageLevel( - studyUuid, - currentNode.id, - currentRootNetworkUuid, - voltageLevelId - ), - [studyUuid, currentNode.id, currentRootNetworkUuid] - ); - - const seriesResistanceField = ( - - ); - - const seriesReactanceField = ( - - ); - - const shuntConductance1Field = ( - - ); - - const shuntSusceptance1Field = ( - - ); - - const shuntConductance2Field = ( - - ); - - const shuntSusceptance2Field = ( - - ); - - const connectivity1Field = ( - - ); - - const connectivity2Field = ( - - ); - - return ( - <> - {displayConnectivity && ( - <> - - - - {connectivity1Field} - - - - {connectivity2Field} - - - )} - - - {seriesResistanceField} - {seriesReactanceField} - - - - {shuntConductance1Field} - {shuntSusceptance1Field} - - - - {shuntConductance2Field} - {shuntSusceptance2Field} - - - - ); -}; - -export default LineCharacteristicsPane; diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.ts b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.ts deleted file mode 100644 index f361b5b61d..0000000000 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog-utils.ts +++ /dev/null @@ -1,46 +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 { EQUIPMENT_ID, EQUIPMENT_NAME, TAB_HEADER } from 'components/utils/field-constants'; -import * as yup from 'yup'; - -const headerValidationSchema = (id: string) => ({ - [id]: yup.object().shape({ - [EQUIPMENT_ID]: yup.string().required(), - [EQUIPMENT_NAME]: yup.string().nullable(), - }), -}); - -export const getHeaderValidationSchema = (id = TAB_HEADER) => { - return headerValidationSchema(id); -}; - -const headerEmptyFormData = (id: string) => ({ - [id]: { - [EQUIPMENT_ID]: '', - [EQUIPMENT_NAME]: '', - }, -}); - -export const getHeaderEmptyFormData = (id = TAB_HEADER) => { - return headerEmptyFormData(id); -}; - -export const getHeaderFormData = ( - { equipmentId, equipmentName = '' }: { equipmentId: string; equipmentName: string | null }, - id = TAB_HEADER -) => ({ - [id]: { - [EQUIPMENT_ID]: equipmentId, - [EQUIPMENT_NAME]: equipmentName, - }, -}); - -export const LineCreationDialogTab = { - CHARACTERISTICS_TAB: 0, - LIMITS_TAB: 1, -}; diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx index 0815e86957..90ed5792ea 100644 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx +++ b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx @@ -7,95 +7,43 @@ import { convertInputValue, - convertOutputValue, copyEquipmentPropertiesForCreation, - creationPropertiesSchema, CustomFormProvider, DeepNullable, - emptyProperties, EquipmentType, FieldConstants, FieldType, - filledTextField, + getAllLimitsFormData, getConnectivityFormData, - getPropertiesFromModification, - ModificationType, - sanitizeString, + getLineCharacteristicsFormData, + LineCreationDto, + lineCreationDtoToForm, + lineCreationEmptyFormData, + LineCreationFormData, + lineCreationFormSchema, + lineCreationFormToDto, + LineForm, + LineFormInfos, snackWithFallback, - TextInput, - toModificationProperties, - UNDEFINED_CONNECTION_DIRECTION, useSnackMessage, } from '@gridsuite/commons-ui'; import { yupResolver } from '@hookform/resolvers/yup'; -import { Box, Grid } from '@mui/material'; import { - B1, - B2, - BUS_OR_BUSBAR_SECTION, - CHARACTERISTICS, - CONNECTED, - CONNECTION_DIRECTION, - CONNECTION_NAME, - CONNECTION_POSITION, - CONNECTIVITY_1, - CONNECTIVITY_2, - EQUIPMENT_ID, - EQUIPMENT_NAME, - FINAL_CURRENT_LIMITS, - G1, - G2, - LIMITS, LINE_SEGMENTS, - OPERATIONAL_LIMITS_GROUPS, - R, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID1, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID2, - TAB_HEADER, - TOTAL_REACTANCE, - TOTAL_RESISTANCE, - TOTAL_SUSCEPTANCE, - VOLTAGE_LEVEL, - X, } from 'components/utils/field-constants'; import { useCallback, useEffect, useState } from 'react'; -import { FieldErrors, useForm } from 'react-hook-form'; +import { useForm } from 'react-hook-form'; import { FetchStatus } from '../../../../../services/utils'; import { FORM_LOADING_DELAY } from 'components/network/constants'; -import * as yup from 'yup'; import { ModificationDialog } from '../../../commons/modificationDialog'; -import LineCharacteristicsPane from '../characteristics-pane/line-characteristics-pane'; -import { - getCharacteristicsEmptyFormData, - getCharacteristicsFormData, - getCharacteristicsValidationSchema, -} from '../characteristics-pane/line-characteristics-pane-utils'; -import { - getHeaderEmptyFormData, - getHeaderFormData, - getHeaderValidationSchema, - LineCreationDialogTab, -} from './line-creation-dialog-utils'; -import { - getAllLimitsFormData, - getLimitsEmptyFormData, - getLimitsValidationSchema, - sanitizeLimitsGroups, -} from '../../../limits/limits-pane-utils'; -import LineDialogTabs from '../line-dialog-tabs'; import EquipmentSearchDialog from 'components/dialogs/equipment-search-dialog'; import { useFormSearchCopy } from 'components/dialogs/commons/use-form-search-copy'; import LineTypeSegmentDialog from '../../../line-types-catalog/line-type-segment-dialog'; import { useOpenShortWaitFetching } from 'components/dialogs/commons/handle-modification-form'; import { createLine } from '../../../../../services/study/network-modifications'; -import GridItem from '../../../commons/grid-item'; import { formatCompleteCurrentLimit } from '../../../../utils/utils'; -import { LimitsPane } from '../../../limits/limits-pane'; -import { LineCreationInfos } from '../../../../../services/network-modification-types'; -import { LineModificationFormSchema } from '../modification/line-modification-type'; import { ComputedLineCharacteristics } from '../../../line-types-catalog/line-catalog.type'; -import { LineCreationFormSchema, LineFormInfos } from './line-creation-type'; import { isNodeBuilt } from 'components/graph/util/model-functions'; import { NetworkModificationDialogProps } from '../../../../graph/menus/network-modifications/network-modification-menu.type'; import { @@ -103,17 +51,13 @@ import { convertToLineSegmentInfos, SegmentFormData, } from '../../../line-types-catalog/segment-utils'; - -const emptyFormData: any = { - ...getHeaderEmptyFormData(), - ...getCharacteristicsEmptyFormData(), - ...getLimitsEmptyFormData(false), - ...emptyProperties, - [LINE_SEGMENTS]: [], -}; +import PositionDiagramPane + from '../../../../grid-layout/cards/diagrams/singleLineDiagram/positionDiagram/position-diagram-pane'; +import useVoltageLevelsListInfos from '../../../../../hooks/use-voltage-levels-list-infos'; +import { fetchBusesOrBusbarSectionsForVoltageLevel } from '../../../../../services/study/network'; type LineCreationDialogProps = NetworkModificationDialogProps & { - editData?: LineCreationInfos; // contains data when we try to edit an existing hypothesis + editData?: LineCreationDto; // contains data when we try to edit an existing hypothesis onCreateLine: typeof createLine; displayConnectivity?: boolean; }; @@ -143,9 +87,7 @@ const LineCreationDialog = ({ }: Readonly) => { const currentNodeUuid = currentNode?.id; const { snackError } = useSnackMessage(); - - const [tabIndex, setTabIndex] = useState(LineCreationDialogTab.CHARACTERISTICS_TAB); - const [tabIndexesWithError, setTabIndexesWithError] = useState([]); + const voltageLevelOptions = useVoltageLevelsListInfos(studyUuid, currentNode?.id, currentRootNetworkUuid); const [isOpenLineTypesCatalogDialog, setIsOpenLineTypesCatalogDialog] = useState(false); @@ -153,37 +95,21 @@ const LineCreationDialog = ({ setIsOpenLineTypesCatalogDialog(false); }; - const formSchema = yup - .object() - .shape({ - ...getHeaderValidationSchema(), - ...getCharacteristicsValidationSchema(CHARACTERISTICS, displayConnectivity), - ...getLimitsValidationSchema(), - }) - .concat(creationPropertiesSchema) - .required(); - - const formMethods = useForm({ - defaultValues: emptyFormData, - resolver: yupResolver(formSchema), + const formMethods = useForm>({ + defaultValues: lineCreationEmptyFormData, + resolver: yupResolver>(lineCreationFormSchema), }); + const { reset, setValue, watch } = formMethods; - const editSegmentValue = watch(LINE_SEGMENTS); + // TODO DBR + //const editSegmentValue = watch(LINE_SEGMENTS); const fromSearchCopyToFormValues = (line: LineFormInfos) => { const formData = { - ...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), + equipmentID: line.id + '(1)', + equipmentName: line.name ?? '', + connectivity: { ...getConnectivityFormData( { voltageLevelId: line.voltageLevelId1, @@ -206,6 +132,14 @@ const LineCreationDialog = ({ }, FieldConstants.CONNECTIVITY_2 ), + }, + ...getLineCharacteristicsFormData({ + 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), }), ...getAllLimitsFormData( formatCompleteCurrentLimit(line.currentLimits), @@ -219,55 +153,8 @@ const LineCreationDialog = ({ }; const fromEditDataToFormValues = useCallback( - (line: LineCreationInfos) => { - const formData = { - ...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, - }, - FieldConstants.CONNECTIVITY_1 - ), - ...getConnectivityFormData( - { - busbarSectionId: line.busOrBusbarSectionId2, - connectionDirection: line.connectionDirection2, - connectionName: line.connectionName2, - connectionPosition: line.connectionPosition2, - voltageLevelId: line.voltageLevelId2, - terminalConnected: line.connected2, - }, - FieldConstants.CONNECTIVITY_2 - ), - }), - ...getAllLimitsFormData( - line?.operationalLimitsGroups?.map(({ id, ...baseData }) => ({ - ...baseData, - name: id, - id: id + baseData.applicability, - })), - line?.selectedOperationalLimitsGroupId1 ?? null, - line?.selectedOperationalLimitsGroupId2 ?? null - ), - ...getPropertiesFromModification(line.properties), - [LINE_SEGMENTS]: line.lineSegments, - }; - reset(formData, { keepDefaultValues: true }); + (line: LineCreationDto) => { + reset(lineCreationDtoToForm(line)); }, [reset] ); @@ -280,6 +167,7 @@ const LineCreationDialog = ({ } }, [fromEditDataToFormValues, editData]); + /* TODO DBR const handleLineSegmentsBuildSubmit = ( data: ComputedLineCharacteristics, lineSegments: DeepNullable[] @@ -302,45 +190,12 @@ const LineCreationDialog = ({ ); setValue(LINE_SEGMENTS, convertToLineSegmentInfos(lineSegments)); }; + */ const onSubmit = useCallback( - (line: LineCreationFormSchema) => { - const header = line[TAB_HEADER]; - const characteristics = line[CHARACTERISTICS]; - const limits = line[LIMITS]; - const segments = line[LINE_SEGMENTS]; - const lineCreationInfos: LineCreationInfos = { - type: ModificationType.LINE_CREATION, - equipmentId: header[EQUIPMENT_ID], - equipmentName: sanitizeString(header[EQUIPMENT_NAME]), - r: characteristics[R] ?? null, - x: characteristics[X] ?? null, - g1: convertOutputValue(FieldType.G1, characteristics[G1]), - b1: convertOutputValue(FieldType.B1, characteristics[B1]), - g2: convertOutputValue(FieldType.G2, characteristics[G2]), - b2: convertOutputValue(FieldType.B2, characteristics[B2]), - voltageLevelId1: characteristics[CONNECTIVITY_1]?.[VOLTAGE_LEVEL]?.id ?? null, - busOrBusbarSectionId1: characteristics[CONNECTIVITY_1]?.[BUS_OR_BUSBAR_SECTION]?.id ?? null, - voltageLevelId2: characteristics[CONNECTIVITY_2]?.[VOLTAGE_LEVEL]?.id ?? null, - busOrBusbarSectionId2: characteristics[CONNECTIVITY_2]?.[BUS_OR_BUSBAR_SECTION]?.id ?? null, - operationalLimitsGroups: sanitizeLimitsGroups(limits[OPERATIONAL_LIMITS_GROUPS] ?? []), - selectedOperationalLimitsGroupId1: limits[SELECTED_OPERATIONAL_LIMITS_GROUP_ID1] ?? null, - selectedOperationalLimitsGroupId2: limits[SELECTED_OPERATIONAL_LIMITS_GROUP_ID2] ?? null, - connectionName1: sanitizeString(characteristics[CONNECTIVITY_1]?.[CONNECTION_NAME]), - connectionDirection1: - characteristics[CONNECTIVITY_1]?.[CONNECTION_DIRECTION] ?? UNDEFINED_CONNECTION_DIRECTION, - connectionName2: sanitizeString(characteristics[CONNECTIVITY_2]?.[CONNECTION_NAME]), - connectionDirection2: - characteristics[CONNECTIVITY_2]?.[CONNECTION_DIRECTION] ?? UNDEFINED_CONNECTION_DIRECTION, - connectionPosition1: characteristics[CONNECTIVITY_1]?.[CONNECTION_POSITION] ?? null, - connectionPosition2: characteristics[CONNECTIVITY_2]?.[CONNECTION_POSITION] ?? null, - connected1: characteristics[CONNECTIVITY_1]?.[CONNECTED] ?? null, - connected2: characteristics[CONNECTIVITY_2]?.[CONNECTED] ?? null, - properties: toModificationProperties(line), - lineSegments: segments, - } satisfies LineCreationInfos; + (lineForm: LineCreationFormData) => { onCreateLine({ - lineCreationInfos, + lineCreationInfos: lineCreationFormToDto(lineForm), studyUuid: studyUuid, nodeUuid: currentNodeUuid, modificationUuid: editData ? editData.uuid : undefined, @@ -352,57 +207,7 @@ const LineCreationDialog = ({ [onCreateLine, studyUuid, currentNodeUuid, editData, snackError] ); - const onValidationError = (errors: FieldErrors) => { - let tabsInError = []; - if (errors?.[CHARACTERISTICS] !== undefined) { - tabsInError.push(LineCreationDialogTab.CHARACTERISTICS_TAB); - } - - if (errors?.[LIMITS] !== undefined) { - tabsInError.push(LineCreationDialogTab.LIMITS_TAB); - } - - if (tabsInError.includes(tabIndex)) { - // error in current tab => do not change tab systematically but remove current tab in error list - setTabIndexesWithError(tabsInError.filter((errorTabIndex) => errorTabIndex !== tabIndex)); - } else if (tabsInError.length > 0) { - // switch to the first tab in the list then remove the tab in the error list - setTabIndex(tabsInError[0]); - setTabIndexesWithError(tabsInError.filter((errorTabIndex, index, arr) => errorTabIndex !== arr[0])); - } - }; - - const clear = useCallback(() => { - reset(emptyFormData); - }, [reset]); - - const lineIdField = ( - - ); - - const lineNameField = ( - - ); - - const headerAndTabs = ( - - - {lineIdField} - {lineNameField} - - - - ); + const clear = useCallback(() => reset(lineCreationEmptyFormData), [reset]); const open = useOpenShortWaitFetching({ isDataFetched: @@ -410,16 +215,38 @@ const LineCreationDialog = ({ delay: FORM_LOADING_DELAY, }); + const fetchBusesOrBusbarSections = useCallback( + (voltageLevelId: string) => + fetchBusesOrBusbarSectionsForVoltageLevel( + studyUuid, + currentNodeUuid, + currentRootNetworkUuid, + voltageLevelId + ), + [studyUuid, currentNodeUuid, currentRootNetworkUuid] + ); + + /* TODO DBR restore + + */ + return ( - + setIsOpenLineTypesCatalogDialog(true)} searchCopy={searchCopy} PaperProps={{ @@ -431,19 +258,11 @@ const LineCreationDialog = ({ isDataFetching={isUpdate && editDataFetchStatus === FetchStatus.RUNNING} {...dialogProps} > - - - - + - ); 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 deleted file mode 100644 index c20c840f30..0000000000 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-type.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2025, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { CurrentLimitsData } from '../../../../../services/study/network-map.type'; -import { - CHARACTERISTICS, - CONNECTIVITY_1, - CONNECTIVITY_2, - EQUIPMENT_ID, - EQUIPMENT_NAME, - LIMITS, - LINE_SEGMENTS, - OPERATIONAL_LIMITS_GROUPS, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID1, - SELECTED_OPERATIONAL_LIMITS_GROUP_ID2, - TAB_HEADER, -} from '../../../../utils/field-constants'; -import { OperationalLimitsGroupFormSchema } from '../../../limits/operational-limits-groups-types'; -import { LineCharacteristics } from '../modification/line-modification-type'; -import { ConnectablePositionInfos, Connectivity, FieldConstants, Property } from '@gridsuite/commons-ui'; -import { LineSegmentInfos } from '../../../../../services/network-modification-types'; - -export interface LineCreationFormSchema { - [TAB_HEADER]: { - [EQUIPMENT_ID]: string; - [EQUIPMENT_NAME]?: string | null; - }; - [CHARACTERISTICS]: LineCharacteristics & { - [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; - }; - [FieldConstants.ADDITIONAL_PROPERTIES]?: Property[]; - [LINE_SEGMENTS]: LineSegmentInfos[] | undefined; -} - -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: ConnectablePositionInfos; - connectablePosition2: ConnectablePositionInfos; - currentLimits: CurrentLimitsData[]; - properties: Record; -} diff --git a/src/components/dialogs/network-modifications/line/line-dialog-tabs.tsx b/src/components/dialogs/network-modifications/line/line-dialog-tabs.tsx deleted file mode 100644 index 6dd298b478..0000000000 --- a/src/components/dialogs/network-modifications/line/line-dialog-tabs.tsx +++ /dev/null @@ -1,70 +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, Tab, Tabs } from '@mui/material'; -import { FormattedMessage } from 'react-intl'; -import { getTabIndicatorStyle, getTabStyle } from '../../../utils/tab-utils'; -import { LineCreationDialogTab } from './creation/line-creation-dialog-utils'; -import { LineModificationDialogTab } from './line-utils'; - -interface LineDialogTabsProps { - tabIndex: number; - tabIndexesWithError: number[]; - setTabIndex: (index: number) => void; - isModification?: boolean; -} - -const LineDialogTabs = ({ - tabIndex, - tabIndexesWithError, - setTabIndex, - isModification = false, -}: LineDialogTabsProps) => { - return ( - - setTabIndex(newValue)} - TabIndicatorProps={{ - sx: getTabIndicatorStyle(tabIndexesWithError, tabIndex), - }} - > - {isModification && ( - } - sx={getTabStyle(tabIndexesWithError, LineModificationDialogTab.CONNECTIVITY_TAB)} - /> - )} - } - sx={getTabStyle( - tabIndexesWithError, - isModification - ? LineModificationDialogTab.CHARACTERISTICS_TAB - : LineCreationDialogTab.CHARACTERISTICS_TAB - )} - /> - } - sx={getTabStyle( - tabIndexesWithError, - isModification ? LineModificationDialogTab.LIMITS_TAB : LineCreationDialogTab.LIMITS_TAB - )} - /> - {isModification && ( - } - sx={getTabStyle(tabIndexesWithError, LineModificationDialogTab.STATE_ESTIMATION_TAB)} - /> - )} - - - ); -}; - -export default LineDialogTabs; diff --git a/src/components/dialogs/network-modifications/line/modification/line-dialog-tabs.tsx b/src/components/dialogs/network-modifications/line/modification/line-dialog-tabs.tsx new file mode 100644 index 0000000000..5a977a49a2 --- /dev/null +++ b/src/components/dialogs/network-modifications/line/modification/line-dialog-tabs.tsx @@ -0,0 +1,51 @@ +/** + * 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, Tab, Tabs } from '@mui/material'; +import { FormattedMessage } from 'react-intl'; +import { getTabIndicatorStyle, getTabStyle } from '../../../../utils/tab-utils'; +import { LineModificationDialogTab } from './line-utils'; + +interface LineDialogTabsProps { + tabIndex: number; + tabIndexesWithError: number[]; + setTabIndex: (index: number) => void; +} + +const LineDialogTabs = ({ tabIndex, tabIndexesWithError, setTabIndex }: LineDialogTabsProps) => { + return ( + + setTabIndex(newValue)} + TabIndicatorProps={{ + sx: getTabIndicatorStyle(tabIndexesWithError, tabIndex), + }} + > + } + sx={getTabStyle(tabIndexesWithError, LineModificationDialogTab.CONNECTIVITY_TAB)} + /> + } + sx={getTabStyle(tabIndexesWithError, LineModificationDialogTab.CHARACTERISTICS_TAB)} + /> + } + sx={getTabStyle(tabIndexesWithError, LineModificationDialogTab.LIMITS_TAB)} + /> + } + sx={getTabStyle(tabIndexesWithError, LineModificationDialogTab.STATE_ESTIMATION_TAB)} + /> + + + ); +}; + +export default LineDialogTabs; diff --git a/src/components/dialogs/network-modifications/line/modification/line-modification-dialog-header.tsx b/src/components/dialogs/network-modifications/line/modification/line-modification-dialog-header.tsx index 55f19fa507..7aaa0fa962 100644 --- a/src/components/dialogs/network-modifications/line/modification/line-modification-dialog-header.tsx +++ b/src/components/dialogs/network-modifications/line/modification/line-modification-dialog-header.tsx @@ -7,10 +7,9 @@ import { EQUIPMENT_NAME } from 'components/utils/field-constants'; import { Box, Grid, TextField } from '@mui/material'; -import LineDialogTabs from '../line-dialog-tabs'; -import { filledTextField, TextInput } from '@gridsuite/commons-ui'; +import LineDialogTabs from './line-dialog-tabs'; +import { BranchInfos, filledTextField, TextInput } from '@gridsuite/commons-ui'; import GridItem from '../../../commons/grid-item'; -import { BranchInfos } from '../../../../../services/study/network-map.type'; export interface LineModificationDialogHeaderProps { lineToModify: BranchInfos | null; @@ -63,12 +62,7 @@ const LineModificationDialogHeader = ({ {lineIdField} {lineNameField} - + ); }; diff --git a/src/components/dialogs/network-modifications/line/modification/line-modification-dialog-tabs.tsx b/src/components/dialogs/network-modifications/line/modification/line-modification-dialog-tabs.tsx index c85d4302ca..e731d3097f 100644 --- a/src/components/dialogs/network-modifications/line/modification/line-modification-dialog-tabs.tsx +++ b/src/components/dialogs/network-modifications/line/modification/line-modification-dialog-tabs.tsx @@ -6,14 +6,17 @@ */ import { Box } from '@mui/material'; -import LineCharacteristicsPane from '../characteristics-pane/line-characteristics-pane'; -import { LineModificationDialogTab } from '../line-utils'; -import { LimitsPane } from '../../../limits/limits-pane'; +import { LineModificationDialogTab } from './line-utils'; import type { UUID } from 'node:crypto'; import { CurrentTreeNode } from '../../../../graph/tree-node.type'; -import { BranchInfos } from '../../../../../services/study/network-map.type'; import { JSX, useCallback } from 'react'; -import { BranchActiveReactivePowerMeasurementsForm, BranchConnectivityForm } from '@gridsuite/commons-ui'; +import { + BranchActiveReactivePowerMeasurementsForm, + BranchConnectivityForm, + BranchInfos, + LimitsPane, + LineCharacteristicsPane, +} from '@gridsuite/commons-ui'; import PositionDiagramPane from '../../../../grid-layout/cards/diagrams/singleLineDiagram/positionDiagram/position-diagram-pane'; import useVoltageLevelsListInfos from '../../../../../hooks/use-voltage-levels-list-infos'; import { fetchBusesOrBusbarSectionsForVoltageLevel } from '../../../../../services/study/network'; @@ -57,19 +60,11 @@ const LineModificationDialogTabs = ({ /> ); 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 cd168e71bd..a47e7b6ca0 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 @@ -50,6 +50,8 @@ import { SwitchInput, useSnackMessage, SegmentInfoFormData, + SegmentsFormData, + FieldConstants, } from '@gridsuite/commons-ui'; import { getLineTypesCatalog, getLineTypeWithLimits } from '../../../services/network-modification'; import GridItem from '../commons/grid-item'; @@ -72,11 +74,16 @@ const styles = { } as const satisfies MuiStyles; export interface LineTypeSegmentFormProps { - editData?: LineSegmentsFormData | null; + editDataCreationCase?: LineSegmentsFormData | null; + editDataModificationCase?: SegmentsFormData; isModification: boolean; } -export const LineTypeSegmentForm: FunctionComponent = ({ editData, isModification }) => { +export const LineTypeSegmentForm: FunctionComponent = ({ + editDataCreationCase, + editDataModificationCase, + isModification, +}) => { const { setValue, getValues, clearErrors, watch } = useFormContext(); const [lineTypesCatalog, setLineTypesCatalog] = useState([]); const [openCatalogDialogIndex, setOpenCatalogDialogIndex] = useState(null); @@ -231,12 +238,23 @@ export const LineTypeSegmentForm: FunctionComponent = }, []); useEffect(() => { - if (!editData?.length) { + if (isModification && !editDataModificationCase?.[FieldConstants.LINE_SEGMENTS]?.length) { + return; + } + if (!isModification && !editDataCreationCase?.length) { return; } arrayRef.current?.replaceItems([]); const updateSegmentsLimits = async () => { - const promises = editData.map((segment) => getSegmentLimits(segment)); + let promises; + if (!isModification && editDataCreationCase) { + promises = editDataCreationCase.map((segment) => getSegmentLimits(segment)); + } + if (isModification && editDataModificationCase) { + promises = editDataModificationCase[FieldConstants.LINE_SEGMENTS]?.map((segment) => + getSegmentLimits(segment) + ); + } try { if (promises) { @@ -254,11 +272,19 @@ export const LineTypeSegmentForm: FunctionComponent = updateSegmentsLimits().then(() => { updateTotals(); keepMostConstrainingLimits(); - // TODO DBR add applySegmentsLimits in main schema ? - //setValue(APPLY_SEGMENTS_LIMITS, editData?.applySegmentsLimits ?? true); - setValue(APPLY_SEGMENTS_LIMITS, true); + const applyLimits = isModification ? (editDataModificationCase?.applySegmentsLimits ?? true) : true; + setValue(APPLY_SEGMENTS_LIMITS, applyLimits); }); - }, [editData, getSegmentLimits, snackError, updateTotals, keepMostConstrainingLimits, setValue]); + }, [ + editDataCreationCase, + editDataModificationCase, + isModification, + getSegmentLimits, + snackError, + updateTotals, + keepMostConstrainingLimits, + setValue, + ]); const onSelectCatalogLine = useCallback( (selectedLine: LineTypeInfo, selectedAreaAndTemperature2LineTypeData: AreaTemperatureShapeFactorInfo) => { diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx index b50161437a..54c6c35f20 100644 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx +++ b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx @@ -158,18 +158,30 @@ const LineCreationDialog = ({ }, [fromEditDataToFormValues, editData]); const handleLineSegmentsBuildSubmit = (data: ComputedLineCharacteristics, lineSegments: LineSegmentsFormData) => { - setValue(`${FieldConstants.CHARACTERISTICS}.${FieldConstants.R}` as any, data[FieldConstants.TOTAL_RESISTANCE], { - shouldDirty: true, - }); + setValue( + `${FieldConstants.CHARACTERISTICS}.${FieldConstants.R}` as any, + data[FieldConstants.TOTAL_RESISTANCE], + { + shouldDirty: true, + } + ); setValue(`${FieldConstants.CHARACTERISTICS}.${FieldConstants.X}` as any, data[FieldConstants.TOTAL_REACTANCE], { shouldDirty: true, }); - setValue(`${FieldConstants.CHARACTERISTICS}.${FieldConstants.B1}` as any, data[FieldConstants.TOTAL_SUSCEPTANCE] / 2, { - shouldDirty: true, - }); - setValue(`${FieldConstants.CHARACTERISTICS}.${FieldConstants.B2}` as any, data[FieldConstants.TOTAL_SUSCEPTANCE] / 2, { - shouldDirty: true, - }); + setValue( + `${FieldConstants.CHARACTERISTICS}.${FieldConstants.B1}` as any, + data[FieldConstants.TOTAL_SUSCEPTANCE] / 2, + { + shouldDirty: true, + } + ); + setValue( + `${FieldConstants.CHARACTERISTICS}.${FieldConstants.B2}` as any, + data[FieldConstants.TOTAL_SUSCEPTANCE] / 2, + { + shouldDirty: true, + } + ); setValue( `${FieldConstants.LIMITS}.${FieldConstants.OPERATIONAL_LIMITS_GROUPS}` as any, convertLimitsToOperationalLimitsGroupFormSchema(data[FieldConstants.FINAL_CURRENT_LIMITS]) @@ -252,8 +264,8 @@ const LineCreationDialog = ({ 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 61bf1deb8d..17e67b88d6 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 @@ -505,9 +505,8 @@ const LineModificationDialog = ({ )} From 7ac6bdf988ecd7574a36408f537beb1eb94d39a9 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Wed, 24 Jun 2026 20:37:54 +0200 Subject: [PATCH 08/11] mv translations Signed-off-by: David BRAQUART --- src/translations/en.json | 30 ++---------------------------- src/translations/fr.json | 29 ++--------------------------- 2 files changed, 4 insertions(+), 55 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 136b9ce9a4..9bdc17f6a7 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -426,11 +426,9 @@ "launchComputationConfirmQuestion": "You are about to start a computation although a change to the computation parameters is in progress.", "ViewOnSpreadsheet": "View in spreadsheet", - "DeleteFromMenu": "Delete", "ModifyFromMenu": "Modify", "ModifyCharacteristics": "Modify characteristics", "Rename": "Rename", - "Duplicate": "Duplicate", "SubstationList": "Substation list", "NetworkModificationTree": "Network modification tree", @@ -533,20 +531,16 @@ "LoadCreationError": "Error while creating load", "LoadModificationError": "Error while modifying load", - "LineCreationError": "Error while creating line", "LineModificationError": "Error while modifying line", "HVDCLineConverterStationError": "Error while getting HVDC line converter stations", "StaticVarCompensatorCreationError": "Error while creating static var compensator", "LccCreationError": "Error while creating HVDC (LCC)", "LimitSetNamingError": "Two limits sets can't have the same name and be applicable on a same side.", - "LimitSetApplicabilityError": "2 limit sets with the same name must have different application sides.", "LimitSetCreationEmptyError": "Limit sets names can't be empty", - "LimitSets": "Limit sets", "LimitSetsTabularModification": "Tabular modification of limits sets", "TabularLimitSets": "Limit sets", "amountTemporaryLimits": "Number of temporary limits", "amountTemporaryLimitsError": "Limits number between 1 and 50", - "permanentLimit": "PATL", "acceptableDuration": "Duration (s)", "value": "Value (A)", "side": "Side", @@ -589,12 +583,10 @@ "reactiveCapabilityCurveQminPmax": "QminPmax", "reactiveCapabilityCurveQmaxPmax": "QmaxPmax", "ModifyLine": "Modify line", - "CreateLine": "Create line", "CreateVsc": "Create HVDC (VSC)", "CreateLcc": "Create HVDC (LCC)", "Filters": "Filters", "Side": "Side", - "BothSides": "Both Sides", "QatNominalV": "Q at nominal voltage", "ModeAutomaton": "Mode", "StandbyAutomaton": "Standby", @@ -604,12 +596,6 @@ "LowVoltageThreshold": "Low voltage threshold", "HighVoltageThreshold": "High voltage threshold", "LimitsTab": "Limits", - "TemporaryCurrentLimitsText": "Temporary current limits", - "TemporaryLimitDuration": "Duration (s)", - "TemporaryLimitName": "Name", - "TemporaryLimitValue": "Value (A)", - "TemporaryLimitNameUnicityError": "Temporary limit names must be unique in the table", - "TemporaryLimitDurationUnicityError": "Temporary limit acceptable durations must be unique in the table", "EstimateTapPositionSection": "Estimate tap position", "RatioTapChangerEstimateTapPosition": "Ratio tap changer", "PhaseTapChangerEstimateTapPosition": "Phase tap changer", @@ -710,7 +696,6 @@ "GenerationDispatchError": "Error while creating a generation dispatch", "lineAttachedToSplitLineVoltageLevel": "Voltage level with attached line to place as splitter", - "PermanentCurrentLimitText": "PATL", "permanentCurrentLimitMustBeGreaterThanZero": "The permanent current limit value must be greater than 0", "permanentCurrentLimitMandatory": "A permanent limit has to be set if the limit set contains temporary current limits", "qMaxAtNominalVMustBeGreaterThanZero": "The Qmax available at nominal voltage value must be greater than 0", @@ -722,13 +707,8 @@ "maxPMustBeGreaterOrEqualToZero": "The maximum active power value must be greater than 0", "activePowerSetpointMinValueError": "The active power value must be greater than 0", "activePowerSetpointMaxValueError": "The active power value must be less than the maximum active power value", - "PermanentCurrentLimitText1": "PATL 1", - "PermanentCurrentLimitText2": "PATL 2", - "SelectedOperationalLimitGroup": "Active limit set", - "SelectedOperationalLimitGroups": "Active limit sets", - "Applicability": "Applicability", - "UniqueName": "Name should be unique", - "FieldNotEmpty": "Field should not be empty", + "PermanentCurrentLimitText1": "IST 1", + "PermanentCurrentLimitText2": "IST 2", "CreateTwoWindingsTransformer": "Create a 2 windings transformer", "ModifyTwoWindingsTransformer": "Modify a 2 windings transformer", @@ -1265,9 +1245,6 @@ "importCaseFailure": "Unable to import case", "networkModificationActivationByRootNetworkError": "An error occurred while enabling / disabling modification by root network", - "unlimited": "Unlimited", - "forbidden": "Forbidden", - "StateEstimationParametersGeneralTabLabel": "General", "StateEstimationParametersWeightsTabLabel": "Weights", "StateEstimationParametersQualityTabLabel": "Thresholds", @@ -1436,13 +1413,10 @@ "Array": "Array", "Boolean": "Boolean", "Enum": "Enumeration", - "Edit": "Edit", - "View": "View", "nodeType": "Type", "nodeStatus": "Status", "CONSTRUCTION": "Construction", "SECURITY": "Security", - "disableOLGedition": "Entered limit sets data will be lost. Do you wish to continue?", "MOVE_VOLTAGE_LEVEL_FEEDER_BAYS": "Move connections", "TOP": "Top", "BOTTOM": "Bottom", diff --git a/src/translations/fr.json b/src/translations/fr.json index 6b99f288c4..ac77cd6e59 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -425,11 +425,9 @@ "launchComputationConfirmQuestion": "Vous êtes sur le point de lancer un calcul alors qu'une modification des paramètres de calcul est en cours.", "ViewOnSpreadsheet": "Visualiser dans le tableur", - "DeleteFromMenu": "Supprimer", "ModifyFromMenu": "Modifier", "ModifyCharacteristics": "Modifier les caractéristiques", "Rename": "Renommer", - "Duplicate": "Dupliquer", "SubstationList": "Liste des postes", "NetworkModificationTree": "Arbre décisionnel", @@ -530,20 +528,16 @@ "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", "LineModificationError": "Erreur lors de la modification d'une ligne", "HVDCLineConverterStationError": "Erreur lors de l'accès aux stations de conversion de la ligne HVDC", "StaticVarCompensatorCreationError": "Erreur lors de la création d'un cspr", "LccCreationError": "Erreur lors de la création d'une HVDC (LCC)", "LimitSetNamingError": "Deux jeux de limites ne peuvent pas avoir le même nom et s'appliquer d'un même côté.", - "LimitSetApplicabilityError": "2 jeux de limites de même nom doivent s'appliquer sur des côtés différents.", "LimitSetCreationEmptyError": "Les noms de jeux de limites ne peuvent pas être vides.", - "LimitSets": "Jeux de limites", "LimitSetsTabularModification": "Modification tabulaire de jeux de limites", "TabularLimitSets": "Jeux de limite", "amountTemporaryLimits": "Nombre de limites temporaires", "amountTemporaryLimitsError": "Nombre de limites entre 1 et 50", - "permanentLimit": "IST", "acceptableDuration": "Tempo (s)", "value": "Valeur (A)", "side": "Côté", @@ -585,11 +579,9 @@ "reactiveCapabilityCurveQminPmax": "QminPmax", "reactiveCapabilityCurveQmaxPmax": "QmaxPmax", "ModifyLine": "Modifier une ligne", - "CreateLine": "Créer une ligne", "CreateVsc": "Création d'une HVDC (VSC)", "CreateLcc": "Création d'une HVDC (LCC)", "Filters": "Filtres", - "BothSides": "Ouvrage", "Side": "Côté", "QatNominalV": "Q à tension nominale", "ModeAutomaton": "Mode", @@ -601,12 +593,6 @@ "HighVoltageThreshold": "U activation haut", "b0": "Susceptance fixe", "LimitsTab": "Limites", - "TemporaryCurrentLimitsText": "Limites d'intensité temporaires", - "TemporaryLimitDuration": "Tempo (s)", - "TemporaryLimitName": "Nom", - "TemporaryLimitValue": "Valeur (A)", - "TemporaryLimitNameUnicityError": "Les noms des limites temporaires doivent être uniques dans la table", - "TemporaryLimitDurationUnicityError": "Les tempos des limites temporaires doivent être uniques dans la table", "EstimateTapPositionSection": "Estimer prise", "RatioTapChangerEstimateTapPosition": "Régleur", "PhaseTapChangerEstimateTapPosition": "Déphaseur", @@ -703,14 +689,8 @@ "GenerationDispatchError": "Erreur lors de la création du démarrage de groupes", "lineAttachedToSplitLineVoltageLevel" : "Poste en piquage à passer en coupure", - "PermanentCurrentLimitText": "IST", "permanentCurrentLimitMustBeGreaterThanZero": "La valeur IST doit être supérieure à 0", "permanentCurrentLimitMandatory": "Un IST doit être fourni si le jeu de limites contient des limites d'intensité temporaires", - "SelectedOperationalLimitGroup": "Jeu de limites actif", - "SelectedOperationalLimitGroups": "Jeux de limites actifs", - "Applicability": "Applicable sur", - "UniqueName": "Le nom doit être unique", - "FieldNotEmpty": "le champs ne doit pas être vide", "qMaxAtNominalVMustBeGreaterThanZero": "La valeur Q installée à tension nominale doit être supérieure à 0", "powerFactorMaxValueError": "La valeur du coefficient Q/P doit être inférieure à 1", @@ -721,8 +701,8 @@ "maxPMustBeGreaterOrEqualToZero": "La valeur de la puissance active max doit être supérieure à 0", "activePowerSetpointMinValueError": "La valeur de la puissance active doit être supérieure à 0", "activePowerSetpointMaxValueError": "La valeur de la puissance active doit être inférieure à la valeur de la puissance active max", - "PermanentCurrentLimitText1": "IST 1", - "PermanentCurrentLimitText2": "IST 2", + "PermanentCurrentLimitText1": "PATL 1", + "PermanentCurrentLimitText2": "PATL 2", "CreateTwoWindingsTransformer": "Créer un transformateur à 2 enroulements", "ModifyTwoWindingsTransformer": "Modifier un transformateur à 2 enroulements", @@ -1262,8 +1242,6 @@ "rootNetwork.loading": "Chargement...", "rootNetwork.originalNotFound": "La situation d'origine est introuvable", "rootNetwork.unknownPath": "", - "unlimited": "Illimité", - "forbidden": "Interdit", "StateEstimationParametersGeneralTabLabel": "Général", "StateEstimationParametersWeightsTabLabel": "Pondération", @@ -1432,13 +1410,10 @@ "Array": "Tableau", "Boolean": "Booléen", "Enum": "Énumération", - "Edit": "Édition", - "View": "Visualisation", "nodeType": "Type", "nodeStatus": "Statut", "CONSTRUCTION": "Construction", "SECURITY": "Sécurité", - "disableOLGedition": "Les données saisies pour les jeux de limites ne seront pas conservées. Voulez-vous continuer ?", "MOVE_VOLTAGE_LEVEL_FEEDER_BAYS": "Déplacer les départs", "TOP": "Haut", "BOTTOM": "Bas", From c1c8748bf2ec9161b581fdbca8897533e6fb12d0 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Thu, 25 Jun 2026 16:23:34 +0200 Subject: [PATCH 09/11] fix to rm connectivity tab from piquage Signed-off-by: David BRAQUART --- package-lock.json | 6 +++--- package.json | 2 +- .../line-attach-to-voltage-level-form.tsx | 1 + .../line/creation/line-creation-dialog.tsx | 4 ++++ .../line/modification/line-modification-dialog.tsx | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41753fb933..c499f955ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@gridsuite/commons-ui": "0.236.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.236.0.tgz", "@hello-pangea/dnd": "^18.0.1", "@hookform/resolvers": "^4.1.3", "@mui/icons-material": "^6.5.0", @@ -3288,8 +3288,8 @@ }, "node_modules/@gridsuite/commons-ui": { "version": "0.236.0", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.236.0.tgz", - "integrity": "sha512-MFbfFK/preckX5EzVnjTaxNMNMezaC3eTnQw9x/Bjj479sxedX8PUukEVnYFqVJ45v8z+82Gp8GbdgFffZfiag==", + "resolved": "file:../commons-ui/gridsuite-commons-ui-0.236.0.tgz", + "integrity": "sha512-Xk4uTMWRgkPgBbqqtLbZO1UhOp/NMAZRzl/fO37MKAr1fUhcx6TB1lJYlWad6bqo4ZP5G8+mJa/jGwVrDfbEYQ==", "license": "MPL-2.0", "dependencies": { "@ag-grid-community/locale": "^35.3.1", diff --git a/package.json b/package.json index b5f02408b2..b05905c99d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@gridsuite/commons-ui": "0.236.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.236.0.tgz", "@hello-pangea/dnd": "^18.0.1", "@hookform/resolvers": "^4.1.3", "@mui/icons-material": "^6.5.0", diff --git a/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-form.tsx b/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-form.tsx index 2edb15a6a7..150aaa0211 100644 --- a/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-form.tsx +++ b/src/components/dialogs/network-modifications/line-attach-to-voltage-level/line-attach-to-voltage-level-form.tsx @@ -264,6 +264,7 @@ const LineAttachToVoltageLevelForm = ({ currentNode={currentNode} studyUuid={studyUuid} currentRootNetworkUuid={currentRootNetworkUuid} + displayConnectivity={false} onCreateLine={onLineCreationDo} editData={lineToEdit} isUpdate={isUpdate} diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx index 54c6c35f20..291f5801a4 100644 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx +++ b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx @@ -53,6 +53,7 @@ import LineTypeSegmentDialog from '../../../line-types-catalog/line-type-segment type LineCreationDialogProps = NetworkModificationDialogProps & { editData?: LineCreationDto; onCreateLine: typeof createLine; + displayConnectivity?: boolean; }; /** @@ -62,6 +63,7 @@ type LineCreationDialogProps = NetworkModificationDialogProps & { * @param currentRootNetworkUuid The root network uuid we are currently working on * @param editData the data to edit * @param onCreateLine callback to customize line creation process + * @param displayConnectivity to display connectivity tab or not * @param isUpdate check if edition form * @param dialogProps props that are forwarded to the generic ModificationDialog component * @param editDataFetchStatus indicates the status of fetching EditData @@ -72,6 +74,7 @@ const LineCreationDialog = ({ currentNode, currentRootNetworkUuid, onCreateLine = createLine, + displayConnectivity = true, isUpdate, editDataFetchStatus, ...dialogProps @@ -252,6 +255,7 @@ const LineCreationDialog = ({ voltageLevelOptions={voltageLevelOptions} PositionDiagramPane={PositionDiagramPane} fetchBusesOrBusbarSections={fetchBusesOrBusbarSections} + withConnectivity={displayConnectivity} /> )} - {selectedId != null && ( + {selectedId != null && ( // TODO when moving to commons-ui, use generic <> Date: Thu, 25 Jun 2026 19:32:10 +0200 Subject: [PATCH 10/11] move another translation Signed-off-by: David BRAQUART --- src/translations/en.json | 1 - src/translations/fr.json | 1 - 2 files changed, 2 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 6330b0bac6..a86e872752 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -696,7 +696,6 @@ "GenerationDispatchError": "Error while creating a generation dispatch", "lineAttachedToSplitLineVoltageLevel": "Voltage level with attached line to place as splitter", - "permanentCurrentLimitMustBeGreaterThanZero": "The permanent current limit value must be greater than 0", "permanentCurrentLimitMandatory": "A permanent limit has to be set if the limit set contains temporary current limits", "qMaxAtNominalVMustBeGreaterThanZero": "The Qmax available at nominal voltage value must be greater than 0", "powerFactorMaxValueError": "The power factor value must be less than 1", diff --git a/src/translations/fr.json b/src/translations/fr.json index 51b34de021..b74b3a0f4b 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -689,7 +689,6 @@ "GenerationDispatchError": "Erreur lors de la création du démarrage de groupes", "lineAttachedToSplitLineVoltageLevel" : "Poste en piquage à passer en coupure", - "permanentCurrentLimitMustBeGreaterThanZero": "La valeur IST doit être supérieure à 0", "permanentCurrentLimitMandatory": "Un IST doit être fourni si le jeu de limites contient des limites d'intensité temporaires", "qMaxAtNominalVMustBeGreaterThanZero": "La valeur Q installée à tension nominale doit être supérieure à 0", From 385b18755a14862d310f0adfb811401b8f0d9495 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Thu, 2 Jul 2026 15:09:05 +0200 Subject: [PATCH 11/11] fix connectivity errors with piquage Signed-off-by: David BRAQUART --- .../line/creation/line-creation-dialog.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx index 291f5801a4..f78eba0e4c 100644 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx +++ b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.tsx @@ -91,7 +91,7 @@ const LineCreationDialog = ({ const formMethods = useForm>({ defaultValues: lineCreationEmptyFormData, - resolver: yupResolver>(lineCreationFormSchema), + resolver: yupResolver>(lineCreationFormSchema(displayConnectivity)), }); const { reset, setValue, watch } = formMethods; @@ -229,7 +229,7 @@ const LineCreationDialog = ({ return (