Skip to content

Commit e429129

Browse files
authored
Add new contingency list by filter (#711)
Signed-off-by: basseche <[email protected]>
1 parent 4619bca commit e429129

17 files changed

+562
-9
lines changed

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"dependencies": {
2323
"@emotion/react": "^11.14.0",
2424
"@emotion/styled": "^11.14.1",
25-
"@gridsuite/commons-ui": "0.126.0",
25+
"@gridsuite/commons-ui": "0.127.0",
2626
"@hookform/resolvers": "^4.1.3",
2727
"@mui/icons-material": "^5.18.0",
2828
"@mui/lab": "5.0.0-alpha.175",

src/components/dialogs/contingency-list/contingency-list-utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import type { SetRequired } from 'type-fest';
1717
import { prepareContingencyListForBackend } from '../contingency-list-helper';
1818
import { ContingencyListType } from '../../../utils/elementType';
19+
import { FilterAttributes } from '../../../utils/contingency-list.type';
1920

2021
export interface Identifier {
2122
type: 'ID_BASED';
@@ -71,6 +72,15 @@ export const getCriteriaBasedFormDataFromFetchedElement = (response: any, name:
7172
...getCriteriaBasedFormData(response),
7273
});
7374

75+
export const getFilterBasedFormDataFromFetchedElement = (response: any, name: string, description: string) => ({
76+
[FieldConstants.NAME]: name,
77+
[FieldConstants.DESCRIPTION]: description,
78+
[FieldConstants.CONTINGENCY_LIST_TYPE]: ContingencyListType.FILTERS.id,
79+
[FieldConstants.FILTERS]: response.filters.map((filter: FilterAttributes) => {
80+
return { id: filter.id, name: filter.name, specificMetadata: { equipmentType: filter.equipmentType } };
81+
}),
82+
});
83+
7484
export const getExplicitNamingFormDataFromFetchedElement = (response: any, name: string, description: string) => {
7585
let result;
7686
if (response.identifierContingencyList?.identifiers?.length) {

src/components/dialogs/contingency-list/creation/contingency-list-creation-form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default function ContingencyListCreationForm() {
4242
const contingencyListTypeField = (
4343
<RadioInput
4444
name={FieldConstants.CONTINGENCY_LIST_TYPE}
45-
options={Object.values(ContingencyListType)}
45+
options={[ContingencyListType.CRITERIA_BASED, ContingencyListType.EXPLICIT_NAMING]}
4646
formProps={{ onChange: handleChange }} // need to override this in order to do not activate the validate button when changing the filter type
4747
/>
4848
);
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/**
2+
* Copyright (c) 2025, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import {
9+
CustomMuiDialog,
10+
FieldConstants,
11+
MAX_CHAR_DESCRIPTION,
12+
TreeViewFinderNodeProps,
13+
useSnackMessage,
14+
yupConfig as yup,
15+
} from '@gridsuite/commons-ui';
16+
import { useForm } from 'react-hook-form';
17+
import { yupResolver } from '@hookform/resolvers/yup';
18+
import { useSelector } from 'react-redux';
19+
import { useCallback, useEffect, useState } from 'react';
20+
import { UUID } from 'crypto';
21+
import { ObjectSchema } from 'yup';
22+
import ContingencyListFilterBasedForm from './contingency-list-filter-based-form';
23+
import { AppState } from '../../../../redux/types';
24+
import {
25+
createFilterBasedContingency,
26+
getContingencyList,
27+
saveFilterBasedContingencyList,
28+
} from '../../../../utils/rest-api';
29+
import { handleNotAllowedError } from '../../../utils/rest-errors';
30+
import { ContingencyListType } from '../../../../utils/elementType';
31+
import { getFilterBasedFormDataFromFetchedElement } from '../contingency-list-utils';
32+
import { FilterBasedContingencyList } from '../../../../utils/contingency-list.type';
33+
34+
const schema: ObjectSchema<ContingencyListFilterBasedFormData> = yup.object().shape({
35+
[FieldConstants.NAME]: yup.string().required(),
36+
[FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION),
37+
[FieldConstants.FILTERS]: yup.array().required(),
38+
});
39+
40+
export interface ContingencyListFilterBasedFormData {
41+
[FieldConstants.NAME]: string;
42+
[FieldConstants.DESCRIPTION]?: string;
43+
[FieldConstants.FILTERS]: TreeViewFinderNodeProps[];
44+
}
45+
46+
const getContingencyListEmptyFormData = (name = '') => ({
47+
[FieldConstants.NAME]: name,
48+
[FieldConstants.DESCRIPTION]: '',
49+
[FieldConstants.FILTERS]: [],
50+
});
51+
52+
const emptyFormData = (name?: string) => getContingencyListEmptyFormData(name);
53+
54+
export interface FilterBasedContingencyListProps {
55+
titleId: string;
56+
open: boolean;
57+
onClose: () => void;
58+
name?: string;
59+
description?: string;
60+
id?: UUID;
61+
}
62+
63+
export default function FilterBasedContingencyListDialog({
64+
titleId,
65+
open,
66+
onClose,
67+
name,
68+
description,
69+
id,
70+
}: Readonly<FilterBasedContingencyListProps>) {
71+
const activeDirectory = useSelector((state: AppState) => state.activeDirectory);
72+
const { snackError } = useSnackMessage();
73+
const [isFetching, setIsFetching] = useState(!!id);
74+
75+
const methods = useForm<ContingencyListFilterBasedFormData>({
76+
defaultValues: emptyFormData(),
77+
resolver: yupResolver(schema),
78+
});
79+
const {
80+
reset,
81+
formState: { errors },
82+
} = methods;
83+
84+
useEffect(() => {
85+
if (id) {
86+
setIsFetching(true);
87+
getContingencyList(ContingencyListType.FILTERS.id, id?.toString())
88+
.then((response) => {
89+
const formData: ContingencyListFilterBasedFormData = getFilterBasedFormDataFromFetchedElement(
90+
response,
91+
name ?? '',
92+
description ?? ''
93+
);
94+
reset({ ...formData });
95+
})
96+
.catch((error) => {
97+
snackError({
98+
messageTxt: error.message,
99+
headerId: 'cannotRetrieveContingencyList',
100+
});
101+
})
102+
.finally(() => setIsFetching(false));
103+
}
104+
}, [id, name, reset, snackError, description]);
105+
106+
const closeAndClear = useCallback(() => {
107+
reset(emptyFormData());
108+
onClose();
109+
}, [onClose, reset]);
110+
111+
const onSubmit = useCallback(
112+
(data: ContingencyListFilterBasedFormData) => {
113+
const filterBaseContingencyList: FilterBasedContingencyList = {
114+
filters: data[FieldConstants.FILTERS]?.map((item: TreeViewFinderNodeProps) => {
115+
return {
116+
id: item.id,
117+
};
118+
}),
119+
};
120+
121+
if (id) {
122+
saveFilterBasedContingencyList(
123+
id,
124+
data[FieldConstants.NAME],
125+
data[FieldConstants.DESCRIPTION] ?? '',
126+
filterBaseContingencyList
127+
)
128+
.then(() => closeAndClear())
129+
.catch((error) => {
130+
if (handleNotAllowedError(error, snackError)) {
131+
return;
132+
}
133+
snackError({
134+
messageTxt: error.message,
135+
headerId: 'contingencyListEditingError',
136+
headerValues: { name: data[FieldConstants.NAME] },
137+
});
138+
});
139+
} else {
140+
createFilterBasedContingency(
141+
data[FieldConstants.NAME],
142+
data[FieldConstants.DESCRIPTION] ?? '',
143+
filterBaseContingencyList,
144+
activeDirectory
145+
)
146+
.then(() => closeAndClear())
147+
.catch((error) => {
148+
if (handleNotAllowedError(error, snackError)) {
149+
return;
150+
}
151+
snackError({
152+
messageTxt: error.message,
153+
headerId: 'contingencyListCreationError',
154+
headerValues: { name: data[FieldConstants.NAME] },
155+
});
156+
});
157+
}
158+
},
159+
[activeDirectory, closeAndClear, id, snackError]
160+
);
161+
162+
const nameError = errors[FieldConstants.NAME];
163+
const isValidating = errors.root?.isValidating;
164+
165+
return (
166+
<CustomMuiDialog
167+
titleId={titleId}
168+
open={open}
169+
onClose={closeAndClear}
170+
onSave={onSubmit}
171+
formSchema={schema}
172+
formMethods={methods}
173+
unscrollableFullHeight
174+
disabledSave={Boolean(!!nameError || isValidating)}
175+
isDataFetching={isFetching}
176+
>
177+
<ContingencyListFilterBasedForm />
178+
</CustomMuiDialog>
179+
);
180+
}

0 commit comments

Comments
 (0)