diff --git a/package-lock.json b/package-lock.json
index 772a07a430..e5da475fd6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
- "@gridsuite/commons-ui": "0.240.0",
+ "@gridsuite/commons-ui": "0.241.0",
"@hello-pangea/dnd": "^18.0.1",
"@hookform/resolvers": "^4.1.3",
"@mui/icons-material": "^6.5.0",
@@ -3318,9 +3318,9 @@
}
},
"node_modules/@gridsuite/commons-ui": {
- "version": "0.240.0",
- "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.240.0.tgz",
- "integrity": "sha512-sGP5jDrxpPje7aCYs4SwLqVxPldqpcLIPTeQL9zArWHocHYdTQGU6Fa6JGQV3YWZa4D6tVNa3iYtJKuUpqKkFg==",
+ "version": "0.241.0",
+ "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.241.0.tgz",
+ "integrity": "sha512-bCVh1o+vkhD0tDJt2skpZarQD7BI1K1iW4DHi6lcbVxUMUf3SSz1FiM//DFBtGkX5MOWTa19MWycwsEIQeeP6g==",
"license": "MPL-2.0",
"dependencies": {
"@ag-grid-community/locale": "^35.3.1",
diff --git a/package.json b/package.json
index 851c2b5cad..25c326f9f3 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
- "@gridsuite/commons-ui": "0.240.0",
+ "@gridsuite/commons-ui": "0.241.0",
"@hello-pangea/dnd": "^18.0.1",
"@hookform/resolvers": "^4.1.3",
"@mui/icons-material": "^6.5.0",
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-general-parameters.tsx b/src/components/dialogs/parameters/state-estimation/state-estimation-general-parameters.tsx
index e8d09f979a..8b3d03acff 100644
--- a/src/components/dialogs/parameters/state-estimation/state-estimation-general-parameters.tsx
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-general-parameters.tsx
@@ -17,49 +17,53 @@ import { parametersStyles } from '../util/styles';
export const StateEstimationGeneralParameters = () => {
return (
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
);
};
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-parameters-form.tsx b/src/components/dialogs/parameters/state-estimation/state-estimation-parameters-form.tsx
new file mode 100644
index 0000000000..496a989210
--- /dev/null
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-parameters-form.tsx
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+import { SyntheticEvent } from 'react';
+import { TabPanel } from '@gridsuite/commons-ui';
+import { Grid2 as Grid, Stack, Tab, Tabs } from '@mui/material';
+import { FormattedMessage } from 'react-intl';
+import { TabValue } from './state-estimation-parameters-utils';
+import { StateEstimationGeneralParameters } from './state-estimation-general-parameters';
+import { StateEstimationWeightsParameters } from './state-estimation-weights-parameters';
+import { StateEstimationQualityParameters } from './state-estimation-quality-parameters';
+import { StateEstimationLoadboundsParameters } from './state-estimation-loadbounds-parameters';
+import { getTabStyle } from '../../../utils/tab-utils';
+
+interface StateEstimationParametersFormProps {
+ tabValue: TabValue;
+ handleTabChange: (event: SyntheticEvent, newValue: TabValue) => void;
+ tabIndexesWithError: TabValue[];
+}
+
+export const StateEstimationParametersForm = ({
+ tabValue,
+ handleTabChange,
+ tabIndexesWithError,
+}: StateEstimationParametersFormProps) => {
+ return (
+
+
+ }
+ value={TabValue.GENERAL}
+ sx={getTabStyle(tabIndexesWithError, TabValue.GENERAL)}
+ />
+ }
+ value={TabValue.WEIGHTS}
+ sx={getTabStyle(tabIndexesWithError, TabValue.WEIGHTS)}
+ />
+ }
+ value={TabValue.QUALITY}
+ sx={getTabStyle(tabIndexesWithError, TabValue.QUALITY)}
+ />
+ }
+ value={TabValue.LOADBOUNDS}
+ sx={getTabStyle(tabIndexesWithError, TabValue.LOADBOUNDS)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/dialogs/parameters/state-estimation/state-estimation-parameters.tsx b/src/components/dialogs/parameters/state-estimation/state-estimation-parameters.tsx
index 9df32336e9..3645870bd4 100644
--- a/src/components/dialogs/parameters/state-estimation/state-estimation-parameters.tsx
+++ b/src/components/dialogs/parameters/state-estimation/state-estimation-parameters.tsx
@@ -5,18 +5,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { SyntheticEvent, useCallback, useEffect, useMemo, useState } from 'react';
+
import {
CustomFormProvider,
- mergeSx,
- PopupConfirmationDialog,
snackWithFallback,
- SubmitButton,
- TabPanel,
useSnackMessage,
+ ParameterLayout,
+ ElementType,
} from '@gridsuite/commons-ui';
-import { Button, DialogActions, Grid2 as Grid, Stack, Tab, Tabs } from '@mui/material';
-import { getTabIndicatorStyle, getTabStyle } from '../../../utils/tab-utils';
-import { FormattedMessage } from 'react-intl';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { useSelector } from 'react-redux';
@@ -24,17 +20,13 @@ import { AppState } from '../../../../redux/reducer.type';
import {
fromStateEstimationParametersFormToParamValues,
fromStateEstimationParametersParamToFormValues,
- StateEstimationParametersForm,
+ StateEstimationParametersForm as StateEstimationFormType,
stateEstimationParametersFormSchema,
TabValue,
} from './state-estimation-parameters-utils';
-import { StateEstimationGeneralParameters } from './state-estimation-general-parameters';
-import { StateEstimationWeightsParameters } from './state-estimation-weights-parameters';
-import { StateEstimationQualityParameters } from './state-estimation-quality-parameters';
-import { StateEstimationLoadboundsParameters } from './state-estimation-loadbounds-parameters';
+import { StateEstimationParametersForm } from './state-estimation-parameters-form';
import { updateStateEstimationParameters } from '../../../../services/study/state-estimation';
import { UseGetStateEstimationParametersProps } from './use-get-state-estimation-parameters';
-import { parametersStyles } from '../util/styles';
export const StateEstimationParameters = ({
useStateEstimationParameters,
@@ -45,7 +37,6 @@ export const StateEstimationParameters = ({
}) => {
const studyUuid = useSelector((state: AppState) => state.studyUuid);
const [stateEstimationParams, setStateEstimationParams] = useStateEstimationParameters;
- const [openResetConfirmation, setOpenResetConfirmation] = useState(false);
const initialFormValues = useMemo(
() =>
@@ -55,7 +46,7 @@ export const StateEstimationParameters = ({
[stateEstimationParams]
);
- const formMethods = useForm({
+ const formMethods = useForm({
defaultValues: initialFormValues,
resolver: yupResolver(stateEstimationParametersFormSchema),
});
@@ -98,19 +89,10 @@ export const StateEstimationParameters = ({
const clear = useCallback(() => {
resetStateEstimationParameters();
onValidationError();
- setOpenResetConfirmation(false);
}, [resetStateEstimationParameters, onValidationError]);
- const handleResetClick = useCallback(() => {
- setOpenResetConfirmation(true);
- }, []);
-
- const handleCancelReset = useCallback(() => {
- setOpenResetConfirmation(false);
- }, []);
-
const onSubmit = useCallback(
- (newParams: StateEstimationParametersForm) => {
+ (newParams: StateEstimationFormType) => {
updateStateEstimationParameters(studyUuid, fromStateEstimationParametersFormToParamValues(newParams))
.then(() => {
setStateEstimationParams(fromStateEstimationParametersFormToParamValues(newParams));
@@ -135,92 +117,19 @@ export const StateEstimationParameters = ({
return (
-
-
-
-
- }
- value={TabValue.GENERAL}
- sx={getTabStyle(tabIndexesWithError, TabValue.GENERAL)}
- />
- }
- value={TabValue.WEIGHTS}
- sx={getTabStyle(tabIndexesWithError, TabValue.WEIGHTS)}
- />
- }
- value={TabValue.QUALITY}
- sx={getTabStyle(tabIndexesWithError, TabValue.QUALITY)}
- />
- }
- value={TabValue.LOADBOUNDS}
- sx={getTabStyle(tabIndexesWithError, TabValue.LOADBOUNDS)}
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Reset Confirmation Dialog */}
- {openResetConfirmation && (
-
- )}
+
);
};
diff --git a/src/components/dialogs/parameters/util/styles.ts b/src/components/dialogs/parameters/util/styles.ts
index 70feffddbc..3bb201e2fb 100644
--- a/src/components/dialogs/parameters/util/styles.ts
+++ b/src/components/dialogs/parameters/util/styles.ts
@@ -14,7 +14,6 @@ export const parametersStyles = {
scrollableGrid: (theme) => ({
overflowY: 'auto',
overflowX: 'hidden',
- maxHeight: '85%', // TODO This needs to be refactored
paddingRight: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(1),
diff --git a/src/components/parameters-tabs.tsx b/src/components/parameters-tabs.tsx
index de8d99da8b..ca1084b7da 100644
--- a/src/components/parameters-tabs.tsx
+++ b/src/components/parameters-tabs.tsx
@@ -5,10 +5,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react';
+import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { useDispatch, useSelector } from 'react-redux';
-import { Box, DialogContentText, Divider, Grid2 as Grid, Stack, Tab, Tabs, Typography } from '@mui/material';
+import {
+ Box,
+ DialogContentText,
+ Divider,
+ Grid2 as Grid,
+ MenuItem,
+ Select,
+ Stack,
+ Tab,
+ Tabs,
+ Typography,
+} from '@mui/material';
import { useOptionalServiceStatus } from 'hooks/use-optional-service-status';
import { OptionalServicesNames, OptionalServicesStatus } from './utils/optional-services';
import { AppState } from 'redux/reducer.type';
@@ -40,15 +51,16 @@ import {
fetchSecurityAnalysisProviders,
getSecurityAnalysisDefaultLimitReductions,
getSecurityAnalysisParameters,
- setSecurityAnalysisParameters,
getSensitivityAnalysisParameters,
LoadFlowParametersInline,
NetworkVisualizationParametersInline,
PARAM_DEVELOPER_MODE,
PARAM_LANGUAGE,
+ ParameterLayoutProvider,
PccMinParametersInLine,
SecurityAnalysisParametersInline,
SensitivityAnalysisParametersInline,
+ setSecurityAnalysisParameters,
ShortCircuitParametersInLine,
useParametersBackend,
VoltageInitParametersInLine,
@@ -480,104 +492,166 @@ const ParametersTabs: FunctionComponent = () => {
networkVisualizationsParameters,
]);
+ const { ref, isXsScreen } = useContainerXs(600);
+
+ const tabOptions = useMemo(() => {
+ const options: { value: string; labelId: string; disabled?: boolean }[] = [
+ {
+ value: TAB_VALUES.lfParamsTabValue,
+ labelId: 'LoadFlow',
+ disabled: computationStatus === RunningStatus.RUNNING && tabValue === TAB_VALUES.lfParamsTabValue,
+ },
+ {
+ value: TAB_VALUES.securityAnalysisParamsTabValue,
+ labelId: 'SecurityAnalysis',
+ disabled: securityAnalysisAvailability !== OptionalServicesStatus.Up,
+ },
+ {
+ value: TAB_VALUES.sensitivityAnalysisParamsTabValue,
+ labelId: 'SensitivityAnalysis',
+ disabled: sensitivityAnalysisAvailability !== OptionalServicesStatus.Up,
+ },
+ {
+ value: TAB_VALUES.shortCircuitParamsTabValue,
+ labelId: 'ShortCircuit',
+ disabled: shortCircuitAvailability !== OptionalServicesStatus.Up,
+ },
+ {
+ value: TAB_VALUES.pccMinTabValue,
+ labelId: 'PccMin',
+ disabled: pccMinAvailability !== OptionalServicesStatus.Up,
+ },
+ ...(isDeveloperMode
+ ? [
+ {
+ value: TAB_VALUES.dynamicSimulationParamsTabValue,
+ labelId: 'DynamicSimulation',
+ disabled: dynamicSimulationAvailability !== OptionalServicesStatus.Up,
+ },
+ {
+ value: TAB_VALUES.dynamicSecurityAnalysisParamsTabValue,
+ labelId: 'DynamicSecurityAnalysis',
+ disabled: dynamicSecurityAnalysisAvailability !== OptionalServicesStatus.Up,
+ },
+ {
+ value: TAB_VALUES.dynamicMarginCalculationParamsTabValue,
+ labelId: 'DynamicMarginCalculation',
+ disabled: dynamicMarginCalculationAvailability !== OptionalServicesStatus.Up,
+ },
+ ]
+ : []),
+ {
+ value: TAB_VALUES.voltageInitParamsTabValue,
+ labelId: 'VoltageInit',
+ disabled: voltageInitAvailability !== OptionalServicesStatus.Up,
+ },
+ ...(isDeveloperMode
+ ? [
+ {
+ value: TAB_VALUES.stateEstimationTabValue,
+ labelId: 'StateEstimation',
+ disabled: stateEstimationAvailability === OptionalServicesStatus.Up,
+ },
+ ]
+ : []),
+ { value: 'divider', labelId: '' }, // visual separator — rendered as Divider in Tabs, hidden in Select
+ {
+ value: TAB_VALUES.networkVisualizationsParams,
+ labelId: 'NetworkVisualizations',
+ },
+ ];
+ return options;
+ }, [
+ computationStatus,
+ tabValue,
+ securityAnalysisAvailability,
+ sensitivityAnalysisAvailability,
+ shortCircuitAvailability,
+ pccMinAvailability,
+ voltageInitAvailability,
+ stateEstimationAvailability,
+ dynamicSimulationAvailability,
+ dynamicSecurityAnalysisAvailability,
+ dynamicMarginCalculationAvailability,
+ isDeveloperMode,
+ ]);
+
return (
<>
-
-
-
-
-
-
-
-
-
-
+ {isXsScreen ? (
+
+
+
-
-
-
-
+ {tabOptions
+ .filter((opt) => opt.value !== 'divider')
+ .map((opt) => (
+
+ ))}
+
+
+
+ ) : (
+
+
+
+
+
+
+
+
+ handleChangeTab(newValue)}
+ aria-label="parameters"
+ orientation={isXsScreen ? 'horizontal' : 'vertical'}
+ sx={tabStyles.listDisplay}
+ >
+ {tabOptions.map((opt) =>
+ opt.value === 'divider' ? (
+ }
+ disabled
+ />
+ ) : (
+ }
+ value={opt.value}
+ disabled={opt.disabled}
+ />
+ )
+ )}
+
+
+
+
+ )}
+
- {displayTab()}
+
+ {displayTab()}
+
@@ -597,4 +671,23 @@ const ParametersTabs: FunctionComponent = () => {
);
};
+const useContainerXs = (threshold = 600) => {
+ const ref = useRef(null);
+ const [isXsScreen, setIsXsScreen] = useState(false);
+
+ useEffect(() => {
+ const el = ref.current;
+ if (!el) return;
+
+ const observer = new ResizeObserver(([entry]) => {
+ setIsXsScreen(entry.contentRect.width < threshold);
+ });
+
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, [threshold]);
+
+ return { ref, isXsScreen };
+};
+
export default ParametersTabs;
diff --git a/src/components/utils/tab-utils.ts b/src/components/utils/tab-utils.ts
index beaf501603..f1fe40958e 100644
--- a/src/components/utils/tab-utils.ts
+++ b/src/components/utils/tab-utils.ts
@@ -45,6 +45,9 @@ export const stylesLayout = {
} as const satisfies MuiStyles;
export const tabStyles = {
+ menuSelect: (theme) => ({
+ backgroundColor: theme.palette.mode === 'light' ? 'white' : 'none',
+ }),
listTitleDisplay: (theme) => ({
paddingTop: 1,
paddingBottom: 1,
@@ -73,6 +76,7 @@ export const tabStyles = {
'.MuiTabScrollButton-root:nth-of-type(1)': {
height: '30px', //40px by default
},
+ height: '100%',
}),
parametersBox: (theme) => ({
backgroundColor:
@@ -86,9 +90,11 @@ export const tabStyles = {
contentBox: (theme) => ({
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
- paddingLeft: theme.spacing(8),
- paddingRight: theme.spacing(8),
+ paddingLeft: theme.spacing(2),
+ paddingRight: theme.spacing(2),
height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
}),
dividerTab: (theme) => ({
padding: 0,