Skip to content

New user interface for limit sets creation #2969

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6268ad4
first try
EtienneLt Jun 20, 2025
da5e0f6
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jun 25, 2025
bb8a115
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jun 25, 2025
f202c7c
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jun 27, 2025
be9b384
first commit
basseche Jun 27, 2025
886cdec
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jun 30, 2025
f6ee32d
Improve User interface
basseche Jun 30, 2025
9724e2b
User interface Ok
basseche Jul 1, 2025
f4acb02
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jul 1, 2025
ada1060
remove warning
basseche Jul 1, 2025
4f4e932
remove callback from components
basseche Jul 1, 2025
1e6ee12
resolve sonar issues
basseche Jul 2, 2025
c6f8a84
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jul 3, 2025
230e33a
separate limits-pane for modification and creation
basseche Jul 3, 2025
aaaaa4a
Fix
basseche Jul 3, 2025
a635cd0
reorganize files
basseche Jul 3, 2025
5eb4b8d
try to fix rename
basseche Jul 3, 2025
4505d1c
undo move files
basseche Jul 3, 2025
51e5e08
Line Creation : information from network-map Ok
basseche Jul 4, 2025
a4553cc
Fix Interface
basseche Jul 8, 2025
092b4bb
User Interface Ok
basseche Jul 9, 2025
6d75eaa
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jul 9, 2025
023e05d
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jul 10, 2025
5ba8ee9
Fix transfo
basseche Jul 10, 2025
c400142
load information from network-modification-server ok
basseche Jul 17, 2025
0f653db
Fix labels + Schema
basseche Jul 18, 2025
0c18958
fix build
basseche Jul 18, 2025
20d374e
fix build
basseche Jul 18, 2025
5781c47
Merge branch 'main' into refactor-and-clean-limit-creation
basseche Jul 18, 2025
50ee4c9
prettier
basseche Jul 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/components/dialogs/commons/grid-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { Box, Grid, SxProps, Theme, Tooltip } from '@mui/material';
import { FormattedMessage, useIntl } from 'react-intl';
import { InfoOutlined } from '@mui/icons-material';
import { mergeSx } from '@gridsuite/commons-ui';

