|
| 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