From 2dabc6b20775c2630c11a433f928b8efa61cfd2d Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Wed, 28 Jan 2026 14:26:42 +0100 Subject: [PATCH 01/14] Dynamic Margin Calculation - Functional implementation (Parameters) Signed-off-by: Thang PHAM --- .../parameter-field.tsx} | 17 +- .../dynamic-margin-calculation/constants.ts | 18 ++ .../dynamic-margin-calculation-dialog.tsx | 6 + .../dynamic-margin-calculation-form.tsx | 81 +++++++ .../dynamic-margin-calculation-inline.tsx | 162 +++++++++++++ .../dynamic-margin-calculation.type.ts | 10 + .../dynamic-margin-calculation/index.ts | 8 + .../loads-variations-parameters.tsx | 97 ++++++++ .../time-delay-parameters.tsx | 41 ++++ ...amic-margin-calculation-parameters-form.ts | 214 ++++++++++++++++++ src/components/parameters/index.ts | 1 + .../loadflow/load-flow-general-parameters.tsx | 10 +- src/services/directory.ts | 18 ++ src/services/dynamic-margin-calculation.ts | 122 ++++++++++ .../dynamic-margin-calculation.type.ts | 48 ++++ src/services/index.ts | 2 + src/translations/en/parameters.ts | 18 ++ src/translations/fr/parameters.ts | 17 ++ src/utils/types/elementType.ts | 1 + 19 files changed, 873 insertions(+), 18 deletions(-) rename src/components/parameters/{loadflow/load-flow-parameter-field.tsx => common/parameter-field.tsx} (90%) create mode 100644 src/components/parameters/dynamic-margin-calculation/constants.ts create mode 100644 src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-dialog.tsx create mode 100644 src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx create mode 100644 src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx create mode 100644 src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation.type.ts create mode 100644 src/components/parameters/dynamic-margin-calculation/index.ts create mode 100644 src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx create mode 100644 src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx create mode 100644 src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts create mode 100644 src/services/dynamic-margin-calculation.ts create mode 100644 src/services/dynamic-margin-calculation.type.ts diff --git a/src/components/parameters/loadflow/load-flow-parameter-field.tsx b/src/components/parameters/common/parameter-field.tsx similarity index 90% rename from src/components/parameters/loadflow/load-flow-parameter-field.tsx rename to src/components/parameters/common/parameter-field.tsx index e1285ef02..5eb2ef492 100644 --- a/src/components/parameters/loadflow/load-flow-parameter-field.tsx +++ b/src/components/parameters/common/parameter-field.tsx @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Grid, Tooltip, Chip, Typography } from '@mui/material'; +import { Chip, Grid, Tooltip, Typography } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import { parametersStyles } from '../parameters-style'; import { ParameterType } from '../../../utils/types/parameters.type'; @@ -19,9 +19,9 @@ import { SwitchInput, TextInput, } from '../../inputs'; -import { LineSeparator } from '../common'; +import { LineSeparator } from './index'; -interface LoadFlowParameterFieldProps { +interface ParameterFieldProps { id: string; name: string; type: string; @@ -30,14 +30,7 @@ interface LoadFlowParameterFieldProps { possibleValues?: { id: string; label: string }[] | string[]; } -function LoadFlowParameterField({ - id, - name, - type, - label, - description, - possibleValues, -}: Readonly) { +function ParameterField({ id, name, type, label, description, possibleValues }: Readonly) { const renderField = () => { switch (type) { case ParameterType.STRING: @@ -94,4 +87,4 @@ function LoadFlowParameterField({ ); } -export default LoadFlowParameterField; +export default ParameterField; diff --git a/src/components/parameters/dynamic-margin-calculation/constants.ts b/src/components/parameters/dynamic-margin-calculation/constants.ts new file mode 100644 index 000000000..2ef772ac5 --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/constants.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2026, 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/. + */ + +// tab TAB_TIME_DELAY +export const START_TIME = 'startTime'; +export const STOP_TIME = 'stopTime'; +// tab TAB_LOADS_VARIATIONS +export const MARGIN_CALCULATION_START_TIME = 'marginCalculationStartTime'; +export const LOAD_INCREASE_START_TIME = 'loadIncreaseStartTime'; +export const LOAD_INCREASE_STOP_TIME = 'loadIncreaseStopTime'; +export const CALCULATION_TYPE = 'calculationType'; +export const ACCURACY = 'accuracy'; +export const LOAD_MODELS_RULE = 'loadModelsRule'; +export const LOADS_VARIATIONS = 'loadsVariations'; diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-dialog.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-dialog.tsx new file mode 100644 index 000000000..06b7e633f --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-dialog.tsx @@ -0,0 +1,6 @@ +/** + * Copyright (c) 2026, 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/. + */ diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx new file mode 100644 index 000000000..4c6469845 --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2026, 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 { ReactNode } from 'react'; +import { Grid, LinearProgress, Tab, Tabs } from '@mui/material'; +import { FormattedMessage } from 'react-intl'; +import { UseDynamicMarginCalculationParametersFormReturn } from './use-dynamic-margin-calculation-parameters-form'; +import { mergeSx } from '../../../utils'; +import { CustomFormProvider } from '../../inputs'; +import { ProviderParam } from '../common'; +import { getTabStyle, parametersStyles } from '../parameters-style'; +import { TabPanel } from '../common/parameters'; +import TimeDelayParameters from './time-delay-parameters'; +import LoadsVariationsParameters from './loads-variations-parameters'; + +import { TabValues } from './dynamic-margin-calculation.type'; + +type DynamicMarginCalculationFormProps = { + dynamicMarginCalculationMethods: UseDynamicMarginCalculationParametersFormReturn; + renderTitleFields?: () => ReactNode; + renderActions?: () => ReactNode; +}; + +export function DynamicMarginCalculationForm({ + dynamicMarginCalculationMethods, + renderTitleFields, + renderActions, +}: Readonly) { + const { formMethods, formSchema, paramsLoaded, formattedProviders, selectedTab, onTabChange, tabsWithError } = + dynamicMarginCalculationMethods; + return ( + + {renderTitleFields?.()} + {paramsLoaded ? ( + + + + + + } + value={TabValues.TAB_TIME_DELAY} + sx={getTabStyle(tabsWithError, TabValues.TAB_TIME_DELAY)} + /> + } + value={TabValues.TAB_LOADS_VARIATIONS} + sx={getTabStyle(tabsWithError, TabValues.TAB_LOADS_VARIATIONS)} + /> + + + + + + + + + + + ) : ( + + )} + {renderActions?.()} + + ); +} diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx new file mode 100644 index 000000000..f30ff8769 --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2026, 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 type { UUID } from 'node:crypto'; +import { Grid } from '@mui/material'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { useCallback, useEffect, useState } from 'react'; +import { UseParametersBackendReturnProps } from '../../../utils/types/parameters.type'; +import { ComputingType } from '../common/computing-type'; +import { ElementType, mergeSx, snackWithFallback } from '../../../utils'; +import { DynamicMarginCalculationForm } from './dynamic-margin-calculation-form'; +import { + toFormValues, + toParamsInfos, + useDynamicMarginCalculationParametersForm, +} from './use-dynamic-margin-calculation-parameters-form'; +import { LabelledButton } from '../common/parameters'; +import { SubmitButton } from '../../inputs/reactHookForm/utils/SubmitButton'; +import { PopupConfirmationDialog } from '../../dialogs/popupConfirmationDialog/PopupConfirmationDialog'; +import { parametersStyles } from '../parameters-style'; +import { CreateParameterDialog } from '../common'; +import { DirectoryItemSelector } from '../../directoryItemSelector'; +import { TreeViewFinderNodeProps } from '../../treeViewFinder'; +import { fetchDynamicMarginCalculationParameters } from '../../../services/dynamic-margin-calculation'; +import { useSnackMessage } from '../../../hooks'; + +type DynamicMarginCalculationInlineProps = { + studyUuid: UUID | null; + parametersBackend: UseParametersBackendReturnProps; + setHaveDirtyFields: (isDirty: boolean) => void; + isDeveloperMode: boolean; +}; +export function DynamicMarginCalculationInline({ + studyUuid, + parametersBackend, + setHaveDirtyFields, + isDeveloperMode, +}: Readonly) { + const [providers, , , , , params, , updateParams, resetParams, ,] = parametersBackend; + const dynamicMarginCalculationMethods = useDynamicMarginCalculationParametersForm({ + providers, + params, + name: null, + description: null, + }); + const intl = useIntl(); + const { snackError } = useSnackMessage(); + + const [openCreateParameterDialog, setOpenCreateParameterDialog] = useState(false); + const [openSelectParameterDialog, setOpenSelectParameterDialog] = useState(false); + const [openResetConfirmation, setOpenResetConfirmation] = useState(false); + + const { formMethods, onError } = dynamicMarginCalculationMethods; + const { reset, handleSubmit, getValues, formState } = formMethods; + + const handleResetClick = useCallback(() => { + setOpenResetConfirmation(true); + }, []); + const handleCancelReset = useCallback(() => { + setOpenResetConfirmation(false); + }, []); + + const handleReset = useCallback(() => { + resetParams(); + setOpenResetConfirmation(false); + }, [resetParams]); + + const onSubmit = useCallback( + (formData: Record) => { + // update params after convert form representation to dto representation + updateParams(toParamsInfos(formData)); + }, + [updateParams] + ); + + const handleLoadParameter = useCallback( + (newParams: TreeViewFinderNodeProps[]) => { + if (newParams?.length) { + setOpenSelectParameterDialog(false); + const parametersUuid = newParams[0].id; + fetchDynamicMarginCalculationParameters(parametersUuid) + .then((_params) => { + reset(toFormValues(_params), { + keepDefaultValues: true, + }); + }) + .catch((error: Error) => { + snackWithFallback(snackError, error, { headerId: 'paramsRetrievingError' }); + }); + } + setOpenSelectParameterDialog(false); + }, + [reset, snackError] + ); + + useEffect(() => { + setHaveDirtyFields(!!Object.keys(formState.dirtyFields).length); + }, [formState, setHaveDirtyFields]); + + const renderActions = () => { + return ( + <> + + + + + + + {openCreateParameterDialog && ( + setOpenCreateParameterDialog(false)} + parameterValues={getValues} + parameterFormatter={toParamsInfos} + parameterType={ElementType.DYNAMIC_MARGIN_CALCULATION_PARAMETERS} + /> + )} + {openSelectParameterDialog && ( + + )} + {/* Reset Confirmation Dialog */} + {openResetConfirmation && ( + + )} + + ); + }; + return ( + + ); +} diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation.type.ts b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation.type.ts new file mode 100644 index 000000000..ca8af931f --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation.type.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2026, 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/. + */ +export enum TabValues { + TAB_TIME_DELAY = 'TAB_TIME_DELAY', + TAB_LOADS_VARIATIONS = 'TAB_LOADS_VARIATIONS', +} diff --git a/src/components/parameters/dynamic-margin-calculation/index.ts b/src/components/parameters/dynamic-margin-calculation/index.ts new file mode 100644 index 000000000..7557ec830 --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/index.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2026, 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/. + */ +export * from './constants'; +export * from './dynamic-margin-calculation-inline'; diff --git a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx new file mode 100644 index 000000000..e6a37d119 --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2026, 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 yup from '../../../utils/yupConfig'; +import { + ACCURACY, + CALCULATION_TYPE, + LOAD_INCREASE_START_TIME, + LOAD_INCREASE_STOP_TIME, + LOAD_MODELS_RULE, + LOADS_VARIATIONS, + MARGIN_CALCULATION_START_TIME, +} from './constants'; +import { ID, ParameterType, SpecificParameterInfos } from '../../../utils'; +import ParameterField from '../common/parameter-field'; +import { NAME } from '../../inputs'; +import { CalculationType, LoadModelsRule } from '../../../services'; + +export const formSchema = yup.object().shape({ + [MARGIN_CALCULATION_START_TIME]: yup.number().required(), + [LOAD_INCREASE_START_TIME]: yup.number().required(), + [LOAD_INCREASE_STOP_TIME]: yup.number().required(), + [CALCULATION_TYPE]: yup.string().required(), + [ACCURACY]: yup.number().required(), + [LOAD_MODELS_RULE]: yup.string().required(), + [LOADS_VARIATIONS]: yup + .array() + .of( + yup.object().shape({ + [ID]: yup.string().required(), + [NAME]: yup.string().required(), + }) + ) + .required(), +}); + +export const emptyFormData = { + [MARGIN_CALCULATION_START_TIME]: 0, + [LOAD_INCREASE_START_TIME]: 0, + [LOAD_INCREASE_STOP_TIME]: 0, + [CALCULATION_TYPE]: '', + [ACCURACY]: 0, + [LOAD_MODELS_RULE]: '', + [LOADS_VARIATIONS]: [], +}; + +const params: SpecificParameterInfos[] = [ + { + name: MARGIN_CALCULATION_START_TIME, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationMarginCalculationStartTime', + }, + { + name: LOAD_INCREASE_START_TIME, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationLoadIncreaseStartTime', + }, + { + name: LOAD_INCREASE_STOP_TIME, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationLoadIncreaseStopTime', + }, + { + name: CALCULATION_TYPE, + type: ParameterType.STRING, + label: 'DynamicMarginCalculationCalculationType', + possibleValues: [ + { id: CalculationType.GLOBAL_MARGIN, label: 'DynamicMarginCalculationCalculationTypeGlobalMargin' }, + { id: CalculationType.LOCAL_MARGIN, label: 'DynamicMarginCalculationCalculationTypeLocalMargin' }, + ], + }, + { + name: ACCURACY, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationAccuracy', + }, + { + name: LOAD_MODELS_RULE, + type: ParameterType.STRING, + label: 'DynamicMarginCalculationLoadModelsRule', + possibleValues: [ + { id: LoadModelsRule.ALL_LOADS, label: 'DynamicMarginCalculationLoadModelsRuleAllLoads' }, + { id: LoadModelsRule.TARGETED_LOADS, label: 'DynamicMarginCalculationLoadModelsRuleTargetedLoads' }, + ], + }, + // TAB_LOADS_VARIATIONS is not yet displayed +]; + +export default function LoadsVariationsParameters({ path }: { path: string }) { + return params.map((param: SpecificParameterInfos) => { + const { name, type, ...otherParams } = param; + return ; + }); +} diff --git a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx new file mode 100644 index 000000000..a68ba38f7 --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2026, 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 yup from '../../../utils/yupConfig'; +import { START_TIME, STOP_TIME } from './constants'; +import { ParameterType, SpecificParameterInfos } from '../../../utils'; +import ParameterField from '../common/parameter-field'; + +export const formSchema = yup.object().shape({ + [START_TIME]: yup.number().required(), + [STOP_TIME]: yup.number().required(), +}); + +export const emptyFormData = { + [START_TIME]: 0, + [STOP_TIME]: 0, +}; + +const params: SpecificParameterInfos[] = [ + { + name: START_TIME, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationStartTime', + }, + { + name: STOP_TIME, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationStopTime', + }, +]; + +export default function TimeDelayParameters({ path }: { path: string }) { + return params.map((param: SpecificParameterInfos) => { + const { name, type, ...otherParams } = param; + return ; + }); +} diff --git a/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts b/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts new file mode 100644 index 000000000..2cc7e9154 --- /dev/null +++ b/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts @@ -0,0 +1,214 @@ +/** + * Copyright (c) 2026, 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 { FieldErrors, useForm, UseFormReturn } from 'react-hook-form'; +import { ObjectSchema } from 'yup'; +import { SyntheticEvent, useCallback, useEffect, useEffectEvent, useMemo, useState } from 'react'; +import { yupResolver } from '@hookform/resolvers/yup'; +import yup from '../../../utils/yupConfig'; +import { + DynamicMarginCalculationParametersFetchReturn, + DynamicMarginCalculationParametersInfos, +} from '../../../services/dynamic-margin-calculation.type'; +import { emptyFormData as timeDelayEmptyFormData, formSchema as timeDelayFormSchema } from './time-delay-parameters'; +import { + emptyFormData as loadsVariationsEmptyFormData, + formSchema as loadsVariationsFormSchema, +} from './loads-variations-parameters'; +import { isObjectEmpty } from '../../../utils'; +import { PROVIDER } from '../common'; +import { getNameElementEditorEmptyFormData, getNameElementEditorSchema } from '../common/name-element-editor'; +import { + ACCURACY, + CALCULATION_TYPE, + LOAD_INCREASE_START_TIME, + LOAD_INCREASE_STOP_TIME, + LOAD_MODELS_RULE, + LOADS_VARIATIONS, + MARGIN_CALCULATION_START_TIME, + START_TIME, + STOP_TIME, +} from './constants'; +import { TabValues } from './dynamic-margin-calculation.type'; + +const formSchema = yup.object().shape({ + [PROVIDER]: yup.string().required(), + [TabValues.TAB_TIME_DELAY]: timeDelayFormSchema, + [TabValues.TAB_LOADS_VARIATIONS]: loadsVariationsFormSchema, +}); + +const emptyFormData = { + [PROVIDER]: '', + [TabValues.TAB_TIME_DELAY]: timeDelayEmptyFormData, + [TabValues.TAB_LOADS_VARIATIONS]: loadsVariationsEmptyFormData, +}; + +export const toFormValues = (_params: DynamicMarginCalculationParametersFetchReturn): Record => ({ + [PROVIDER]: _params.provider, + [TabValues.TAB_TIME_DELAY]: { + [START_TIME]: _params.startTime, + [STOP_TIME]: _params.stopTime, + }, + [TabValues.TAB_LOADS_VARIATIONS]: { + [MARGIN_CALCULATION_START_TIME]: _params.marginCalculationStartTime, + [LOAD_INCREASE_START_TIME]: _params.loadIncreaseStartTime, + [LOAD_INCREASE_STOP_TIME]: _params.loadIncreaseStopTime, + [CALCULATION_TYPE]: _params.calculationType, + [ACCURACY]: _params.accuracy, + [LOAD_MODELS_RULE]: _params.loadModelsRule, + [LOADS_VARIATIONS]: _params.loadsVariationsInfos, + }, +}); + +export const toParamsInfos = (_formData: Record): DynamicMarginCalculationParametersFetchReturn => ({ + provider: _formData[PROVIDER], + startTime: _formData[TabValues.TAB_TIME_DELAY][START_TIME], + stopTime: _formData[TabValues.TAB_TIME_DELAY][STOP_TIME], + marginCalculationStartTime: _formData[TabValues.TAB_LOADS_VARIATIONS][MARGIN_CALCULATION_START_TIME], + loadIncreaseStartTime: _formData[TabValues.TAB_LOADS_VARIATIONS][LOAD_INCREASE_START_TIME], + loadIncreaseStopTime: _formData[TabValues.TAB_LOADS_VARIATIONS][LOAD_INCREASE_STOP_TIME], + calculationType: _formData[TabValues.TAB_LOADS_VARIATIONS][CALCULATION_TYPE], + accuracy: _formData[TabValues.TAB_LOADS_VARIATIONS][ACCURACY], + loadModelsRule: _formData[TabValues.TAB_LOADS_VARIATIONS][LOAD_MODELS_RULE], +}); + +export type UseTabsReturn = { + selectedTab: TTabValue; + tabsWithError: TTabValue[]; + onTabChange: (event: SyntheticEvent, newValue: TTabValue) => void; + onError: (errors: FieldErrors) => void; +}; + +type UseTabsProps = { + defaultTab: TTabValue; + tabEnum: Record; +}; + +function useTabs({ + defaultTab, + tabEnum, +}: Readonly>): UseTabsReturn { + const [tabValue, setTabValue] = useState(defaultTab); + const [tabValuesWithError, setTabValuesWithError] = useState([]); + const handleTabChange = useCallback((event: SyntheticEvent, newValue: TTabValue) => { + setTabValue(newValue); + }, []); + + const onError = useCallback( + (errors: FieldErrors) => { + if (!errors || isObjectEmpty(errors)) { + return; + } + + const tabsInError: TTabValue[] = []; + // do not show error when being in the current tab + Object.values(tabEnum).forEach((tab) => { + if (errors?.[tab] && tab !== tabValue) { + tabsInError.push(tab); + } + }); + + if (tabsInError.includes(tabValue)) { + // error in current tab => do not change tab systematically but remove current tab in error list + setTabValuesWithError(tabsInError.filter((errorTab) => errorTab !== tabValue)); + } else if (tabsInError.length > 0) { + // switch to the first tab in the list then remove the tab in the error list + setTabValue(tabsInError[0]); + setTabValuesWithError(tabsInError.filter((errorTab, index, arr) => errorTab !== arr[0])); + } + }, + [tabValue, tabEnum] + ); + + return { + selectedTab: tabValue, + tabsWithError: tabValuesWithError, + onTabChange: handleTabChange, + onError, + }; +} + +export type UseComputationParametersFormReturn = UseTabsReturn & { + formMethods: UseFormReturn; + formSchema: ObjectSchema; + paramsLoaded: boolean; + formattedProviders: { id: string; label: string }[]; +}; + +export type UseDynamicMarginCalculationParametersFormReturn = UseComputationParametersFormReturn & {}; +export type UseParametersFormProps = { + providers: Record; + params: Record | null; + // default values fields managed in grid-explore via directory server + name: string | null; + description: string | null; +}; +export type UseDynamicMarginCalculationParametersFormProps = UseParametersFormProps; + +export function useDynamicMarginCalculationParametersForm({ + providers, + params, + name: initialName, + description: initialDescription, +}: Readonly): UseDynamicMarginCalculationParametersFormReturn { + const paramsLoaded = useMemo(() => !!params, [params]); + + const formattedProviders = useMemo( + () => + Object.entries(providers).map(([key, value]) => ({ + id: key, + label: value, + })), + [providers] + ); + + const returnFormSchema = useMemo(() => { + return initialName !== null ? formSchema.concat(getNameElementEditorSchema(initialName)) : formSchema; + }, [initialName]); + + const newEmptyFormData: any = useMemo(() => { + return { + ...(initialName !== null ? getNameElementEditorEmptyFormData(initialName, initialDescription) : {}), + ...emptyFormData, + }; + }, [initialName, initialDescription]); + + const returnFormMethods = useForm({ + defaultValues: newEmptyFormData, + resolver: yupResolver(returnFormSchema), + }); + + const { reset } = returnFormMethods; + + const resetForm = useEffectEvent((_params: DynamicMarginCalculationParametersInfos) => { + reset(toFormValues(_params)); + }); + + useEffect(() => { + if (params) { + resetForm(params); + } + }, [params, paramsLoaded]); + + /* tab-related handling */ + const { selectedTab, tabsWithError, onTabChange, onError } = useTabs({ + defaultTab: TabValues.TAB_TIME_DELAY, + tabEnum: TabValues, + }); + + return { + formMethods: returnFormMethods, + formSchema: returnFormSchema, + paramsLoaded, + formattedProviders, + /* tab-related handling */ + selectedTab, + tabsWithError, + onTabChange, + onError, + }; +} diff --git a/src/components/parameters/index.ts b/src/components/parameters/index.ts index 985547ce1..e6c74063e 100644 --- a/src/components/parameters/index.ts +++ b/src/components/parameters/index.ts @@ -13,3 +13,4 @@ export * from './voltage-init'; export * from './pcc-min'; export * from './security-analysis'; export * from './sensi'; +export * from './dynamic-margin-calculation'; diff --git a/src/components/parameters/loadflow/load-flow-general-parameters.tsx b/src/components/parameters/loadflow/load-flow-general-parameters.tsx index a3df5b9ff..b8ef33311 100644 --- a/src/components/parameters/loadflow/load-flow-general-parameters.tsx +++ b/src/components/parameters/loadflow/load-flow-general-parameters.tsx @@ -6,7 +6,7 @@ */ import { memo } from 'react'; -import LoadFlowParameterField from './load-flow-parameter-field'; +import ParameterField from '../common/parameter-field'; import { BALANCE_TYPE, CONNECTED_MODE, @@ -151,7 +151,7 @@ function LoadFlowGeneralParameters({ provider, specificParams }: Readonly {basicParams.map((item) => ( - + ))} {showAdvancedLfParams && - advancedParams.map((item) => ( - - ))} + advancedParams.map((item) => )} {showSpecificLfParams && specificParams?.map((item) => ( - + ))} diff --git a/src/services/directory.ts b/src/services/directory.ts index 219b3d614..e6d54d746 100644 --- a/src/services/directory.ts +++ b/src/services/directory.ts @@ -10,6 +10,24 @@ import { backendFetch, backendFetchJson, getRequestParamFromList } from './utils import { ElementAttributes } from '../utils'; const PREFIX_EXPLORE_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/explore`; +const PREFIX_DIRECTORY_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/directory`; + +export function fetchContingencyAndFiltersLists(listIds: UUID[]): Promise { + console.info('Fetching contingency and filters lists'); + + // Add params to Url + const idsParams = getRequestParamFromList( + 'ids', + listIds.filter((id) => id) // filter falsy elements + ); + const urlSearchParams = new URLSearchParams(idsParams); + + urlSearchParams.append('strictMode', 'false'); + + const url = `${PREFIX_DIRECTORY_SERVER_QUERIES}/v1/elements?${urlSearchParams}`; + console.debug(url); + return backendFetchJson(url); +} export function fetchRootFolders(types: string[]): Promise { console.info('Fetching Root Directories'); diff --git a/src/services/dynamic-margin-calculation.ts b/src/services/dynamic-margin-calculation.ts new file mode 100644 index 000000000..f66e1cd80 --- /dev/null +++ b/src/services/dynamic-margin-calculation.ts @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2026, 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 type { UUID } from 'node:crypto'; +import { + DynamicMarginCalculationParametersFetchReturn, + DynamicMarginCalculationParametersInfos, +} from './dynamic-margin-calculation.type'; +import { fetchContingencyAndFiltersLists } from './directory'; +import { backendFetch, backendFetchJson, backendFetchText } from './utils'; +import { PREFIX_STUDY_QUERIES } from './loadflow'; + +const PREFIX_DYNAMIC_MARGIN_CALCULATION_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/dynamic-margin-calculation`; + +function getDynamicMarginCalculationUrl() { + return `${PREFIX_DYNAMIC_MARGIN_CALCULATION_SERVER_QUERIES}/v1/`; +} + +export function fetchDynamicMarginCalculationProviders() { + console.info('fetch dynamic margin calculation providers'); + const url = `${getDynamicMarginCalculationUrl()}providers`; + console.debug(url); + return backendFetchJson(url); +} + +export function fetchDefaultDynamicMarginCalculationProvider() { + console.info('Fetching default dynamic margin calculation provider'); + const url = `${PREFIX_STUDY_QUERIES}/v1/dynamic-margin-calculation-default-provider`; + console.debug(url); + return backendFetchText(url); +} + +export function enrichLoadFilterNames( + parameters: DynamicMarginCalculationParametersInfos +): Promise { + // enrich LoadsVariationInfos by LoadsVariationFetchReturn with id and name infos + if (parameters?.loadsVariations) { + const loadsVariations = parameters?.loadsVariations; + const allLoadFilterUuids = loadsVariations.flatMap((loadVariation) => loadVariation.loadFilterUuids ?? []); + return fetchContingencyAndFiltersLists(allLoadFilterUuids).then((loadFilterInfos) => { + // eslint-disable-next-line no-param-reassign + delete parameters.loadsVariations; + const loadFilterInfosMap = Object.fromEntries( + loadFilterInfos.map((info) => [info.elementUuid, info.elementName]) + ); + return { + ...parameters, + loadsVariationsInfos: loadsVariations?.map((infos) => { + const newLoadVariationInfos = { + ...infos, + loadFiltersInfos: infos.loadFilterUuids?.map((loadFilterUuid) => ({ + id: loadFilterUuid, + name: loadFilterInfosMap[loadFilterUuid], + })), + }; + delete newLoadVariationInfos.loadFilterUuids; + return newLoadVariationInfos; + }), + }; + }); + } + + // eslint-disable-next-line no-param-reassign + delete parameters.loadsVariations; + return Promise.resolve({ + ...parameters, + loadsVariationsInfos: [], + }); +} + +export function fetchDynamicMarginCalculationParameters( + parameterUuid: UUID +): Promise { + console.info(`Fetching dynamic margin calculation parameters having uuid '${parameterUuid}' ...`); + const url = `${getDynamicMarginCalculationUrl()}parameters/${encodeURIComponent(parameterUuid)}`; + console.debug(url); + const parametersPromise: Promise = backendFetchJson(url); + return parametersPromise.then(enrichLoadFilterNames); +} + +export function cleanLoadFilterNames( + newParams: DynamicMarginCalculationParametersFetchReturn +): DynamicMarginCalculationParametersInfos { + // send to back raw LoadsVariations instead of LoadsVariationsInfos + const newParameters = + newParams != null + ? { + ...newParams, + loadsVariations: newParams?.loadsVariationsInfos?.map((infos) => { + const newLoadsVariationInfos = { + ...infos, + loadFilterUuids: infos.loadFiltersInfos?.map((loadFilterInfos) => loadFilterInfos.id), + }; + delete newLoadsVariationInfos.loadFiltersInfos; + return newLoadsVariationInfos; + }), + } + : newParams; + delete newParameters?.loadsVariationsInfos; + return newParameters; +} + +export function updateDynamicMarginCalculationParameters( + parameterUuid: UUID, + newParams: DynamicMarginCalculationParametersFetchReturn +): Promise { + console.info(`Setting dynamic margin calculation parameters having uuid '${parameterUuid}' ...`); + const url = `${getDynamicMarginCalculationUrl()}parameters/${parameterUuid}`; + console.debug(url); + const newParameters = cleanLoadFilterNames(newParams); + return backendFetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(newParameters), + }); +} diff --git a/src/services/dynamic-margin-calculation.type.ts b/src/services/dynamic-margin-calculation.type.ts new file mode 100644 index 000000000..91e69bc56 --- /dev/null +++ b/src/services/dynamic-margin-calculation.type.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2026, 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 type { UUID } from 'node:crypto'; + +export enum CalculationType { + GLOBAL_MARGIN = 'GLOBAL_MARGIN', + LOCAL_MARGIN = 'LOCAL_MARGIN', +} + +export enum LoadModelsRule { + ALL_LOADS = 'ALL_LOADS', + TARGETED_LOADS = 'TARGETED_LOADS', +} + +export type LoadsVariationInfos = { + id?: UUID; // persisted id of the info to be modified + loadFilterUuids?: UUID[]; + variation: number; +}; + +export type LoadsVariationFetchReturn = Exclude & { + loadFiltersInfos?: { id: UUID; name: string }[]; +}; + +export type DynamicMarginCalculationParametersInfos = { + provider?: string; + startTime?: number; + stopTime?: number; + marginCalculationStartTime?: number; + loadIncreaseStartTime?: number; + loadIncreaseStopTime?: number; + calculationType?: CalculationType; + accuracy?: number; // integer + loadModelsRule?: LoadModelsRule; + loadsVariations?: LoadsVariationInfos[]; +}; + +export type DynamicMarginCalculationParametersFetchReturn = Exclude< + DynamicMarginCalculationParametersInfos, + 'loadsVariations' +> & { + loadsVariationsInfos?: LoadsVariationFetchReturn[]; +}; diff --git a/src/services/index.ts b/src/services/index.ts index 12f8d1be1..9731f44c9 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -17,3 +17,5 @@ export * from './userAdmin'; export * from './utils'; export * from './voltage-init'; export * from './short-circuit-analysis'; +export * from './dynamic-margin-calculation'; +export * from './dynamic-margin-calculation.type'; diff --git a/src/translations/en/parameters.ts b/src/translations/en/parameters.ts index 0a9b41654..3124026ba 100644 --- a/src/translations/en/parameters.ts +++ b/src/translations/en/parameters.ts @@ -280,4 +280,22 @@ export const parametersEn = { PccMinParametersError: 'An error occurred while updating the pcc min parameters', updatePccMinParametersError: 'An error occurred while updating the pcc min parameters', pccMinParamFilter: 'Definition of contingencies on voltage levels', + // DynamicMarginCalculation + DynamicMarginCalculationParametersError: + 'An error occurred while updating the dynamic margin calculation parameters', + updateDynamicMarginCalculationParametersError: + 'An error occurred while updating the dynamic margin calculation parameters', + DynamicMarginCalculationStartTime: 'Start time', + DynamicMarginCalculationStopTime: 'Stop time', + DynamicMarginCalculationMarginCalculationStartTime: 'Margin calculation start time', + DynamicMarginCalculationLoadIncreaseStartTime: 'Load increase start time', + DynamicMarginCalculationLoadIncreaseStopTime: 'Load increase stop time', + DynamicMarginCalculationCalculationType: 'Calculation type', + DynamicMarginCalculationAccuracy: 'Accuracy', + DynamicMarginCalculationLoadModelsRule: 'Load models rule', + DynamicMarginCalculationLoadsVariations: 'Load variations', + DynamicMarginCalculationCalculationTypeGlobalMargin: 'Global margin', + DynamicMarginCalculationCalculationTypeLocalMargin: 'Local margin', + DynamicMarginCalculationLoadModelsRuleAllLoads: 'All loads', + DynamicMarginCalculationLoadModelsRuleTargetedLoads: 'Targeted loads', }; diff --git a/src/translations/fr/parameters.ts b/src/translations/fr/parameters.ts index 5abde27e5..ee7e2f85b 100644 --- a/src/translations/fr/parameters.ts +++ b/src/translations/fr/parameters.ts @@ -305,4 +305,21 @@ export const parametersFr = { PccMinParametersError: 'Erreur lors de la mise à jour des paramètres de pcc du min', updatePccMinParametersError: 'Une erreur est survenue lors de la mise a jour des paramètres de pcc min', pccMinParamFilter: 'Définition des postes en défaut', + // DynamicMarginCalculation + DynamicMarginCalculationParametersError: 'Erreur lors de la mise à jour des paramètres du calcul du marge', + updateDynamicMarginCalculationParametersError: + 'Une erreur est survenue lors de la mise a jour des paramètres du calcul du marge', + DynamicMarginCalculationStartTime: 'Temps de début', + DynamicMarginCalculationStopTime: "Temps d'arrêt", + DynamicMarginCalculationMarginCalculationStartTime: 'Temps de début de du calcul de marge', + DynamicMarginCalculationLoadIncreaseStartTime: "Temps de début de l'augmentation de charge", + DynamicMarginCalculationLoadIncreaseStopTime: "Temps d'arrêt de l'augmentation de charge", + DynamicMarginCalculationCalculationType: 'Type de calcul', + DynamicMarginCalculationAccuracy: 'Précision', + DynamicMarginCalculationLoadModelsRule: 'Règle des modèles de charge', + DynamicMarginCalculationLoadsVariations: 'Variations de charge', + DynamicMarginCalculationCalculationTypeGlobalMargin: 'Margin global', + DynamicMarginCalculationCalculationTypeLocalMargin: 'Margin local', + DynamicMarginCalculationLoadModelsRuleAllLoads: 'Tous les charges', + DynamicMarginCalculationLoadModelsRuleTargetedLoads: 'Charges ciblées', }; diff --git a/src/utils/types/elementType.ts b/src/utils/types/elementType.ts index 991c59b2f..b278a8991 100644 --- a/src/utils/types/elementType.ts +++ b/src/utils/types/elementType.ts @@ -21,6 +21,7 @@ export enum ElementType { LOADFLOW_PARAMETERS = 'LOADFLOW_PARAMETERS', SENSITIVITY_PARAMETERS = 'SENSITIVITY_PARAMETERS', SHORT_CIRCUIT_PARAMETERS = 'SHORT_CIRCUIT_PARAMETERS', + DYNAMIC_MARGIN_CALCULATION_PARAMETERS = 'DYNAMIC_MARGIN_CALCULATION_PARAMETERS', NETWORK_VISUALIZATIONS_PARAMETERS = 'NETWORK_VISUALIZATIONS_PARAMETERS', SPREADSHEET_CONFIG = 'SPREADSHEET_CONFIG', SPREADSHEET_CONFIG_COLLECTION = 'SPREADSHEET_CONFIG_COLLECTION', From 535fb8b759e73cd680484547b5f56c1c8d6f1c9a Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Wed, 28 Jan 2026 14:41:10 +0100 Subject: [PATCH 02/14] Remove isDeveloperMode Signed-off-by: Thang PHAM --- .../dynamic-margin-calculation-inline.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx index f30ff8769..5ab80f0b3 100644 --- a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx @@ -32,13 +32,11 @@ type DynamicMarginCalculationInlineProps = { studyUuid: UUID | null; parametersBackend: UseParametersBackendReturnProps; setHaveDirtyFields: (isDirty: boolean) => void; - isDeveloperMode: boolean; }; export function DynamicMarginCalculationInline({ studyUuid, parametersBackend, setHaveDirtyFields, - isDeveloperMode, }: Readonly) { const [providers, , , , , params, , updateParams, resetParams, ,] = parametersBackend; const dynamicMarginCalculationMethods = useDynamicMarginCalculationParametersForm({ From a7644b275d65a294ce54bb1074b4596eee786388 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Thu, 29 Jan 2026 20:25:54 +0100 Subject: [PATCH 03/14] Correct params representation Signed-off-by: Thang PHAM --- .../dynamic-margin-calculation-form.tsx | 4 ++-- .../loads-variations-parameters.tsx | 2 +- .../dynamic-margin-calculation/time-delay-parameters.tsx | 2 +- src/translations/en/parameters.ts | 2 ++ src/translations/fr/parameters.ts | 2 ++ 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx index 4c6469845..1882b24f3 100644 --- a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx @@ -53,12 +53,12 @@ export function DynamicMarginCalculationForm({ aria-label="parameters" > } + label={} value={TabValues.TAB_TIME_DELAY} sx={getTabStyle(tabsWithError, TabValues.TAB_TIME_DELAY)} /> } + label={} value={TabValues.TAB_LOADS_VARIATIONS} sx={getTabStyle(tabsWithError, TabValues.TAB_LOADS_VARIATIONS)} /> diff --git a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx index e6a37d119..01d6aedef 100644 --- a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx @@ -92,6 +92,6 @@ const params: SpecificParameterInfos[] = [ export default function LoadsVariationsParameters({ path }: { path: string }) { return params.map((param: SpecificParameterInfos) => { const { name, type, ...otherParams } = param; - return ; + return ; }); } diff --git a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx index a68ba38f7..5886e7637 100644 --- a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx @@ -36,6 +36,6 @@ const params: SpecificParameterInfos[] = [ export default function TimeDelayParameters({ path }: { path: string }) { return params.map((param: SpecificParameterInfos) => { const { name, type, ...otherParams } = param; - return ; + return ; }); } diff --git a/src/translations/en/parameters.ts b/src/translations/en/parameters.ts index 3124026ba..563b49f0f 100644 --- a/src/translations/en/parameters.ts +++ b/src/translations/en/parameters.ts @@ -285,6 +285,8 @@ export const parametersEn = { 'An error occurred while updating the dynamic margin calculation parameters', updateDynamicMarginCalculationParametersError: 'An error occurred while updating the dynamic margin calculation parameters', + DynamicMarginCalculationTimeDelayTab: 'Time delay', + DynamicMarginCalculationLoadsVariationsTab: 'Load variations', DynamicMarginCalculationStartTime: 'Start time', DynamicMarginCalculationStopTime: 'Stop time', DynamicMarginCalculationMarginCalculationStartTime: 'Margin calculation start time', diff --git a/src/translations/fr/parameters.ts b/src/translations/fr/parameters.ts index ee7e2f85b..f2e8fdd17 100644 --- a/src/translations/fr/parameters.ts +++ b/src/translations/fr/parameters.ts @@ -309,6 +309,8 @@ export const parametersFr = { DynamicMarginCalculationParametersError: 'Erreur lors de la mise à jour des paramètres du calcul du marge', updateDynamicMarginCalculationParametersError: 'Une erreur est survenue lors de la mise a jour des paramètres du calcul du marge', + DynamicMarginCalculationTimeDelayTab: 'Temporisation', + DynamicMarginCalculationLoadsVariationsTab: 'Variations de charge', DynamicMarginCalculationStartTime: 'Temps de début', DynamicMarginCalculationStopTime: "Temps d'arrêt", DynamicMarginCalculationMarginCalculationStartTime: 'Temps de début de du calcul de marge', From d4368e8c02acd1224f475a869706f3016b876b1f Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Fri, 30 Jan 2026 13:02:51 +0100 Subject: [PATCH 04/14] move time-related params to time delay tab Signed-off-by: Thang PHAM --- .../parameters/common/ProviderParam.tsx | 4 +-- .../loads-variations-parameters.tsx | 31 +------------------ .../time-delay-parameters.tsx | 29 ++++++++++++++++- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/components/parameters/common/ProviderParam.tsx b/src/components/parameters/common/ProviderParam.tsx index ab82c4df5..a89ffd717 100644 --- a/src/components/parameters/common/ProviderParam.tsx +++ b/src/components/parameters/common/ProviderParam.tsx @@ -26,7 +26,7 @@ const styles = { export function ProviderParam({ options }: Readonly) { return ( <> - + @@ -34,7 +34,7 @@ export function ProviderParam({ options }: Readonly) { - + diff --git a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx index 01d6aedef..91a66e547 100644 --- a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx @@ -5,24 +5,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import yup from '../../../utils/yupConfig'; -import { - ACCURACY, - CALCULATION_TYPE, - LOAD_INCREASE_START_TIME, - LOAD_INCREASE_STOP_TIME, - LOAD_MODELS_RULE, - LOADS_VARIATIONS, - MARGIN_CALCULATION_START_TIME, -} from './constants'; +import { ACCURACY, CALCULATION_TYPE, LOAD_MODELS_RULE, LOADS_VARIATIONS } from './constants'; import { ID, ParameterType, SpecificParameterInfos } from '../../../utils'; import ParameterField from '../common/parameter-field'; import { NAME } from '../../inputs'; import { CalculationType, LoadModelsRule } from '../../../services'; export const formSchema = yup.object().shape({ - [MARGIN_CALCULATION_START_TIME]: yup.number().required(), - [LOAD_INCREASE_START_TIME]: yup.number().required(), - [LOAD_INCREASE_STOP_TIME]: yup.number().required(), [CALCULATION_TYPE]: yup.string().required(), [ACCURACY]: yup.number().required(), [LOAD_MODELS_RULE]: yup.string().required(), @@ -38,9 +27,6 @@ export const formSchema = yup.object().shape({ }); export const emptyFormData = { - [MARGIN_CALCULATION_START_TIME]: 0, - [LOAD_INCREASE_START_TIME]: 0, - [LOAD_INCREASE_STOP_TIME]: 0, [CALCULATION_TYPE]: '', [ACCURACY]: 0, [LOAD_MODELS_RULE]: '', @@ -48,21 +34,6 @@ export const emptyFormData = { }; const params: SpecificParameterInfos[] = [ - { - name: MARGIN_CALCULATION_START_TIME, - type: ParameterType.DOUBLE, - label: 'DynamicMarginCalculationMarginCalculationStartTime', - }, - { - name: LOAD_INCREASE_START_TIME, - type: ParameterType.DOUBLE, - label: 'DynamicMarginCalculationLoadIncreaseStartTime', - }, - { - name: LOAD_INCREASE_STOP_TIME, - type: ParameterType.DOUBLE, - label: 'DynamicMarginCalculationLoadIncreaseStopTime', - }, { name: CALCULATION_TYPE, type: ParameterType.STRING, diff --git a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx index 5886e7637..75635377f 100644 --- a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx @@ -6,18 +6,30 @@ */ import yup from '../../../utils/yupConfig'; -import { START_TIME, STOP_TIME } from './constants'; +import { + LOAD_INCREASE_START_TIME, + LOAD_INCREASE_STOP_TIME, + MARGIN_CALCULATION_START_TIME, + START_TIME, + STOP_TIME, +} from './constants'; import { ParameterType, SpecificParameterInfos } from '../../../utils'; import ParameterField from '../common/parameter-field'; export const formSchema = yup.object().shape({ [START_TIME]: yup.number().required(), [STOP_TIME]: yup.number().required(), + [MARGIN_CALCULATION_START_TIME]: yup.number().required(), + [LOAD_INCREASE_START_TIME]: yup.number().required(), + [LOAD_INCREASE_STOP_TIME]: yup.number().required(), }); export const emptyFormData = { [START_TIME]: 0, [STOP_TIME]: 0, + [MARGIN_CALCULATION_START_TIME]: 0, + [LOAD_INCREASE_START_TIME]: 0, + [LOAD_INCREASE_STOP_TIME]: 0, }; const params: SpecificParameterInfos[] = [ @@ -31,6 +43,21 @@ const params: SpecificParameterInfos[] = [ type: ParameterType.DOUBLE, label: 'DynamicMarginCalculationStopTime', }, + { + name: MARGIN_CALCULATION_START_TIME, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationMarginCalculationStartTime', + }, + { + name: LOAD_INCREASE_START_TIME, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationLoadIncreaseStartTime', + }, + { + name: LOAD_INCREASE_STOP_TIME, + type: ParameterType.DOUBLE, + label: 'DynamicMarginCalculationLoadIncreaseStopTime', + }, ]; export default function TimeDelayParameters({ path }: { path: string }) { From 7c07cc9d35f78cb841cd17b7ade7eaeaed267803 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Sat, 31 Jan 2026 02:32:53 +0100 Subject: [PATCH 05/14] using dnd table for loads variations Signed-off-by: Thang PHAM --- src/components/dnd-table/dnd-table.tsx | 6 +- src/components/dnd-table/dnd-table.type.ts | 7 ++ .../common/parameter-dnd-table-field.tsx | 72 ++++++++++++++ .../dynamic-margin-calculation/constants.ts | 3 + .../loads-variations-parameters.tsx | 97 ++++++++++++++++--- ...amic-margin-calculation-parameters-form.ts | 11 ++- .../voltage-limits-parameters.tsx | 2 +- src/services/dynamic-margin-calculation.ts | 10 +- .../dynamic-margin-calculation.type.ts | 3 +- src/translations/en/parameters.ts | 3 + src/translations/fr/parameters.ts | 3 + 11 files changed, 188 insertions(+), 29 deletions(-) create mode 100644 src/components/parameters/common/parameter-dnd-table-field.tsx diff --git a/src/components/dnd-table/dnd-table.tsx b/src/components/dnd-table/dnd-table.tsx index 4cd273333..c611111d7 100644 --- a/src/components/dnd-table/dnd-table.tsx +++ b/src/components/dnd-table/dnd-table.tsx @@ -34,6 +34,7 @@ import { ErrorInput, FieldErrorAlert, RawReadOnlyInput, + SwitchInput, TableNumericalInput, TableTextInput, } from '../inputs'; @@ -168,12 +169,15 @@ function EditableTableCell({ {column.type === DndColumnType.CHIP_ITEMS && ( )} + {column.type === DndColumnType.SWITCH && ( + + )} {column.type === DndColumnType.CUSTOM && column.component(rowIndex)} ); } -interface DndTableProps { +export interface DndTableProps { arrayFormName: string; useFieldArrayOutput: UseFieldArrayReturn; columnsDefinition: DndColumn[]; diff --git a/src/components/dnd-table/dnd-table.type.ts b/src/components/dnd-table/dnd-table.type.ts index 0d26e30ef..48fa96100 100644 --- a/src/components/dnd-table/dnd-table.type.ts +++ b/src/components/dnd-table/dnd-table.type.ts @@ -17,6 +17,7 @@ export enum DndColumnType { AUTOCOMPLETE = 'AUTOCOMPLETE', CHIP_ITEMS = 'CHIP_ITEMS', DIRECTORY_ITEMS = 'DIRECTORY_ITEMS', + SWITCH = 'SWITCH', CUSTOM = 'CUSTOM', } @@ -28,6 +29,7 @@ export interface ColumnBase { extra?: JSX.Element; editable?: boolean; type: DndColumnType; + initialValue?: any; // should conform to the type field } export interface ColumnText extends ColumnBase { @@ -58,6 +60,10 @@ export interface ColumnChipsItem extends ColumnBase { type: DndColumnType.CHIP_ITEMS; } +export interface ColumnSwitchItem extends ColumnBase { + type: DndColumnType.SWITCH; +} + export interface ColumnCustom extends ColumnBase { type: DndColumnType.CUSTOM; component: (rowIndex: number) => ReactNode; @@ -69,4 +75,5 @@ export type DndColumn = | ColumnText | ColumnDirectoryItem | ColumnChipsItem + | ColumnSwitchItem | ColumnCustom; diff --git a/src/components/parameters/common/parameter-dnd-table-field.tsx b/src/components/parameters/common/parameter-dnd-table-field.tsx new file mode 100644 index 000000000..ba72300b7 --- /dev/null +++ b/src/components/parameters/common/parameter-dnd-table-field.tsx @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2026, 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 { Grid, SxProps, Tooltip, TooltipProps, Typography } from '@mui/material'; +import { FormattedMessage } from 'react-intl'; +import { useFieldArray } from 'react-hook-form'; +import { Info as InfoIcon } from '@mui/icons-material'; +import { useCallback, useMemo } from 'react'; +import { DndTable, DndTableProps } from '../../dnd-table'; + +export type ParameterDndTableFieldProps = { + name: string; + label: string; + tooltipProps?: Omit; + sxContainerProps?: SxProps; +} & Omit; + +export default function ParameterDndTableField({ + name, + label, + columnsDefinition, + tooltipProps, + sxContainerProps, + ...otherProps +}: Readonly) { + const useFieldArrayOutput = useFieldArray({ + name, + }); + + const newDefaultRowData = useMemo(() => { + const newRowData: Record = {}; + columnsDefinition.forEach((columnDefinition) => { + newRowData[columnDefinition.dataKey] = columnDefinition.initialValue || null; + }); + return newRowData; + }, [columnsDefinition]); + + const createRows = useCallback(() => [newDefaultRowData], [newDefaultRowData]); + + const { title, ...otherTooltipProps } = tooltipProps || {}; + return ( + + + + + + {tooltipProps && ( + : title} + placement="right-start" + sx={{ marginLeft: 1 }} + {...otherTooltipProps} + > + + + )} + + + + ); +} diff --git a/src/components/parameters/dynamic-margin-calculation/constants.ts b/src/components/parameters/dynamic-margin-calculation/constants.ts index 2ef772ac5..b52a29663 100644 --- a/src/components/parameters/dynamic-margin-calculation/constants.ts +++ b/src/components/parameters/dynamic-margin-calculation/constants.ts @@ -16,3 +16,6 @@ export const CALCULATION_TYPE = 'calculationType'; export const ACCURACY = 'accuracy'; export const LOAD_MODELS_RULE = 'loadModelsRule'; export const LOADS_VARIATIONS = 'loadsVariations'; +export const LOAD_FILTERS = 'loadFilters'; +export const VARIATION = 'variation'; +export const ACTIVE = 'active'; diff --git a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx index 91a66e547..721b2d39d 100644 --- a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx @@ -4,26 +4,45 @@ * 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 { useMemo } from 'react'; +import { useIntl } from 'react-intl'; import yup from '../../../utils/yupConfig'; -import { ACCURACY, CALCULATION_TYPE, LOAD_MODELS_RULE, LOADS_VARIATIONS } from './constants'; -import { ID, ParameterType, SpecificParameterInfos } from '../../../utils'; +import { + ACCURACY, + ACTIVE, + CALCULATION_TYPE, + LOAD_FILTERS, + LOAD_MODELS_RULE, + LOADS_VARIATIONS, + VARIATION, +} from './constants'; +import { ElementType, EquipmentType, ID, ParameterType, SpecificParameterInfos } from '../../../utils'; import ParameterField from '../common/parameter-field'; import { NAME } from '../../inputs'; import { CalculationType, LoadModelsRule } from '../../../services'; +import ParameterDndTableField from '../common/parameter-dnd-table-field'; +import { DndColumn, DndColumnType } from '../../dnd-table'; export const formSchema = yup.object().shape({ [CALCULATION_TYPE]: yup.string().required(), [ACCURACY]: yup.number().required(), [LOAD_MODELS_RULE]: yup.string().required(), - [LOADS_VARIATIONS]: yup - .array() - .of( - yup.object().shape({ - [ID]: yup.string().required(), - [NAME]: yup.string().required(), - }) - ) - .required(), + [LOADS_VARIATIONS]: yup.array().of( + yup.object().shape({ + [ID]: yup.string().nullable(), // not shown in form, used to identify a row + [LOAD_FILTERS]: yup + .array() + .of( + yup.object().shape({ + [ID]: yup.string().required(), + [NAME]: yup.string().required(), + }) + ) + .min(1), + [VARIATION]: yup.number().min(0).required(), + [ACTIVE]: yup.boolean().nullable().notRequired(), + }) + ), }); export const emptyFormData = { @@ -57,12 +76,58 @@ const params: SpecificParameterInfos[] = [ { id: LoadModelsRule.TARGETED_LOADS, label: 'DynamicMarginCalculationLoadModelsRuleTargetedLoads' }, ], }, - // TAB_LOADS_VARIATIONS is not yet displayed + // LOADS_VARIATIONS displayed in a separated component, i.e., ParameterDndTableField +]; + +const loadsVariationsColumnsDefinition: DndColumn[] = [ + { + label: 'DynamicMarginCalculationLoadsFilter', + dataKey: LOAD_FILTERS, + initialValue: [], + editable: true, + type: DndColumnType.DIRECTORY_ITEMS, + equipmentTypes: [EquipmentType.LOAD], + elementType: ElementType.FILTER, + titleId: 'FiltersListsSelection', + }, + { + label: 'DynamicMarginCalculationLoadsVariation', + dataKey: VARIATION, + editable: true, + type: DndColumnType.NUMERIC, + textAlign: 'right', + }, + { + label: 'DynamicMarginCalculationLoadsActive', + initialValue: true, + dataKey: ACTIVE, + editable: true, + width: 100, + type: DndColumnType.SWITCH, + }, ]; export default function LoadsVariationsParameters({ path }: { path: string }) { - return params.map((param: SpecificParameterInfos) => { - const { name, type, ...otherParams } = param; - return ; - }); + const inlt = useIntl(); + const translatedColumnsDefinition = useMemo(() => { + return loadsVariationsColumnsDefinition.map((colDef) => ({ + ...colDef, + label: inlt.formatMessage({ id: colDef.label }), + })); + }, [inlt]); + return ( + <> + {params.map((param: SpecificParameterInfos) => { + const { name, type, ...otherParams } = param; + return ; + })} + + + ); } diff --git a/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts b/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts index 2cc7e9154..568cfd4cc 100644 --- a/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts +++ b/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts @@ -52,11 +52,11 @@ export const toFormValues = (_params: DynamicMarginCalculationParametersFetchRet [TabValues.TAB_TIME_DELAY]: { [START_TIME]: _params.startTime, [STOP_TIME]: _params.stopTime, - }, - [TabValues.TAB_LOADS_VARIATIONS]: { [MARGIN_CALCULATION_START_TIME]: _params.marginCalculationStartTime, [LOAD_INCREASE_START_TIME]: _params.loadIncreaseStartTime, [LOAD_INCREASE_STOP_TIME]: _params.loadIncreaseStopTime, + }, + [TabValues.TAB_LOADS_VARIATIONS]: { [CALCULATION_TYPE]: _params.calculationType, [ACCURACY]: _params.accuracy, [LOAD_MODELS_RULE]: _params.loadModelsRule, @@ -68,12 +68,13 @@ export const toParamsInfos = (_formData: Record): DynamicMarginCalc provider: _formData[PROVIDER], startTime: _formData[TabValues.TAB_TIME_DELAY][START_TIME], stopTime: _formData[TabValues.TAB_TIME_DELAY][STOP_TIME], - marginCalculationStartTime: _formData[TabValues.TAB_LOADS_VARIATIONS][MARGIN_CALCULATION_START_TIME], - loadIncreaseStartTime: _formData[TabValues.TAB_LOADS_VARIATIONS][LOAD_INCREASE_START_TIME], - loadIncreaseStopTime: _formData[TabValues.TAB_LOADS_VARIATIONS][LOAD_INCREASE_STOP_TIME], + marginCalculationStartTime: _formData[TabValues.TAB_TIME_DELAY][MARGIN_CALCULATION_START_TIME], + loadIncreaseStartTime: _formData[TabValues.TAB_TIME_DELAY][LOAD_INCREASE_START_TIME], + loadIncreaseStopTime: _formData[TabValues.TAB_TIME_DELAY][LOAD_INCREASE_STOP_TIME], calculationType: _formData[TabValues.TAB_LOADS_VARIATIONS][CALCULATION_TYPE], accuracy: _formData[TabValues.TAB_LOADS_VARIATIONS][ACCURACY], loadModelsRule: _formData[TabValues.TAB_LOADS_VARIATIONS][LOAD_MODELS_RULE], + loadsVariationsInfos: _formData[TabValues.TAB_LOADS_VARIATIONS][LOADS_VARIATIONS], }); export type UseTabsReturn = { diff --git a/src/components/parameters/voltage-init/voltage-limits-parameters.tsx b/src/components/parameters/voltage-init/voltage-limits-parameters.tsx index 35263d39a..cba7c9168 100644 --- a/src/components/parameters/voltage-init/voltage-limits-parameters.tsx +++ b/src/components/parameters/voltage-init/voltage-limits-parameters.tsx @@ -113,7 +113,7 @@ export function VoltageLimitsParameters() { adornment: VoltageAdornment, textAlign: 'right', }, - ] satisfies (DndColumn & { initialValue: unknown[] | null })[] + ] satisfies DndColumn[] ).map((column) => ({ ...column, label: intl diff --git a/src/services/dynamic-margin-calculation.ts b/src/services/dynamic-margin-calculation.ts index f66e1cd80..3e3e702c0 100644 --- a/src/services/dynamic-margin-calculation.ts +++ b/src/services/dynamic-margin-calculation.ts @@ -40,18 +40,18 @@ export function enrichLoadFilterNames( if (parameters?.loadsVariations) { const loadsVariations = parameters?.loadsVariations; const allLoadFilterUuids = loadsVariations.flatMap((loadVariation) => loadVariation.loadFilterUuids ?? []); - return fetchContingencyAndFiltersLists(allLoadFilterUuids).then((loadFilterInfos) => { + return fetchContingencyAndFiltersLists(allLoadFilterUuids).then((loadFilter) => { // eslint-disable-next-line no-param-reassign delete parameters.loadsVariations; const loadFilterInfosMap = Object.fromEntries( - loadFilterInfos.map((info) => [info.elementUuid, info.elementName]) + loadFilter.map((info) => [info.elementUuid, info.elementName]) ); return { ...parameters, loadsVariationsInfos: loadsVariations?.map((infos) => { const newLoadVariationInfos = { ...infos, - loadFiltersInfos: infos.loadFilterUuids?.map((loadFilterUuid) => ({ + loadFilters: infos.loadFilterUuids?.map((loadFilterUuid) => ({ id: loadFilterUuid, name: loadFilterInfosMap[loadFilterUuid], })), @@ -92,9 +92,9 @@ export function cleanLoadFilterNames( loadsVariations: newParams?.loadsVariationsInfos?.map((infos) => { const newLoadsVariationInfos = { ...infos, - loadFilterUuids: infos.loadFiltersInfos?.map((loadFilterInfos) => loadFilterInfos.id), + loadFilterUuids: infos.loadFilters?.map((loadFilterInfos) => loadFilterInfos.id), }; - delete newLoadsVariationInfos.loadFiltersInfos; + delete newLoadsVariationInfos.loadFilters; return newLoadsVariationInfos; }), } diff --git a/src/services/dynamic-margin-calculation.type.ts b/src/services/dynamic-margin-calculation.type.ts index 91e69bc56..236eba5d6 100644 --- a/src/services/dynamic-margin-calculation.type.ts +++ b/src/services/dynamic-margin-calculation.type.ts @@ -21,10 +21,11 @@ export type LoadsVariationInfos = { id?: UUID; // persisted id of the info to be modified loadFilterUuids?: UUID[]; variation: number; + active: boolean; }; export type LoadsVariationFetchReturn = Exclude & { - loadFiltersInfos?: { id: UUID; name: string }[]; + loadFilters?: { id: UUID; name: string }[]; }; export type DynamicMarginCalculationParametersInfos = { diff --git a/src/translations/en/parameters.ts b/src/translations/en/parameters.ts index 563b49f0f..e1adb00dc 100644 --- a/src/translations/en/parameters.ts +++ b/src/translations/en/parameters.ts @@ -300,4 +300,7 @@ export const parametersEn = { DynamicMarginCalculationCalculationTypeLocalMargin: 'Local margin', DynamicMarginCalculationLoadModelsRuleAllLoads: 'All loads', DynamicMarginCalculationLoadModelsRuleTargetedLoads: 'Targeted loads', + DynamicMarginCalculationLoadsFilter: 'Loads filter', + DynamicMarginCalculationLoadsVariation: 'Load variation', + DynamicMarginCalculationLoadsActive: 'Active', }; diff --git a/src/translations/fr/parameters.ts b/src/translations/fr/parameters.ts index f2e8fdd17..396c166e7 100644 --- a/src/translations/fr/parameters.ts +++ b/src/translations/fr/parameters.ts @@ -324,4 +324,7 @@ export const parametersFr = { DynamicMarginCalculationCalculationTypeLocalMargin: 'Margin local', DynamicMarginCalculationLoadModelsRuleAllLoads: 'Tous les charges', DynamicMarginCalculationLoadModelsRuleTargetedLoads: 'Charges ciblées', + DynamicMarginCalculationLoadsFilter: 'Regroupement de charges', + DynamicMarginCalculationLoadsVariation: 'Variation de charge', + DynamicMarginCalculationLoadsActive: 'Actif', }; From 38586816e6dbb13cb7ead540a4ebaae9ac72a12b Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Sat, 31 Jan 2026 13:31:02 +0100 Subject: [PATCH 06/14] Correct layout Signed-off-by: Thang PHAM --- .../dynamic-margin-calculation-form.tsx | 57 +++++++++---------- .../dynamic-margin-calculation-inline.tsx | 22 +++---- .../loads-variations-parameters.tsx | 5 +- .../time-delay-parameters.tsx | 13 +++-- 4 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx index 1882b24f3..7637bef12 100644 --- a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-form.tsx @@ -36,46 +36,45 @@ export function DynamicMarginCalculationForm({ {renderTitleFields?.()} {paramsLoaded ? ( - - + + + + + + + } + value={TabValues.TAB_TIME_DELAY} + sx={getTabStyle(tabsWithError, TabValues.TAB_TIME_DELAY)} + /> + } + value={TabValues.TAB_LOADS_VARIATIONS} + sx={getTabStyle(tabsWithError, TabValues.TAB_LOADS_VARIATIONS)} + /> + + - - - } - value={TabValues.TAB_TIME_DELAY} - sx={getTabStyle(tabsWithError, TabValues.TAB_TIME_DELAY)} - /> - } - value={TabValues.TAB_LOADS_VARIATIONS} - sx={getTabStyle(tabsWithError, TabValues.TAB_LOADS_VARIATIONS)} - /> - - - - - - - - + + + + + + + {renderActions?.()} ) : ( )} - {renderActions?.()} ); } diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx index 5ab80f0b3..41119969e 100644 --- a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx @@ -102,16 +102,18 @@ export function DynamicMarginCalculationInline({ const renderActions = () => { return ( <> - - - - - + + + + + + + {openCreateParameterDialog && ( + {params.map((param: SpecificParameterInfos) => { const { name, type, ...otherParams } = param; return ; @@ -128,6 +129,6 @@ export default function LoadsVariationsParameters({ path }: { path: string }) { columnsDefinition={translatedColumnsDefinition} tableHeight={270} /> - + ); } diff --git a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx index 75635377f..ea8641aae 100644 --- a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { Grid } from '@mui/material'; import yup from '../../../utils/yupConfig'; import { LOAD_INCREASE_START_TIME, @@ -61,8 +62,12 @@ const params: SpecificParameterInfos[] = [ ]; export default function TimeDelayParameters({ path }: { path: string }) { - return params.map((param: SpecificParameterInfos) => { - const { name, type, ...otherParams } = param; - return ; - }); + return ( + + {params.map((param: SpecificParameterInfos) => { + const { name, type, ...otherParams } = param; + return ; + })} + + ); } From 167aa3d1f75974a2f60e821b7ed5feb84a2de1fa Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Sat, 31 Jan 2026 14:20:17 +0100 Subject: [PATCH 07/14] show button save and load parameters but disabled Signed-off-by: Thang PHAM --- .../dynamic-margin-calculation-inline.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx index 41119969e..7d144b785 100644 --- a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx @@ -109,6 +109,12 @@ export function DynamicMarginCalculationInline({ paddingLeft: 0, })} > + setOpenSelectParameterDialog(true)} + label="settings.button.chooseSettings" + /> + setOpenCreateParameterDialog(true)} label="save" /> From aa20de2bcffbb7ae41637578d0446f2d2b7a5536 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Sat, 31 Jan 2026 14:30:13 +0100 Subject: [PATCH 08/14] remove unused endpoint get default provider Signed-off-by: Thang PHAM --- src/services/dynamic-margin-calculation.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/services/dynamic-margin-calculation.ts b/src/services/dynamic-margin-calculation.ts index 3e3e702c0..169922327 100644 --- a/src/services/dynamic-margin-calculation.ts +++ b/src/services/dynamic-margin-calculation.ts @@ -10,8 +10,7 @@ import { DynamicMarginCalculationParametersInfos, } from './dynamic-margin-calculation.type'; import { fetchContingencyAndFiltersLists } from './directory'; -import { backendFetch, backendFetchJson, backendFetchText } from './utils'; -import { PREFIX_STUDY_QUERIES } from './loadflow'; +import { backendFetch, backendFetchJson } from './utils'; const PREFIX_DYNAMIC_MARGIN_CALCULATION_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/dynamic-margin-calculation`; @@ -26,13 +25,6 @@ export function fetchDynamicMarginCalculationProviders() { return backendFetchJson(url); } -export function fetchDefaultDynamicMarginCalculationProvider() { - console.info('Fetching default dynamic margin calculation provider'); - const url = `${PREFIX_STUDY_QUERIES}/v1/dynamic-margin-calculation-default-provider`; - console.debug(url); - return backendFetchText(url); -} - export function enrichLoadFilterNames( parameters: DynamicMarginCalculationParametersInfos ): Promise { From 694619a0e4962120ec8cc1311c71649e73a1d182 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Sun, 1 Feb 2026 22:59:21 +0100 Subject: [PATCH 09/14] do not fetch filter names from front-end Signed-off-by: Thang PHAM --- .../dynamic-margin-calculation-inline.tsx | 3 +- .../loads-variations-parameters.tsx | 15 +++- .../time-delay-parameters.tsx | 2 +- ...amic-margin-calculation-parameters-form.ts | 32 ++++---- src/services/directory.ts | 18 ----- src/services/dynamic-margin-calculation.ts | 76 ++----------------- src/services/index.ts | 1 - .../types}/dynamic-margin-calculation.type.ts | 34 ++++----- src/utils/types/index.ts | 1 + src/utils/types/parameters.type.ts | 9 ++- 10 files changed, 53 insertions(+), 138 deletions(-) rename src/{services => utils/types}/dynamic-margin-calculation.type.ts (53%) diff --git a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx index 7d144b785..a188bc404 100644 --- a/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx +++ b/src/components/parameters/dynamic-margin-calculation/dynamic-margin-calculation-inline.tsx @@ -9,6 +9,7 @@ import type { UUID } from 'node:crypto'; import { Grid } from '@mui/material'; import { FormattedMessage, useIntl } from 'react-intl'; import { useCallback, useEffect, useState } from 'react'; +import { FieldValues } from 'react-hook-form'; import { UseParametersBackendReturnProps } from '../../../utils/types/parameters.type'; import { ComputingType } from '../common/computing-type'; import { ElementType, mergeSx, snackWithFallback } from '../../../utils'; @@ -68,7 +69,7 @@ export function DynamicMarginCalculationInline({ }, [resetParams]); const onSubmit = useCallback( - (formData: Record) => { + (formData: FieldValues) => { // update params after convert form representation to dto representation updateParams(toParamsInfos(formData)); }, diff --git a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx index 6cb74d5f2..388f0f28d 100644 --- a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx @@ -17,10 +17,17 @@ import { LOADS_VARIATIONS, VARIATION, } from './constants'; -import { ElementType, EquipmentType, ID, ParameterType, SpecificParameterInfos } from '../../../utils'; +import { + CalculationType, + ElementType, + EquipmentType, + ID, + LoadModelsRule, + ParameterType, + SpecificParameterInfos, +} from '../../../utils'; import ParameterField from '../common/parameter-field'; import { NAME } from '../../inputs'; -import { CalculationType, LoadModelsRule } from '../../../services'; import ParameterDndTableField from '../common/parameter-dnd-table-field'; import { DndColumn, DndColumnType } from '../../dnd-table'; @@ -36,7 +43,7 @@ export const formSchema = yup.object().shape({ .of( yup.object().shape({ [ID]: yup.string().required(), - [NAME]: yup.string().required(), + [NAME]: yup.string().nullable().notRequired(), }) ) .min(1), @@ -108,7 +115,7 @@ const loadsVariationsColumnsDefinition: DndColumn[] = [ }, ]; -export default function LoadsVariationsParameters({ path }: { path: string }) { +export default function LoadsVariationsParameters({ path }: Readonly<{ path: string }>) { const inlt = useIntl(); const translatedColumnsDefinition = useMemo(() => { return loadsVariationsColumnsDefinition.map((colDef) => ({ diff --git a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx index ea8641aae..a09650883 100644 --- a/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/time-delay-parameters.tsx @@ -61,7 +61,7 @@ const params: SpecificParameterInfos[] = [ }, ]; -export default function TimeDelayParameters({ path }: { path: string }) { +export default function TimeDelayParameters({ path }: Readonly<{ path: string }>) { return ( {params.map((param: SpecificParameterInfos) => { diff --git a/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts b/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts index 568cfd4cc..867dbdf35 100644 --- a/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts +++ b/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts @@ -5,21 +5,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { FieldErrors, useForm, UseFormReturn } from 'react-hook-form'; +import { FieldErrors, FieldValues, useForm, UseFormReturn } from 'react-hook-form'; import { ObjectSchema } from 'yup'; -import { SyntheticEvent, useCallback, useEffect, useEffectEvent, useMemo, useState } from 'react'; +import { SyntheticEvent, useCallback, useEffect, useMemo, useState } from 'react'; import { yupResolver } from '@hookform/resolvers/yup'; import yup from '../../../utils/yupConfig'; -import { - DynamicMarginCalculationParametersFetchReturn, - DynamicMarginCalculationParametersInfos, -} from '../../../services/dynamic-margin-calculation.type'; +import { DynamicMarginCalculationParametersInfos } from '../../../utils/types/dynamic-margin-calculation.type'; import { emptyFormData as timeDelayEmptyFormData, formSchema as timeDelayFormSchema } from './time-delay-parameters'; import { emptyFormData as loadsVariationsEmptyFormData, formSchema as loadsVariationsFormSchema, } from './loads-variations-parameters'; -import { isObjectEmpty } from '../../../utils'; +import { ID, isObjectEmpty } from '../../../utils'; import { PROVIDER } from '../common'; import { getNameElementEditorEmptyFormData, getNameElementEditorSchema } from '../common/name-element-editor'; import { @@ -47,7 +44,8 @@ const emptyFormData = { [TabValues.TAB_LOADS_VARIATIONS]: loadsVariationsEmptyFormData, }; -export const toFormValues = (_params: DynamicMarginCalculationParametersFetchReturn): Record => ({ +export const toFormValues = (_params: DynamicMarginCalculationParametersInfos): FieldValues => ({ + [ID]: _params.id, // not shown in form [PROVIDER]: _params.provider, [TabValues.TAB_TIME_DELAY]: { [START_TIME]: _params.startTime, @@ -60,11 +58,12 @@ export const toFormValues = (_params: DynamicMarginCalculationParametersFetchRet [CALCULATION_TYPE]: _params.calculationType, [ACCURACY]: _params.accuracy, [LOAD_MODELS_RULE]: _params.loadModelsRule, - [LOADS_VARIATIONS]: _params.loadsVariationsInfos, + [LOADS_VARIATIONS]: _params.loadsVariations, }, }); -export const toParamsInfos = (_formData: Record): DynamicMarginCalculationParametersFetchReturn => ({ +export const toParamsInfos = (_formData: FieldValues): DynamicMarginCalculationParametersInfos => ({ + id: _formData[ID], provider: _formData[PROVIDER], startTime: _formData[TabValues.TAB_TIME_DELAY][START_TIME], stopTime: _formData[TabValues.TAB_TIME_DELAY][STOP_TIME], @@ -74,7 +73,7 @@ export const toParamsInfos = (_formData: Record): DynamicMarginCalc calculationType: _formData[TabValues.TAB_LOADS_VARIATIONS][CALCULATION_TYPE], accuracy: _formData[TabValues.TAB_LOADS_VARIATIONS][ACCURACY], loadModelsRule: _formData[TabValues.TAB_LOADS_VARIATIONS][LOAD_MODELS_RULE], - loadsVariationsInfos: _formData[TabValues.TAB_LOADS_VARIATIONS][LOADS_VARIATIONS], + loadsVariations: _formData[TabValues.TAB_LOADS_VARIATIONS][LOADS_VARIATIONS], }); export type UseTabsReturn = { @@ -143,7 +142,7 @@ export type UseComputationParametersFormReturn = UseTa export type UseDynamicMarginCalculationParametersFormReturn = UseComputationParametersFormReturn & {}; export type UseParametersFormProps = { providers: Record; - params: Record | null; + params: DynamicMarginCalculationParametersInfos | null; // default values fields managed in grid-explore via directory server name: string | null; description: string | null; @@ -185,15 +184,12 @@ export function useDynamicMarginCalculationParametersForm({ const { reset } = returnFormMethods; - const resetForm = useEffectEvent((_params: DynamicMarginCalculationParametersInfos) => { - reset(toFormValues(_params)); - }); - useEffect(() => { if (params) { - resetForm(params); + console.log('xxx Resetting form with params:', params); + reset(toFormValues(params)); } - }, [params, paramsLoaded]); + }, [params, paramsLoaded, reset]); /* tab-related handling */ const { selectedTab, tabsWithError, onTabChange, onError } = useTabs({ diff --git a/src/services/directory.ts b/src/services/directory.ts index e6d54d746..219b3d614 100644 --- a/src/services/directory.ts +++ b/src/services/directory.ts @@ -10,24 +10,6 @@ import { backendFetch, backendFetchJson, getRequestParamFromList } from './utils import { ElementAttributes } from '../utils'; const PREFIX_EXPLORE_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/explore`; -const PREFIX_DIRECTORY_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/directory`; - -export function fetchContingencyAndFiltersLists(listIds: UUID[]): Promise { - console.info('Fetching contingency and filters lists'); - - // Add params to Url - const idsParams = getRequestParamFromList( - 'ids', - listIds.filter((id) => id) // filter falsy elements - ); - const urlSearchParams = new URLSearchParams(idsParams); - - urlSearchParams.append('strictMode', 'false'); - - const url = `${PREFIX_DIRECTORY_SERVER_QUERIES}/v1/elements?${urlSearchParams}`; - console.debug(url); - return backendFetchJson(url); -} export function fetchRootFolders(types: string[]): Promise { console.info('Fetching Root Directories'); diff --git a/src/services/dynamic-margin-calculation.ts b/src/services/dynamic-margin-calculation.ts index 169922327..01b9be4fc 100644 --- a/src/services/dynamic-margin-calculation.ts +++ b/src/services/dynamic-margin-calculation.ts @@ -5,11 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import type { UUID } from 'node:crypto'; -import { - DynamicMarginCalculationParametersFetchReturn, - DynamicMarginCalculationParametersInfos, -} from './dynamic-margin-calculation.type'; -import { fetchContingencyAndFiltersLists } from './directory'; +import { DynamicMarginCalculationParametersInfos } from '../utils/types/dynamic-margin-calculation.type'; import { backendFetch, backendFetchJson } from './utils'; const PREFIX_DYNAMIC_MARGIN_CALCULATION_SERVER_QUERIES = `${import.meta.env.VITE_API_GATEWAY}/dynamic-margin-calculation`; @@ -25,90 +21,28 @@ export function fetchDynamicMarginCalculationProviders() { return backendFetchJson(url); } -export function enrichLoadFilterNames( - parameters: DynamicMarginCalculationParametersInfos -): Promise { - // enrich LoadsVariationInfos by LoadsVariationFetchReturn with id and name infos - if (parameters?.loadsVariations) { - const loadsVariations = parameters?.loadsVariations; - const allLoadFilterUuids = loadsVariations.flatMap((loadVariation) => loadVariation.loadFilterUuids ?? []); - return fetchContingencyAndFiltersLists(allLoadFilterUuids).then((loadFilter) => { - // eslint-disable-next-line no-param-reassign - delete parameters.loadsVariations; - const loadFilterInfosMap = Object.fromEntries( - loadFilter.map((info) => [info.elementUuid, info.elementName]) - ); - return { - ...parameters, - loadsVariationsInfos: loadsVariations?.map((infos) => { - const newLoadVariationInfos = { - ...infos, - loadFilters: infos.loadFilterUuids?.map((loadFilterUuid) => ({ - id: loadFilterUuid, - name: loadFilterInfosMap[loadFilterUuid], - })), - }; - delete newLoadVariationInfos.loadFilterUuids; - return newLoadVariationInfos; - }), - }; - }); - } - - // eslint-disable-next-line no-param-reassign - delete parameters.loadsVariations; - return Promise.resolve({ - ...parameters, - loadsVariationsInfos: [], - }); -} - export function fetchDynamicMarginCalculationParameters( parameterUuid: UUID -): Promise { +): Promise { console.info(`Fetching dynamic margin calculation parameters having uuid '${parameterUuid}' ...`); const url = `${getDynamicMarginCalculationUrl()}parameters/${encodeURIComponent(parameterUuid)}`; console.debug(url); - const parametersPromise: Promise = backendFetchJson(url); - return parametersPromise.then(enrichLoadFilterNames); -} - -export function cleanLoadFilterNames( - newParams: DynamicMarginCalculationParametersFetchReturn -): DynamicMarginCalculationParametersInfos { - // send to back raw LoadsVariations instead of LoadsVariationsInfos - const newParameters = - newParams != null - ? { - ...newParams, - loadsVariations: newParams?.loadsVariationsInfos?.map((infos) => { - const newLoadsVariationInfos = { - ...infos, - loadFilterUuids: infos.loadFilters?.map((loadFilterInfos) => loadFilterInfos.id), - }; - delete newLoadsVariationInfos.loadFilters; - return newLoadsVariationInfos; - }), - } - : newParams; - delete newParameters?.loadsVariationsInfos; - return newParameters; + return backendFetchJson(url); } export function updateDynamicMarginCalculationParameters( parameterUuid: UUID, - newParams: DynamicMarginCalculationParametersFetchReturn + newParams: DynamicMarginCalculationParametersInfos ): Promise { console.info(`Setting dynamic margin calculation parameters having uuid '${parameterUuid}' ...`); const url = `${getDynamicMarginCalculationUrl()}parameters/${parameterUuid}`; console.debug(url); - const newParameters = cleanLoadFilterNames(newParams); return backendFetch(url, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, - body: JSON.stringify(newParameters), + body: JSON.stringify(newParams), }); } diff --git a/src/services/index.ts b/src/services/index.ts index 9731f44c9..0d3c574fb 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -18,4 +18,3 @@ export * from './utils'; export * from './voltage-init'; export * from './short-circuit-analysis'; export * from './dynamic-margin-calculation'; -export * from './dynamic-margin-calculation.type'; diff --git a/src/services/dynamic-margin-calculation.type.ts b/src/utils/types/dynamic-margin-calculation.type.ts similarity index 53% rename from src/services/dynamic-margin-calculation.type.ts rename to src/utils/types/dynamic-margin-calculation.type.ts index 236eba5d6..1786554dd 100644 --- a/src/services/dynamic-margin-calculation.type.ts +++ b/src/utils/types/dynamic-margin-calculation.type.ts @@ -17,33 +17,25 @@ export enum LoadModelsRule { TARGETED_LOADS = 'TARGETED_LOADS', } +export type IdNameInfos = { id: UUID; name?: string }; + export type LoadsVariationInfos = { id?: UUID; // persisted id of the info to be modified - loadFilterUuids?: UUID[]; + loadFilters?: IdNameInfos[]; variation: number; active: boolean; }; -export type LoadsVariationFetchReturn = Exclude & { - loadFilters?: { id: UUID; name: string }[]; -}; - export type DynamicMarginCalculationParametersInfos = { - provider?: string; - startTime?: number; - stopTime?: number; - marginCalculationStartTime?: number; - loadIncreaseStartTime?: number; - loadIncreaseStopTime?: number; - calculationType?: CalculationType; - accuracy?: number; // integer - loadModelsRule?: LoadModelsRule; + id?: UUID; + provider: string; + startTime: number; + stopTime: number; + marginCalculationStartTime: number; + loadIncreaseStartTime: number; + loadIncreaseStopTime: number; + calculationType: CalculationType; + accuracy: number; // integer + loadModelsRule: LoadModelsRule; loadsVariations?: LoadsVariationInfos[]; }; - -export type DynamicMarginCalculationParametersFetchReturn = Exclude< - DynamicMarginCalculationParametersInfos, - 'loadsVariations' -> & { - loadsVariationsInfos?: LoadsVariationFetchReturn[]; -}; diff --git a/src/utils/types/index.ts b/src/utils/types/index.ts index ec7b3f2dd..b296e5cf6 100644 --- a/src/utils/types/index.ts +++ b/src/utils/types/index.ts @@ -19,3 +19,4 @@ export * from './dynamic-security-analysis.type'; export * from './dynamic-simulation.type'; export * from './loadflow.type'; export * from './sensitivity-analysis.type'; +export * from './dynamic-margin-calculation.type'; diff --git a/src/utils/types/parameters.type.ts b/src/utils/types/parameters.type.ts index 98c1036ab..8ff10d959 100644 --- a/src/utils/types/parameters.type.ts +++ b/src/utils/types/parameters.type.ts @@ -16,6 +16,7 @@ import type { import { DynamicSimulationParametersFetchReturn } from './dynamic-simulation.type'; import { SensitivityAnalysisParametersInfos } from './sensitivity-analysis.type'; import { type ShortCircuitParametersInfos } from '../../components/parameters/short-circuit/short-circuit-parameters.type'; +import { DynamicMarginCalculationParametersInfos } from './dynamic-margin-calculation.type'; export enum ParameterType { BOOLEAN = 'BOOLEAN', @@ -51,9 +52,11 @@ export type ParametersInfos = T extends ComputingType.S ? DynamicSimulationParametersFetchReturn : T extends ComputingType.DYNAMIC_SECURITY_ANALYSIS ? DynamicSecurityAnalysisParametersFetchReturn - : T extends ComputingType.SHORT_CIRCUIT - ? ShortCircuitParametersInfos - : Record; + : T extends ComputingType.DYNAMIC_MARGIN_CALCULATION + ? DynamicMarginCalculationParametersInfos + : T extends ComputingType.SHORT_CIRCUIT + ? ShortCircuitParametersInfos + : Record; export type UseParametersBackendReturnProps = [ Record, From 9d05b01dd5dfa203d4e62a1427fd402a68bc7265 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Mon, 2 Feb 2026 18:33:52 +0100 Subject: [PATCH 10/14] make select field fullwidth Signed-off-by: Thang PHAM --- src/components/parameters/common/parameter-field.tsx | 7 ++++--- .../loads-variations-parameters.tsx | 6 ++++-- .../use-dynamic-margin-calculation-parameters-form.ts | 2 +- src/translations/en/businessErrorsEn.ts | 2 ++ src/translations/fr/businessErrorsFr.ts | 2 ++ 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/parameters/common/parameter-field.tsx b/src/components/parameters/common/parameter-field.tsx index 5eb2ef492..7f1a24113 100644 --- a/src/components/parameters/common/parameter-field.tsx +++ b/src/components/parameters/common/parameter-field.tsx @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Chip, Grid, Tooltip, Typography } from '@mui/material'; +import { Chip, Grid, SxProps, Tooltip, Typography } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import { parametersStyles } from '../parameters-style'; import { ParameterType } from '../../../utils/types/parameters.type'; @@ -28,14 +28,15 @@ interface ParameterFieldProps { label?: string; description?: string; possibleValues?: { id: string; label: string }[] | string[]; + sx?: SxProps; } -function ParameterField({ id, name, type, label, description, possibleValues }: Readonly) { +function ParameterField({ id, name, type, label, description, possibleValues, sx }: Readonly) { const renderField = () => { switch (type) { case ParameterType.STRING: return possibleValues ? ( - + ) : ( ); diff --git a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx index 388f0f28d..8abb06ad4 100644 --- a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx @@ -6,7 +6,7 @@ */ import { useMemo } from 'react'; import { useIntl } from 'react-intl'; -import { Grid } from '@mui/material'; +import { Grid, SxProps } from '@mui/material'; import yup from '../../../utils/yupConfig'; import { ACCURACY, @@ -60,7 +60,7 @@ export const emptyFormData = { [LOADS_VARIATIONS]: [], }; -const params: SpecificParameterInfos[] = [ +const params: (SpecificParameterInfos & { sx?: SxProps })[] = [ { name: CALCULATION_TYPE, type: ParameterType.STRING, @@ -69,6 +69,7 @@ const params: SpecificParameterInfos[] = [ { id: CalculationType.GLOBAL_MARGIN, label: 'DynamicMarginCalculationCalculationTypeGlobalMargin' }, { id: CalculationType.LOCAL_MARGIN, label: 'DynamicMarginCalculationCalculationTypeLocalMargin' }, ], + sx: { width: '100%' }, }, { name: ACCURACY, @@ -83,6 +84,7 @@ const params: SpecificParameterInfos[] = [ { id: LoadModelsRule.ALL_LOADS, label: 'DynamicMarginCalculationLoadModelsRuleAllLoads' }, { id: LoadModelsRule.TARGETED_LOADS, label: 'DynamicMarginCalculationLoadModelsRuleTargetedLoads' }, ], + sx: { width: '100%' }, }, // LOADS_VARIATIONS displayed in a separated component, i.e., ParameterDndTableField ]; diff --git a/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts b/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts index 867dbdf35..12d7a3894 100644 --- a/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts +++ b/src/components/parameters/dynamic-margin-calculation/use-dynamic-margin-calculation-parameters-form.ts @@ -139,7 +139,7 @@ export type UseComputationParametersFormReturn = UseTa formattedProviders: { id: string; label: string }[]; }; -export type UseDynamicMarginCalculationParametersFormReturn = UseComputationParametersFormReturn & {}; +export type UseDynamicMarginCalculationParametersFormReturn = UseComputationParametersFormReturn; export type UseParametersFormProps = { providers: Record; params: DynamicMarginCalculationParametersInfos | null; diff --git a/src/translations/en/businessErrorsEn.ts b/src/translations/en/businessErrorsEn.ts index d8060c6ec..d14cee520 100644 --- a/src/translations/en/businessErrorsEn.ts +++ b/src/translations/en/businessErrorsEn.ts @@ -72,4 +72,6 @@ export const businessErrorsEn = { 'sensitivityAnalysis.tooManyFactors': 'Too many factors to run sensitivity analysis: {resultCount} results (limit: {resultCountLimit}) and {variableCount} variables (limit: {variableCountLimit}).', 'pccMin.missingFilter': 'The configuration contains one filter that has been deleted.', + 'dynamicMarginCalculation.providerNotFound': 'Dynamic margin calculation provider not found.', + 'dynamicMarginCalculation.loadFilterNotFound': 'Some load filters do not exist: {filterUuids}', }; diff --git a/src/translations/fr/businessErrorsFr.ts b/src/translations/fr/businessErrorsFr.ts index daa0231ca..154520313 100644 --- a/src/translations/fr/businessErrorsFr.ts +++ b/src/translations/fr/businessErrorsFr.ts @@ -73,4 +73,6 @@ export const businessErrorsFr = { 'sensitivityAnalysis.tooManyFactors': 'Trop de facteurs pour exécuter l’analyse de sensibilité : {resultCount} résultats (limite : {resultCountLimit}) et {variableCount} variables (limite : {variableCountLimit}).', 'pccMin.missingFilter': 'La configuration contient un filtre qui a été supprimé.', + 'dynamicMarginCalculation.providerNotFound': 'Simulateur du calcul dynamique de marge non trouvé.', + 'dynamicMarginCalculation.loadFilterNotFound': "Certains filtres de consommations n'existent pas : {filterUuids}", }; From 18c961aa2a02843fe62c2333d7c0858b55eaee4e Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Mon, 2 Feb 2026 23:37:48 +0100 Subject: [PATCH 11/14] translation Signed-off-by: Thang PHAM --- src/translations/fr/businessErrorsFr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/fr/businessErrorsFr.ts b/src/translations/fr/businessErrorsFr.ts index 154520313..d3051fff5 100644 --- a/src/translations/fr/businessErrorsFr.ts +++ b/src/translations/fr/businessErrorsFr.ts @@ -73,6 +73,6 @@ export const businessErrorsFr = { 'sensitivityAnalysis.tooManyFactors': 'Trop de facteurs pour exécuter l’analyse de sensibilité : {resultCount} résultats (limite : {resultCountLimit}) et {variableCount} variables (limite : {variableCountLimit}).', 'pccMin.missingFilter': 'La configuration contient un filtre qui a été supprimé.', - 'dynamicMarginCalculation.providerNotFound': 'Simulateur du calcul dynamique de marge non trouvé.', + 'dynamicMarginCalculation.providerNotFound': 'Simulateur du calcul de marge dynamique non trouvé.', 'dynamicMarginCalculation.loadFilterNotFound': "Certains filtres de consommations n'existent pas : {filterUuids}", }; From ad9da78025edc85bf4666712e55a3cbb65bb9438 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Tue, 3 Feb 2026 14:12:19 +0100 Subject: [PATCH 12/14] fix no rows Signed-off-by: Thang PHAM --- src/components/dnd-table/dnd-table-bottom-right-buttons.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx b/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx index 16cafac7c..0fbc018b1 100644 --- a/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx +++ b/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx @@ -42,10 +42,10 @@ export function DndTableBottomRightButtons({ const currentRows: ({ selected: boolean } & Record)[] = useWatch({ name: arrayFormName, }); - + console.log('xxx current rows', { currentRows }); const noRowsSelected = currentRows ? !currentRows.some((row) => row[SELECTED]) : true; - const firstRowSelected = currentRows[0]?.[SELECTED]; - const lastRowSelected = currentRows[currentRows.length - 1]?.[SELECTED]; + const firstRowSelected = currentRows?.[0]?.[SELECTED]; + const lastRowSelected = currentRows?.[currentRows.length - 1]?.[SELECTED]; return ( From d191dfe02bf5b1d0c4c9ae40f25b0f0ee6ce1279 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Tue, 3 Feb 2026 14:44:21 +0100 Subject: [PATCH 13/14] rectify check no rows Signed-off-by: Thang PHAM --- src/components/dnd-table/dnd-table-bottom-right-buttons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx b/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx index 0fbc018b1..ba986bad7 100644 --- a/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx +++ b/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx @@ -44,8 +44,8 @@ export function DndTableBottomRightButtons({ }); console.log('xxx current rows', { currentRows }); const noRowsSelected = currentRows ? !currentRows.some((row) => row[SELECTED]) : true; - const firstRowSelected = currentRows?.[0]?.[SELECTED]; - const lastRowSelected = currentRows?.[currentRows.length - 1]?.[SELECTED]; + const firstRowSelected = noRowsSelected ? undefined : currentRows[0]?.[SELECTED]; + const lastRowSelected = noRowsSelected ? undefined : currentRows[currentRows.length - 1]?.[SELECTED]; return ( From 2d3b9f6f3b90bea886acb6c0e52ed14d6a2dffe5 Mon Sep 17 00:00:00 2001 From: Thang PHAM Date: Tue, 3 Feb 2026 16:42:08 +0100 Subject: [PATCH 14/14] clean log and add key prop in loop Signed-off-by: Thang PHAM --- src/components/dnd-table/dnd-table-bottom-right-buttons.tsx | 2 +- .../loads-variations-parameters.tsx | 4 +++- .../dynamic-margin-calculation/time-delay-parameters.tsx | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx b/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx index ba986bad7..95dc9bdb3 100644 --- a/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx +++ b/src/components/dnd-table/dnd-table-bottom-right-buttons.tsx @@ -42,7 +42,7 @@ export function DndTableBottomRightButtons({ const currentRows: ({ selected: boolean } & Record)[] = useWatch({ name: arrayFormName, }); - console.log('xxx current rows', { currentRows }); + const noRowsSelected = currentRows ? !currentRows.some((row) => row[SELECTED]) : true; const firstRowSelected = noRowsSelected ? undefined : currentRows[0]?.[SELECTED]; const lastRowSelected = noRowsSelected ? undefined : currentRows[currentRows.length - 1]?.[SELECTED]; diff --git a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx index 8abb06ad4..3d16f71ca 100644 --- a/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx +++ b/src/components/parameters/dynamic-margin-calculation/loads-variations-parameters.tsx @@ -129,7 +129,9 @@ export default function LoadsVariationsParameters({ path }: Readonly<{ path: str {params.map((param: SpecificParameterInfos) => { const { name, type, ...otherParams } = param; - return ; + return ( + + ); })} {params.map((param: SpecificParameterInfos) => { const { name, type, ...otherParams } = param; - return ; + return ( + + ); })} );