Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b1afd69
Components chosen
basseche Aug 14, 2025
4212a0a
Add new filter based contingency list - IHM Ok
basseche Aug 22, 2025
1cac0c5
Merge branch 'main' into contingencyListByFilter
basseche Aug 22, 2025
4e0d846
Save Contingency list ok
basseche Sep 1, 2025
2ab313d
Merge branch 'main' into contingencyListByFilter
basseche Sep 2, 2025
6d55a16
Edtit contingency list Ok
basseche Sep 4, 2025
6c3af4f
All Ok
basseche Sep 9, 2025
8e2eeb8
remove todo
basseche Sep 9, 2025
9b3f7f8
Improve GUI
basseche Sep 10, 2025
21775b2
exclude filter based contingency list from types created by contingen…
basseche Sep 10, 2025
6de6d80
Merge branch 'main' into contingencyListByFilter
basseche Sep 11, 2025
999734f
add filtering
basseche Sep 12, 2025
ecc5f99
remove console log
basseche Sep 12, 2025
9af1cd9
Merge branch 'main' into contingencyListByFilter
basseche Sep 16, 2025
5bd6b63
change to adapt to server modifications
basseche Sep 16, 2025
5e53494
add not found
basseche Sep 17, 2025
de82257
desactivate sort and filter
basseche Sep 17, 2025
b5af04f
fix
basseche Sep 17, 2025
c54de55
Merge branch 'main' into contingencyListByFilter
basseche Sep 17, 2025
cf5011b
modification to fit back
basseche Sep 19, 2025
bc151d9
Merge branch 'main' into contingencyListByFilter
basseche Sep 22, 2025
a346a79
review
basseche Sep 22, 2025
3ae3628
change function name commun-ui
basseche Sep 23, 2025
47ed523
Update src/utils/rest-api.ts
basseche Sep 24, 2025
5eff019
Update src/utils/rest-api.ts
basseche Sep 24, 2025
a250861
review
basseche Sep 24, 2025
dfedcca
review
basseche Sep 24, 2025
b63f5f7
move type
thangqp Sep 24, 2025
c4339dd
up version commons-ui
basseche Sep 24, 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
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@gridsuite/commons-ui": "0.126.0",
"@gridsuite/commons-ui": "0.127.0",
"@hookform/resolvers": "^4.1.3",
"@mui/icons-material": "^5.18.0",
"@mui/lab": "5.0.0-alpha.175",
Expand Down
10 changes: 10 additions & 0 deletions src/components/dialogs/contingency-list/contingency-list-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import type { SetRequired } from 'type-fest';
import { prepareContingencyListForBackend } from '../contingency-list-helper';
import { ContingencyListType } from '../../../utils/elementType';
import { FilterAttributes } from '../../../utils/contingency-list.type';

export interface Identifier {
type: 'ID_BASED';
Expand Down Expand Up @@ -71,6 +72,15 @@ export const getCriteriaBasedFormDataFromFetchedElement = (response: any, name:
...getCriteriaBasedFormData(response),
});

export const getFilterBasedFormDataFromFetchedElement = (response: any, name: string, description: string) => ({
[FieldConstants.NAME]: name,
[FieldConstants.DESCRIPTION]: description,
[FieldConstants.CONTINGENCY_LIST_TYPE]: ContingencyListType.FILTERS.id,
[FieldConstants.FILTERS]: response.filters.map((filter: FilterAttributes) => {
return { id: filter.id, name: filter.name, specificMetadata: { equipmentType: filter.equipmentType } };
}),
});

