From 37b419904453276ab3f2b8119d11a5f84b4607f0 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Sat, 14 Dec 2024 01:19:15 +0100 Subject: [PATCH 01/16] WIP kinda working, but missing a part in the reducer to link notifications to the Model. Signed-off-by: BOUTIER Charly --- .../graph/network-modification-tree-model.ts | 20 ++++++++-- .../graph/nodes/network-modification-node.tsx | 27 ++++++++++++- .../network-modification-tree-pane.jsx | 5 +++ src/components/network-modification-tree.jsx | 38 ++++++++++++++++++- src/redux/actions.ts | 7 +++- src/redux/reducer.ts | 11 +++--- src/services/study/tree-subtree.js | 14 +++++++ src/translations/en.json | 1 + src/translations/fr.json | 1 + 9 files changed, 111 insertions(+), 13 deletions(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index 33833af3de..cc1903b205 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -30,6 +30,14 @@ export default class NetworkModificationTreeModel { isAnyNodeBuilding = false; + // TODO CHARLY commentaire + childrenNodeSorter(a, b) { + if (a.columnPosition !== undefined && b.columnPosition !== undefined) { + return a.columnPosition - b.columnPosition; + } + return 0; + } + /** * Will switch the order of two nodes in the tree. * The nodeToMove will be moved, either to the left or right of the destinationNode, depending @@ -133,7 +141,7 @@ export default class NetworkModificationTreeModel { return null; } - switchBranches(nodeToMove: CurrentTreeNode, destinationNode: CurrentTreeNode) { + switchBranches(studyUuid: UUID, nodeToMove: CurrentTreeNode, destinationNode: CurrentTreeNode) { // We find the nodes from the two branches that share the same parent const commonAncestor = this.getCommonAncestor(nodeToMove, destinationNode); if (commonAncestor) { @@ -141,8 +149,14 @@ export default class NetworkModificationTreeModel { const siblingFromDestinationNodeBranch = this.getChildOfAncestorInLineage(commonAncestor, destinationNode); if (siblingFromNodeToMoveBranch && siblingFromDestinationNodeBranch) { this.switchSiblingsOrder(siblingFromNodeToMoveBranch, siblingFromDestinationNodeBranch); + //this.saveChildrenColumnPositions(studyUuid, commonAncestor); } } + return commonAncestor; + } + + reorganizeNodes(parentNodeId: string, nodeIdToPositions: Map) { +// TODO CHARLY ici : point de sortie du Reducer } addChild( @@ -241,7 +255,7 @@ export default class NetworkModificationTreeModel { if (!skipChildren) { // Add children of this node recursively if (newNode.children) { - newNode.children.forEach((child) => { + newNode.children.sort(this.childrenNodeSorter).forEach((child) => { this.addChild(child, newNode.id, undefined, undefined); }); } @@ -321,7 +335,7 @@ export default class NetworkModificationTreeModel { // handle root node this.treeNodes.push(convertNodetoReactFlowModelNode(elements)); // handle root children - elements.children.forEach((child) => { + elements.children.sort(this.childrenNodeSorter).forEach((child) => { this.addChild(child, elements.id); }); this.setBuildingStatus(); diff --git a/src/components/graph/nodes/network-modification-node.tsx b/src/components/graph/nodes/network-modification-node.tsx index 3f2d941cb2..0d025b290c 100644 --- a/src/components/graph/nodes/network-modification-node.tsx +++ b/src/components/graph/nodes/network-modification-node.tsx @@ -170,6 +170,7 @@ const NetworkModificationNode = (props: NodeProps) => { return styles.bottomBuildBannerNotBuilt; } } + const debug = true; return ( <> @@ -186,7 +187,31 @@ const NetworkModificationNode = (props: NodeProps) => { )} - + {debug && ( + { + event.stopPropagation(); + navigator.clipboard.writeText(props.id).catch((err) => { + console.error('Failed to copy text: ', err); + }); + console.error('NODE ID COPIED IN CLIPBOARD: ' + props.id); // TODO Remove all this before merge + }} + > + {props.id.substring(0, 3)} + + )} ) + console.error("CHARLY nodeIdToPositions", nodeIdToPositions); } else if (studyUpdatedForce.eventData.headers['updateType'] === 'nodeMoved') { fetchNetworkModificationTreeNode(studyUuid, studyUpdatedForce.eventData.headers['movedNode']).then( (node) => { diff --git a/src/components/network-modification-tree.jsx b/src/components/network-modification-tree.jsx index beb1fee819..df649c886d 100644 --- a/src/components/network-modification-tree.jsx +++ b/src/components/network-modification-tree.jsx @@ -10,7 +10,7 @@ import { ReactFlow, Controls, useStore, useReactFlow, MiniMap, useEdgesState, us import MapIcon from '@mui/icons-material/Map'; import CenterFocusIcon from '@mui/icons-material/CenterFocusStrong'; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { setModificationsDrawerOpen, setCurrentTreeNode, networkModificationTreeSwitchNodes } from '../redux/actions'; +import { setModificationsDrawerOpen, setCurrentTreeNode } from '../redux/actions'; import { useDispatch, useSelector } from 'react-redux'; import { isSameNode } from './graph/util/model-functions'; import { DRAWER_NODE_EDITOR_WIDTH } from '../utils/UIconstants'; @@ -29,6 +29,8 @@ import { snapGrid, } from './graph/layout'; import TreeControlButton from './graph/util/tree-control-button'; +import {updateNodesColumnPositions} from "../services/study/tree-subtree.js"; +import {useSnackMessage} from "@gridsuite/commons-ui"; const NetworkModificationTree = ({ studyMapTreeDisplay, @@ -38,6 +40,7 @@ const NetworkModificationTree = ({ isStudyDrawerOpen, }) => { const dispatch = useDispatch(); + const { snackError } = useSnackMessage(); const currentNode = useSelector((state) => state.currentTreeNode); @@ -218,6 +221,26 @@ const NetworkModificationTree = ({ } }; + + const saveChildrenColumnPositions = (parentNode) => { + console.error("CHARLY lets go2 "+parentNode?.id); + const nodesToUpdate = treeModel.treeNodes + .filter(n => n.parentId === parentNode.id) + .map((node, index) => ({ + id: node.id, + type: node.type, + columnPosition: index + })); + console.error("CHARLY lets go3 ",nodesToUpdate); + updateNodesColumnPositions(studyUuid, parentNode.id, nodesToUpdate) + .catch((error) => { + snackError({ + messageTxt: error.message, + headerId: 'NodeUpdateColumnPositions', + }); + }); + }; + /** * When the user stops dragging a node and releases it to its new position, we check if we need * to switch the order of the moved branch with a neighboring branch. @@ -240,7 +263,18 @@ const NetworkModificationTree = ({ movedNodeXPositionAfterDrag ); if (nodeToSwitchWith) { - dispatch(networkModificationTreeSwitchNodes(movedNode.id, nodeToSwitchWith.id)); + const nodeToMove = treeModel.treeNodes.find((n) => n.id === movedNode.id); + const destinationNode = treeModel.treeNodes.find( + (n) => n.id === nodeToSwitchWith.id + ); + if (nodeToMove && destinationNode) { + const parentNode = treeModel.switchBranches(studyUuid, nodeToMove, destinationNode); + if (parentNode) { + console.error("CHARLY lets go"); + saveChildrenColumnPositions(parentNode); + } + } + //dispatch(networkModificationTreeSwitchNodes(studyUuid, movedNode.id, nodeToSwitchWith.id)); } } }; diff --git a/src/redux/actions.ts b/src/redux/actions.ts index 787d9cf1ca..26dcb38fce 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -317,23 +317,26 @@ export function networkModificationTreeNodeMoved( }; } -export const NETWORK_MODIFICATION_TREE_SWITCH_NODES = 'NETWORK_MODIFICATION_TREE_SWITCH_NODES'; +/*export const NETWORK_MODIFICATION_TREE_SWITCH_NODES = 'NETWORK_MODIFICATION_TREE_SWITCH_NODES'; export type NetworkModificationTreeSwitchNodesAction = Readonly< Action > & { + studyUuid: UUID; nodeToMoveId: string; destinationNodeId: string; }; export function networkModificationTreeSwitchNodes( + studyUuid: UUID, nodeToMoveId: string, destinationNodeId: string ): NetworkModificationTreeSwitchNodesAction { return { type: NETWORK_MODIFICATION_TREE_SWITCH_NODES, + studyUuid, nodeToMoveId, destinationNodeId, }; -} +}*/ export const NETWORK_MODIFICATION_HANDLE_SUBTREE = 'NETWORK_MODIFICATION_HANDLE_SUBTREE'; export type NetworkModificationHandleSubtreeAction = Readonly> & { diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index 60514f3641..6cb2be5c2e 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -120,7 +120,7 @@ import { NETWORK_MODIFICATION_TREE_NODE_MOVED, NETWORK_MODIFICATION_TREE_NODES_REMOVED, NETWORK_MODIFICATION_TREE_NODES_UPDATED, - NETWORK_MODIFICATION_TREE_SWITCH_NODES, + //NETWORK_MODIFICATION_TREE_SWITCH_NODES, NetworkAreaDiagramNbVoltageLevelsAction, NetworkModificationHandleSubtreeAction, NetworkModificationTreeNodeAddedAction, @@ -649,7 +649,7 @@ const initialState: AppState = { centerOnSubstation: null, notificationIdList: [], isModificationsInProgress: false, - studyDisplayMode: StudyDisplayMode.HYBRID, + studyDisplayMode: StudyDisplayMode.TREE, diagramStates: [], nadNodeMovements: [], reloadMap: true, @@ -903,23 +903,24 @@ export const reducer = createReducer(initialState, (builder) => { } ); - builder.addCase( + /*builder.addCase( NETWORK_MODIFICATION_TREE_SWITCH_NODES, (state, action: NetworkModificationTreeSwitchNodesAction) => { if (state.networkModificationTreeModel) { let newModel = state.networkModificationTreeModel.newSharedForUpdate(); const nodeToMove = newModel.treeNodes.find((n: CurrentTreeNode) => n.id === action.nodeToMoveId); + const studyUuid = action.studyUuid; const destinationNode = newModel.treeNodes.find( (n: CurrentTreeNode) => n.id === action.destinationNodeId ); if (nodeToMove && destinationNode) { - newModel.switchBranches(nodeToMove, destinationNode); + newModel.switchBranches("5c661016-67c4-440d-ae05-f87eea474a91", nodeToMove, destinationNode); } state.networkModificationTreeModel = newModel; } } - ); + );*/ builder.addCase(NETWORK_MODIFICATION_TREE_NODE_ADDED, (state, action: NetworkModificationTreeNodeAddedAction) => { if (state.networkModificationTreeModel) { diff --git a/src/services/study/tree-subtree.js b/src/services/study/tree-subtree.js index 132f1fd282..7e3434a698 100644 --- a/src/services/study/tree-subtree.js +++ b/src/services/study/tree-subtree.js @@ -130,6 +130,20 @@ export function updateTreeNode(studyUuid, node) { }); } +export function updateNodesColumnPositions(studyUuid, parentNodeId, nodes) { + const nodeUpdateUrl = getStudyUrl(studyUuid) + '/tree/nodes/columnpositions/' + encodeURIComponent(parentNodeId); + console.debug(nodeUpdateUrl); + console.error("CHARLY hello to back"); + return backendFetch(nodeUpdateUrl, { + method: 'put', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(nodes), + }); +} + export function fetchNetworkModificationTreeNode(studyUuid, nodeUuid) { console.info('Fetching network modification tree node : ', nodeUuid); const url = getStudyUrl(studyUuid) + '/tree/nodes/' + encodeURIComponent(nodeUuid); diff --git a/src/translations/en.json b/src/translations/en.json index 74eeda4331..b2c49c4ccb 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -595,6 +595,7 @@ "NodeCreateError": "An error occurred while creating node", "NodeDeleteError": "An error occurred while deleting node", "NodeUpdateError": "An error occurred while updating node", + "NodeUpdateColumnPositions": "An error occured while updating the nodes' positions in the tree", "NodeBuildingError": "An error occurred while building node", "NodeUnbuildingError": "An error occurred while unbuilding node", "NetworkModifications": "Network modifications", diff --git a/src/translations/fr.json b/src/translations/fr.json index 4528ea4c1f..64c3aabfa0 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -597,6 +597,7 @@ "NodeCreateError": "Une erreur est survenue lors de la création du nœud", "NodeDeleteError": "Une erreur est survenue lors de la suppression du nœud", "NodeUpdateError": "Une erreur est survenue lors de la mise à jour du nœud", + "NodeUpdateColumnPositions": "Une erreur est survenue lors de la mise à jour de la positions des nœuds dans l'arbre", "NodeBuildingError": "Une erreur est survenue lors de la réalisation du nœud", "NodeUnbuildingError": "Une erreur est survenue lors de la déréalisation du nœud", "NetworkModifications": "Modifications de réseau", From de79d0c7fe26061c300fab613b50977b0b7e32fb Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Sun, 15 Dec 2024 02:12:49 +0100 Subject: [PATCH 02/16] WIP Almost done, still needs the reordering part. Signed-off-by: BOUTIER Charly --- .../graph/network-modification-tree-model.ts | 27 ++++++++++++-- .../network-modification-tree-pane.jsx | 12 ++++-- src/components/network-modification-tree.jsx | 31 +++++++--------- src/redux/actions.ts | 37 +++++++++---------- src/redux/reducer.ts | 22 ++++------- src/services/study/tree-subtree.js | 1 - 6 files changed, 68 insertions(+), 62 deletions(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index cc1903b205..a7d4024b04 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -37,7 +37,7 @@ export default class NetworkModificationTreeModel { } return 0; } - + /** * Will switch the order of two nodes in the tree. * The nodeToMove will be moved, either to the left or right of the destinationNode, depending @@ -149,14 +149,33 @@ export default class NetworkModificationTreeModel { const siblingFromDestinationNodeBranch = this.getChildOfAncestorInLineage(commonAncestor, destinationNode); if (siblingFromNodeToMoveBranch && siblingFromDestinationNodeBranch) { this.switchSiblingsOrder(siblingFromNodeToMoveBranch, siblingFromDestinationNodeBranch); - //this.saveChildrenColumnPositions(studyUuid, commonAncestor); } } return commonAncestor; } - reorganizeNodes(parentNodeId: string, nodeIdToPositions: Map) { -// TODO CHARLY ici : point de sortie du Reducer + reorderNodes(parentNodeId: string, nodeIds: string[]) { + // We check if the current position is already correct + const children = this.treeNodes.filter((n) => n.parentId === parentNodeId); + if (nodeIds.length !== children.length) { + console.warn('reorderNodes : synchronization error, reorder cancelled'); + return; + } + if (children.map((child) => child.id).join(',') === nodeIds.join(',')) { + // Alreay in the same order. + return; + } + // Let's reorder the children : + // We create a map of children node ids and number of nodes in each of these child's family, + // then in nodeIds order, we cut and paste the corresponding number of nodes in this.treeNodes. + + const nodeIdAndFamilySize = new Map(nodeIds.map((id) => [id, 1 + countNodes(this.treeNodes, id)])); + + nodeIdAndFamilySize.forEach((value, key) => { + console.error('%s', `CHARLY => ${key.substring(0, 3)}: ${value}`); + }); + + // TODO Faire la manip dans le tableau this.treeNodes. } addChild( diff --git a/src/components/network-modification-tree-pane.jsx b/src/components/network-modification-tree-pane.jsx index ffbb85ceab..17f3b30ea8 100644 --- a/src/components/network-modification-tree-pane.jsx +++ b/src/components/network-modification-tree-pane.jsx @@ -15,6 +15,7 @@ import { networkModificationHandleSubtree, setSelectionForCopy, resetLogsFilter, + networkModificationTreeReorderNodes, } from '../redux/actions'; import { useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; @@ -215,10 +216,13 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) } ); } else if (studyUpdatedForce.eventData.headers['updateType'] === 'columnsChanged') { - const payload = JSON.parse(studyUpdatedForce.eventData.payload); - const nodeIdToPositions = new Map(Object.entries(payload)); - //dispatch(); // TODO CHARLY nouvel appel au Reducer, jusqu'à arriver au model::reorganizeNodes(parentNodeId: string, nodeIdToPositions: Map) - console.error("CHARLY nodeIdToPositions", nodeIdToPositions); + const orderedChildrenNodeIds = JSON.parse(studyUpdatedForce.eventData.payload); + dispatch( + networkModificationTreeReorderNodes( + studyUpdatedForce.eventData.headers['parentNode'], + orderedChildrenNodeIds + ) + ); } else if (studyUpdatedForce.eventData.headers['updateType'] === 'nodeMoved') { fetchNetworkModificationTreeNode(studyUuid, studyUpdatedForce.eventData.headers['movedNode']).then( (node) => { diff --git a/src/components/network-modification-tree.jsx b/src/components/network-modification-tree.jsx index df649c886d..ce876a110b 100644 --- a/src/components/network-modification-tree.jsx +++ b/src/components/network-modification-tree.jsx @@ -29,8 +29,8 @@ import { snapGrid, } from './graph/layout'; import TreeControlButton from './graph/util/tree-control-button'; -import {updateNodesColumnPositions} from "../services/study/tree-subtree.js"; -import {useSnackMessage} from "@gridsuite/commons-ui"; +import { updateNodesColumnPositions } from '../services/study/tree-subtree.js'; +import { useSnackMessage } from '@gridsuite/commons-ui'; const NetworkModificationTree = ({ studyMapTreeDisplay, @@ -221,24 +221,23 @@ const NetworkModificationTree = ({ } }; - + /** + * Saves the new order of parentNode's children in the backend + */ const saveChildrenColumnPositions = (parentNode) => { - console.error("CHARLY lets go2 "+parentNode?.id); const nodesToUpdate = treeModel.treeNodes - .filter(n => n.parentId === parentNode.id) + .filter((n) => n.parentId === parentNode.id) .map((node, index) => ({ id: node.id, type: node.type, - columnPosition: index + columnPosition: index, })); - console.error("CHARLY lets go3 ",nodesToUpdate); - updateNodesColumnPositions(studyUuid, parentNode.id, nodesToUpdate) - .catch((error) => { - snackError({ - messageTxt: error.message, - headerId: 'NodeUpdateColumnPositions', - }); + updateNodesColumnPositions(studyUuid, parentNode.id, nodesToUpdate).catch((error) => { + snackError({ + messageTxt: error.message, + headerId: 'NodeUpdateColumnPositions', }); + }); }; /** @@ -264,17 +263,13 @@ const NetworkModificationTree = ({ ); if (nodeToSwitchWith) { const nodeToMove = treeModel.treeNodes.find((n) => n.id === movedNode.id); - const destinationNode = treeModel.treeNodes.find( - (n) => n.id === nodeToSwitchWith.id - ); + const destinationNode = treeModel.treeNodes.find((n) => n.id === nodeToSwitchWith.id); if (nodeToMove && destinationNode) { const parentNode = treeModel.switchBranches(studyUuid, nodeToMove, destinationNode); if (parentNode) { - console.error("CHARLY lets go"); saveChildrenColumnPositions(parentNode); } } - //dispatch(networkModificationTreeSwitchNodes(studyUuid, movedNode.id, nodeToSwitchWith.id)); } } }; diff --git a/src/redux/actions.ts b/src/redux/actions.ts index 26dcb38fce..93bb5520b8 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -92,7 +92,7 @@ export type AppActions = | NetworkModificationHandleSubtreeAction | NetworkModificationTreeNodesRemovedAction | NetworkModificationTreeNodesUpdatedAction - | NetworkModificationTreeSwitchNodesAction + | NetworkModificationTreeReorderNodesAction | SelectThemeAction | SelectLanguageAction | SelectComputedLanguageAction @@ -317,26 +317,23 @@ export function networkModificationTreeNodeMoved( }; } -/*export const NETWORK_MODIFICATION_TREE_SWITCH_NODES = 'NETWORK_MODIFICATION_TREE_SWITCH_NODES'; -export type NetworkModificationTreeSwitchNodesAction = Readonly< - Action +export const NETWORK_MODIFICATION_TREE_REORDER_NODES = 'NETWORK_MODIFICATION_TREE_REORDER_NODES'; +export type NetworkModificationTreeReorderNodesAction = Readonly< + Action > & { - studyUuid: UUID; - nodeToMoveId: string; - destinationNodeId: string; -}; -export function networkModificationTreeSwitchNodes( - studyUuid: UUID, - nodeToMoveId: string, - destinationNodeId: string -): NetworkModificationTreeSwitchNodesAction { - return { - type: NETWORK_MODIFICATION_TREE_SWITCH_NODES, - studyUuid, - nodeToMoveId, - destinationNodeId, - }; -}*/ + parentNodeId: string; + nodeIds: string[]; +}; +export function networkModificationTreeReorderNodes( + parentNodeId: string, + nodeIds: string[] +): NetworkModificationTreeReorderNodesAction { + return { + type: NETWORK_MODIFICATION_TREE_REORDER_NODES, + parentNodeId, + nodeIds, + }; +} export const NETWORK_MODIFICATION_HANDLE_SUBTREE = 'NETWORK_MODIFICATION_HANDLE_SUBTREE'; export type NetworkModificationHandleSubtreeAction = Readonly> & { diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index 6cb2be5c2e..bb319aade2 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -120,14 +120,14 @@ import { NETWORK_MODIFICATION_TREE_NODE_MOVED, NETWORK_MODIFICATION_TREE_NODES_REMOVED, NETWORK_MODIFICATION_TREE_NODES_UPDATED, - //NETWORK_MODIFICATION_TREE_SWITCH_NODES, + NETWORK_MODIFICATION_TREE_REORDER_NODES, NetworkAreaDiagramNbVoltageLevelsAction, NetworkModificationHandleSubtreeAction, NetworkModificationTreeNodeAddedAction, NetworkModificationTreeNodeMovedAction, NetworkModificationTreeNodesRemovedAction, NetworkModificationTreeNodesUpdatedAction, - NetworkModificationTreeSwitchNodesAction, + NetworkModificationTreeReorderNodesAction, OPEN_DIAGRAM, OPEN_NAD_LIST, OPEN_STUDY, @@ -903,24 +903,16 @@ export const reducer = createReducer(initialState, (builder) => { } ); - /*builder.addCase( - NETWORK_MODIFICATION_TREE_SWITCH_NODES, - (state, action: NetworkModificationTreeSwitchNodesAction) => { + builder.addCase( + NETWORK_MODIFICATION_TREE_REORDER_NODES, + (state, action: NetworkModificationTreeReorderNodesAction) => { if (state.networkModificationTreeModel) { let newModel = state.networkModificationTreeModel.newSharedForUpdate(); - - const nodeToMove = newModel.treeNodes.find((n: CurrentTreeNode) => n.id === action.nodeToMoveId); - const studyUuid = action.studyUuid; - const destinationNode = newModel.treeNodes.find( - (n: CurrentTreeNode) => n.id === action.destinationNodeId - ); - if (nodeToMove && destinationNode) { - newModel.switchBranches("5c661016-67c4-440d-ae05-f87eea474a91", nodeToMove, destinationNode); - } + newModel.reorderNodes(action.parentNodeId, action.nodeIds); state.networkModificationTreeModel = newModel; } } - );*/ + ); builder.addCase(NETWORK_MODIFICATION_TREE_NODE_ADDED, (state, action: NetworkModificationTreeNodeAddedAction) => { if (state.networkModificationTreeModel) { diff --git a/src/services/study/tree-subtree.js b/src/services/study/tree-subtree.js index 7e3434a698..ce90fcc89f 100644 --- a/src/services/study/tree-subtree.js +++ b/src/services/study/tree-subtree.js @@ -133,7 +133,6 @@ export function updateTreeNode(studyUuid, node) { export function updateNodesColumnPositions(studyUuid, parentNodeId, nodes) { const nodeUpdateUrl = getStudyUrl(studyUuid) + '/tree/nodes/columnpositions/' + encodeURIComponent(parentNodeId); console.debug(nodeUpdateUrl); - console.error("CHARLY hello to back"); return backendFetch(nodeUpdateUrl, { method: 'put', headers: { From 4ee4f37e5ab688012379a383dd913781d0361592 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Sun, 15 Dec 2024 15:45:21 +0100 Subject: [PATCH 03/16] Working. Signed-off-by: BOUTIER Charly --- .../graph/network-modification-tree-model.ts | 28 +++++++++++-------- src/components/graph/tree-node.type.ts | 1 + src/redux/reducer.ts | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index a7d4024b04..5b57d3689a 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -11,7 +11,7 @@ import { BUILD_STATUS } from '../network/constants'; import { UUID } from 'crypto'; import { CurrentTreeNode, isReactFlowRootNodeData } from '../../redux/reducer'; import { Edge } from '@xyflow/react'; -import { NetworkModificationNodeData, RootNodeData } from './tree-node.type'; +import { AbstractNode, NetworkModificationNodeData, RootNodeData } from './tree-node.type'; // Function to count children nodes for a given parentId recursively in an array of nodes. // TODO refactoring when changing NetworkModificationTreeModel as it becomes an object containing nodes @@ -30,8 +30,8 @@ export default class NetworkModificationTreeModel { isAnyNodeBuilding = false; - // TODO CHARLY commentaire - childrenNodeSorter(a, b) { + // Will sort if columnPosition is defined, and not move the nodes if undefined + childrenNodeSorter(a: AbstractNode, b: AbstractNode) { if (a.columnPosition !== undefined && b.columnPosition !== undefined) { return a.columnPosition - b.columnPosition; } @@ -154,7 +154,12 @@ export default class NetworkModificationTreeModel { return commonAncestor; } - reorderNodes(parentNodeId: string, nodeIds: string[]) { + /** + * Will reorganize this.treeNode and put the children of parentNodeId in the order provided in nodeIds array. + * @param parentNodeId parent ID of the to be reordered children nodes + * @param nodeIds array of children ID in the order we want + */ + reorderChildrenNodes(parentNodeId: string, nodeIds: string[]) { // We check if the current position is already correct const children = this.treeNodes.filter((n) => n.parentId === parentNodeId); if (nodeIds.length !== children.length) { @@ -168,14 +173,15 @@ export default class NetworkModificationTreeModel { // Let's reorder the children : // We create a map of children node ids and number of nodes in each of these child's family, // then in nodeIds order, we cut and paste the corresponding number of nodes in this.treeNodes. - - const nodeIdAndFamilySize = new Map(nodeIds.map((id) => [id, 1 + countNodes(this.treeNodes, id)])); - - nodeIdAndFamilySize.forEach((value, key) => { - console.error('%s', `CHARLY => ${key.substring(0, 3)}: ${value}`); + const justAfterParentIndex = 1 + this.treeNodes.findIndex((n) => n.id === parentNodeId); // we add 1 to have the index just after the parent node + let insertedNodes = 0; + const nodeIdAndFamilySize = new Map(nodeIds.map((id) => [id, 1 + countNodes(this.treeNodes, id as UUID)])); + nodeIdAndFamilySize.forEach((familySize, nodeId) => { + const nodesToMoveIndex = this.treeNodes.findIndex((n) => n.id === nodeId); + const nodesToMove = this.treeNodes.splice(nodesToMoveIndex, familySize); + this.treeNodes.splice(justAfterParentIndex + insertedNodes, 0, ...nodesToMove); + insertedNodes += familySize; }); - - // TODO Faire la manip dans le tableau this.treeNodes. } addChild( diff --git a/src/components/graph/tree-node.type.ts b/src/components/graph/tree-node.type.ts index bf6f1cdd7f..5ab7831947 100644 --- a/src/components/graph/tree-node.type.ts +++ b/src/components/graph/tree-node.type.ts @@ -21,6 +21,7 @@ export type AbstractNode = { readOnly?: boolean; reportUuid?: UUID; type: NodeType; + columnPosition?: number; }; export interface NodeBuildStatus { diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index bb319aade2..b6428ea6f2 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -908,7 +908,7 @@ export const reducer = createReducer(initialState, (builder) => { (state, action: NetworkModificationTreeReorderNodesAction) => { if (state.networkModificationTreeModel) { let newModel = state.networkModificationTreeModel.newSharedForUpdate(); - newModel.reorderNodes(action.parentNodeId, action.nodeIds); + newModel.reorderChildrenNodes(action.parentNodeId, action.nodeIds); state.networkModificationTreeModel = newModel; } } From f70f0c0d486a5dbc14f103d271d6779b6adb1622 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Sun, 15 Dec 2024 15:48:46 +0100 Subject: [PATCH 04/16] Code cleaning Signed-off-by: BOUTIER Charly --- .../graph/nodes/network-modification-node.tsx | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/components/graph/nodes/network-modification-node.tsx b/src/components/graph/nodes/network-modification-node.tsx index 0d025b290c..3f2d941cb2 100644 --- a/src/components/graph/nodes/network-modification-node.tsx +++ b/src/components/graph/nodes/network-modification-node.tsx @@ -170,7 +170,6 @@ const NetworkModificationNode = (props: NodeProps) => { return styles.bottomBuildBannerNotBuilt; } } - const debug = true; return ( <> @@ -187,31 +186,7 @@ const NetworkModificationNode = (props: NodeProps) => { )} - {debug && ( - { - event.stopPropagation(); - navigator.clipboard.writeText(props.id).catch((err) => { - console.error('Failed to copy text: ', err); - }); - console.error('NODE ID COPIED IN CLIPBOARD: ' + props.id); // TODO Remove all this before merge - }} - > - {props.id.substring(0, 3)} - - )} + Date: Sun, 15 Dec 2024 15:58:52 +0100 Subject: [PATCH 05/16] Code cleaning Signed-off-by: BOUTIER Charly --- src/redux/reducer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index b6428ea6f2..171dc85d23 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -649,7 +649,7 @@ const initialState: AppState = { centerOnSubstation: null, notificationIdList: [], isModificationsInProgress: false, - studyDisplayMode: StudyDisplayMode.TREE, + studyDisplayMode: StudyDisplayMode.HYBRID, diagramStates: [], nadNodeMovements: [], reloadMap: true, From 0d6b9d2ac8b8ecb631c298b09278627ec422d8d0 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Mon, 16 Dec 2024 08:23:02 +0100 Subject: [PATCH 06/16] Code cleaning Signed-off-by: BOUTIER Charly --- src/components/graph/network-modification-tree-model.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index 5b57d3689a..b7dd287bb9 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -155,7 +155,7 @@ export default class NetworkModificationTreeModel { } /** - * Will reorganize this.treeNode and put the children of parentNodeId in the order provided in nodeIds array. + * Will reorganize treeNodes and put the children of parentNodeId in the order provided in nodeIds array. * @param parentNodeId parent ID of the to be reordered children nodes * @param nodeIds array of children ID in the order we want */ @@ -172,10 +172,11 @@ export default class NetworkModificationTreeModel { } // Let's reorder the children : // We create a map of children node ids and number of nodes in each of these child's family, - // then in nodeIds order, we cut and paste the corresponding number of nodes in this.treeNodes. - const justAfterParentIndex = 1 + this.treeNodes.findIndex((n) => n.id === parentNodeId); // we add 1 to have the index just after the parent node + // then in nodeIds order, we cut and paste the corresponding number of nodes in treeNodes. + const justAfterParentIndex = 1 + this.treeNodes.findIndex((n) => n.id === parentNodeId); // we add 1 here to set the index just after the parent node let insertedNodes = 0; const nodeIdAndFamilySize = new Map(nodeIds.map((id) => [id, 1 + countNodes(this.treeNodes, id as UUID)])); + nodeIdAndFamilySize.forEach((familySize, nodeId) => { const nodesToMoveIndex = this.treeNodes.findIndex((n) => n.id === nodeId); const nodesToMove = this.treeNodes.splice(nodesToMoveIndex, familySize); From e317d22c493cb3498c88368e0e988900953282ec Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Mon, 16 Dec 2024 08:26:01 +0100 Subject: [PATCH 07/16] Code cleaning Signed-off-by: BOUTIER Charly --- src/components/graph/network-modification-tree-model.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index b7dd287bb9..7606cb873f 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -175,7 +175,12 @@ export default class NetworkModificationTreeModel { // then in nodeIds order, we cut and paste the corresponding number of nodes in treeNodes. const justAfterParentIndex = 1 + this.treeNodes.findIndex((n) => n.id === parentNodeId); // we add 1 here to set the index just after the parent node let insertedNodes = 0; - const nodeIdAndFamilySize = new Map(nodeIds.map((id) => [id, 1 + countNodes(this.treeNodes, id as UUID)])); + const nodeIdAndFamilySize = new Map( + nodeIds.map((id) => [ + id, + 1 + countNodes(this.treeNodes, id as UUID), // We add 1 here to include the current node in its family size + ]) + ); nodeIdAndFamilySize.forEach((familySize, nodeId) => { const nodesToMoveIndex = this.treeNodes.findIndex((n) => n.id === nodeId); From e10127595604a56823c4ee619651eb0ebb955023 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Tue, 17 Dec 2024 11:05:57 +0100 Subject: [PATCH 08/16] PR Review Signed-off-by: BOUTIER Charly --- src/services/study/tree-subtree.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/study/tree-subtree.js b/src/services/study/tree-subtree.js index ce90fcc89f..4c15fcab03 100644 --- a/src/services/study/tree-subtree.js +++ b/src/services/study/tree-subtree.js @@ -131,7 +131,8 @@ export function updateTreeNode(studyUuid, node) { } export function updateNodesColumnPositions(studyUuid, parentNodeId, nodes) { - const nodeUpdateUrl = getStudyUrl(studyUuid) + '/tree/nodes/columnpositions/' + encodeURIComponent(parentNodeId); + const nodeUpdateUrl = + getStudyUrl(studyUuid) + '/tree/nodes/' + encodeURIComponent(parentNodeId) + '/children-column-positions'; console.debug(nodeUpdateUrl); return backendFetch(nodeUpdateUrl, { method: 'put', From 6150efdd2736b86eb475f21b0100df06efcd10c5 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Tue, 17 Dec 2024 13:57:23 +0100 Subject: [PATCH 09/16] PR Review Signed-off-by: BOUTIER Charly --- src/components/network-modification-tree-pane.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/network-modification-tree-pane.jsx b/src/components/network-modification-tree-pane.jsx index 17f3b30ea8..4b35903930 100644 --- a/src/components/network-modification-tree-pane.jsx +++ b/src/components/network-modification-tree-pane.jsx @@ -215,7 +215,7 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) ); } ); - } else if (studyUpdatedForce.eventData.headers['updateType'] === 'columnsChanged') { + } else if (studyUpdatedForce.eventData.headers['updateType'] === 'nodesColumnPositionsChanged') { const orderedChildrenNodeIds = JSON.parse(studyUpdatedForce.eventData.payload); dispatch( networkModificationTreeReorderNodes( From e47fbc0cc1ba8a03141145c5995c32976fd93a2b Mon Sep 17 00:00:00 2001 From: Slimane AMAR Date: Tue, 17 Dec 2024 15:14:01 +0100 Subject: [PATCH 10/16] Remove Sonar issues --- .../graph/network-modification-tree-model.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index 7606cb873f..5da3cd0ec8 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -269,7 +269,7 @@ export default class NetworkModificationTreeModel { }); // overwrite old children nodes parentUuid when inserting new nodes - const nextNodes = this.treeNodes.map((node) => { + this.treeNodes = this.treeNodes.map((node) => { if (newNode.childrenIds.includes(node.id)) { return { ...node, @@ -278,15 +278,14 @@ export default class NetworkModificationTreeModel { } return node; }); - - this.treeNodes = nextNodes; this.treeEdges = filteredEdges; } if (!skipChildren) { // Add children of this node recursively if (newNode.children) { - newNode.children.sort(this.childrenNodeSorter).forEach((child) => { + newNode.children.sort(this.childrenNodeSorter); + newNode.children.forEach((child) => { this.addChild(child, newNode.id, undefined, undefined); }); } @@ -324,7 +323,7 @@ export default class NetworkModificationTreeModel { if (!nodeToDelete) { return; } - const nextTreeNodes = filteredNodes.map((node) => { + this.treeNodes = filteredNodes.map((node) => { if (node.parentId === nodeId) { return { ...node, @@ -333,8 +332,6 @@ export default class NetworkModificationTreeModel { } return node; }); - - this.treeNodes = nextTreeNodes; }); } @@ -366,7 +363,8 @@ export default class NetworkModificationTreeModel { // handle root node this.treeNodes.push(convertNodetoReactFlowModelNode(elements)); // handle root children - elements.children.sort(this.childrenNodeSorter).forEach((child) => { + elements.children.sort(this.childrenNodeSorter); + elements.children.forEach((child) => { this.addChild(child, elements.id); }); this.setBuildingStatus(); @@ -374,8 +372,7 @@ export default class NetworkModificationTreeModel { newSharedForUpdate() { /* shallow clone of the network https://stackoverflow.com/a/44782052 */ - let newTreeModel = Object.assign(Object.create(Object.getPrototypeOf(this)), this); - return newTreeModel; + return Object.assign(Object.create(Object.getPrototypeOf(this)), this); } setBuildingStatus() { From b47c5faa32604a85055dec6c75de945a1211b008 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Tue, 17 Dec 2024 17:10:58 +0100 Subject: [PATCH 11/16] PR Review Signed-off-by: BOUTIER Charly --- .../graph/network-modification-tree-model.ts | 16 ++++++++++------ src/components/network-modification-tree.jsx | 14 ++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index 5da3cd0ec8..55908a8342 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -154,19 +154,23 @@ export default class NetworkModificationTreeModel { return commonAncestor; } + getChildren(parentNodeId: string): CurrentTreeNode[] { + return this.treeNodes.filter((n) => n.parentId === parentNodeId); + } + /** * Will reorganize treeNodes and put the children of parentNodeId in the order provided in nodeIds array. * @param parentNodeId parent ID of the to be reordered children nodes - * @param nodeIds array of children ID in the order we want + * @param orderedNodeIds array of children ID in the order we want */ - reorderChildrenNodes(parentNodeId: string, nodeIds: string[]) { + reorderChildrenNodes(parentNodeId: string, orderedNodeIds: string[]) { // We check if the current position is already correct - const children = this.treeNodes.filter((n) => n.parentId === parentNodeId); - if (nodeIds.length !== children.length) { + const children = this.getChildren(parentNodeId); + if (orderedNodeIds.length !== children.length) { console.warn('reorderNodes : synchronization error, reorder cancelled'); return; } - if (children.map((child) => child.id).join(',') === nodeIds.join(',')) { + if (children.map((child) => child.id).join(',') === orderedNodeIds.join(',')) { // Alreay in the same order. return; } @@ -176,7 +180,7 @@ export default class NetworkModificationTreeModel { const justAfterParentIndex = 1 + this.treeNodes.findIndex((n) => n.id === parentNodeId); // we add 1 here to set the index just after the parent node let insertedNodes = 0; const nodeIdAndFamilySize = new Map( - nodeIds.map((id) => [ + orderedNodeIds.map((id) => [ id, 1 + countNodes(this.treeNodes, id as UUID), // We add 1 here to include the current node in its family size ]) diff --git a/src/components/network-modification-tree.jsx b/src/components/network-modification-tree.jsx index ce876a110b..73fc563a08 100644 --- a/src/components/network-modification-tree.jsx +++ b/src/components/network-modification-tree.jsx @@ -225,14 +225,12 @@ const NetworkModificationTree = ({ * Saves the new order of parentNode's children in the backend */ const saveChildrenColumnPositions = (parentNode) => { - const nodesToUpdate = treeModel.treeNodes - .filter((n) => n.parentId === parentNode.id) - .map((node, index) => ({ - id: node.id, - type: node.type, - columnPosition: index, - })); - updateNodesColumnPositions(studyUuid, parentNode.id, nodesToUpdate).catch((error) => { + const children = treeModel.getChildren(parentNode.id).map((node, index) => ({ + id: node.id, + type: node.type, + columnPosition: index, + })); + updateNodesColumnPositions(studyUuid, parentNode.id, children).catch((error) => { snackError({ messageTxt: error.message, headerId: 'NodeUpdateColumnPositions', From 3190a6da06e7b632948c1e81ff2ee909ae8a6927 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Tue, 17 Dec 2024 18:40:15 +0100 Subject: [PATCH 12/16] PR Review Signed-off-by: BOUTIER Charly --- src/components/graph/layout.ts | 4 ++-- .../graph/network-modification-tree-model.ts | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/graph/layout.ts b/src/components/graph/layout.ts index 21217854f6..356d8da274 100644 --- a/src/components/graph/layout.ts +++ b/src/components/graph/layout.ts @@ -261,9 +261,9 @@ function compressTreePlacements(nodes: CurrentTreeNode[], placements: PlacementG // to the maximum column placement values (per row) of the nodes on the left. // The resulting space we find represents how much we can shift the current column to the left. - const currentNodeFamilySize = 1 + countNodes(nodes, currentNodeId as UUID); + const currentSubTreeSize = 1 + countNodes(nodes, currentNodeId as UUID); const indexOfCurrentNode = nodes.findIndex((n) => n.id === currentNodeId); - const nodesOfTheCurrentBranch = nodes.slice(indexOfCurrentNode, indexOfCurrentNode + currentNodeFamilySize); + const nodesOfTheCurrentBranch = nodes.slice(indexOfCurrentNode, indexOfCurrentNode + currentSubTreeSize); const currentBranchMinimumColumnByRow = getMinimumColumnByRows(nodesOfTheCurrentBranch, placements); // We have to compare with all the left nodes, not only the current branch's left neighbor, because in some diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index 55908a8342..811dcd83c9 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -52,7 +52,7 @@ export default class NetworkModificationTreeModel { const nodeToMoveIndex = this.treeNodes.findIndex((node) => node.id === nodeToMove.id); const destinationNodeIndex = this.treeNodes.findIndex((node) => node.id === destinationNode.id); - const numberOfNodesToMove: number = 1 + countNodes(this.treeNodes, nodeToMove.id); + const numberOfNodesToMove: number = 1 + countNodes(this.treeNodes, nodeToMove.id); // We add 1 here to include the node in its subtree size const nodesToMove = this.treeNodes.splice(nodeToMoveIndex, numberOfNodesToMove); if (nodeToMoveIndex > destinationNodeIndex) { @@ -62,8 +62,8 @@ export default class NetworkModificationTreeModel { // We also need to find the correct position of nodeToMove, to the right of the destination node, meaning we need to find // how many children the destination node has and add all of them to the new index. const destinationNodeIndexAfterSplice = this.treeNodes.findIndex((node) => node.id === destinationNode.id); - const destinationNodeFamilySize: number = 1 + countNodes(this.treeNodes, destinationNode.id); - this.treeNodes.splice(destinationNodeIndexAfterSplice + destinationNodeFamilySize, 0, ...nodesToMove); + const destinationNodeSubTreeSize: number = 1 + countNodes(this.treeNodes, destinationNode.id); // We add 1 here to include the destination node in its subtree size + this.treeNodes.splice(destinationNodeIndexAfterSplice + destinationNodeSubTreeSize, 0, ...nodesToMove); } this.treeNodes = [...this.treeNodes]; @@ -175,22 +175,22 @@ export default class NetworkModificationTreeModel { return; } // Let's reorder the children : - // We create a map of children node ids and number of nodes in each of these child's family, + // We create a map of children node ids and number of nodes in each of these child's subtree, // then in nodeIds order, we cut and paste the corresponding number of nodes in treeNodes. const justAfterParentIndex = 1 + this.treeNodes.findIndex((n) => n.id === parentNodeId); // we add 1 here to set the index just after the parent node let insertedNodes = 0; - const nodeIdAndFamilySize = new Map( + const nodeIdAndCorrespondingSubTreeSize = new Map( orderedNodeIds.map((id) => [ id, - 1 + countNodes(this.treeNodes, id as UUID), // We add 1 here to include the current node in its family size + 1 + countNodes(this.treeNodes, id as UUID), // We add 1 here to include the current node in its subtree size ]) ); - nodeIdAndFamilySize.forEach((familySize, nodeId) => { + nodeIdAndCorrespondingSubTreeSize.forEach((subTreeSize, nodeId) => { const nodesToMoveIndex = this.treeNodes.findIndex((n) => n.id === nodeId); - const nodesToMove = this.treeNodes.splice(nodesToMoveIndex, familySize); + const nodesToMove = this.treeNodes.splice(nodesToMoveIndex, subTreeSize); this.treeNodes.splice(justAfterParentIndex + insertedNodes, 0, ...nodesToMove); - insertedNodes += familySize; + insertedNodes += subTreeSize; }); } From dce4b3a2169025db18935fc126284d71be0c606d Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Tue, 17 Dec 2024 18:50:36 +0100 Subject: [PATCH 13/16] PR Review Signed-off-by: BOUTIER Charly --- src/components/network-modification-tree-pane.jsx | 4 ++-- src/redux/actions.ts | 14 +++++++------- src/redux/reducer.ts | 8 ++++---- src/translations/fr.json | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/network-modification-tree-pane.jsx b/src/components/network-modification-tree-pane.jsx index 4b35903930..3731377677 100644 --- a/src/components/network-modification-tree-pane.jsx +++ b/src/components/network-modification-tree-pane.jsx @@ -15,7 +15,7 @@ import { networkModificationHandleSubtree, setSelectionForCopy, resetLogsFilter, - networkModificationTreeReorderNodes, + reorderNetworkModificationTreeNodes, } from '../redux/actions'; import { useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; @@ -218,7 +218,7 @@ export const NetworkModificationTreePane = ({ studyUuid, studyMapTreeDisplay }) } else if (studyUpdatedForce.eventData.headers['updateType'] === 'nodesColumnPositionsChanged') { const orderedChildrenNodeIds = JSON.parse(studyUpdatedForce.eventData.payload); dispatch( - networkModificationTreeReorderNodes( + reorderNetworkModificationTreeNodes( studyUpdatedForce.eventData.headers['parentNode'], orderedChildrenNodeIds ) diff --git a/src/redux/actions.ts b/src/redux/actions.ts index 93bb5520b8..f0883f01f2 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -92,7 +92,7 @@ export type AppActions = | NetworkModificationHandleSubtreeAction | NetworkModificationTreeNodesRemovedAction | NetworkModificationTreeNodesUpdatedAction - | NetworkModificationTreeReorderNodesAction + | NetworkModificationTreeNodesReorderAction | SelectThemeAction | SelectLanguageAction | SelectComputedLanguageAction @@ -317,19 +317,19 @@ export function networkModificationTreeNodeMoved( }; } -export const NETWORK_MODIFICATION_TREE_REORDER_NODES = 'NETWORK_MODIFICATION_TREE_REORDER_NODES'; -export type NetworkModificationTreeReorderNodesAction = Readonly< - Action +export const NETWORK_MODIFICATION_TREE_NODES_REORDER = 'NETWORK_MODIFICATION_TREE_NODES_REORDER'; +export type NetworkModificationTreeNodesReorderAction = Readonly< + Action > & { parentNodeId: string; nodeIds: string[]; }; -export function networkModificationTreeReorderNodes( +export function reorderNetworkModificationTreeNodes( parentNodeId: string, nodeIds: string[] -): NetworkModificationTreeReorderNodesAction { +): NetworkModificationTreeNodesReorderAction { return { - type: NETWORK_MODIFICATION_TREE_REORDER_NODES, + type: NETWORK_MODIFICATION_TREE_NODES_REORDER, parentNodeId, nodeIds, }; diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index 171dc85d23..6ceb679284 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -120,14 +120,14 @@ import { NETWORK_MODIFICATION_TREE_NODE_MOVED, NETWORK_MODIFICATION_TREE_NODES_REMOVED, NETWORK_MODIFICATION_TREE_NODES_UPDATED, - NETWORK_MODIFICATION_TREE_REORDER_NODES, + NETWORK_MODIFICATION_TREE_NODES_REORDER, NetworkAreaDiagramNbVoltageLevelsAction, NetworkModificationHandleSubtreeAction, NetworkModificationTreeNodeAddedAction, NetworkModificationTreeNodeMovedAction, NetworkModificationTreeNodesRemovedAction, NetworkModificationTreeNodesUpdatedAction, - NetworkModificationTreeReorderNodesAction, + NetworkModificationTreeNodesReorderAction, OPEN_DIAGRAM, OPEN_NAD_LIST, OPEN_STUDY, @@ -904,8 +904,8 @@ export const reducer = createReducer(initialState, (builder) => { ); builder.addCase( - NETWORK_MODIFICATION_TREE_REORDER_NODES, - (state, action: NetworkModificationTreeReorderNodesAction) => { + NETWORK_MODIFICATION_TREE_NODES_REORDER, + (state, action: NetworkModificationTreeNodesReorderAction) => { if (state.networkModificationTreeModel) { let newModel = state.networkModificationTreeModel.newSharedForUpdate(); newModel.reorderChildrenNodes(action.parentNodeId, action.nodeIds); diff --git a/src/translations/fr.json b/src/translations/fr.json index 214e0f67c8..d9906be9a2 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -597,7 +597,7 @@ "NodeCreateError": "Une erreur est survenue lors de la création du nœud", "NodeDeleteError": "Une erreur est survenue lors de la suppression du nœud", "NodeUpdateError": "Une erreur est survenue lors de la mise à jour du nœud", - "NodeUpdateColumnPositions": "Une erreur est survenue lors de la mise à jour de la positions des nœuds dans l'arbre", + "NodeUpdateColumnPositions": "Une erreur est survenue lors de la mise à jour de la position des nœuds dans l'arbre", "NodeBuildingError": "Une erreur est survenue lors de la réalisation du nœud", "NodeUnbuildingError": "Une erreur est survenue lors de la déréalisation du nœud", "NetworkModifications": "Modifications de réseau", From 975e18d8de8831deba3dc3d27bbea367a674656a Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Tue, 17 Dec 2024 20:12:47 +0100 Subject: [PATCH 14/16] Code cleaning : removed the switch branch function and use the reorder everytime. Signed-off-by: BOUTIER Charly --- src/components/graph/layout.ts | 33 ----- .../graph/network-modification-tree-model.ts | 122 +----------------- src/components/network-modification-tree.jsx | 41 +++--- 3 files changed, 21 insertions(+), 175 deletions(-) diff --git a/src/components/graph/layout.ts b/src/components/graph/layout.ts index 356d8da274..11e37d9d31 100644 --- a/src/components/graph/layout.ts +++ b/src/components/graph/layout.ts @@ -343,39 +343,6 @@ export function getFirstAncestorWithSibling( return undefined; } -/** - * Will find the sibling node whose X position is closer to xDestination in the X range provided. - */ -export function findClosestSiblingInRange( - nodes: CurrentTreeNode[], - node: CurrentTreeNode, - xOrigin: number, - xDestination: number -): CurrentTreeNode | null { - const minX = Math.min(xOrigin, xDestination); - const maxX = Math.max(xOrigin, xDestination); - const siblingNodes = findSiblings(nodes, node); - const nodesBetween = siblingNodes.filter((n) => n.position.x < maxX && n.position.x > minX); - if (nodesBetween.length > 0) { - const closestNode = nodesBetween.reduce( - (closest, current) => - Math.abs(current.position.x - xDestination) < Math.abs(closest.position.x - xDestination) - ? current - : closest, - nodesBetween[0] - ); - return closestNode; - } - return null; -} - -/** - * Will find the siblings of a provided node (all siblings have the same parent). - */ -function findSiblings(nodes: CurrentTreeNode[], node: CurrentTreeNode): CurrentTreeNode[] { - return nodes.filter((n) => n.parentId === node.parentId && n.id !== node.id); -} - /** * Computes the absolute position of a node by calculating the sum of all the relative positions of * the node's lineage. diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index 811dcd83c9..0f747e3cf0 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -38,122 +38,6 @@ export default class NetworkModificationTreeModel { return 0; } - /** - * Will switch the order of two nodes in the tree. - * The nodeToMove will be moved, either to the left or right of the destinationNode, depending - * on their initial positions. - * Both nodes should have the same parent. - */ - switchSiblingsOrder(nodeToMove: CurrentTreeNode, destinationNode: CurrentTreeNode) { - if (!nodeToMove.parentId || nodeToMove.parentId !== destinationNode.parentId) { - console.error('Both nodes should have the same parent to switch their order'); - return; - } - const nodeToMoveIndex = this.treeNodes.findIndex((node) => node.id === nodeToMove.id); - const destinationNodeIndex = this.treeNodes.findIndex((node) => node.id === destinationNode.id); - - const numberOfNodesToMove: number = 1 + countNodes(this.treeNodes, nodeToMove.id); // We add 1 here to include the node in its subtree size - const nodesToMove = this.treeNodes.splice(nodeToMoveIndex, numberOfNodesToMove); - - if (nodeToMoveIndex > destinationNodeIndex) { - this.treeNodes.splice(destinationNodeIndex, 0, ...nodesToMove); - } else { - // When moving nodeToMove to the right, we have to take into account the splice function that changed the nodes' indexes. - // We also need to find the correct position of nodeToMove, to the right of the destination node, meaning we need to find - // how many children the destination node has and add all of them to the new index. - const destinationNodeIndexAfterSplice = this.treeNodes.findIndex((node) => node.id === destinationNode.id); - const destinationNodeSubTreeSize: number = 1 + countNodes(this.treeNodes, destinationNode.id); // We add 1 here to include the destination node in its subtree size - this.treeNodes.splice(destinationNodeIndexAfterSplice + destinationNodeSubTreeSize, 0, ...nodesToMove); - } - - this.treeNodes = [...this.treeNodes]; - } - - /** - * Finds the lowest common ancestor of two nodes in the tree. - * - * Example tree: - * A - * / \ - * B D - * / / \ - * C E F - * - * Examples: - * - getCommonAncestor(B, E) will return A - * - getCommonAncestor(E, F) will return D - */ - getCommonAncestor(nodeA: CurrentTreeNode, nodeB: CurrentTreeNode): CurrentTreeNode | null { - const getAncestors = (node: CurrentTreeNode) => { - const ancestors = []; - let current: CurrentTreeNode | undefined = node; - while (current && current.parentId) { - const parentId: string = current.parentId; - ancestors.push(parentId); - current = this.treeNodes.find((n) => n.id === parentId); - } - return ancestors; - }; - // We get the entire ancestors of one of the nodes in an array, then iterate over the other node's ancestors - // until we find a node that is in the first array : this common node is an ancestor of both intial nodes. - const ancestorsA: string[] = getAncestors(nodeA); - let current: CurrentTreeNode | undefined = nodeB; - while (current && current.parentId) { - const parentId: string = current.parentId; - current = this.treeNodes.find((n) => n.id === parentId); - if (current && ancestorsA.includes(current.id)) { - return current; - } - } - console.warn('No common ancestor found !'); - return null; - } - - /** - * Finds the child of the ancestor node that is on the path to the descendant node. - * - * Example tree: - * A - * / \ - * B D - * / / \ - * C E F - * - * Examples: - * - getChildOfAncestorInLineage(A, E) will return D - * - getChildOfAncestorInLineage(D, F) will return F - * - * @param ancestor node, must be an ancestor of descendant node - * @param descendant node, must be a descendant of ancestor - * @returns The child of the ancestor node in the lineage or null if not found. - * @private - */ - getChildOfAncestorInLineage(ancestor: CurrentTreeNode, descendant: CurrentTreeNode): CurrentTreeNode | null { - let current: CurrentTreeNode | undefined = descendant; - while (current && current.parentId) { - const parentId: string = current.parentId; - if (parentId === ancestor.id) { - return current; - } - current = this.treeNodes.find((n) => n.id === parentId); - } - console.warn('The ancestor and descendant do not share the same branch !'); - return null; - } - - switchBranches(studyUuid: UUID, nodeToMove: CurrentTreeNode, destinationNode: CurrentTreeNode) { - // We find the nodes from the two branches that share the same parent - const commonAncestor = this.getCommonAncestor(nodeToMove, destinationNode); - if (commonAncestor) { - const siblingFromNodeToMoveBranch = this.getChildOfAncestorInLineage(commonAncestor, nodeToMove); - const siblingFromDestinationNodeBranch = this.getChildOfAncestorInLineage(commonAncestor, destinationNode); - if (siblingFromNodeToMoveBranch && siblingFromDestinationNodeBranch) { - this.switchSiblingsOrder(siblingFromNodeToMoveBranch, siblingFromDestinationNodeBranch); - } - } - return commonAncestor; - } - getChildren(parentNodeId: string): CurrentTreeNode[] { return this.treeNodes.filter((n) => n.parentId === parentNodeId); } @@ -162,17 +46,18 @@ export default class NetworkModificationTreeModel { * Will reorganize treeNodes and put the children of parentNodeId in the order provided in nodeIds array. * @param parentNodeId parent ID of the to be reordered children nodes * @param orderedNodeIds array of children ID in the order we want + * @returns true if the order was changed */ reorderChildrenNodes(parentNodeId: string, orderedNodeIds: string[]) { // We check if the current position is already correct const children = this.getChildren(parentNodeId); if (orderedNodeIds.length !== children.length) { console.warn('reorderNodes : synchronization error, reorder cancelled'); - return; + return false; } if (children.map((child) => child.id).join(',') === orderedNodeIds.join(',')) { // Alreay in the same order. - return; + return false; } // Let's reorder the children : // We create a map of children node ids and number of nodes in each of these child's subtree, @@ -192,6 +77,7 @@ export default class NetworkModificationTreeModel { this.treeNodes.splice(justAfterParentIndex + insertedNodes, 0, ...nodesToMove); insertedNodes += subTreeSize; }); + return true; } addChild( diff --git a/src/components/network-modification-tree.jsx b/src/components/network-modification-tree.jsx index 73fc563a08..23837e4abb 100644 --- a/src/components/network-modification-tree.jsx +++ b/src/components/network-modification-tree.jsx @@ -20,7 +20,6 @@ import { nodeTypes } from './graph/util/model-constants'; import { BUILD_STATUS } from './network/constants'; import { StudyDisplayMode } from './network-modification.type'; import { - findClosestSiblingInRange, getAbsolutePosition, getFirstAncestorWithSibling, getTreeNodesWithUpdatedPositions, @@ -224,13 +223,13 @@ const NetworkModificationTree = ({ /** * Saves the new order of parentNode's children in the backend */ - const saveChildrenColumnPositions = (parentNode) => { - const children = treeModel.getChildren(parentNode.id).map((node, index) => ({ + const saveChildrenColumnPositions = (parentNodeId) => { + const children = treeModel.getChildren(parentNodeId).map((node, index) => ({ id: node.id, type: node.type, columnPosition: index, })); - updateNodesColumnPositions(studyUuid, parentNode.id, children).catch((error) => { + updateNodesColumnPositions(studyUuid, parentNodeId, children).catch((error) => { snackError({ messageTxt: error.message, headerId: 'NodeUpdateColumnPositions', @@ -240,34 +239,28 @@ const NetworkModificationTree = ({ /** * When the user stops dragging a node and releases it to its new position, we check if we need - * to switch the order of the moved branch with a neighboring branch. + * to reorder the nodes */ const handleEndNodeDragging = () => { let movedNode = nodesMap.get(draggedBranchIdRef.current); draggedBranchIdRef.current = null; - if (movedNode) { + if (movedNode && movedNode.parentId) { // In the treeModel.treeNodes variable we can find the positions of the nodes before the user started // dragging something, whereas in the movedNode variable (which comes from the nodes variable), we can // find the position of the node which has been updated by ReactFlow's onNodesChanges function as the // user kept on dragging the node. - const movedNodeXPositionBeforeDrag = treeModel.treeNodes.find((n) => n.id === movedNode.id).position.x; - const movedNodeXPositionAfterDrag = movedNode.position.x; - - const nodeToSwitchWith = findClosestSiblingInRange( - nodes, - movedNode, - movedNodeXPositionBeforeDrag, - movedNodeXPositionAfterDrag - ); - if (nodeToSwitchWith) { - const nodeToMove = treeModel.treeNodes.find((n) => n.id === movedNode.id); - const destinationNode = treeModel.treeNodes.find((n) => n.id === nodeToSwitchWith.id); - if (nodeToMove && destinationNode) { - const parentNode = treeModel.switchBranches(studyUuid, nodeToMove, destinationNode); - if (parentNode) { - saveChildrenColumnPositions(parentNode); - } - } + // We want the new positions post-drag, so we get the original positions, remove the moved node from them, + // and add the updated movedNode to the list. + const childrenWithOriginalPositions = treeModel.getChildren(movedNode.parentId); + + const childrenWithUpdatedPositions = childrenWithOriginalPositions.filter((n) => n.id !== movedNode.id); + childrenWithUpdatedPositions.push(movedNode); + // We want the ids in the correct order, so we sort by the nodes' X position. + childrenWithUpdatedPositions.sort((a, b) => a.position.x - b.position.x); + const orderedChildrenIds = childrenWithUpdatedPositions.map((node) => node.id); + + if (treeModel.reorderChildrenNodes(movedNode.parentId, orderedChildrenIds)) { + saveChildrenColumnPositions(movedNode.parentId); } } }; From ad3ae70250749aecb7dc70b5721241205cde8426 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Fri, 20 Dec 2024 14:59:09 +0100 Subject: [PATCH 15/16] fix old comment Signed-off-by: BOUTIER Charly --- src/components/graph/network-modification-tree-model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index 0f747e3cf0..04465dd304 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -61,7 +61,7 @@ export default class NetworkModificationTreeModel { } // Let's reorder the children : // We create a map of children node ids and number of nodes in each of these child's subtree, - // then in nodeIds order, we cut and paste the corresponding number of nodes in treeNodes. + // then in orderedNodeIds order, we cut and paste the corresponding number of nodes in treeNodes. const justAfterParentIndex = 1 + this.treeNodes.findIndex((n) => n.id === parentNodeId); // we add 1 here to set the index just after the parent node let insertedNodes = 0; const nodeIdAndCorrespondingSubTreeSize = new Map( From 52a218f360d6eb9e363a02e5c4f2c7a8495a22f6 Mon Sep 17 00:00:00 2001 From: BOUTIER Charly Date: Fri, 20 Dec 2024 15:39:35 +0100 Subject: [PATCH 16/16] Code clarification and simplification Signed-off-by: BOUTIER Charly --- .../graph/network-modification-tree-model.ts | 18 ++++++++---------- src/components/network-modification-tree.jsx | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/components/graph/network-modification-tree-model.ts b/src/components/graph/network-modification-tree-model.ts index 04465dd304..589da0fc86 100644 --- a/src/components/graph/network-modification-tree-model.ts +++ b/src/components/graph/network-modification-tree-model.ts @@ -56,24 +56,22 @@ export default class NetworkModificationTreeModel { return false; } if (children.map((child) => child.id).join(',') === orderedNodeIds.join(',')) { - // Alreay in the same order. + // Already in the same order. return false; } // Let's reorder the children : - // We create a map of children node ids and number of nodes in each of these child's subtree, - // then in orderedNodeIds order, we cut and paste the corresponding number of nodes in treeNodes. + // In orderedNodeIds order, we cut and paste the corresponding number of nodes in treeNodes. const justAfterParentIndex = 1 + this.treeNodes.findIndex((n) => n.id === parentNodeId); // we add 1 here to set the index just after the parent node let insertedNodes = 0; - const nodeIdAndCorrespondingSubTreeSize = new Map( - orderedNodeIds.map((id) => [ - id, - 1 + countNodes(this.treeNodes, id as UUID), // We add 1 here to include the current node in its subtree size - ]) - ); - nodeIdAndCorrespondingSubTreeSize.forEach((subTreeSize, nodeId) => { + orderedNodeIds.forEach((nodeId) => { const nodesToMoveIndex = this.treeNodes.findIndex((n) => n.id === nodeId); + const subTreeSize = 1 + countNodes(this.treeNodes, nodeId as UUID); // We add 1 here to include the current node in its subtree size + + // We remove from treeNodes the nodes that we want to move, (...) const nodesToMove = this.treeNodes.splice(nodesToMoveIndex, subTreeSize); + + // (...) and now we put them in their new position in the array this.treeNodes.splice(justAfterParentIndex + insertedNodes, 0, ...nodesToMove); insertedNodes += subTreeSize; }); diff --git a/src/components/network-modification-tree.jsx b/src/components/network-modification-tree.jsx index 23837e4abb..d015521e9c 100644 --- a/src/components/network-modification-tree.jsx +++ b/src/components/network-modification-tree.jsx @@ -244,7 +244,7 @@ const NetworkModificationTree = ({ const handleEndNodeDragging = () => { let movedNode = nodesMap.get(draggedBranchIdRef.current); draggedBranchIdRef.current = null; - if (movedNode && movedNode.parentId) { + if (movedNode?.parentId) { // In the treeModel.treeNodes variable we can find the positions of the nodes before the user started // dragging something, whereas in the movedNode variable (which comes from the nodes variable), we can // find the position of the node which has been updated by ReactFlow's onNodesChanges function as the