diff --git a/src/components/dialogs/limits/limits-groups-contextual-menu.tsx b/src/components/dialogs/limits/limits-groups-contextual-menu.tsx index b9edc7cc1c..d5598c4fe3 100644 --- a/src/components/dialogs/limits/limits-groups-contextual-menu.tsx +++ b/src/components/dialogs/limits/limits-groups-contextual-menu.tsx @@ -18,9 +18,9 @@ import ListItemIcon from '@mui/material/ListItemIcon'; import { ContentCopy, Delete, Edit } from '@mui/icons-material'; import ListItemText from '@mui/material/ListItemText'; import { useIntl } from 'react-intl'; -import { OperationalLimitsGroup } from '../../../services/network-modification-types'; import { PopoverProps } from '@mui/material/Popover'; import { APPLICABILITY } from '../../network/constants'; +import { OperationalLimitsGroupFormInfos } from '../network-modifications/line/modification/line-modification-type'; export interface LimitsGroupsContextualMenuProps { parentFormName: string; @@ -81,11 +81,11 @@ export function LimitsGroupsContextualMenu({ const handleDuplicateTab = () => { let newName: string = ''; if (indexSelectedLimitSet !== null) { - const duplicatedLimits1: OperationalLimitsGroup = getValues( + const duplicatedLimits1: OperationalLimitsGroupFormInfos = getValues( `${operationalLimitsGroupsFormName}[${indexSelectedLimitSet}]` ); newName = duplicatedLimits1.name + '_COPY'; - const newLimitsGroup1: OperationalLimitsGroup = { + const newLimitsGroup1: OperationalLimitsGroupFormInfos = { ...duplicatedLimits1, [ID]: newName, }; diff --git a/src/components/dialogs/limits/limits-pane-utils.ts b/src/components/dialogs/limits/limits-pane-utils.ts index 5d06db6be5..f0ac8b9677 100644 --- a/src/components/dialogs/limits/limits-pane-utils.ts +++ b/src/components/dialogs/limits/limits-pane-utils.ts @@ -9,8 +9,11 @@ import { sanitizeString } from '../dialog-utils'; import { APPLICABIlITY, CURRENT_LIMITS, + DELETION_MARK, ID, + LIMIT_SETS_MODIFICATION_TYPE, LIMITS, + NAME, OPERATIONAL_LIMITS_GROUPS, PERMANENT_LIMIT, SELECTED_LIMITS_GROUP_1, @@ -20,24 +23,28 @@ import { TEMPORARY_LIMIT_NAME, TEMPORARY_LIMIT_VALUE, TEMPORARY_LIMITS, - NAME, - LIMIT_SETS_MODIFICATION_TYPE, } from 'components/utils/field-constants'; -import { areArrayElementsUnique, formatTemporaryLimits, toModificationOperation } from 'components/utils/utils'; +import { + areArrayElementsUnique, + formatTemporaryLimits, + formatToTemporaryLimitsFormInfos, + toModificationOperation, +} from 'components/utils/utils'; import yup from 'components/utils/yup-config'; -import { isNodeBuilt } from '../../graph/util/model-functions'; import { AttributeModification, CurrentLimits, - LineModificationInfos, OperationalLimitsGroup, OperationType, TemporaryLimit, } from '../../../services/network-modification-types'; -import { CurrentTreeNode } from '../../graph/tree-node.type'; import { BranchInfos } from '../../../services/study/network-map.type'; import { areOperationalLimitsGroupUnique, OperationalLimitsId } from './limits-utils'; -import { LineModificationFormInfos } from '../network-modifications/line/modification/line-modification-type'; +import { + LineModificationFormInfos, + OperationalLimitsGroupFormInfos, + TemporaryLimitFormInfos, +} from '../network-modifications/line/modification/line-modification-type'; const limitsGroupValidationSchema = (isModification: boolean) => ({ [ID]: yup.string().nonNullable().required(), @@ -117,7 +124,9 @@ export const getLimitsEmptyFormData = (id = LIMITS) => { return limitsEmptyFormData(id); }; -export const formatOpLimitGroups = (limitGroups: OperationalLimitsGroup[]): OperationalLimitsGroup[] => { +export const formatOpLimitGroupsToFormInfos = ( + limitGroups: OperationalLimitsGroup[] +): OperationalLimitsGroupFormInfos[] => { if (!limitGroups) { return []; } @@ -126,19 +135,17 @@ export const formatOpLimitGroups = (limitGroups: OperationalLimitsGroup[]): Oper id: opLimitGroup.id + opLimitGroup.applicability, name: opLimitGroup.id, applicability: opLimitGroup.applicability, - modificationType: opLimitGroup.modificationType, currentLimits: { id: opLimitGroup.currentLimits.id, - applicability: opLimitGroup.applicability, permanentLimit: opLimitGroup.currentLimits.permanentLimit, - temporaryLimits: formatTemporaryLimits(opLimitGroup.currentLimits.temporaryLimits), + temporaryLimits: formatToTemporaryLimitsFormInfos(opLimitGroup.currentLimits.temporaryLimits), }, }; }); }; export const getAllLimitsFormData = ( - operationalLimitsGroups: OperationalLimitsGroup[] = [], + operationalLimitsGroups: OperationalLimitsGroupFormInfos[] = [], selectedOperationalLimitsGroup1: string | null = null, selectedOperationalLimitsGroup2: string | null = null, id = LIMITS @@ -155,7 +162,9 @@ export const getAllLimitsFormData = ( /** * sanitizes limit names and filters out the empty temporary limits lines */ -export const sanitizeLimitsGroups = (limitsGroups: OperationalLimitsGroup[]): OperationalLimitsGroup[] => +export const sanitizeLimitsGroups = ( + limitsGroups: OperationalLimitsGroupFormInfos[] +): OperationalLimitsGroupFormInfos[] => limitsGroups.map(({ currentLimits, ...baseData }) => ({ ...baseData, id: baseData.name, @@ -180,69 +189,56 @@ export const sanitizeLimitsGroups = (limitsGroups: OperationalLimitsGroup[]): Op }, })); -export const sanitizeLimitNames = (temporaryLimitList: TemporaryLimit[]): TemporaryLimit[] => +export const sanitizeLimitNames = (temporaryLimitList: TemporaryLimitFormInfos[]): TemporaryLimitFormInfos[] => temporaryLimitList - ?.filter((limit: TemporaryLimit) => limit?.name?.trim()) + ?.filter((limit: TemporaryLimitFormInfos) => limit?.name?.trim()) .map(({ name, ...temporaryLimit }) => ({ ...temporaryLimit, name: sanitizeString(name) ?? '', })) || []; -const findTemporaryLimit = (temporaryLimits: TemporaryLimit[], limit: TemporaryLimit) => - temporaryLimits?.find((l) => l.name === limit.name && l.acceptableDuration === limit.acceptableDuration); +const findTemporaryLimitForm = (temporaryLimits: TemporaryLimitFormInfos[], limit: TemporaryLimit) => + temporaryLimits?.find( + (l: TemporaryLimitFormInfos) => l.name === limit.name && l.acceptableDuration === limit.acceptableDuration + ); export const updateTemporaryLimits = ( - modifiedTemporaryLimits: TemporaryLimit[], // from the form (ie network modification values) + temporaryLimitsFormInfos: TemporaryLimitFormInfos[], temporaryLimitsToModify: TemporaryLimit[] // from map server ) => { - let updatedTemporaryLimits = modifiedTemporaryLimits ?? []; - //add temporary limits from from map server that are not in the form values + let updatedTemporaryLimits = temporaryLimitsFormInfos ?? []; + //add temporary limits from map server that are not in the form values temporaryLimitsToModify?.forEach((limit: TemporaryLimit) => { - if (findTemporaryLimit(updatedTemporaryLimits, limit) === undefined) { - updatedTemporaryLimits?.push({ - ...limit, - }); + if (findTemporaryLimitForm(updatedTemporaryLimits, limit) === undefined) { + updatedTemporaryLimits?.push(temporaryLimitToTemporaryLimitFormInfos(limit)); } }); //remove deleted temporary limits from current and previous modifications - updatedTemporaryLimits = updatedTemporaryLimits?.filter( - (limit: TemporaryLimit) => - limit.modificationType !== TEMPORARY_LIMIT_MODIFICATION_TYPE.DELETE && - !( - (limit.modificationType === null || - limit.modificationType === TEMPORARY_LIMIT_MODIFICATION_TYPE.MODIFY) && - findTemporaryLimit(temporaryLimitsToModify, limit) === undefined - ) - ); + updatedTemporaryLimits = updatedTemporaryLimits?.filter((limit: TemporaryLimitFormInfos) => !limit[DELETION_MARK]); - //update temporary limits values - updatedTemporaryLimits?.forEach((limit: TemporaryLimit) => { - if (limit.modificationType === null) { - limit.value = findTemporaryLimit(temporaryLimitsToModify, limit)?.value ?? null; - } - }); return updatedTemporaryLimits; }; /** - * extract data loaded from the map server and merge it with local data in order to fill the operational limits groups modification interface + * extract data loaded from the map server and merge it with local data + * in order to fill the operational limits groups modification interface */ -export const updateOpLimitsGroups = ( +export const combineFormAndMapServerLimitsGroups = ( formBranchModification: LineModificationFormInfos, mapServerBranch: BranchInfos -): OperationalLimitsGroup[] => { - let updatedOpLG: OperationalLimitsGroup[] = formBranchModification.limits.operationalLimitsGroups ?? []; +): OperationalLimitsGroupFormInfos[] => { + let updatedOpLG: OperationalLimitsGroupFormInfos[] = formBranchModification.limits.operationalLimitsGroups ?? []; // updates limit values : - updatedOpLG.forEach((opLG: OperationalLimitsGroup) => { + updatedOpLG.forEach((opLG: OperationalLimitsGroupFormInfos) => { const equivalentFromMapServer = mapServerBranch.currentLimits?.find( (currentLimit: CurrentLimits) => - currentLimit.id === opLG.name && currentLimit.applicability === opLG.applicability + currentLimit.id === opLG.name && currentLimit.applicability === opLG[APPLICABIlITY] ); if (equivalentFromMapServer !== undefined) { opLG.currentLimits.temporaryLimits = updateTemporaryLimits( - formatTemporaryLimits(opLG.currentLimits.temporaryLimits), + opLG.currentLimits.temporaryLimits, formatTemporaryLimits(equivalentFromMapServer.temporaryLimits) ); } @@ -251,8 +247,8 @@ export const updateOpLimitsGroups = ( // adds all the operational limits groups from mapServerBranch THAT ARE NOT DELETED by the netmod mapServerBranch.currentLimits?.forEach((currentLimit: CurrentLimits) => { const equivalentFromNetMod = updatedOpLG.find( - (opLG: OperationalLimitsGroup) => - currentLimit.id === opLG.name && currentLimit.applicability === opLG.applicability + (opLG: OperationalLimitsGroupFormInfos) => + currentLimit.id === opLG.name && currentLimit.applicability === opLG[APPLICABIlITY] ); if (equivalentFromNetMod === undefined) { updatedOpLG.push({ @@ -261,90 +257,31 @@ export const updateOpLimitsGroups = ( applicability: currentLimit.applicability, currentLimits: { id: currentLimit.id, - applicability: currentLimit.applicability, permanentLimit: null, - temporaryLimits: formatTemporaryLimits(currentLimit.temporaryLimits), + temporaryLimits: formatToTemporaryLimitsFormInfos(currentLimit.temporaryLimits), }, }); } }); - //remove deleted operational limits groups - updatedOpLG = updatedOpLG?.filter( - (opLG: OperationalLimitsGroup) => opLG.modificationType !== TEMPORARY_LIMIT_MODIFICATION_TYPE.DELETE - ); - return updatedOpLG; }; export const addModificationTypeToTemporaryLimits = ( - temporaryLimits: TemporaryLimit[], - temporaryLimitsToModify: TemporaryLimit[], - networkTemporaryLimits: TemporaryLimit[], - currentNode: CurrentTreeNode + formTemporaryLimits: TemporaryLimitFormInfos[] ): TemporaryLimit[] => { - const formattedTemporaryLimitsToModify = formatTemporaryLimits(temporaryLimitsToModify); - const formattedNetworkTemporaryLimits = formatTemporaryLimits(networkTemporaryLimits); - const updatedTemporaryLimits: TemporaryLimit[] = temporaryLimits.map((limit) => { - const limitWithSameName = findTemporaryLimit(formattedTemporaryLimitsToModify, limit); - if (limitWithSameName) { - const currentLimitWithSameName: TemporaryLimit | undefined = findTemporaryLimit( - formattedNetworkTemporaryLimits, - limitWithSameName - ); - if ( - (currentLimitWithSameName?.modificationType === TEMPORARY_LIMIT_MODIFICATION_TYPE.MODIFY && - isNodeBuilt(currentNode)) || - currentLimitWithSameName?.modificationType === TEMPORARY_LIMIT_MODIFICATION_TYPE.ADD - ) { - return { - ...limit, - modificationType: currentLimitWithSameName.modificationType, - }; - } else { - return limitWithSameName.value === limit.value - ? { - ...limit, - modificationType: null, - } - : { - ...limit, - modificationType: TEMPORARY_LIMIT_MODIFICATION_TYPE.MODIFY, - }; - } - } else { - return { - ...limit, - modificationType: TEMPORARY_LIMIT_MODIFICATION_TYPE.ADD, - }; - } - }); - //add deleted limits - formattedTemporaryLimitsToModify?.forEach((limit) => { - if (!findTemporaryLimit(temporaryLimits, limit)) { - updatedTemporaryLimits.push({ - ...limit, - modificationType: TEMPORARY_LIMIT_MODIFICATION_TYPE.DELETE, - }); - } - }); - //add previously deleted limits - formattedNetworkTemporaryLimits?.forEach((limit) => { - if ( - !findTemporaryLimit(updatedTemporaryLimits, limit) && - limit.modificationType === TEMPORARY_LIMIT_MODIFICATION_TYPE.DELETE - ) { - updatedTemporaryLimits.push({ - ...limit, - modificationType: TEMPORARY_LIMIT_MODIFICATION_TYPE.DELETE, - }); - } + return formTemporaryLimits.map((limit: TemporaryLimitFormInfos) => { + return { + ...limit, + modificationType: limit[DELETION_MARK] + ? TEMPORARY_LIMIT_MODIFICATION_TYPE.DELETE + : TEMPORARY_LIMIT_MODIFICATION_TYPE.MODIFY_OR_ADD, + }; }); - return updatedTemporaryLimits; }; export function addOperationTypeToSelectedOpLG( - selectedOpLG: string, + selectedOpLG: string | null, noSelectionString: string ): AttributeModification | null { return selectedOpLG === noSelectionString @@ -358,53 +295,41 @@ export function addOperationTypeToSelectedOpLG( /** * 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 ADD and MODIFY_OR_ADD are handled, the others have been disabled for various reasons + * note : for now only MODIFY_OR_ADD is handled, the others have been disabled for various reasons * - * @param limitsGroups current data from the form - * @param networkLine data of the line modified by the network modification - * @param editData data from the existing network modification, if the user is editing a netmod already stored in database - * @param currentNode + * @param limitsGroupsForm current data from the form */ export const addModificationTypeToOpLimitsGroups = ( - limitsGroups: OperationalLimitsGroup[], - networkLine: BranchInfos | null, - editData: LineModificationInfos | null | undefined, - currentNode: CurrentTreeNode -) => { - let modificationLimitsGroups: OperationalLimitsGroup[] = sanitizeLimitsGroups(limitsGroups); - - modificationLimitsGroups = modificationLimitsGroups.map((formLimitsGroup: OperationalLimitsGroup) => { - const modificationType: string = LIMIT_SETS_MODIFICATION_TYPE.MODIFY_OR_ADD; - const networkCurrentLimits = networkLine?.currentLimits.find( - (lineOpLimitGroup: CurrentLimits) => - lineOpLimitGroup.id === formLimitsGroup.name && - lineOpLimitGroup.applicability === formLimitsGroup.applicability - ); + limitsGroupsForm: OperationalLimitsGroupFormInfos[] +): OperationalLimitsGroup[] => { + let modificationLimitsGroupsForm: OperationalLimitsGroupFormInfos[] = sanitizeLimitsGroups(limitsGroupsForm); + return modificationLimitsGroupsForm.map((limitsGroupForm: OperationalLimitsGroupFormInfos) => { const temporaryLimits: TemporaryLimit[] = addModificationTypeToTemporaryLimits( - sanitizeLimitNames(formLimitsGroup.currentLimits?.[TEMPORARY_LIMITS]), - networkCurrentLimits?.temporaryLimits ?? [], - editData?.operationalLimitsGroups?.find( - (editDataOpLimitGroup: OperationalLimitsGroup) => - editDataOpLimitGroup.id === formLimitsGroup.name && - editDataOpLimitGroup.applicability === formLimitsGroup.applicability - )?.currentLimits?.temporaryLimits ?? [], - currentNode + sanitizeLimitNames(limitsGroupForm[CURRENT_LIMITS]?.[TEMPORARY_LIMITS]) ); - let currentLimits = formLimitsGroup.currentLimits; - if (formLimitsGroup.currentLimits?.[PERMANENT_LIMIT] || temporaryLimits.length > 0) { - currentLimits.permanentLimit = formLimitsGroup.currentLimits?.[PERMANENT_LIMIT]; - currentLimits.temporaryLimits = temporaryLimits; - } + const currentLimits: CurrentLimits = { + id: limitsGroupForm[CURRENT_LIMITS][ID], + applicability: limitsGroupForm?.[APPLICABIlITY], + permanentLimit: limitsGroupForm[CURRENT_LIMITS]?.[PERMANENT_LIMIT] ?? null, + temporaryLimits: temporaryLimits ?? [], + }; return { - id: formLimitsGroup.id, - name: formLimitsGroup.name, - applicability: formLimitsGroup.applicability, + id: limitsGroupForm.id, + name: limitsGroupForm.name, + applicability: limitsGroupForm.applicability, currentLimits: currentLimits, - modificationType: modificationType, + modificationType: LIMIT_SETS_MODIFICATION_TYPE.MODIFY_OR_ADD, }; }); +}; - return modificationLimitsGroups; +export const temporaryLimitToTemporaryLimitFormInfos = (temporaryLimit: TemporaryLimit): TemporaryLimitFormInfos => { + return { + [TEMPORARY_LIMIT_NAME]: temporaryLimit.name, + [TEMPORARY_LIMIT_DURATION]: temporaryLimit.acceptableDuration, + [TEMPORARY_LIMIT_VALUE]: temporaryLimit.value, + [DELETION_MARK]: false, + }; }; diff --git a/src/components/dialogs/limits/limits-pane.tsx b/src/components/dialogs/limits/limits-pane.tsx index 727a629460..4187a20a36 100644 --- a/src/components/dialogs/limits/limits-pane.tsx +++ b/src/components/dialogs/limits/limits-pane.tsx @@ -17,7 +17,7 @@ import { LimitsSidePane } from './limits-side-pane'; import { SelectedOperationalLimitGroup } from './selected-operational-limit-group.js'; import { useCallback, useRef, useState } from 'react'; import { useWatch } from 'react-hook-form'; -import { CurrentLimits, OperationalLimitsGroup } from '../../../services/network-modification-types'; +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'; @@ -26,6 +26,7 @@ import GridSection from '../commons/grid-section'; import { styles } from '../dialog-utils'; import AddIcon from '@mui/icons-material/ControlPoint'; import { APPLICABILITY } from '../../network/constants'; +import { OperationalLimitsGroupFormInfos } from '../network-modifications/line/modification/line-modification-type'; export interface LimitsPaneProps { id?: string; @@ -44,7 +45,7 @@ export function LimitsPane({ const myRef: any = useRef(null); - const limitsGroups: OperationalLimitsGroup[] = useWatch({ + const limitsGroups: OperationalLimitsGroupFormInfos[] = useWatch({ name: `${id}.${OPERATIONAL_LIMITS_GROUPS}`, }); @@ -72,10 +73,11 @@ export function LimitsPane({ } // checks if limit sets with that name already exist - const sameNameInLs: OperationalLimitsGroup[] = limitsGroups + const sameNameInLs: OperationalLimitsGroupFormInfos[] = limitsGroups .filter((_ls, index: number) => index !== indexSelectedLimitSet) .filter( - (limitsGroup: OperationalLimitsGroup) => limitsGroup.name.trim() === editedLimitGroupName.trim() + (limitsGroup: OperationalLimitsGroupFormInfos) => + limitsGroup.name.trim() === editedLimitGroupName.trim() ); // only 2 limit sets with the same name are allowed and only if there have SIDE1 and SIDE2 applicability @@ -151,7 +153,7 @@ export function LimitsPane({ {indexSelectedLimitSet !== null && limitsGroups.map( - (operationalLimitsGroup: OperationalLimitsGroup, index: number) => + (operationalLimitsGroup: OperationalLimitsGroupFormInfos, index: number) => index === indexSelectedLimitSet && ( >; checkLimitSetUnicity: (editedLimitGroupName: string, newSelectedApplicability: string) => string; @@ -285,7 +286,7 @@ export const OperationalLimitsGroupsTabs = forwardRef - {limitsGroups.map((opLg: OperationalLimitsGroup, index: number) => ( + {limitsGroups.map((opLg: OperationalLimitsGroupFormInfos, index: number) => ( setHoveredRowIndex(index)} onMouseLeave={() => setHoveredRowIndex(-1)} diff --git a/src/components/dialogs/limits/selected-operational-limit-group.tsx b/src/components/dialogs/limits/selected-operational-limit-group.tsx index 58c68f2412..e59e9fcc29 100644 --- a/src/components/dialogs/limits/selected-operational-limit-group.tsx +++ b/src/components/dialogs/limits/selected-operational-limit-group.tsx @@ -8,9 +8,9 @@ import { useMemo } from 'react'; import { useWatch } from 'react-hook-form'; import { Box } from '@mui/material'; import { AutocompleteInput } from '@gridsuite/commons-ui'; -import { OperationalLimitsGroup } from '../../../services/network-modification-types'; import { APPLICABILITY } from '../../network/constants'; import { useIntl } from 'react-intl'; +import { OperationalLimitsGroupFormInfos } from '../network-modifications/line/modification/line-modification-type'; export interface SelectedOperationalLimitGroupProps { selectedFormName: string; @@ -29,7 +29,7 @@ export const SelectedOperationalLimitGroup = ({ previousValue, isABranchModif, }: Readonly) => { - const optionsValues: OperationalLimitsGroup[] = useWatch({ + const optionsValues: OperationalLimitsGroupFormInfos[] = useWatch({ name: optionsFormName, }); const intl = useIntl(); @@ -38,12 +38,12 @@ export const SelectedOperationalLimitGroup = ({ const finalOptions: string[] = optionsValues ? optionsValues .filter( - (optionObj: OperationalLimitsGroup) => + (optionObj: OperationalLimitsGroupFormInfos) => optionObj.applicability && (optionObj.applicability === filteredApplicability || optionObj.applicability === APPLICABILITY.EQUIPMENT.id) ) - .map((filteredoptionObj: OperationalLimitsGroup) => filteredoptionObj.name) + .map((filteredoptionObj: OperationalLimitsGroupFormInfos) => filteredoptionObj.name) .filter((id: string) => id != null) : []; if (isABranchModif) { diff --git a/src/components/dialogs/limits/temporary-limits-table.tsx b/src/components/dialogs/limits/temporary-limits-table.tsx index ae7506849f..d730835701 100644 --- a/src/components/dialogs/limits/temporary-limits-table.tsx +++ b/src/components/dialogs/limits/temporary-limits-table.tsx @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { useState } from 'react'; -import { useFieldArray } from 'react-hook-form'; +import { useCallback, useState } from 'react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; import { Box, Grid, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material'; import { type ColumnNumeric, @@ -21,7 +21,7 @@ import { 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 { DELETION_MARK, SELECTED } from '../../utils/field-constants'; import { TemporaryLimit } from '../../../services/network-modification-types'; const styles = { @@ -92,8 +92,9 @@ function TemporaryLimitsTable({ isValueModified, disableAddingRows = false, }: Readonly) { - const { fields, append, remove } = useFieldArray({ name: arrayFormName }); + const { fields, append } = useFieldArray({ name: arrayFormName }); const [hoveredRowIndex, setHoveredRowIndex] = useState(-1); + const { setValue, getValues } = useFormContext(); function renderTableCell(rowId: string, rowIndex: number, column: ColumnText | ColumnNumeric) { const name = `${arrayFormName}[${rowIndex}].${column.dataKey}`; @@ -144,11 +145,19 @@ function TemporaryLimitsTable({ ); } + const deletionMarkFormName = useCallback( + (index: number) => `${arrayFormName}[${index}].${DELETION_MARK}`, + [arrayFormName] + ); + const renderTableRow = (rowId: string, index: number) => ( setHoveredRowIndex(index)} onMouseLeave={() => setHoveredRowIndex(-1)}> {columnsDefinition.map((column) => renderTableCell(rowId, index, column))} - remove(index)}> + setValue(deletionMarkFormName(index), true, { shouldDirty: true })} + > @@ -158,7 +167,10 @@ function TemporaryLimitsTable({ function renderTableBody() { return ( - {fields.map((value: Record<'id', string>, index: number) => renderTableRow(value.id, index))} + {fields.map( + (value: Record<'id', string>, index: number) => + !getValues(deletionMarkFormName(index)) && renderTableRow(value.id, index) + )} ); } 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 56c1d62870..876ff8a45d 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 @@ -58,8 +58,8 @@ import { getLimitsValidationSchema, addModificationTypeToOpLimitsGroups, getAllLimitsFormData, - formatOpLimitGroups, - updateOpLimitsGroups, + formatOpLimitGroupsToFormInfos, + combineFormAndMapServerLimitsGroups, addOperationTypeToSelectedOpLG, } from '../../../limits/limits-pane-utils'; import { @@ -101,7 +101,7 @@ import { UUID } from 'crypto'; import { CurrentTreeNode } from '../../../../graph/tree-node.type'; import { BranchInfos } from '../../../../../services/study/network-map.type'; import { useIntl } from 'react-intl'; -import { LineModificationFormInfos } from './line-modification-type'; +import { LimitsDialogFormInfos, LineModificationFormInfos } from './line-modification-type'; import { LineModificationInfos } from '../../../../../services/network-modification-types'; import { toModificationOperation } from '../../../../utils/utils'; @@ -205,7 +205,7 @@ const LineModificationDialog = ({ b2: convertInputValue(FieldType.B2, lineModification.b2?.value ?? null), }), ...getAllLimitsFormData( - formatOpLimitGroups(lineModification.operationalLimitsGroups), + formatOpLimitGroupsToFormInfos(lineModification.operationalLimitsGroups), lineModification.selectedOperationalLimitsGroup1?.value ?? null, lineModification.selectedOperationalLimitsGroup2?.value ?? null ), @@ -227,7 +227,7 @@ const LineModificationDialog = ({ const connectivity2 = line[CONNECTIVITY]?.[CONNECTIVITY_2]; const characteristics = line[CHARACTERISTICS]; const stateEstimationData = line[STATE_ESTIMATION]; - const limits = line[LIMITS]; + const limits: LimitsDialogFormInfos = line[LIMITS]; modifyLine({ studyUuid: studyUuid, @@ -241,12 +241,7 @@ const LineModificationDialog = ({ b1: convertOutputValue(FieldType.B1, characteristics[B1]), g2: convertOutputValue(FieldType.G2, characteristics[G2]), b2: convertOutputValue(FieldType.B2, characteristics[B2]), - operationalLimitsGroups: addModificationTypeToOpLimitsGroups( - limits[OPERATIONAL_LIMITS_GROUPS], - lineToModify, - editData, - currentNode - ), + operationalLimitsGroups: addModificationTypeToOpLimitsGroups(limits[OPERATIONAL_LIMITS_GROUPS]), selectedOperationalLimitsGroup1: addOperationTypeToSelectedOpLG( limits[SELECTED_LIMITS_GROUP_1], intl.formatMessage({ @@ -287,7 +282,7 @@ const LineModificationDialog = ({ }); }); }, - [studyUuid, currentNodeUuid, editData, selectedId, lineToModify, currentNode, intl, snackError] + [studyUuid, currentNodeUuid, editData, selectedId, intl, snackError] ); const clear = useCallback(() => { @@ -315,7 +310,10 @@ const LineModificationDialog = ({ ...formValues, ...{ [LIMITS]: { - [OPERATIONAL_LIMITS_GROUPS]: updateOpLimitsGroups(formValues, line), + [OPERATIONAL_LIMITS_GROUPS]: combineFormAndMapServerLimitsGroups( + formValues, + line + ), }, }, [ADDITIONAL_PROPERTIES]: getConcatenatedProperties(line, getValues), diff --git a/src/components/dialogs/network-modifications/line/modification/line-modification-type.ts b/src/components/dialogs/network-modifications/line/modification/line-modification-type.ts index 7d4e155c60..758436c397 100644 --- a/src/components/dialogs/network-modifications/line/modification/line-modification-type.ts +++ b/src/components/dialogs/network-modifications/line/modification/line-modification-type.ts @@ -8,6 +8,21 @@ import { UUID } from 'crypto'; import { OperationalLimitsGroup } from '../../../../../services/network-modification-types'; import { Property } from '../../common/properties/property-utils'; +import { + APPLICABIlITY, + CURRENT_LIMITS, + DELETION_MARK, + ID, + NAME, + OPERATIONAL_LIMITS_GROUPS, + PERMANENT_LIMIT, + SELECTED_LIMITS_GROUP_1, + SELECTED_LIMITS_GROUP_2, + TEMPORARY_LIMIT_DURATION, + TEMPORARY_LIMIT_NAME, + TEMPORARY_LIMIT_VALUE, + TEMPORARY_LIMITS, +} from '../../../../utils/field-constants'; export interface LineModificationFormInfos { equipmentId?: string; @@ -51,5 +66,33 @@ export interface LineModificationFormInfos { AdditionalProperties: any; characteristics: any; stateEstimation: any; - limits: any; + limits: LimitsDialogFormInfos; +} + +export interface LimitsDialogFormInfos { + [SELECTED_LIMITS_GROUP_1]: string | null; + [SELECTED_LIMITS_GROUP_2]: string | null; + [OPERATIONAL_LIMITS_GROUPS]: OperationalLimitsGroupFormInfos[]; +} + +export interface OperationalLimitsGroupFormInfos { + // here 'id' is a concatenation of NAME and APPLICABIlITY because 2 limits sets on side1 and 2 may have the same name + // "ID" from the map server is stored as NAME in the form because of this + [ID]: string; + [APPLICABIlITY]?: string; + [NAME]: string; + [CURRENT_LIMITS]: CurrentLimitsFormInfos; +} + +export interface CurrentLimitsFormInfos { + [ID]: string; + [PERMANENT_LIMIT]: number | null; + [TEMPORARY_LIMITS]: TemporaryLimitFormInfos[]; +} + +export interface TemporaryLimitFormInfos { + [TEMPORARY_LIMIT_NAME]: string; + [TEMPORARY_LIMIT_DURATION]: number | null; + [TEMPORARY_LIMIT_VALUE]: number | null; + [DELETION_MARK]: boolean; } diff --git a/src/components/dialogs/network-modifications/two-windings-transformer/modification/two-windings-transformer-modification-dialog.jsx b/src/components/dialogs/network-modifications/two-windings-transformer/modification/two-windings-transformer-modification-dialog.jsx index 51e39d777f..5a19b64513 100644 --- a/src/components/dialogs/network-modifications/two-windings-transformer/modification/two-windings-transformer-modification-dialog.jsx +++ b/src/components/dialogs/network-modifications/two-windings-transformer/modification/two-windings-transformer-modification-dialog.jsx @@ -91,11 +91,11 @@ import { import { addModificationTypeToOpLimitsGroups, addOperationTypeToSelectedOpLG, - formatOpLimitGroups, + formatOpLimitGroupsToFormInfos, getAllLimitsFormData, getLimitsEmptyFormData, getLimitsValidationSchema, - updateOpLimitsGroups, + combineFormAndMapServerLimitsGroups, } from '../../../limits/limits-pane-utils'; import { useOpenShortWaitFetching } from 'components/dialogs/commons/handle-modification-form'; import TwoWindingsTransformerModificationDialogHeader from './two-windings-transformer-modification-dialog-header'; @@ -247,7 +247,7 @@ const TwoWindingsTransformerModificationDialog = ({ }), ...getStateEstimationEditData(STATE_ESTIMATION, twtModification), ...getAllLimitsFormData( - formatOpLimitGroups(twtModification.operationalLimitsGroups), + formatOpLimitGroupsToFormInfos(twtModification.operationalLimitsGroups), twtModification.selectedOperationalLimitsGroup1?.value ?? null, twtModification.selectedOperationalLimitsGroup2?.value ?? null ), @@ -483,12 +483,7 @@ const TwoWindingsTransformerModificationDialog = ({ ratedS: toModificationOperation(characteristics[RATED_S]), ratedU1: toModificationOperation(characteristics[RATED_U1]), ratedU2: toModificationOperation(characteristics[RATED_U2]), - operationalLimitsGroups: addModificationTypeToOpLimitsGroups( - limits[OPERATIONAL_LIMITS_GROUPS], - twtToModify, - editData, - currentNode - ), + operationalLimitsGroups: addModificationTypeToOpLimitsGroups(limits[OPERATIONAL_LIMITS_GROUPS]), selectedLimitsGroup1: addOperationTypeToSelectedOpLG( limits[SELECTED_LIMITS_GROUP_1], intl.formatMessage({ @@ -538,8 +533,6 @@ const TwoWindingsTransformerModificationDialog = ({ currentNodeUuid, editData, selectedId, - twtToModify, - currentNode, intl, computeRatioTapForSubmit, computePhaseTapForSubmit, @@ -660,7 +653,10 @@ const TwoWindingsTransformerModificationDialog = ({ ...formValues, ...{ [LIMITS]: { - [OPERATIONAL_LIMITS_GROUPS]: updateOpLimitsGroups(formValues, twt), + [OPERATIONAL_LIMITS_GROUPS]: combineFormAndMapServerLimitsGroups( + formValues, + twt + ), }, }, ...getRatioTapChangerFormData({ diff --git a/src/components/utils/field-constants.ts b/src/components/utils/field-constants.ts index b335eb6428..0ff1576bf9 100644 --- a/src/components/utils/field-constants.ts +++ b/src/components/utils/field-constants.ts @@ -204,6 +204,7 @@ export const TEMPORARY_LIMIT_DURATION = 'acceptableDuration'; export const TEMPORARY_LIMIT_VALUE = 'value'; export const TEMPORARY_LIMIT_MODIFICATION_TYPE = { MODIFY: 'MODIFY', + MODIFY_OR_ADD: 'MODIFY_OR_ADD', // if the limit exists it is modified, if not it is created ADD: 'ADD', DELETE: 'DELETE', REPLACE: 'REPLACE', diff --git a/src/components/utils/utils.ts b/src/components/utils/utils.ts index 363be30020..40944f57b9 100644 --- a/src/components/utils/utils.ts +++ b/src/components/utils/utils.ts @@ -16,7 +16,20 @@ import { } from 'services/network-modification-types'; import { VoltageLevel } from './equipment-types'; import { Option } from '@gridsuite/commons-ui'; -import { APPLICABIlITY, CURRENT_LIMITS, ID, NAME, SELECTED } from './field-constants'; +import { + APPLICABIlITY, + CURRENT_LIMITS, + DELETION_MARK, + ID, + MODIFICATION_TYPE, + NAME, + SELECTED, + TEMPORARY_LIMIT_DURATION, + TEMPORARY_LIMIT_MODIFICATION_TYPE, + TEMPORARY_LIMIT_NAME, + TEMPORARY_LIMIT_VALUE, +} from './field-constants'; +import { TemporaryLimitFormInfos } from '../dialogs/network-modifications/line/modification/line-modification-type'; export const UNDEFINED_ACCEPTABLE_DURATION = Math.pow(2, 31) - 1; @@ -121,10 +134,20 @@ export function toModificationUnsetOperation( export const formatTemporaryLimits = (temporaryLimits: TemporaryLimit[]): TemporaryLimit[] => temporaryLimits?.map((limit: TemporaryLimit) => { return { - name: limit?.name ?? '', - value: limit?.value ?? null, - acceptableDuration: limit?.acceptableDuration ?? null, - modificationType: limit?.modificationType ?? null, + [TEMPORARY_LIMIT_NAME]: limit?.[TEMPORARY_LIMIT_NAME] ?? '', + [TEMPORARY_LIMIT_VALUE]: limit?.[TEMPORARY_LIMIT_VALUE] ?? null, + [TEMPORARY_LIMIT_DURATION]: limit?.[TEMPORARY_LIMIT_DURATION] ?? null, + [MODIFICATION_TYPE]: limit?.[MODIFICATION_TYPE] ?? null, + }; + }); + +export const formatToTemporaryLimitsFormInfos = (temporaryLimits: TemporaryLimit[]): TemporaryLimitFormInfos[] => + temporaryLimits?.map((limit: TemporaryLimit) => { + return { + [TEMPORARY_LIMIT_NAME]: limit?.[TEMPORARY_LIMIT_NAME] ?? '', + [TEMPORARY_LIMIT_VALUE]: limit?.[TEMPORARY_LIMIT_VALUE] ?? null, + [TEMPORARY_LIMIT_DURATION]: limit?.[TEMPORARY_LIMIT_DURATION] ?? null, + [DELETION_MARK]: limit?.modificationType === TEMPORARY_LIMIT_MODIFICATION_TYPE.DELETE, }; });