diff --git a/src/components/app.jsx b/src/components/app.jsx index 9dbea2c4c7..5a6637baef 100644 --- a/src/components/app.jsx +++ b/src/components/app.jsx @@ -30,7 +30,7 @@ import { } from '@gridsuite/commons-ui'; import PageNotFound from './page-not-found'; import { FormattedMessage } from 'react-intl'; -import { APP_NAME, PARAM_FAVORITE_CONTINGENCY_LISTS, PARAM_USE_NAME } from '../utils/config-params'; +import { APP_NAME, PARAM_USE_NAME } from '../utils/config-params'; import AppTopBar from './app-top-bar'; import { StudyContainer } from './study-container'; import { fetchDefaultParametersValues, fetchIdpSettings } from '../services/utils'; @@ -42,7 +42,6 @@ import { renameTableDefinition, saveSpreadsheetGlobalFilters, selectComputedLanguage, - selectFavoriteContingencyLists, selectIsDeveloperMode, selectLanguage, selectTheme, @@ -129,9 +128,6 @@ const App = () => { case PARAM_USE_NAME: dispatch(selectUseName(param.value === 'true')); break; - case PARAM_FAVORITE_CONTINGENCY_LISTS: - dispatch(selectFavoriteContingencyLists(param.value.split(',').filter((list) => list))); - break; case LAST_SELECTED_DIRECTORY: localStorage.setItem(LAST_SELECTED_DIRECTORY, param.value); break; diff --git a/src/components/dialogs/contingency-list-selector.tsx b/src/components/dialogs/contingency-list-selector.tsx deleted file mode 100644 index 806db67536..0000000000 --- a/src/components/dialogs/contingency-list-selector.tsx +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Copyright (c) 2020, 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 { useCallback, useEffect, useState } from 'react'; -import { FormattedMessage, useIntl } from 'react-intl'; -import { PARAM_FAVORITE_CONTINGENCY_LISTS } from '../../utils/config-params'; -import { useSelector } from 'react-redux'; -import { - CheckBoxList, - DirectoryItemSelector, - ElementType, - TreeViewFinderNodeProps, - useSnackMessage, -} from '@gridsuite/commons-ui'; -import { fetchContingencyAndFiltersLists } from '../../services/directory'; -import { fetchContingencyCount } from '../../services/study'; -import { isNodeBuilt } from 'components/graph/util/model-functions'; -import DeleteIcon from '@mui/icons-material/Delete'; -import IconButton from '@mui/material/IconButton'; -import type { UUID } from 'node:crypto'; -import { toggleElementFromList } from 'components/utils/utils'; -import { Alert, Button, Dialog, DialogActions, DialogContent, DialogTitle, Grid, Typography } from '@mui/material'; -import { AppState } from '../../redux/reducer'; -import { useParameterState } from './parameters/use-parameters-state'; - -function makeButton(onClick: () => void, message: string, disabled: boolean) { - return ( - - - - ); -} - -const CONTINGENCY_TYPES = [ElementType.CONTINGENCY_LIST]; - -type ContingencyListInfo = { - id: UUID; - name: string; -}; - -interface ContingencyListSelectorProps { - open: boolean; - onClose: () => void; - onStart: (selectedUuids: UUID[]) => void; -} - -export function ContingencyListSelector({ open, onClose, onStart }: Readonly) { - const [favoriteContingencyListUuids, saveFavorites] = useParameterState( - PARAM_FAVORITE_CONTINGENCY_LISTS, - (newList) => newList.join() - ); - const studyUuid = useSelector((state: AppState) => state.studyUuid as UUID); - const currentNode = useSelector((state: AppState) => state.currentTreeNode); - const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetworkUuid); - - const [contingencyList, setContingencyList] = useState([]); - - const [simulatedContingencyCount, setSimulatedContingencyCount] = useState(0); - - const [checkedContingencyList, setCheckedContingencyList] = useState([]); - - const [favoriteSelectorOpen, setFavoriteSelectorOpen] = useState(false); - - const { snackError } = useSnackMessage(); - - const intl = useIntl(); - - const handleClose = () => { - onClose(); - }; - - const handleStart = () => { - onStart(checkedContingencyList.map((c) => c.id)); - }; - - useEffect(() => { - if (!open || !currentNode || !currentRootNetworkUuid || !isNodeBuilt(currentNode)) { - return; - } - - if (checkedContingencyList.length === 0) { - setSimulatedContingencyCount(0); - return; - } - - setSimulatedContingencyCount(null); - let discardResult = false; - fetchContingencyCount( - studyUuid, - currentNode.id, - currentRootNetworkUuid, - checkedContingencyList.map((c) => c.id) - ).then((contingencyCount) => { - if (!discardResult) { - setSimulatedContingencyCount(contingencyCount); - } - }); - return () => { - discardResult = true; - }; - }, [open, studyUuid, currentNode, checkedContingencyList, currentRootNetworkUuid]); - - useEffect(() => { - if (favoriteContingencyListUuids?.length && open) { - fetchContingencyAndFiltersLists(favoriteContingencyListUuids) - .then((elements) => { - const favoriteElements = elements - .map((e) => ({ - id: e.elementUuid, - name: e.elementName, - })) - .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())); - setContingencyList(favoriteElements); - }) - .catch(() => { - snackError({ - headerId: 'getContingencyListError', - }); - }); - } else { - setContingencyList([]); - } - }, [favoriteContingencyListUuids, snackError, open]); - - function getSimulatedContingencyCountLabel() { - return simulatedContingencyCount ?? '...'; - } - - const handleAddFavorite = () => { - setFavoriteSelectorOpen(true); - }; - - const removeFromFavorites = useCallback( - (itemsToRemove: ContingencyListInfo[]) => { - const toRemoveIdsSet = new Set(itemsToRemove.map((e) => e.id)); - saveFavorites(contingencyList.map((e) => e.id).filter((id) => !toRemoveIdsSet.has(id))); - - setCheckedContingencyList((oldChecked) => oldChecked.filter((item) => !toRemoveIdsSet.has(item.id))); - }, - [contingencyList, saveFavorites] - ); - - const addFavorites = (nodes: TreeViewFinderNodeProps[]) => { - if (nodes?.length) { - // avoid duplicates here - const newFavoriteIdsSet = new Set([ - ...contingencyList.map((e) => e.id), - ...nodes.map((node) => node.id as UUID), - ]); - saveFavorites(Array.from([...newFavoriteIdsSet])); - } - setFavoriteSelectorOpen(false); - }; - - const handleSecondaryAction = useCallback( - (item: ContingencyListInfo, isItemHovered: boolean) => - !isItemHovered ? null : ( - { - e.stopPropagation(); - removeFromFavorites([item]); - }} - size={'small'} - > - - - ), - [removeFromFavorites] - ); - - return ( - <> - - - - - - - - v.id} - getItemLabel={(v) => v.name} - selectedItems={checkedContingencyList} - onSelectionChange={setCheckedContingencyList} - secondaryAction={handleSecondaryAction} - onItemClick={(contingencyList) => - setCheckedContingencyList((oldCheckedElements) => [ - ...toggleElementFromList(contingencyList, oldCheckedElements, (element) => element.id), - ]) - } - /> - - - - - - {makeButton(handleClose, 'close', false)} - {makeButton(handleAddFavorite, 'AddContingencyList', false)} - {makeButton( - () => removeFromFavorites(checkedContingencyList), - 'DeleteContingencyList', - checkedContingencyList.length === 0 - )} - {makeButton(handleStart, 'Execute', simulatedContingencyCount === 0)} - - - - - ); -} diff --git a/src/components/graph/menus/network-modifications/DescriptionRenderer.tsx b/src/components/graph/menus/network-modifications/DescriptionRenderer.tsx index 9e31bcce8e..a523fcd8de 100644 --- a/src/components/graph/menus/network-modifications/DescriptionRenderer.tsx +++ b/src/components/graph/menus/network-modifications/DescriptionRenderer.tsx @@ -17,7 +17,6 @@ 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 = { @@ -44,17 +43,17 @@ const DescriptionRenderer = (props: DescriptionRendererProps) => { const empty = !description; const updateModification = useCallback( - async (uuid: UUID, descriptionRecord: Record) => { + async (descriptionRecord: Record) => { setIsLoading(true); - return setModificationMetadata(studyUuid, currentNode?.id, uuid, { + return setModificationMetadata(studyUuid, currentNode?.id, modificationUuid, { description: descriptionRecord.description, type: data?.type, }).finally(() => { setIsLoading(false); }); }, - [studyUuid, currentNode?.id, data?.type] + [studyUuid, currentNode?.id, modificationUuid, data?.type] ); const handleDescDialogClose = useCallback(() => { @@ -72,7 +71,6 @@ const DescriptionRenderer = (props: DescriptionRendererProps) => { diff --git a/src/components/parameters-tabs.tsx b/src/components/parameters-tabs.tsx index 10c400f305..f71445dfcf 100644 --- a/src/components/parameters-tabs.tsx +++ b/src/components/parameters-tabs.tsx @@ -69,6 +69,7 @@ import { } from 'services/study/short-circuit-analysis'; import { useGetPccMinParameters } from './dialogs/parameters/use-get-pcc-min-parameters'; import { useWorkspacePanelActions } from './workspace/hooks/use-workspace-panel-actions'; +import { fetchContingencyCount } from '../services/study'; enum TAB_VALUES { lfParamsTabValue = 'LOAD_FLOW', @@ -295,6 +296,9 @@ const ParametersTabs: FunctionComponent = () => { + fetchContingencyCount(studyUuid, currentNodeUuid, currentRootNetworkUuid, contingencyLists) + } setHaveDirtyFields={setDirtyFields} isDeveloperMode={isDeveloperMode} /> diff --git a/src/components/run-button-container.jsx b/src/components/run-button-container.jsx index eb23b761db..e333fc2596 100644 --- a/src/components/run-button-container.jsx +++ b/src/components/run-button-container.jsx @@ -27,7 +27,6 @@ import { } from '@gridsuite/commons-ui'; import RunButton from './run-button'; import { DynamicSimulationParametersSelector } from './dialogs/dynamicsimulation/dynamic-simulation-parameters-selector'; -import { ContingencyListSelector } from './dialogs/contingency-list-selector'; import { startSensitivityAnalysis, stopSensitivityAnalysis } from '../services/study/sensitivity-analysis'; import { fetchDynamicSimulationParameters, @@ -101,8 +100,6 @@ export function RunButtonContainer({ studyUuid, currentNode, currentRootNetworkU const stateEstimationStatus = useSelector((state) => state.computingStatus[ComputingType.STATE_ESTIMATION]); const pccMinStatus = useSelector((state) => state.computingStatus[ComputingType.PCC_MIN]); - const [showContingencyListSelector, setShowContingencyListSelector] = useState(false); - const [showDynamicSimulationParametersSelector, setShowDynamicSimulationParametersSelector] = useState(false); // a transient state which is used only for a run with popup dialog @@ -219,16 +216,16 @@ export function RunButtonContainer({ studyUuid, currentNode, currentRootNetworkU [currentNode, snackError] ); - const handleStartSecurityAnalysis = (contingencyListNames) => { + const handleStartSecurityAnalysis = useCallback(() => { startComputationAsync( ComputingType.SECURITY_ANALYSIS, null, - () => startSecurityAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid, contingencyListNames), + () => startSecurityAnalysis(studyUuid, currentNode?.id, currentRootNetworkUuid), () => {}, null, null ); - }; + }, [studyUuid, currentNode?.id, currentRootNetworkUuid, startComputationAsync]); const handleStartDynamicSimulation = (dynamicSimulationConfiguration, debug) => { startComputationAsync( @@ -310,7 +307,7 @@ export function RunButtonContainer({ studyUuid, currentNode, currentRootNetworkU [ComputingType.SECURITY_ANALYSIS]: { messageId: 'SecurityAnalysis', startComputation() { - setShowContingencyListSelector(true); + handleStartSecurityAnalysis(); }, actionOnRunnable() { actionOnRunnables(ComputingType.SECURITY_ANALYSIS, () => @@ -508,6 +505,7 @@ export function RunButtonContainer({ studyUuid, currentNode, currentRootNetworkU checkForbiddenProvider, studyUuid, handleStartLoadFlow, + handleStartSecurityAnalysis, currentNode?.id, currentRootNetworkUuid, startComputationAsync, @@ -600,14 +598,6 @@ export function RunButtonContainer({ studyUuid, currentNode, currentRootNetworkU computationStopped={computationStopped} disabled={isModificationsInProgress || disabled} /> - setShowContingencyListSelector(false)} - onStart={(params) => { - handleStartSecurityAnalysis(params); - setShowContingencyListSelector(false); - }} - /> {!disabled && showDynamicSimulationParametersSelector && ( > & { - [PARAM_FAVORITE_CONTINGENCY_LISTS]: UUID[]; -}; - -export function selectFavoriteContingencyLists(favoriteContingencyLists: UUID[]): FavoriteContingencyListsAction { - return { - type: FAVORITE_CONTINGENCY_LISTS, - [PARAM_FAVORITE_CONTINGENCY_LISTS]: favoriteContingencyLists, - }; -} - export const SET_BASE_VOLTAGE_LIST = 'SET_BASE_VOLTAGE_LIST'; export type SetBaseVoltageListAction = Readonly> & { baseVoltages: BaseVoltage[]; diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index efcbbafd45..927d3f4880 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -66,8 +66,6 @@ import { type DeleteEquipmentsAction, ENABLE_DEVELOPER_MODE, type EnableDeveloperModeAction, - FAVORITE_CONTINGENCY_LISTS, - type FavoriteContingencyListsAction, HIGHLIGHT_MODIFICATION, HighlightModificationAction, INIT_TABLE_DEFINITIONS, @@ -236,13 +234,7 @@ import { saveLocalStorageLanguage, saveLocalStorageTheme, } from './session-storage/local-storage'; -import { - PARAM_COMPUTED_LANGUAGE, - PARAM_FAVORITE_CONTINGENCY_LISTS, - PARAM_LIMIT_REDUCTION, - PARAM_USE_NAME, - PARAMS_LOADED, -} from '../utils/config-params'; +import { PARAM_COMPUTED_LANGUAGE, PARAM_LIMIT_REDUCTION, PARAM_USE_NAME, PARAMS_LOADED } from '../utils/config-params'; import NetworkModificationTreeModel from '../components/graph/network-modification-tree-model'; import { getAllChildren, getNetworkModificationNode } from 'components/graph/util/model-functions'; import { RunningStatus } from 'components/utils/running-status'; @@ -496,7 +488,6 @@ export interface AppConfigState { [PARAM_COMPUTED_LANGUAGE]: GsLangUser; [PARAM_LIMIT_REDUCTION]: number; [PARAM_USE_NAME]: boolean; - [PARAM_FAVORITE_CONTINGENCY_LISTS]: UUID[]; [PARAM_DEVELOPER_MODE]: boolean; [PARAMS_LOADED]: boolean; } @@ -756,7 +747,6 @@ const initialState: AppState = { [PARAM_LANGUAGE]: getLocalStorageLanguage(), [PARAM_USE_NAME]: true, [PARAM_LIMIT_REDUCTION]: 100, - [PARAM_FAVORITE_CONTINGENCY_LISTS]: [], [PARAM_DEVELOPER_MODE]: false, [PARAMS_LOADED]: false, @@ -1252,10 +1242,6 @@ export const reducer = createReducer(initialState, (builder) => { state.isMapEquipmentsInitialized = action.newValue; }); - builder.addCase(FAVORITE_CONTINGENCY_LISTS, (state, action: FavoriteContingencyListsAction) => { - state[PARAM_FAVORITE_CONTINGENCY_LISTS] = action[PARAM_FAVORITE_CONTINGENCY_LISTS]; - }); - builder.addCase(CURRENT_TREE_NODE, (state, action: CurrentTreeNodeAction) => { state.currentTreeNode = action.currentTreeNode; state.reloadMapNeeded = true; diff --git a/src/services/study/index.ts b/src/services/study/index.ts index 06c4be02dc..cf24c79fa4 100644 --- a/src/services/study/index.ts +++ b/src/services/study/index.ts @@ -28,7 +28,7 @@ export const getStudyUrl = (studyUuid: UUID | null) => export const getStudyUrlWithNodeUuidAndRootNetworkUuid = ( studyUuid: string | null | undefined, - nodeUuid: string | undefined, + nodeUuid: string | null | undefined, rootNetworkUuid: string | undefined | null ) => `${PREFIX_STUDY_QUERIES}/v1/studies/${safeEncodeURIComponent(studyUuid)}/root-networks/${safeEncodeURIComponent( @@ -197,16 +197,16 @@ export function searchEquipmentsInfos( } export function fetchContingencyCount( - studyUuid: UUID, - currentNodeUuid: UUID, - currentRootNetworkUuid: UUID, - contingencyListNames: string[] + studyUuid: UUID | null, + currentNodeUuid: UUID | null, + currentRootNetworkUuid: UUID | null, + contingencyListIds: UUID[] | null ): Promise { console.info( - `Fetching contingency count for ${contingencyListNames} on '${studyUuid}' for root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}'...` + `Fetching contingency count for ${contingencyListIds} on '${studyUuid}' for root network '${currentRootNetworkUuid}' and node '${currentNodeUuid}'...` ); - const contingencyListNamesParams = getRequestParamFromList(contingencyListNames, 'contingencyListName'); + const contingencyListNamesParams = getRequestParamFromList(contingencyListIds ?? [], 'contingencyListIds'); const urlSearchParams = new URLSearchParams(contingencyListNamesParams); const url = diff --git a/src/services/study/network-modifications.ts b/src/services/study/network-modifications.ts index e3389e2e4b..88e118af57 100644 --- a/src/services/study/network-modifications.ts +++ b/src/services/study/network-modifications.ts @@ -105,7 +105,7 @@ export function stashModifications(studyUuid: UUID | null, nodeUuid: UUID | unde export function setModificationMetadata( studyUuid: UUID | null, nodeUuid: UUID | undefined, - modificationUuid: UUID, + modificationUuid: UUID | undefined, metadata: Partial ): Promise { const urlSearchParams = new URLSearchParams(); diff --git a/src/services/study/security-analysis.ts b/src/services/study/security-analysis.ts index 18b3f4e00f..0060cd7144 100644 --- a/src/services/study/security-analysis.ts +++ b/src/services/study/security-analysis.ts @@ -6,7 +6,6 @@ */ import { getStudyUrl, getStudyUrlWithNodeUuidAndRootNetworkUuid, PREFIX_STUDY_QUERIES } from './index'; -import { getRequestParamFromList } from '../utils'; import type { UUID } from 'node:crypto'; import { backendFetch, backendFetchFile, backendFetchJson, backendFetchText, GsLangUser } from '@gridsuite/commons-ui'; import { SecurityAnalysisQueryParams } from '../../components/results/securityanalysis/security-analysis.type'; @@ -14,21 +13,17 @@ import { SecurityAnalysisQueryParams } from '../../components/results/securityan export function startSecurityAnalysis( studyUuid: UUID, currentNodeUuid: UUID, - currentRootNetworkUuid: UUID, - contingencyListUuids: UUID[] + currentRootNetworkUuid: UUID ): Promise { console.info( `Running security analysis on ${studyUuid} on root network ${currentRootNetworkUuid} and node ${currentNodeUuid} ...` ); - // Add params to Url - const contingencyListsQueryParams = getRequestParamFromList(contingencyListUuids, 'contingencyListName'); - const urlSearchParams = new URLSearchParams(contingencyListsQueryParams); const url = `${getStudyUrlWithNodeUuidAndRootNetworkUuid( studyUuid, currentNodeUuid, currentRootNetworkUuid - )}/security-analysis/run?${urlSearchParams}`; + )}/security-analysis/run`; console.debug(url); return backendFetch(url, { method: 'post' }); diff --git a/src/utils/config-params.ts b/src/utils/config-params.ts index c7c998e623..482409bb02 100644 --- a/src/utils/config-params.ts +++ b/src/utils/config-params.ts @@ -12,7 +12,6 @@ export const PARAM_USE_NAME = 'useName'; export const PARAM_LIMIT_REDUCTION = 'limitReduction'; export const PARAM_COMPUTED_LANGUAGE = 'computedLanguage'; export const PARAMS_LOADED = 'paramsLoaded'; -export const PARAM_FAVORITE_CONTINGENCY_LISTS = 'favoriteContingencyLists'; // SA Param names export const PARAM_SA_PROVIDER = 'provider';