export interface GridSectionProps {
title: string;
Expand All @@ -15,6 +16,8 @@ export interface GridSectionProps {
customStyle?: SxProps<Theme>;
tooltipEnabled?: boolean;
tooltipMessage?: string;
children?: React.ReactNode | React.ReactNode[];
formatDisabled?: boolean;
}

export default function GridSection({
Expand All @@ -24,13 +27,23 @@ export default function GridSection({
customStyle,
tooltipEnabled = false,
tooltipMessage,
children,
formatDisabled,
}: Readonly<GridSectionProps>) {
const intl = useIntl();
return (
<Grid container spacing={2}>
<Grid item xs={size}>
<Box sx={customStyle} component={`h${heading}`}>
<FormattedMessage id={title} />
<Box
sx={mergeSx(customStyle, {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
})}
component={`h${heading}`}
>
{formatDisabled ? title : <FormattedMessage id={title} />}
{children}
{tooltipEnabled && (
<Tooltip sx={{ paddingLeft: 1 }} title={intl.formatMessage({ id: tooltipMessage })}>
<InfoOutlined color="info" fontSize="medium" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,104 +7,80 @@

import {
ID,
OPERATIONAL_LIMITS_GROUPS_1,
OPERATIONAL_LIMITS_GROUPS_2,
OPERATIONAL_LIMITS_GROUPS,
SELECTED_LIMITS_GROUP_1,
SELECTED_LIMITS_GROUP_2,
} from '../../utils/field-constants';
} from '../../../utils/field-constants';
import { useFieldArray, useFormContext } from 'react-hook-form';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import { ContentCopy, Delete, Edit } from '@mui/icons-material';
import ListItemText from '@mui/material/ListItemText';
import { useIntl } from 'react-intl';
import { OperationalLimitsGroup } from '../../../services/network-modification-types';
import { OperationalLimitsGroup } from '../../../../services/network-modification-types';
import { PopoverProps } from '@mui/material/Popover';

export interface LimitsGroupsContextualMenuProps {
parentFormName: string;
indexSelectedLimitSet1: number | null;
indexSelectedLimitSet2: number | null;
setIndexSelectedLimitSet1: React.Dispatch<React.SetStateAction<number | null>>;
setIndexSelectedLimitSet2: React.Dispatch<React.SetStateAction<number | null>>;
indexSelectedLimitSet: number | null;
setIndexSelectedLimitSet: React.Dispatch<React.SetStateAction<number | null>>;
menuAnchorEl: PopoverProps['anchorEl'];
handleCloseMenu: () => void;
activatedByMenuTabIndex: number | null;
startEditingLimitsGroup: (index: number, name: string | null) => void;
selectedLimitsGroups1: string;
selectedLimitsGroups2: string;
editedLimitGroupName: string;
}

export function LimitsGroupsContextualMenu({
parentFormName,
indexSelectedLimitSet1,
indexSelectedLimitSet2,
indexSelectedLimitSet,
setIndexSelectedLimitSet,
menuAnchorEl,
handleCloseMenu,
activatedByMenuTabIndex,
startEditingLimitsGroup,
selectedLimitsGroups1,
selectedLimitsGroups2,
editedLimitGroupName,
setIndexSelectedLimitSet1,
setIndexSelectedLimitSet2,
}: Readonly<LimitsGroupsContextualMenuProps>) {
const intl = useIntl();
const { append: appendToLimitsGroups1, remove: removeLimitsGroups1 } = useFieldArray({
name: `${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_1}`,
});
const { append: appendToLimitsGroups2, remove: removeLimitsGroups2 } = useFieldArray({
name: `${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_2}`,
const { append: appendToLimitsGroups, remove: removeLimitsGroups } = useFieldArray({
name: `${parentFormName}.${OPERATIONAL_LIMITS_GROUPS}`,
});
const { getValues, setValue } = useFormContext();

const handleDeleteTab = () => {
if (indexSelectedLimitSet1 !== null) {
if (indexSelectedLimitSet !== null) {
const tabId: string = getValues(`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS}`)?.[indexSelectedLimitSet]
?.id;
// if this operational limit was selected, deselect it
if (selectedLimitsGroups1 === editedLimitGroupName) {
if (selectedLimitsGroups1 === tabId) {
setValue(`${parentFormName}.${SELECTED_LIMITS_GROUP_1}`, '');
}
removeLimitsGroups1(indexSelectedLimitSet1);
setIndexSelectedLimitSet1(null);
}
if (indexSelectedLimitSet2 !== null) {
if (selectedLimitsGroups2 === editedLimitGroupName) {
if (selectedLimitsGroups2 === tabId) {
setValue(`${parentFormName}.${SELECTED_LIMITS_GROUP_2}`, '');
}
removeLimitsGroups2(indexSelectedLimitSet2);
setIndexSelectedLimitSet2(null);
removeLimitsGroups(indexSelectedLimitSet);
setIndexSelectedLimitSet(null);
}
handleCloseMenu();
};

const handleDuplicateTab = () => {
let newName: string = '';
if (indexSelectedLimitSet1 !== null) {
if (indexSelectedLimitSet !== null) {
const duplicatedLimits1: OperationalLimitsGroup = getValues(
`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_1}[${indexSelectedLimitSet1}]`
`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS}[${indexSelectedLimitSet}]`
);
newName = duplicatedLimits1.id + '_COPY';
const newLimitsGroup1: OperationalLimitsGroup = {
...duplicatedLimits1,
[ID]: newName,
};
appendToLimitsGroups1(newLimitsGroup1);
}

if (indexSelectedLimitSet2 !== null) {
const duplicatedLimits2: OperationalLimitsGroup = getValues(
`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_2}[${indexSelectedLimitSet2}]`
);
newName = duplicatedLimits2.id + '_COPY';
const newLimitsGroup2: OperationalLimitsGroup = {
...duplicatedLimits2,
[ID]: newName,
};
appendToLimitsGroups2(newLimitsGroup2);
appendToLimitsGroups(newLimitsGroup1);
}
startEditingLimitsGroup(getValues(`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS_1}`).length - 1, newName);
startEditingLimitsGroup(getValues(`${parentFormName}.${OPERATIONAL_LIMITS_GROUPS}`).length - 1, newName);
};

return (
Expand Down
139 changes: 139 additions & 0 deletions src/components/dialogs/limits/creation/limits-pane-creation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* Copyright (c) 2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { Button, Grid } from '@mui/material';
import {
CURRENT_LIMITS,
LIMITS,
OPERATIONAL_LIMITS_GROUPS,
SELECTED_LIMITS_GROUP_1,
SELECTED_LIMITS_GROUP_2,
} from 'components/utils/field-constants';
import { LimitsSidePane } from '../limits-side-pane';
import { SelectedOperationalLimitGroup } from '../selected-operational-limit-group.js';
import { useCallback, useRef, useState } from 'react';
import { useWatch } from 'react-hook-form';
import { CurrentLimits, OperationalLimitsGroup } from '../../../../services/network-modification-types';
import { OperationalLimitsGroupsTabs } from '../operational-limits-groups-tabs';
import { tabStyles } from 'components/utils/tab-utils';
import { CurrentTreeNode } from '../../../graph/tree-node.type';
import GridSection from '../../commons/grid-section';
import { styles } from '../../dialog-utils';
import AddIcon from '@mui/icons-material/ControlPoint';

const OperationalLimitGroupSelect = ({
selectedFormName,
optionsFormName,
label,
}: {
selectedFormName: string;
optionsFormName: string;
label: string;
}) => (
<Grid item xs={3}>
<SelectedOperationalLimitGroup
selectedFormName={selectedFormName}
optionsFormName={optionsFormName}
label={label}
/>
</Grid>
);

export interface LimitsPaneCreationProps {
id?: string;
currentNode?: CurrentTreeNode;
equipmentToModify?: any;
clearableFields?: boolean;
}

export function LimitsPaneCreation({
id = LIMITS,
currentNode,
equipmentToModify,
clearableFields,
}: Readonly<LimitsPaneCreationProps>) {
const [indexSelectedLimitSet, setIndexSelectedLimitSet] = useState<number | null>(null);

const myRef: any = useRef<any>(null);

const limitsGroups1: OperationalLimitsGroup[] = useWatch({
name: `${id}.${OPERATIONAL_LIMITS_GROUPS}`,
});

const onAddClick = useCallback(() => myRef.current?.addNewLimitSet(), []);

const getCurrentLimits1 = (equipmentToModify: any): CurrentLimits | null => {
if (equipmentToModify?.currentLimits1) {
return equipmentToModify.currentLimits1.find(
(currentLimit: CurrentLimits) => currentLimit.id === equipmentToModify.selectedOperationalLimitsGroup1
);
}
return null;
};

return (
<>
{/* active limit sets */}
<GridSection title="SelectedOperationalLimitGroups" />
<Grid container item xs={8} columns={10.25} spacing={0}>
<OperationalLimitGroupSelect
selectedFormName={`${id}.${SELECTED_LIMITS_GROUP_1}`}
optionsFormName={`${id}.${OPERATIONAL_LIMITS_GROUPS}`}
label="Side1"
/>
<OperationalLimitGroupSelect
selectedFormName={`${id}.${SELECTED_LIMITS_GROUP_2}`}
optionsFormName={`${id}.${OPERATIONAL_LIMITS_GROUPS}`}
label="Side2"
/>
</Grid>

{/* limits */}
<Grid container item xs={4.9}>
<GridSection
title="LimitSets"
children={<Button sx={styles.button} startIcon={<AddIcon onClick={onAddClick} />} />}
/>
</Grid>
<Grid container item xs={12} columns={10.25}>
<Grid item xs={4}>
<OperationalLimitsGroupsTabs
ref={myRef}
parentFormName={id}
limitsGroups={limitsGroups1}
indexSelectedLimitSet={indexSelectedLimitSet}
setIndexSelectedLimitSet={setIndexSelectedLimitSet}
/>
</Grid>
<Grid item xs={6} sx={tabStyles.parametersBox} marginLeft={2}>
{indexSelectedLimitSet !== null &&
limitsGroups1.map(
(operationalLimitsGroup: OperationalLimitsGroup, index: number) =>
indexSelectedLimitSet != null &&
index === indexSelectedLimitSet && (
<LimitsSidePane
key={operationalLimitsGroup.id + 'leftPanel'}
limitsGroupFormName={`${id}.${OPERATIONAL_LIMITS_GROUPS}[${index}].${CURRENT_LIMITS}`}
limitsGroupApplicabilityName={`${id}.${OPERATIONAL_LIMITS_GROUPS}[${index}]`}
clearableFields={clearableFields}
permanentCurrentLimitPreviousValue={
getCurrentLimits1(equipmentToModify)?.permanentLimit
}
temporaryLimitsPreviousValues={
getCurrentLimits1(equipmentToModify)?.temporaryLimits ?? []
}
currentNode={currentNode}
onlySelectedLimitsGroup={false}
selectedLimitSetId={operationalLimitsGroup.id}
/>
)
)}
</Grid>
</Grid>
</>
);
}
Loading
Loading