diff --git a/src/components/dialogs/import-modification-dialog.tsx b/src/components/dialogs/import-composite/import-modification-dialog.tsx similarity index 80% rename from src/components/dialogs/import-modification-dialog.tsx rename to src/components/dialogs/import-composite/import-modification-dialog.tsx index 74f3caa0d2..fa40635dae 100644 --- a/src/components/dialogs/import-modification-dialog.tsx +++ b/src/components/dialogs/import-composite/import-modification-dialog.tsx @@ -7,6 +7,7 @@ import { FormattedMessage, useIntl } from 'react-intl'; import { + CheckboxInput, CustomFormProvider, DirectoryItemSelector, DndColumn, @@ -17,15 +18,14 @@ import { TreeViewFinderNodeProps, useSnackMessage, } from '@gridsuite/commons-ui'; -import { insertCompositeModifications, type ModificationPair } from '../../services/study'; +import { type CompositesToBeInserted, insertCompositeModifications } from '../../../services/study'; import { JSX, useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; -import { AppState } from 'redux/reducer.type'; -import { CompositeModificationAction } from 'components/graph/menus/network-modifications/network-modification-menu.type'; +import { AppState } from '../../../redux/reducer.type'; +import { CompositeModificationAction } from '../../graph/menus/network-modifications/network-modification-menu.type'; import { Box, Button, - Checkbox, Dialog, DialogActions, DialogContent, @@ -33,29 +33,19 @@ import { Divider, FormControl, FormControlLabel, - FormHelperText, Radio, RadioGroup, Step, StepLabel, Stepper, - TextField, Typography, } from '@mui/material'; - -import { - Controller, - useController, - useFieldArray, - useForm, - UseFieldArrayReturn, - useFormContext, - useWatch, -} from 'react-hook-form'; +import { useController, useFieldArray, UseFieldArrayReturn, useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; -import { ACTION, SELECTED_MODIFICATIONS } from 'components/utils/field-constants'; +import { ACTION, IS_SHARED, SELECTED_MODIFICATIONS } from '../../utils/field-constants'; import * as yup from 'yup'; import { UUID } from 'node:crypto'; +import InsertNameCell from './insert-name-cell'; /** * Dialog to select composite network modifications and append them to the current node. @@ -95,44 +85,8 @@ interface SharedCellProps { } function SharedCell({ rowIndex }: Readonly) { - const isShared: boolean = useWatch({ - name: `${SELECTED_MODIFICATIONS}.${rowIndex}.isShared`, - }); - - return ( - } - label={ - - - - } - sx={{ mr: 0, alignItems: 'center' }} - /> - ); -} - -interface InsertNameCellProps { - rowIndex: number; -} - -function InsertNameCell({ rowIndex }: Readonly) { - const { control } = useFormContext(); - const originalName: string = useWatch({ - name: `${SELECTED_MODIFICATIONS}.${rowIndex}.originalName`, - }); - return ( - - ( - - )} - /> - {originalName} - + ); } @@ -192,8 +146,8 @@ const ImportModificationDialog = ({ open, onClose }: Readonly ({ label: '', - dataKey: 'isShared', + dataKey: IS_SHARED, initialValue: false, editable: true, width: '25%', @@ -217,7 +171,7 @@ const ImportModificationDialog = ({ open, onClose }: Readonly [ { @@ -228,9 +182,8 @@ const ImportModificationDialog = ({ open, onClose }: Readonly { - setIsSelectorOpen(false); - - if (!selectedElements.length) { - if (selectedModifications.length === 0) { - onClose(); - } - return; - } - - const newRows: SelectedComposite[] = selectedElements.map((e) => ({ - id: e.id as UUID, - name: e.name, - originalName: e.name, - isShared: false, // currently always false, to be computed later when shared modifications are added - })); - - setValue(SELECTED_MODIFICATIONS, newRows, { - shouldValidate: true, - shouldDirty: true, - }); - }, - [setValue, selectedModifications.length, onClose] - ); - const handleNext = useCallback(() => { if (isNextDisabled) return; setActiveStep(STEP_ORGANIZATION); @@ -324,9 +251,12 @@ const ImportModificationDialog = ({ open, onClose }: Readonly { if (!studyUuid || !currentNode || !isValid) return; - const modificationsToInsert: ModificationPair[] = selectedModifications.map((m) => ({ - first: m.id, - second: action === CompositeModificationAction.SPLIT ? m.originalName : m.name, + const modificationsToInsert: CompositesToBeInserted[] = selectedModifications.map((m: SelectedComposite) => ({ + id: m.id, + // only inserted non shared composites may be renamed + name: action === CompositeModificationAction.SPLIT || m.isShared ? m.originalName : m.name, + // SPLIT modifications are never shared + isShared: action === CompositeModificationAction.INSERT ? m.isShared : false, })); insertCompositeModifications(studyUuid, currentNode.id, modificationsToInsert, action).catch((error) => @@ -338,11 +268,10 @@ const ImportModificationDialog = ({ open, onClose }: Readonly - + - setIsSelectorOpen(false)} types={[ElementType.MODIFICATION]} multiSelect inline diff --git a/src/components/dialogs/import-composite/insert-name-cell.tsx b/src/components/dialogs/import-composite/insert-name-cell.tsx new file mode 100644 index 0000000000..bf162fbe4e --- /dev/null +++ b/src/components/dialogs/import-composite/insert-name-cell.tsx @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +import { IS_SHARED, SELECTED_MODIFICATIONS } from '../../utils/field-constants'; +import { Box, FormHelperText, TextField, Typography } from '@mui/material'; +import { Controller, useFormContext, useWatch } from 'react-hook-form'; +import { JSX } from 'react'; + +interface InsertNameCellProps { + rowIndex: number; +} + +const InsertNameCell = ({ rowIndex }: Readonly): JSX.Element => { + const { control } = useFormContext(); + const originalName: string = useWatch({ + name: `${SELECTED_MODIFICATIONS}.${rowIndex}.originalName`, + }); + const isShared: boolean = useWatch({ + name: `${SELECTED_MODIFICATIONS}.${rowIndex}.${IS_SHARED}`, + }); + + return ( + + {isShared ? ( + + {originalName} + + ) : ( + <> + ( + + )} + /> + {originalName} + + )} + + ); +}; + +export default InsertNameCell; diff --git a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx index 5491618810..422855951b 100644 --- a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx +++ b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx @@ -83,16 +83,16 @@ import TwoWindingsTransformerModificationDialog from '../../../dialogs/network-m import { useIsAnyNodeBuilding } from '../../../utils/is-any-node-building-hook'; import { FileUpload, RestoreFromTrash } from '@mui/icons-material'; -import ImportModificationDialog from 'components/dialogs/import-modification-dialog'; +import ImportModificationDialog from '../../../dialogs/import-composite/import-modification-dialog'; import RestoreModificationDialog from 'components/dialogs/restore-modification-dialog'; import type { UUID } from 'node:crypto'; import { AppState } from 'redux/reducer.type'; import { createCompositeModifications, updateCompositeModifications } from '../../../../services/explore'; import { copyOrMoveModifications } from '../../../../services/study'; import { + assembleModificationsIntoComposite, fetchExcludedNetworkModifications, fetchNetworkModifications, - assembleModificationsIntoComposite, stashModifications, } from '../../../../services/study/network-modifications'; import { @@ -177,6 +177,14 @@ const NetworkModificationNodeEditor = () => { [] ); + // TODO : this is temporary, until copy/paste/save is done for the shared modifications in GRD-4785 : + const selectionContainsShared: boolean = useMemo(() => { + return selectedNetworkModifications.some( + (modification: ComposedModificationMetadata) => + modification.type === ModificationType.MODIFICATION_REFERENCE + ); + }, [selectedNetworkModifications]); + const [isDragging, setIsDragging] = useState(false); const [isAssemblyDepthExceeded, setIsAssemblyDepthExceeded] = useState(false); @@ -1314,7 +1322,7 @@ const NetworkModificationNodeEditor = () => { @@ -1330,7 +1338,8 @@ const NetworkModificationNodeEditor = () => { isAnyNodeBuilding || mapDataLoading || !currentNode || - isRootNode + isRootNode || + selectionContainsShared } > @@ -1346,7 +1355,8 @@ const NetworkModificationNodeEditor = () => { selectedNetworkModifications.length === 0 || isAnyNodeBuilding || mapDataLoading || - isRootNode + isRootNode || + selectionContainsShared } > @@ -1368,7 +1378,7 @@ const NetworkModificationNodeEditor = () => { diff --git a/src/components/utils/field-constants.ts b/src/components/utils/field-constants.ts index 2db1defa2b..40519c8d88 100644 --- a/src/components/utils/field-constants.ts +++ b/src/components/utils/field-constants.ts @@ -459,4 +459,4 @@ export const CONNECTION_SIDE = 'connectionSide'; export const ACTION = 'action'; export const SELECTED_MODIFICATIONS = 'selectedModifications'; -export const COMPOSITE_NAMES = 'compositeNames'; +export const IS_SHARED = 'isShared'; diff --git a/src/services/study/index.ts b/src/services/study/index.ts index 2d25709d69..87a52949c5 100644 --- a/src/services/study/index.ts +++ b/src/services/study/index.ts @@ -286,15 +286,16 @@ export function copyOrMoveModifications( }); } -export interface ModificationPair { - first: UUID; - second: string; +export interface CompositesToBeInserted { + id: UUID; + name: string; + isShared: boolean; } export const insertCompositeModifications = ( studyUuid: string, nodeUuid: string, - modifications: ModificationPair[], + modifications: CompositesToBeInserted[], action: CompositeModificationAction ): Promise => { const urlSearchParams = new URLSearchParams({ action });