Skip to content

Commit

Permalink
Merge branch 'main' into add-control-on-regulating-point
Browse files Browse the repository at this point in the history
  • Loading branch information
EtienneLt authored Feb 13, 2025
2 parents ad5cb59 + 6f8d8e8 commit 0425827
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 189 deletions.
10 changes: 4 additions & 6 deletions src/components/dialogs/connectivity/connectivity-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ export const ConnectivityForm = ({
return;
}
if (watchVoltageLevelId) {
setValue(`${id}.${BUS_OR_BUSBAR_SECTION}`, null);
const existingVoltageLevelOption = voltageLevelOptions.find((option) => option.id === watchVoltageLevelId);
if (existingVoltageLevelOption) {
fetchBusesOrBusbarSectionsForVoltageLevel(
Expand All @@ -95,17 +94,13 @@ export const ConnectivityForm = ({
} else {
setBusOrBusbarSectionOptions([]);
}
} else {
setBusOrBusbarSectionOptions([]);
setValue(`${id}.${BUS_OR_BUSBAR_SECTION}`, null);
}
}, [
watchVoltageLevelId,
studyUuid,
currentNodeUuid,
currentRootNetworkUuid,
voltageLevelOptions,
setValue,
id,
isEquipmentModification,
]);
Expand All @@ -121,7 +116,9 @@ export const ConnectivityForm = ({

const handleChange = useCallback(() => {
onVoltageLevelChangeCallback?.();
}, [onVoltageLevelChangeCallback]);
setBusOrBusbarSectionOptions([]);
setValue(`${id}.${BUS_OR_BUSBAR_SECTION}`, null);
}, [id, onVoltageLevelChangeCallback, setValue]);

useEffect(() => {
if (isEquipmentModification) {
Expand Down Expand Up @@ -150,6 +147,7 @@ export const ConnectivityForm = ({
onChangeCallback={handleChange}
allowNewValue
forcePopupIcon
selectOnFocus
name={`${id}.${VOLTAGE_LEVEL}`}
label={voltageLevelSelectLabel}
options={voltageLevelOptions}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ const DeleteEquipmentForm = ({ studyUuid, currentNode, currentRootNetworkUuid, e
EQUIPMENT_TYPES.BUS,
EQUIPMENT_TYPES.BUSBAR_SECTION,
EQUIPMENT_TYPES.TIE_LINE,
EQUIPMENT_TYPES.BREAKER,
EQUIPMENT_TYPES.DISCONNECTOR,
]);
return Object.values(EQUIPMENT_TYPES).filter((equipmentType) => !equipmentTypesToExclude.has(equipmentType));
}, []);
Expand Down
6 changes: 6 additions & 0 deletions src/components/spreadsheet/config/spreadsheet.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { UUID } from 'crypto';
import type { EQUIPMENT_TYPES } from '../../utils/equipment-types';
import type { CustomAggridFilterParams, CustomColDef } from '../../custom-aggrid/custom-aggrid-header.type';
import { Identifiable } from '@gridsuite/commons-ui';

export type EquipmentFetcher = (
studyUuid: UUID,
Expand All @@ -31,4 +32,9 @@ export interface SpreadsheetTabDefinition<TData = any, F extends CustomAggridFil
columns: CustomColDef<TData, F>[];
}

export type SpreadsheetEquipmentsByNodes = {
nodesId: string[];
equipmentsByNodeId: Record<string, Identifiable[]>;
};

export type ColumnState = { colId: string; visible: boolean };
52 changes: 20 additions & 32 deletions src/components/spreadsheet/table-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,8 @@ export const TableWrapper: FunctionComponent<TableWrapperProps> = ({
() => new Set(lockedColumns[tabIndex] ? JSON.parse(lockedColumns[tabIndex]) : []),
[lockedColumns, tabIndex]
);
const nodesAliases = useSelector((state: AppState) => state.customColumnsNodesAliases);
const tablesDefinitions = useSelector((state: AppState) => state.tables.definitions);
const additionalEquipmentsByNodesForCustomColumns = useSelector(
(state: AppState) => state.additionalEquipmentsByNodesForCustomColumns
);
const developerMode = useSelector((state: AppState) => state[PARAM_DEVELOPER_MODE]);

const [manualTabSwitch, setManualTabSwitch] = useState<boolean>(true);
Expand Down Expand Up @@ -207,45 +205,32 @@ export const TableWrapper: FunctionComponent<TableWrapperProps> = ({
}, [errorMessage, snackError]);

useEffect(() => {
if (disabled || !equipments) {
if (disabled || equipments.nodesId.find((nodeId) => nodeId === currentNode.id) === undefined) {
return;
}

let equipmentsWithCustomColumnInfo = [...equipments];
let localRowData: Identifiable[] = [];
if (tableDefinition.type) {
Object.entries(additionalEquipmentsByNodesForCustomColumns).forEach(([nodeAlias, equipments]) => {
const equipmentsToAdd = equipments[tableDefinition.type];
if (equipmentsToAdd) {
equipmentsToAdd.forEach((equipmentToAdd) => {
let matchingEquipmentIndex = equipmentsWithCustomColumnInfo.findIndex(
(equipmentWithCustomColumnInfo) => equipmentWithCustomColumnInfo.id === equipmentToAdd.id
);
let matchingEquipment = equipmentsWithCustomColumnInfo[matchingEquipmentIndex];
if (matchingEquipment) {
let equipmentWithAddedInfo: RecursiveIdentifiable = { ...matchingEquipment };
equipmentWithAddedInfo[nodeAlias] = equipmentToAdd;
equipmentsWithCustomColumnInfo[matchingEquipmentIndex] = equipmentWithAddedInfo;
}
});
}
equipments.equipmentsByNodeId[currentNode.id].forEach((equipment) => {
let equipmentToAdd: RecursiveIdentifiable = { ...equipment };
Object.entries(equipments.equipmentsByNodeId).forEach(([nodeId, equipments]) => {
let matchingEquipment = equipments.find((eq) => eq.id === equipment.id);
let nodeAlias = nodesAliases.find((value) => value.id === nodeId);
if (nodeAlias !== undefined && matchingEquipment !== undefined) {
equipmentToAdd[nodeAlias.alias] = matchingEquipment;
}
});
localRowData.push(equipmentToAdd);
});
}
setRowData(equipmentsWithCustomColumnInfo);

// To handle cases where a "customSpreadsheet" tab is opened.
// This ensures that the grid correctly displays data specific to the custom tab.
if (gridRef.current?.api) {
gridRef.current.api.setGridOption('rowData', equipmentsWithCustomColumnInfo);
gridRef.current.api.setGridOption('rowData', localRowData);
updateSortConfig();
}
}, [
tabIndex,
disabled,
equipments,
additionalEquipmentsByNodesForCustomColumns,
tableDefinition.type,
updateSortConfig,
]);
setRowData(localRowData);
}, [tabIndex, disabled, equipments, tableDefinition.type, nodesAliases, currentNode.id, updateSortConfig]);

const handleSwitchTab = useCallback(
(value: number) => {
Expand Down Expand Up @@ -382,7 +367,10 @@ export const TableWrapper: FunctionComponent<TableWrapperProps> = ({
currentNode={currentNode}
rowData={rowData}
columnData={mergedColumnData}
fetched={!!equipments || !!errorMessage}
fetched={
equipments.nodesId.find((nodeId) => nodeId === currentNode.id) !== undefined ||
!!errorMessage
}
handleColumnDrag={handleColumnDrag}
handleRowDataUpdated={handleRowDataUpdated}
shouldHidePinnedHeaderRightBorder={isLockedColumnNamesEmpty}
Expand Down
139 changes: 68 additions & 71 deletions src/components/spreadsheet/use-spreadsheet-equipments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@

import { Identifiable } from '@gridsuite/commons-ui';
import { EQUIPMENT_TYPES } from 'components/utils/equipment-types';
import { UUID } from 'crypto';
import { useGetStudyImpacts } from 'hooks/use-get-study-impacts';
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
addAdditionalEquipmentsByNodesForCustomColumns,
AdditionalNodeData,
deleteEquipments,
EquipmentToDelete,
loadEquipments,
Expand All @@ -26,28 +25,18 @@ import type { SpreadsheetEquipmentType } from './config/spreadsheet.type';
import { fetchAllEquipments } from 'services/study/network-map';
import { getFetcher } from './config/equipment/common-config';
import { isNodeBuilt } from 'components/graph/util/model-functions';
import { SpreadsheetEquipmentsByNodes } from './config/spreadsheet.type';

type FormatFetchedEquipments = (equipments: Identifiable[]) => Identifiable[];

const filterUndefined = (
res: AdditionalNodeData | undefined
): res is {
alias: string;
identifiables: Identifiable[];
} => {
return res !== undefined;
};

export const useSpreadsheetEquipments = (
type: SpreadsheetEquipmentType,
formatFetchedEquipments: FormatFetchedEquipments
) => {
const dispatch = useDispatch();
const allEquipments = useSelector((state: AppState) => state.spreadsheetNetwork);
const equipments = allEquipments[type];
const allAdditionalEquipments = useSelector((state: AppState) => state.additionalEquipmentsByNodesForCustomColumns);
const equipments = useMemo(() => allEquipments[type], [allEquipments, type]);
const customColumnsDefinitions = useSelector((state: AppState) => state.tables.allCustomColumnsDefinitions);
const customColumnsNodesAliases = useSelector((state: AppState) => state.customColumnsNodesAliases);
const studyUuid = useSelector((state: AppState) => state.studyUuid);
const isNetworkModificationTreeModelUpToDate = useSelector(
(state: AppState) => state.isNetworkModificationTreeModelUpToDate
Expand All @@ -56,6 +45,25 @@ export const useSpreadsheetEquipments = (
const currentNode = useSelector((state: AppState) => state.currentTreeNode);
const [errorMessage, setErrorMessage] = useState<string | null>();
const [isFetching, setIsFetching] = useState(false);
const nodesAliases = useSelector((state: AppState) => state.customColumnsNodesAliases);

const nodesIdToFetch = useMemo(() => {
let nodesIdToFetch = new Set<string>();
// We check if we have the data for the currentNode and if we don't we save the fact that we need to fetch it
if (equipments.nodesId.find((nodeId) => nodeId === currentNode?.id) === undefined) {
nodesIdToFetch.add(currentNode?.id as string);
}
//Then we do the same for the other nodes we need the data of (the ones defined in aliases)
nodesAliases.forEach((nodeAlias) => {
if (equipments.nodesId.find((nodeId) => nodeId === nodeAlias.id) === undefined) {
nodesIdToFetch.add(nodeAlias.id);
}
});
return nodesIdToFetch;
}, [currentNode?.id, equipments.nodesId, nodesAliases]);

const shouldFetchEquipments = useMemo(() => nodesIdToFetch.size > 0, [nodesIdToFetch]);

const {
impactedSubstationsIds,
deletedEquipments,
Expand All @@ -65,16 +73,33 @@ export const useSpreadsheetEquipments = (
resetImpactedElementTypes,
} = useGetStudyImpacts();

const shouldFetchEquipments = !equipments;
useEffect(() => {
const currentNodeId = currentNode?.id as UUID;

let unwantedFetchedNodes = new Set<string>();
Object.values(allEquipments).forEach((value) => {
unwantedFetchedNodes = new Set([...unwantedFetchedNodes, ...value.nodesId]);
});
const usedNodesId = new Set(nodesAliases.map((nodeAlias) => nodeAlias.id));
usedNodesId.add(currentNodeId);
usedNodesId.forEach((nodeId) => unwantedFetchedNodes.delete(nodeId));
if (unwantedFetchedNodes.size !== 0) {
dispatch(removeNodeData(Array.from(unwantedFetchedNodes)));
}
}, [dispatch, nodesAliases, currentNode, allEquipments]);

useEffect(() => {
// updating data related to impacted elements
const nodeId = currentNode?.id as UUID;

// If we dont have any data in the spreadsheet, we don't need to update the equipments
const hasSpreadsheedData = () => {
return Object.values(allEquipments).some((value) => Array.isArray(value) && value.length > 0);
const hasSpreadsheetData = () => {
return Object.values(allEquipments)
.map((value) => value.equipmentsByNodeId[nodeId])
.filter((value) => value !== undefined)
.some((value) => value.length > 0);
};
if (!hasSpreadsheedData()) {
if (!hasSpreadsheetData()) {
resetImpactedSubstationsIds();
resetDeletedEquipments();
resetImpactedElementTypes();
Expand All @@ -97,11 +122,9 @@ export const useSpreadsheetEquipments = (
}
if (impactedSubstationsIds.length > 0 && studyUuid && currentRootNetworkUuid && currentNode?.id) {
// The formatting of the fetched equipments is done in the reducer
fetchAllEquipments(studyUuid, currentNode.id, currentRootNetworkUuid, impactedSubstationsIds).then(
(values) => {
dispatch(updateEquipments(values));
}
);
fetchAllEquipments(studyUuid, nodeId, currentRootNetworkUuid, impactedSubstationsIds).then((values) => {
dispatch(updateEquipments(values, nodeId));
});
resetImpactedSubstationsIds();
}
if (deletedEquipments.length > 0) {
Expand All @@ -122,8 +145,9 @@ export const useSpreadsheetEquipments = (
const equipmentsToDeleteArray: EquipmentToDelete[] = equipmentsToDelete.map((equipment) => ({
equipmentType: equipment.equipmentType as SpreadsheetEquipmentType,
equipmentId: equipment.equipmentId,
nodeId: nodeId,
}));
dispatch(deleteEquipments(equipmentsToDeleteArray));
dispatch(deleteEquipments(equipmentsToDeleteArray, nodeId));
}

resetDeletedEquipments();
Expand Down Expand Up @@ -153,19 +177,29 @@ export const useSpreadsheetEquipments = (
) {
setErrorMessage(null);
setIsFetching(true);
getFetcher(type)(studyUuid, currentNode?.id, currentRootNetworkUuid)
.then((results) => {
let fetchers: Promise<unknown>[] = [];
let spreadsheetEquipmentsByNodes: SpreadsheetEquipmentsByNodes = {
nodesId: [],
equipmentsByNodeId: {},
};

nodesIdToFetch.forEach((nodeId) => {
const fetcherPromises = getFetcher(type)(studyUuid, nodeId as UUID, currentRootNetworkUuid, []);
fetchers.push(fetcherPromises);
fetcherPromises.then((results) => {
let fetchedEquipments = results.flat();
spreadsheetEquipmentsByNodes.nodesId.push(nodeId);
if (formatFetchedEquipments) {
fetchedEquipments = formatFetchedEquipments(fetchedEquipments);
spreadsheetEquipmentsByNodes.equipmentsByNodeId[nodeId] = fetchedEquipments;
}
dispatch(loadEquipments(type, fetchedEquipments));
setIsFetching(false);
})
.catch((err) => {
setErrorMessage(err);
setIsFetching(false);
});
});

Promise.all(fetchers).then(() => {
dispatch(loadEquipments(type, spreadsheetEquipmentsByNodes));
setIsFetching(false);
});
}
}, [
shouldFetchEquipments,
Expand All @@ -176,46 +210,9 @@ export const useSpreadsheetEquipments = (
dispatch,
formatFetchedEquipments,
customColumnsDefinitions,
nodesIdToFetch,
type,
]);

useEffect(() => {
if (studyUuid && currentRootNetworkUuid) {
// Clean nodes that are not loaded anymore
const unwantedFetchedNodes = new Set(Object.keys(allAdditionalEquipments));
const usedNodes = new Set(customColumnsNodesAliases);
usedNodes.forEach((node) => unwantedFetchedNodes.delete(node.alias));
if (unwantedFetchedNodes.size !== 0) {
dispatch(removeNodeData(Array.from(unwantedFetchedNodes)));
}

// Fetch new nodes for the current type if required
const fetchedEquipments = customColumnsNodesAliases.map(async (aliasInfo) => {
if (allAdditionalEquipments[aliasInfo.alias]?.[type] !== undefined) {
return undefined;
}
const identifiableList = await getFetcher(type)(studyUuid, aliasInfo.id, currentRootNetworkUuid);
return {
alias: aliasInfo.alias,
identifiables: formatFetchedEquipments(identifiableList.flat()),
} satisfies AdditionalNodeData;
});
Promise.all(fetchedEquipments).then((results) => {
const filteredResults = results.filter(filterUndefined);
if (filteredResults.length !== 0) {
dispatch(addAdditionalEquipmentsByNodesForCustomColumns(type, filteredResults));
}
});
}
}, [
dispatch,
studyUuid,
currentRootNetworkUuid,
formatFetchedEquipments,
customColumnsNodesAliases,
type,
allAdditionalEquipments,
]);

return { equipments, errorMessage, isFetching };
};
Loading

0 comments on commit 0425827

Please sign in to comment.