Skip to content

Commit efc291d

Browse files
authored
Add confirmation dialog to launch computation when parameters are dirty (#3251)
--------- Authored-by: Hugo Marcellin <[email protected]>
1 parent c0ef875 commit efc291d

File tree

9 files changed

+115
-49
lines changed

9 files changed

+115
-49
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"dependencies": {
1212
"@emotion/react": "^11.14.0",
1313
"@emotion/styled": "^11.14.0",
14-
"@gridsuite/commons-ui": "0.123.0",
14+
"@gridsuite/commons-ui": "0.125.0",
1515
"@hello-pangea/dnd": "^18.0.1",
1616
"@hookform/resolvers": "^4.0.0",
1717
"@mui/icons-material": "^5.16.14",

src/components/dialogs/parameters/state-estimation/state-estimation-parameters.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* License, v. 2.0. If a copy of the MPL was not distributed with this
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
7-
import { Dispatch, SetStateAction, SyntheticEvent, useCallback, useEffect, useMemo, useState } from 'react';
7+
import { SyntheticEvent, useCallback, useEffect, useMemo, useState } from 'react';
88
import {
99
CustomFormProvider,
1010
mergeSx,
@@ -40,7 +40,7 @@ export const StateEstimationParameters = ({
4040
setHaveDirtyFields,
4141
}: {
4242
useStateEstimationParameters: UseGetStateEstimationParametersProps;
43-
setHaveDirtyFields: Dispatch<SetStateAction<boolean>>;
43+
setHaveDirtyFields: (isDirty: boolean) => void;
4444
}) => {
4545
const studyUuid = useSelector((state: AppState) => state.studyUuid);
4646
const [stateEstimationParams, setStateEstimationParams] = useStateEstimationParameters;

src/components/parameters-tabs.tsx

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,19 @@ import { useGetNonEvacuatedEnergyParameters } from './dialogs/parameters/non-eva
5353
import { stylesLayout, tabStyles } from './utils/tab-utils';
5454
import { useParameterState } from './dialogs/parameters/use-parameters-state';
5555
import { useGetShortCircuitParameters } from './dialogs/parameters/use-get-short-circuit-parameters';
56-
import { cancelLeaveParametersTab, confirmLeaveParametersTab } from 'redux/actions';
56+
import { cancelLeaveParametersTab, confirmLeaveParametersTab, setDirtyComputationParameters } from 'redux/actions';
5757
import { StudyView, StudyViewType } from './utils/utils';
5858
import {
59+
ComputingType,
60+
fetchSecurityAnalysisProviders,
61+
getSecurityAnalysisDefaultLimitReductions,
5962
LoadFlowParametersInline,
6063
NetworkVisualizationParametersInline,
6164
SecurityAnalysisParametersInline,
6265
SensitivityAnalysisParametersInline,
6366
ShortCircuitParametersInLine,
6467
useParametersBackend,
65-
ComputingType,
6668
VoltageInitParametersInLine,
67-
fetchSecurityAnalysisProviders,
68-
getSecurityAnalysisDefaultLimitReductions,
6969
} from '@gridsuite/commons-ui';
7070
import { useParametersNotification } from './dialogs/parameters/use-parameters-notification';
7171
import { useGetVoltageInitParameters } from './dialogs/parameters/use-get-voltage-init-parameters';
@@ -97,9 +97,9 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
9797

9898
const [tabValue, setTabValue] = useState<string>(TAB_VALUES.networkVisualizationsParams);
9999
const [nextTabValue, setNextTabValue] = useState<string | undefined>(undefined);
100-
const [haveDirtyFields, setHaveDirtyFields] = useState<boolean>(false);
100+
const isDirtyComputationParameters = useSelector((state: AppState) => state.isDirtyComputationParameters);
101101

102-
const [isPopupOpen, setIsPopupOpen] = useState<boolean>(false);
102+
const [isLeavingPopupOpen, setIsLeavingPopupOpen] = useState<boolean>(false);
103103

104104
const [enableDeveloperMode] = useParameterState(PARAM_DEVELOPER_MODE);
105105
const [languageLocal] = useParameterState(PARAM_LANGUAGE);
@@ -121,6 +121,13 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
121121
(state: AppState) => state.computingStatus[ComputingType.SHORT_CIRCUIT_ONE_BUS]
122122
);
123123

124+
const setDirtyFields = useCallback(
125+
(isDirty: boolean) => {
126+
dispatch(setDirtyComputationParameters(isDirty));
127+
},
128+
[dispatch]
129+
);
130+
124131
const shouldDisplayGlassPane = useMemo(() => {
125132
return (
126133
computationStatus === RunningStatus.RUNNING ||
@@ -200,18 +207,18 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
200207

201208
useEffect(() => {
202209
if (attemptedLeaveParametersTabIndex !== null) {
203-
if (haveDirtyFields) {
204-
setIsPopupOpen(true);
210+
if (isDirtyComputationParameters) {
211+
setIsLeavingPopupOpen(true);
205212
} else {
206213
dispatch(confirmLeaveParametersTab());
207214
}
208215
}
209-
}, [attemptedLeaveParametersTabIndex, haveDirtyFields, dispatch]);
216+
}, [attemptedLeaveParametersTabIndex, isDirtyComputationParameters, dispatch]);
210217

211218
const handleChangeTab = (newValue: string) => {
212-
if (haveDirtyFields) {
219+
if (isDirtyComputationParameters) {
213220
setNextTabValue(newValue);
214-
setIsPopupOpen(true);
221+
setIsLeavingPopupOpen(true);
215222
} else {
216223
setTabValue(newValue);
217224
}
@@ -224,12 +231,12 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
224231
} else if (attemptedLeaveParametersTabIndex !== null) {
225232
dispatch(confirmLeaveParametersTab());
226233
}
227-
setHaveDirtyFields(false);
228-
setIsPopupOpen(false);
234+
dispatch(setDirtyComputationParameters(false));
235+
setIsLeavingPopupOpen(false);
229236
}, [nextTabValue, attemptedLeaveParametersTabIndex, dispatch]);
230237

231-
const handlePopupClose = useCallback(() => {
232-
setIsPopupOpen(false);
238+
const handleLeavingPopupClose = useCallback(() => {
239+
setIsLeavingPopupOpen(false);
233240
setNextTabValue(undefined);
234241

235242
if (attemptedLeaveParametersTabIndex !== null) {
@@ -269,7 +276,7 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
269276
studyUuid={studyUuid}
270277
language={languageLocal}
271278
parametersBackend={loadFlowParametersBackend}
272-
setHaveDirtyFields={setHaveDirtyFields}
279+
setHaveDirtyFields={setDirtyFields}
273280
enableDeveloperMode={enableDeveloperMode}
274281
/>
275282
);
@@ -278,7 +285,7 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
278285
<SecurityAnalysisParametersInline
279286
studyUuid={studyUuid}
280287
parametersBackend={securityAnalysisParametersBackend}
281-
setHaveDirtyFields={setHaveDirtyFields}
288+
setHaveDirtyFields={setDirtyFields}
282289
enableDeveloperMode={enableDeveloperMode}
283290
/>
284291
);
@@ -289,7 +296,7 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
289296
currentNodeUuid={currentNodeUuid}
290297
currentRootNetworkUuid={currentRootNetworkUuid}
291298
parametersBackend={sensitivityAnalysisBackend}
292-
setHaveDirtyFields={setHaveDirtyFields}
299+
setHaveDirtyFields={setDirtyFields}
293300
enableDeveloperMode={enableDeveloperMode}
294301
/>
295302
);
@@ -304,34 +311,34 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
304311
return (
305312
<ShortCircuitParametersInLine
306313
studyUuid={studyUuid}
307-
setHaveDirtyFields={setHaveDirtyFields}
314+
setHaveDirtyFields={setDirtyFields}
308315
shortCircuitParameters={shortCircuitParameters}
309316
/>
310317
);
311318
case TAB_VALUES.dynamicSimulationParamsTabValue:
312-
return <DynamicSimulationParameters user={user} setHaveDirtyFields={setHaveDirtyFields} />;
319+
return <DynamicSimulationParameters user={user} setHaveDirtyFields={setDirtyFields} />;
313320
case TAB_VALUES.dynamicSecurityAnalysisParamsTabValue:
314-
return <DynamicSecurityAnalysisParameters user={user} setHaveDirtyFields={setHaveDirtyFields} />;
321+
return <DynamicSecurityAnalysisParameters user={user} setHaveDirtyFields={setDirtyFields} />;
315322
case TAB_VALUES.voltageInitParamsTabValue:
316323
return (
317324
<VoltageInitParametersInLine
318325
studyUuid={studyUuid}
319-
setHaveDirtyFields={setHaveDirtyFields}
326+
setHaveDirtyFields={setDirtyFields}
320327
voltageInitParameters={voltageInitParameters}
321328
/>
322329
);
323330
case TAB_VALUES.stateEstimationTabValue:
324331
return (
325332
<StateEstimationParameters
326-
setHaveDirtyFields={setHaveDirtyFields}
333+
setHaveDirtyFields={setDirtyFields}
327334
useStateEstimationParameters={useStateEstimationParameters}
328335
/>
329336
);
330337
case TAB_VALUES.networkVisualizationsParams:
331338
return (
332339
<NetworkVisualizationParametersInline
333340
studyUuid={studyUuid}
334-
setHaveDirtyFields={setHaveDirtyFields}
341+
setHaveDirtyFields={setDirtyFields}
335342
user={user}
336343
parameters={networkVisualizationsParameters}
337344
/>
@@ -343,6 +350,7 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
343350
studyUuid,
344351
languageLocal,
345352
loadFlowParametersBackend,
353+
setDirtyFields,
346354
enableDeveloperMode,
347355
securityAnalysisParametersBackend,
348356
currentNodeUuid,
@@ -352,9 +360,9 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
352360
useNonEvacuatedEnergyParameters,
353361
shortCircuitParameters,
354362
user,
363+
voltageInitParameters,
355364
useStateEstimationParameters,
356365
networkVisualizationsParameters,
357-
voltageInitParameters,
358366
]);
359367

360368
return (
@@ -448,8 +456,8 @@ const ParametersTabs: FunctionComponent<ParametersTabsProps> = ({ view }) => {
448456
</Grid>
449457
<SelectOptionsDialog
450458
title={''}
451-
open={isPopupOpen}
452-
onClose={handlePopupClose}
459+
open={isLeavingPopupOpen}
460+
onClose={handleLeavingPopupClose}
453461
onClick={handlePopupChangeTab}
454462
child={
455463
<DialogContentText>

src/components/run-button.jsx

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@
77

88
import { useCallback, useEffect, useMemo, useState } from 'react';
99
import PropTypes from 'prop-types';
10-
import { useIntl } from 'react-intl';
10+
import { FormattedMessage, useIntl } from 'react-intl';
1111

1212
import SplitButton from './utils/split-button';
1313
import RunningStatus from './utils/running-status';
1414
import { ComputingType } from '@gridsuite/commons-ui';
15+
import { useSelector } from 'react-redux';
16+
import { SelectOptionsDialog } from '../utils/dialogs';
17+
import { DialogContentText } from '@mui/material';
1518

1619
const RunButton = ({ runnables, activeRunnables, getStatus, computationStopped, disabled }) => {
1720
const intl = useIntl();
21+
const isDirtyComputationParameters = useSelector((state) => state.isDirtyComputationParameters);
22+
const [isLaunchingPopupOpen, setIsLaunchingPopupOpen] = useState(false);
1823

1924
const runnablesText = useMemo(
2025
() => Object.fromEntries(activeRunnables.map((k) => [k, intl.formatMessage({ id: runnables[k].messageId })])),
@@ -76,19 +81,50 @@ const RunButton = ({ runnables, activeRunnables, getStatus, computationStopped,
7681
return getRunningStatus() === RunningStatus.RUNNING;
7782
}
7883

84+
const attemptStartComputation = useCallback(() => {
85+
if (isDirtyComputationParameters) {
86+
setIsLaunchingPopupOpen(true);
87+
} else {
88+
runnables[selectedRunnable].startComputation();
89+
}
90+
}, [isDirtyComputationParameters, runnables, selectedRunnable]);
91+
92+
const handleLaunchingPopupClose = useCallback(() => {
93+
setIsLaunchingPopupOpen(false);
94+
}, []);
95+
96+
const handleLaunchingPopup = useCallback(() => {
97+
setIsLaunchingPopupOpen(false);
98+
runnables[selectedRunnable].startComputation();
99+
}, [runnables, selectedRunnable]);
100+
79101
return (
80-
<SplitButton
81-
options={getOptions()}
82-
selectedIndex={activeRunnables.indexOf(selectedRunnable)}
83-
onSelectionChange={(index) => setSelectedRunnable(activeRunnables[index])}
84-
onClick={runnables[selectedRunnable].startComputation}
85-
runningStatus={getRunningStatus()}
86-
buttonDisabled={disabled || isButtonDisable()}
87-
selectionDisabled={disabled}
88-
text={runnablesText[selectedRunnable] || ''}
89-
actionOnRunnable={runnables[selectedRunnable].actionOnRunnable}
90-
computationStopped={computationStopped}
91-
/>
102+
<>
103+
<SplitButton
104+
options={getOptions()}
105+
selectedIndex={activeRunnables.indexOf(selectedRunnable)}
106+
onSelectionChange={(index) => setSelectedRunnable(activeRunnables[index])}
107+
onClick={attemptStartComputation}
108+
runningStatus={getRunningStatus()}
109+
buttonDisabled={disabled || isButtonDisable()}
110+
selectionDisabled={disabled}
111+
text={runnablesText[selectedRunnable] || ''}
112+
actionOnRunnable={runnables[selectedRunnable].actionOnRunnable}
113+
computationStopped={computationStopped}
114+
/>
115+
<SelectOptionsDialog
116+
title={''}
117+
open={isLaunchingPopupOpen}
118+
onClose={handleLaunchingPopupClose}
119+
onClick={handleLaunchingPopup}
120+
child={
121+
<DialogContentText>
122+
<FormattedMessage id="launchComputationConfirmQuestion" />
123+
</DialogContentText>
124+
}
125+
validateKey={'dialog.button.launch'}
126+
/>
127+
</>
92128
);
93129
};
94130

src/redux/actions.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,18 @@ export function cancelLeaveParametersTab(): CancelLeaveParametersTabAction {
206206
};
207207
}
208208

209+
export const SET_DIRTY_COMPUTATION_PARAMETERS = 'SET_DIRTY_COMPUTATION_PARAMETERS';
210+
export type SetDirtyComputationParametersAction = Readonly<Action<typeof SET_DIRTY_COMPUTATION_PARAMETERS>> & {
211+
isDirty: boolean;
212+
};
213+
214+
export function setDirtyComputationParameters(isDirty: boolean): SetDirtyComputationParametersAction {
215+
return {
216+
type: SET_DIRTY_COMPUTATION_PARAMETERS,
217+
isDirty,
218+
};
219+
}
220+
209221
export const LOAD_EQUIPMENTS = 'LOAD_EQUIPMENTS';
210222
export type LoadEquipmentsAction = Readonly<Action<typeof LOAD_EQUIPMENTS>> & {
211223
equipmentType: SpreadsheetEquipmentType;
@@ -705,6 +717,7 @@ export function setNodeSelectionForCopy(
705717

706718
export const SET_MODIFICATIONS_DRAWER_OPEN = 'SET_MODIFICATIONS_DRAWER_OPEN';
707719
export type SetModificationsDrawerOpenAction = Readonly<Action<typeof SET_MODIFICATIONS_DRAWER_OPEN>>;
720+
708721
export function setModificationsDrawerOpen(): SetModificationsDrawerOpenAction {
709722
return {
710723
type: SET_MODIFICATIONS_DRAWER_OPEN,

src/redux/reducer.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ import {
165165
SET_COMPUTING_STATUS,
166166
SET_COMPUTING_STATUS_INFOS,
167167
SET_DIAGRAM_GRID_LAYOUT,
168+
SET_DIRTY_COMPUTATION_PARAMETERS,
168169
SET_LAST_COMPLETED_COMPUTATION,
169170
SET_MODIFICATIONS_DRAWER_OPEN,
170171
SET_MODIFICATIONS_IN_PROGRESS,
@@ -183,6 +184,7 @@ import {
183184
type SetComputingStatusAction,
184185
type SetComputingStatusParametersAction,
185186
type SetDiagramGridLayoutAction,
187+
type SetDirtyComputationParametersAction,
186188
type SetLastCompletedComputationAction,
187189
type SetModificationsDrawerOpenAction,
188190
type SetModificationsInProgressAction,
@@ -553,10 +555,9 @@ export interface AppState extends CommonStoreState, AppConfigState {
553555
signInCallbackError: Error | null;
554556
authenticationRouterError: AuthenticationRouterErrorState | null;
555557
showAuthenticationRouterLogin: boolean;
556-
557558
appTabIndex: number;
558559
attemptedLeaveParametersTabIndex: number | null;
559-
560+
isDirtyComputationParameters: boolean;
560561
studyUpdated: StudyUpdated;
561562
studyUuid: UUID | null;
562563
currentTreeNode: CurrentTreeNode | null;
@@ -719,6 +720,7 @@ const initialState: AppState = {
719720
syncEnabled: false,
720721
appTabIndex: 0,
721722
attemptedLeaveParametersTabIndex: null,
723+
isDirtyComputationParameters: false,
722724
studyUuid: null,
723725
currentTreeNode: null,
724726
currentRootNetworkUuid: null,
@@ -958,6 +960,9 @@ export const reducer = createReducer(initialState, (builder) => {
958960
builder.addCase(CANCEL_LEAVE_PARAMETERS_TAB, (state) => {
959961
state.attemptedLeaveParametersTabIndex = null;
960962
});
963+
builder.addCase(SET_DIRTY_COMPUTATION_PARAMETERS, (state, action: SetDirtyComputationParametersAction) => {
964+
state.isDirtyComputationParameters = action.isDirty;
965+
});
961966
builder.addCase(OPEN_STUDY, (state, action: OpenStudyAction) => {
962967
state.studyUuid = action.studyRef[0];
963968
// Load toggleOptions for this study

0 commit comments

Comments
 (0)