Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"@powsybl/network-viewer": "3.1.0",
"@reduxjs/toolkit": "^2.9.0",
"@svgdotjs/svg.js": "^3.2.4",
"@tanstack/react-table": "^8.21.3",
"@tanstack/react-virtual": "^3.13.18",
"@xyflow/react": "^12.8.4",
"ag-grid-community": "^33.1.0",
"ag-grid-react": "^33.3.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
IElementUpdateDialog,
MODIFICATION_TYPES,
ModificationType,
NetworkModificationMetadata,
NotificationsUrlKeys,
snackWithFallback,
useNotificationsListener,
Expand Down Expand Up @@ -95,8 +94,7 @@ import ByFormulaDialog from '../../../dialogs/network-modifications/by-filter/by
import ByFilterDeletionDialog from '../../../dialogs/network-modifications/by-filter/by-filter-deletion/by-filter-deletion-dialog';
import { LccCreationDialog } from '../../../dialogs/network-modifications/hvdc-line/lcc/creation/lcc-creation-dialog';
import { styles } from './network-modification-node-editor-utils';
import NetworkModificationsTable from './network-modifications-table';
import { CellClickedEvent, RowDragEndEvent, RowDragEnterEvent } from 'ag-grid-community';
import NetworkModificationsTable from './tanstack-poc/network-modifications-table';
import {
isModificationsDeleteFinishedNotification,
isModificationsUpdateFinishedNotification,
Expand All @@ -122,6 +120,8 @@ import { EQUIPMENT_TYPES } from '../../../utils/equipment-types';
import CreateVoltageLevelSectionDialog from '../../../dialogs/network-modifications/voltage-level/section/create-voltage-level-section-dialog';
import MoveVoltageLevelFeederBaysDialog from '../../../dialogs/network-modifications/voltage-level/move-feeder-bays/move-voltage-level-feeder-bays-dialog';
import { useCopiedNetworkModifications } from 'hooks/copy-paste/use-copied-network-modifications';
import { DragStart, DropResult } from '@hello-pangea/dnd';
import { NetworkModificationMetadata } from './tanstack-poc/network-modifications-table';

const nonEditableModificationTypes = new Set([
'EQUIPMENT_ATTRIBUTE_MODIFICATION',
Expand Down Expand Up @@ -174,6 +174,37 @@ const NetworkModificationNodeEditor = () => {
const [isUpdate, setIsUpdate] = useState(false);
const buttonAddRef = useRef<HTMLButtonElement>(null);

const alteredModifications = useMemo<NetworkModificationMetadata[]>(() => {
const MAX_DEPTH = 5;
const MAX_CHILDREN = 3;
const createChildren = (base: NetworkModificationMetadata, depth: number): NetworkModificationMetadata[] => {
if (depth >= MAX_DEPTH) return [];
const childCount = Math.floor(Math.random() * (MAX_CHILDREN + 1));
return Array.from({ length: childCount }).map(() => {
const child: NetworkModificationMetadata = {
...base,
uuid: crypto.randomUUID(),
subModifications: [],
};

child.subModifications = createChildren(child, depth + 1);

return child;
});
};

return modifications.map((modification) => {
const { subModifications, ...rest } = modification;
const root: NetworkModificationMetadata = {
...rest,
uuid: modification.uuid,
subModifications: [],
};
root.subModifications = createChildren(root, 1);
return root;
});
}, [modifications]);

const { networkModificationsToCopy, copyInfos, copyNetworkModifications, cutNetworkModifications, cleanClipboard } =
useCopiedNetworkModifications();

Expand Down Expand Up @@ -1052,10 +1083,9 @@ const NetworkModificationNodeEditor = () => {
setEditDialogOpen(id);
setIsUpdate(false);
};
const handleRowSelected = (event: any) => {
const selectedRows = event.api.getSelectedRows(); // Get selected rows
const handleRowSelected = useCallback((selectedRows: NetworkModificationMetadata[]) => {
setSelectedNetworkModifications(selectedRows);
};
}, []);

const renderDialog = () => {
const menuItem = subMenuItemsList.find(
Expand Down Expand Up @@ -1095,12 +1125,11 @@ const NetworkModificationNodeEditor = () => {
return (
<NetworkModificationsTable
handleCellClick={debounce(handleCellClick, 300)}
modifications={modifications}
modifications={alteredModifications}
setModifications={setModifications}
onRowDragStart={onRowDragStart}
onRowDragEnd={onRowDragEnd}
onRowSelected={handleRowSelected}
isDragging
isRowDragDisabled={isImpactedByNotification() || isAnyNodeBuilding || mapDataLoading}
isImpactedByNotification={isImpactedByNotification}
notificationMessageId={notificationMessageId}
Expand Down Expand Up @@ -1144,36 +1173,38 @@ const NetworkModificationNodeEditor = () => {
};

const handleCellClick = useCallback(
(event: CellClickedEvent) => {
const { colDef, data } = event;
if (colDef.colId === 'modificationName' && isModificationClickable(data)) {
(modification: NetworkModificationMetadata) => {
if (isModificationClickable(modification)) {
// Check if the clicked column is the 'modificationName' column
doEditModification(data.uuid, data.type);
doEditModification(modification.uuid, modification.type as ModificationType);
}
},
[doEditModification, isModificationClickable]
);

const onRowDragStart = (event: RowDragEnterEvent<NetworkModificationMetadata>) => {
const onRowDragStart = (event: DragStart) => {
setIsDragging(true);
setInitialPosition(event.overIndex);
setInitialPosition(event.source.index);
};
const onRowDragEnd = (event: RowDragEndEvent<NetworkModificationMetadata>) => {
let newPosition = event.overIndex;

const onRowDragEnd = (event: DropResult) => {
if (!event.destination) {
setIsDragging(false);
return;
}

const newPosition = event.destination.index;
const oldPosition = initialPosition;

if (!currentNode?.id || newPosition === undefined || oldPosition === undefined || newPosition === oldPosition) {
setIsDragging(false);
return;
}
if (newPosition === -1) {
newPosition = modifications.length;
}

const previousModifications = [...modifications];
const updatedModifications = [...modifications];

const [movedItem] = updatedModifications.splice(oldPosition, 1);

updatedModifications.splice(newPosition, 0, movedItem);

setModifications(updatedModifications);
Expand All @@ -1185,7 +1216,9 @@ const NetworkModificationNodeEditor = () => {
snackWithFallback(snackError, error, { headerId: 'errReorderModificationMsg' });
setModifications(previousModifications);
})
.finally(() => setIsDragging(false));
.finally(() => {
setIsDragging(false);
});
};

const isPasteButtonDisabled = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* 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 {
DescriptionModificationDialog,
EditNoteIcon,
MuiStyles,
NetworkModificationMetadata,
} from '@gridsuite/commons-ui';
import { useCallback, useState } from 'react';
import { Tooltip } from '@mui/material';
import { useIsAnyNodeBuilding } from '../../../../utils/is-any-node-building-hook';
import { useSelector } from 'react-redux';
import { AppState } from '../../../../../redux/reducer';
import IconButton from '@mui/material/IconButton';
import type { UUID } from 'node:crypto';
import { setModificationMetadata } from '../../../../../services/study/network-modifications';

const styles = {
coloredButton: (theme) => ({
color: theme.palette.text.primary,
}),
} as const satisfies MuiStyles;

export interface DescriptionRendererProps {
data: NetworkModificationMetadata;
}

const DescriptionRenderer = (props: DescriptionRendererProps) => {
const { data } = props;
const studyUuid = useSelector((state: AppState) => state.studyUuid);
const currentNode = useSelector((state: AppState) => state.currentTreeNode);
const [isLoading, setIsLoading] = useState(false);
const isAnyNodeBuilding = useIsAnyNodeBuilding();
const mapDataLoading = useSelector((state: AppState) => state.mapDataLoading);
const [openDescModificationDialog, setOpenDescModificationDialog] = useState(false);

const modificationUuid = data?.uuid;
const description = data?.description;
const empty = !description;

const updateModification = useCallback(
async (uuid: UUID, descriptionRecord: Record<string, string>) => {
setIsLoading(true);

return setModificationMetadata(studyUuid, currentNode?.id, uuid, {
description: descriptionRecord.description,
type: data?.type,
}).finally(() => {
setIsLoading(false);
});
},
[studyUuid, currentNode?.id, data?.type]
);

const handleDescDialogClose = useCallback(() => {
setOpenDescModificationDialog(false);
}, []);

const handleModifyDescription = useCallback((e: React.MouseEvent) => {
e.stopPropagation(); // Prevent row click from firing
setOpenDescModificationDialog(true);
}, []);

return (
<>
{openDescModificationDialog && modificationUuid && (
<DescriptionModificationDialog
open
description={description ?? ''}
elementUuid={modificationUuid}
onClose={handleDescDialogClose}
updateElement={updateModification}
/>
)}
<Tooltip title={description} arrow placement="right">
<IconButton
className="edit-description-button"
onClick={handleModifyDescription}
disabled={isLoading || isAnyNodeBuilding || mapDataLoading}
sx={{
...styles.coloredButton,
opacity: 0,
pointerEvents: 'none',
}}
>
<EditNoteIcon empty={empty} />
</IconButton>
</Tooltip>
</>
);
};

export default DescriptionRenderer;
Loading