export const getExplicitNamingFormDataFromFetchedElement = (response: any, name: string, description: string) => {
let result;
if (response.identifierContingencyList?.identifiers?.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function ContingencyListCreationForm() {
const contingencyListTypeField = (
<RadioInput
name={FieldConstants.CONTINGENCY_LIST_TYPE}
options={Object.values(ContingencyListType)}
options={[ContingencyListType.CRITERIA_BASED, ContingencyListType.EXPLICIT_NAMING]}
formProps={{ onChange: handleChange }} // need to override this in order to do not activate the validate button when changing the filter type
/>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/**
* 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 {
CustomMuiDialog,
FieldConstants,
MAX_CHAR_DESCRIPTION,
TreeViewFinderNodeProps,
useSnackMessage,
yupConfig as yup,
} from '@gridsuite/commons-ui';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { useSelector } from 'react-redux';
import { useCallback, useEffect, useState } from 'react';
import { UUID } from 'crypto';
import { ObjectSchema } from 'yup';
import ContingencyListFilterBasedForm from './contingency-list-filter-based-form';
import { AppState } from '../../../../redux/types';
import {
createFilterBasedContingency,
getContingencyList,
saveFilterBasedContingencyList,
} from '../../../../utils/rest-api';
import { handleNotAllowedError } from '../../../utils/rest-errors';
import { ContingencyListType } from '../../../../utils/elementType';
import { getFilterBasedFormDataFromFetchedElement } from '../contingency-list-utils';
import { FilterBasedContingencyList } from '../../../../utils/contingency-list.type';

const schema: ObjectSchema<ContingencyListFilterBasedFormData> = yup.object().shape({
[FieldConstants.NAME]: yup.string().required(),
[FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION),
[FieldConstants.FILTERS]: yup.array().required(),
});

export interface ContingencyListFilterBasedFormData {
[FieldConstants.NAME]: string;
[FieldConstants.DESCRIPTION]?: string;
[FieldConstants.FILTERS]: TreeViewFinderNodeProps[];
}

const getContingencyListEmptyFormData = (name = '') => ({
[FieldConstants.NAME]: name,
[FieldConstants.DESCRIPTION]: '',
[FieldConstants.FILTERS]: [],
});

const emptyFormData = (name?: string) => getContingencyListEmptyFormData(name);

export interface FilterBasedContingencyListProps {
titleId: string;
open: boolean;
onClose: () => void;
name?: string;
description?: string;
id?: UUID;
}

export default function FilterBasedContingencyListDialog({
titleId,
open,
onClose,
name,
description,
id,
}: Readonly<FilterBasedContingencyListProps>) {
const activeDirectory = useSelector((state: AppState) => state.activeDirectory);
const { snackError } = useSnackMessage();
const [isFetching, setIsFetching] = useState(!!id);

const methods = useForm<ContingencyListFilterBasedFormData>({
defaultValues: emptyFormData(),
resolver: yupResolver(schema),
});
const {
reset,
formState: { errors },
} = methods;

useEffect(() => {
if (id) {
setIsFetching(true);
getContingencyList(ContingencyListType.FILTERS.id, id?.toString())
.then((response) => {
const formData: ContingencyListFilterBasedFormData = getFilterBasedFormDataFromFetchedElement(
response,
name ?? '',
description ?? ''
);
reset({ ...formData });
})
.catch((error) => {
snackError({
messageTxt: error.message,
headerId: 'cannotRetrieveContingencyList',
});
})
.finally(() => setIsFetching(false));
}
}, [id, name, reset, snackError, description]);

const closeAndClear = useCallback(() => {
reset(emptyFormData());
onClose();
}, [onClose, reset]);

const onSubmit = useCallback(
(data: ContingencyListFilterBasedFormData) => {
const filterBaseContingencyList: FilterBasedContingencyList = {
filters: data[FieldConstants.FILTERS]?.map((item: TreeViewFinderNodeProps) => {
return {
id: item.id,
};
}),
};

if (id) {
saveFilterBasedContingencyList(
id,
data[FieldConstants.NAME],
data[FieldConstants.DESCRIPTION] ?? '',
filterBaseContingencyList
)
.then(() => closeAndClear())
.catch((error) => {
if (handleNotAllowedError(error, snackError)) {
return;
}
snackError({
messageTxt: error.message,
headerId: 'contingencyListEditingError',
headerValues: { name: data[FieldConstants.NAME] },
});
});
} else {
createFilterBasedContingency(
data[FieldConstants.NAME],
data[FieldConstants.DESCRIPTION] ?? '',
filterBaseContingencyList,
activeDirectory
)
.then(() => closeAndClear())
.catch((error) => {
if (handleNotAllowedError(error, snackError)) {
return;
}
snackError({
messageTxt: error.message,
headerId: 'contingencyListCreationError',
headerValues: { name: data[FieldConstants.NAME] },
});
});
}
},
[activeDirectory, closeAndClear, id, snackError]
);

const nameError = errors[FieldConstants.NAME];
const isValidating = errors.root?.isValidating;

return (
<CustomMuiDialog
titleId={titleId}
open={open}
onClose={closeAndClear}
onSave={onSubmit}
formSchema={schema}
formMethods={methods}
unscrollableFullHeight
disabledSave={Boolean(!!nameError || isValidating)}
isDataFetching={isFetching}
>
<ContingencyListFilterBasedForm />
</CustomMuiDialog>
);
}
Loading
Loading