diff --git a/src/components/filter/criteriaBased/CriteriaBasedForm.tsx b/src/components/contingencyList/criteriaBased/CriteriaBasedForm.tsx similarity index 97% rename from src/components/filter/criteriaBased/CriteriaBasedForm.tsx rename to src/components/contingencyList/criteriaBased/CriteriaBasedForm.tsx index af341498..78c829cb 100644 --- a/src/components/filter/criteriaBased/CriteriaBasedForm.tsx +++ b/src/components/contingencyList/criteriaBased/CriteriaBasedForm.tsx @@ -11,7 +11,7 @@ import { ReactElement, useEffect } from 'react'; import { FieldConstants } from '../../../utils/constants/fieldConstants'; import { SelectInput } from '../../inputs/reactHookForm/selectInputs/SelectInput'; import { InputWithPopupConfirmation } from '../../inputs/reactHookForm/selectInputs/InputWithPopupConfirmation'; -import { FormEquipment, FormField } from '../utils/filterFormUtils'; +import { FormEquipment, FormField } from '../../filter/utils/filterFormUtils'; import { useSnackMessage } from '../../../hooks/useSnackMessage'; import { unscrollableDialogStyles } from '../../dialogs'; diff --git a/src/components/contingencyList/criteriaBased/criteriaBasedUtils.ts b/src/components/contingencyList/criteriaBased/criteriaBasedUtils.ts new file mode 100644 index 00000000..f4456eb8 --- /dev/null +++ b/src/components/contingencyList/criteriaBased/criteriaBasedUtils.ts @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2021, 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 ObjectSchema } from 'yup'; +import { FieldConstants } from '../../../utils/constants/fieldConstants'; +import yup from '../../../utils/yupConfig'; +import { + DEFAULT_RANGE_VALUE, + getRangeInputSchema, + type RangeInputData, +} from '../../inputs/reactHookForm/numbers/RangeInput'; + +export type CriteriaBasedData = { + [FieldConstants.COUNTRIES]?: string[]; + [FieldConstants.COUNTRIES_1]?: string[]; + [FieldConstants.COUNTRIES_2]?: string[]; + [FieldConstants.NOMINAL_VOLTAGE]?: RangeInputData | null; + [FieldConstants.NOMINAL_VOLTAGE_1]?: RangeInputData | null; + [FieldConstants.NOMINAL_VOLTAGE_2]?: RangeInputData | null; + [FieldConstants.NOMINAL_VOLTAGE_3]?: RangeInputData | null; + [key: string]: any; +}; + +export function getCriteriaBasedSchema(extraFields: Record = {}) { + return { + [FieldConstants.CRITERIA_BASED]: yup.object().shape({ + [FieldConstants.COUNTRIES]: yup.array().of(yup.string().required()), + [FieldConstants.COUNTRIES_1]: yup.array().of(yup.string().required()), + [FieldConstants.COUNTRIES_2]: yup.array().of(yup.string().required()), + ...getRangeInputSchema(FieldConstants.NOMINAL_VOLTAGE), + ...getRangeInputSchema(FieldConstants.NOMINAL_VOLTAGE_1), + ...getRangeInputSchema(FieldConstants.NOMINAL_VOLTAGE_2), + ...getRangeInputSchema(FieldConstants.NOMINAL_VOLTAGE_3), + ...extraFields, + }), + } as const satisfies Record>; +} + +export function getCriteriaBasedFormData( + criteriaValues?: Record, + extraFields: Record = {} +) { + return { + [FieldConstants.CRITERIA_BASED]: { + [FieldConstants.COUNTRIES]: criteriaValues?.[FieldConstants.COUNTRIES] ?? [], + [FieldConstants.COUNTRIES_1]: criteriaValues?.[FieldConstants.COUNTRIES_1] ?? [], + [FieldConstants.COUNTRIES_2]: criteriaValues?.[FieldConstants.COUNTRIES_2] ?? [], + [FieldConstants.NOMINAL_VOLTAGE]: criteriaValues?.[FieldConstants.NOMINAL_VOLTAGE] ?? DEFAULT_RANGE_VALUE, + [FieldConstants.NOMINAL_VOLTAGE_1]: + criteriaValues?.[FieldConstants.NOMINAL_VOLTAGE_1] ?? DEFAULT_RANGE_VALUE, + [FieldConstants.NOMINAL_VOLTAGE_2]: + criteriaValues?.[FieldConstants.NOMINAL_VOLTAGE_2] ?? DEFAULT_RANGE_VALUE, + [FieldConstants.NOMINAL_VOLTAGE_3]: + criteriaValues?.[FieldConstants.NOMINAL_VOLTAGE_3] ?? DEFAULT_RANGE_VALUE, + ...extraFields, + }, + } as const; +} diff --git a/src/components/filter/criteriaBased/index.ts b/src/components/contingencyList/criteriaBased/index.ts similarity index 54% rename from src/components/filter/criteriaBased/index.ts rename to src/components/contingencyList/criteriaBased/index.ts index 4918a578..cb28dff5 100644 --- a/src/components/filter/criteriaBased/index.ts +++ b/src/components/contingencyList/criteriaBased/index.ts @@ -4,10 +4,6 @@ * 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 './CriteriaBasedFilterEditionDialog'; -export * from './CriteriaBasedFilterForm'; + export * from './CriteriaBasedForm'; -export * from './FilterFreeProperties'; -export * from './FilterProperties'; -export * from './FilterProperty'; -export * from './criteriaBasedFilterUtils'; +export * from './criteriaBasedUtils'; diff --git a/src/components/filter/FilterCreationDialog.tsx b/src/components/filter/FilterCreationDialog.tsx index 3030684f..348cf702 100644 --- a/src/components/filter/FilterCreationDialog.tsx +++ b/src/components/filter/FilterCreationDialog.tsx @@ -9,10 +9,9 @@ import { useCallback } from 'react'; import { FieldValues, Resolver, useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { UUID } from 'crypto'; -import { saveCriteriaBasedFilter, saveExpertFilter, saveExplicitNamingFilter } from './utils/filterApi'; +import { saveExpertFilter, saveExplicitNamingFilter } from './utils/filterApi'; import { useSnackMessage } from '../../hooks/useSnackMessage'; import { CustomMuiDialog } from '../dialogs/customMuiDialog/CustomMuiDialog'; -import { criteriaBasedFilterEmptyFormData, criteriaBasedFilterSchema } from './criteriaBased/CriteriaBasedFilterForm'; import { explicitNamingFilterSchema, FILTER_EQUIPMENTS_ATTRIBUTES, @@ -30,7 +29,6 @@ const emptyFormData = { [FieldConstants.DESCRIPTION]: '', [FieldConstants.FILTER_TYPE]: FilterType.EXPERT.id, [FieldConstants.EQUIPMENT_TYPE]: null, - ...criteriaBasedFilterEmptyFormData, ...getExplicitNamingFilterEmptyFormData(), ...getExpertFilterEmptyFormData(), }; @@ -43,7 +41,6 @@ const formSchema = yup [FieldConstants.DESCRIPTION]: yup.string().max(500, 'descriptionLimitError'), [FieldConstants.FILTER_TYPE]: yup.string().required(), [FieldConstants.EQUIPMENT_TYPE]: yup.string().required(), - ...criteriaBasedFilterSchema, ...explicitNamingFilterSchema, ...expertFilterSchema, }) @@ -101,12 +98,6 @@ export function FilterCreationDialog({ onClose, activeDirectory ); - } else if (filterForm[FieldConstants.FILTER_TYPE] === FilterType.CRITERIA_BASED.id) { - saveCriteriaBasedFilter(filterForm, activeDirectory, onClose, (error?: string) => { - snackError({ - messageTxt: error, - }); - }); } else if (filterForm[FieldConstants.FILTER_TYPE] === FilterType.EXPERT.id) { saveExpertFilter( null, diff --git a/src/components/filter/FilterForm.tsx b/src/components/filter/FilterForm.tsx index bbc0218f..5e50c118 100644 --- a/src/components/filter/FilterForm.tsx +++ b/src/components/filter/FilterForm.tsx @@ -10,7 +10,6 @@ import { useFormContext, useWatch } from 'react-hook-form'; import React, { useEffect } from 'react'; import { HeaderFilterForm, FilterFormProps } from './HeaderFilterForm'; import { FieldConstants } from '../../utils/constants/fieldConstants'; -import { CriteriaBasedFilterForm } from './criteriaBased/CriteriaBasedFilterForm'; import { ExplicitNamingFilterForm } from './explicitNaming/ExplicitNamingFilterForm'; import { ExpertFilterForm } from './expert/ExpertFilterForm'; import { FilterType } from './constants/FilterConstants'; @@ -48,7 +47,6 @@ export function FilterForm({ handleFilterTypeChange={handleFilterTypeChange} /> - {filterType === FilterType.CRITERIA_BASED.id && } {filterType === FilterType.EXPLICIT_NAMING.id && ( ; -export interface CriteriaBasedFilterEditionDialogProps { - id: string; - name: string; - titleId: string; - open: boolean; - onClose: () => void; - broadcastChannel: BroadcastChannel; - getFilterById: (id: string) => Promise; - itemSelectionForCopy: ItemSelectionForCopy; - setItemSelectionForCopy: (selection: ItemSelectionForCopy) => void; - activeDirectory?: UUID; - elementExists?: ElementExistsType; - language?: string; -} - -export function CriteriaBasedFilterEditionDialog({ - id, - name, - titleId, - open, - onClose, - broadcastChannel, - getFilterById, - itemSelectionForCopy, - setItemSelectionForCopy, - activeDirectory, - elementExists, - language, -}: Readonly) { - const { snackError } = useSnackMessage(); - const [dataFetchStatus, setDataFetchStatus] = useState(FetchStatus.IDLE); - - // default values are set via reset when we fetch data - const formMethods = useForm({ - resolver: yupResolver(formSchema), - }); - - const { - reset, - formState: { errors }, - } = formMethods; - - const nameError: FieldError | undefined = errors[FieldConstants.NAME]; - const isValidating = errors.root?.isValidating; - - // Fetch the filter data from back-end if necessary and fill the form with it - useEffect(() => { - if (id && open) { - setDataFetchStatus(FetchStatus.FETCHING); - getFilterById(id) - .then((response: any) => { - setDataFetchStatus(FetchStatus.FETCH_SUCCESS); - reset({ - [FieldConstants.NAME]: name, - [FieldConstants.FILTER_TYPE]: FilterType.CRITERIA_BASED.id, - ...backToFrontTweak(response), - }); - }) - .catch((error: { message?: string }) => { - setDataFetchStatus(FetchStatus.FETCH_ERROR); - snackError({ - messageTxt: error.message, - headerId: 'cannotRetrieveFilter', - }); - }); - } - }, [id, name, open, reset, snackError, getFilterById]); - - const onSubmit = useCallback( - (filterForm: FormSchemaType) => { - saveFilter(frontToBackTweak(id, filterForm), filterForm[FieldConstants.NAME]) - .then(() => { - if (itemSelectionForCopy.sourceItemUuid === id) { - setItemSelectionForCopy(NO_ITEM_SELECTION_FOR_COPY); - broadcastChannel.postMessage({ NO_SELECTION_FOR_COPY: NO_ITEM_SELECTION_FOR_COPY }); - } - }) - .catch((error) => { - snackError({ - messageTxt: error.message, - }); - }); - }, - [broadcastChannel, id, itemSelectionForCopy.sourceItemUuid, snackError, setItemSelectionForCopy] - ); - - const isDataReady = dataFetchStatus === FetchStatus.FETCH_SUCCESS; - - return ( - - {isDataReady && } - - ); -} diff --git a/src/components/filter/criteriaBased/CriteriaBasedFilterForm.tsx b/src/components/filter/criteriaBased/CriteriaBasedFilterForm.tsx deleted file mode 100644 index 28a03203..00000000 --- a/src/components/filter/criteriaBased/CriteriaBasedFilterForm.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) 2024, 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 { FilterProperties, filterPropertiesYupSchema } from './FilterProperties'; -import { FieldConstants } from '../../../utils/constants/fieldConstants'; -import yup from '../../../utils/yupConfig'; -import { CriteriaBasedForm } from './CriteriaBasedForm'; -import { getCriteriaBasedFormData, getCriteriaBasedSchema } from './criteriaBasedFilterUtils'; -import { FILTER_EQUIPMENTS } from '../utils/filterFormUtils'; -import { FreePropertiesTypes } from './FilterFreeProperties'; - -export const criteriaBasedFilterSchema = getCriteriaBasedSchema({ - [FieldConstants.ENERGY_SOURCE]: yup.string().nullable(), - ...filterPropertiesYupSchema, -}); - -export const criteriaBasedFilterEmptyFormData = getCriteriaBasedFormData(undefined, { - [FieldConstants.ENERGY_SOURCE]: null, - [FreePropertiesTypes.SUBSTATION_FILTER_PROPERTIES]: [], - [FreePropertiesTypes.FREE_FILTER_PROPERTIES]: [], -}); - -export function CriteriaBasedFilterForm() { - return ( - - - - ); -} diff --git a/src/components/filter/criteriaBased/FilterFreeProperties.tsx b/src/components/filter/criteriaBased/FilterFreeProperties.tsx deleted file mode 100644 index f1062a71..00000000 --- a/src/components/filter/criteriaBased/FilterFreeProperties.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) 2024, 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, ListItem } from '@mui/material'; -import { useFieldArray, useWatch } from 'react-hook-form'; -import { FormattedMessage } from 'react-intl'; -import AddIcon from '@mui/icons-material/Add'; -import { useMemo } from 'react'; -import { ErrorInput } from '../../inputs/reactHookForm/errorManagement/ErrorInput'; -import { FieldErrorAlert } from '../../inputs/reactHookForm/errorManagement/FieldErrorAlert'; -import { FieldConstants } from '../../../utils/constants/fieldConstants'; -import { FilterProperty, PROPERTY_NAME, PROPERTY_VALUES, PROPERTY_VALUES_1, PROPERTY_VALUES_2 } from './FilterProperty'; -import { Hvdc, Line } from '../../../utils/types/equipmentTypes'; - -import { PredefinedProperties } from '../../../utils/types/types'; - -export enum FreePropertiesTypes { - SUBSTATION_FILTER_PROPERTIES = 'substationFreeProperties', - FREE_FILTER_PROPERTIES = 'freeProperties', -} - -interface FilterFreePropertiesProps { - freePropertiesType: FreePropertiesTypes; - predefined: PredefinedProperties; -} - -export function FilterFreeProperties({ freePropertiesType, predefined }: FilterFreePropertiesProps) { - const watchEquipmentType = useWatch({ - name: FieldConstants.EQUIPMENT_TYPE, - }); - const isForLineOrHvdcLineSubstation = - (watchEquipmentType === Line.type || watchEquipmentType === Hvdc.type) && - freePropertiesType === FreePropertiesTypes.SUBSTATION_FILTER_PROPERTIES; - - const fieldName = `${FieldConstants.CRITERIA_BASED}.${freePropertiesType}`; - - const { - fields: filterProperties, // don't use it to access form data ! check doc, - append, - remove, - } = useFieldArray({ - name: fieldName, - }); - - function addNewProp() { - if (isForLineOrHvdcLineSubstation) { - append({ - [PROPERTY_NAME]: null, - [PROPERTY_VALUES_1]: [], - [PROPERTY_VALUES_2]: [], - }); - } else { - append({ [PROPERTY_NAME]: null, [PROPERTY_VALUES]: [] }); - } - } - - const valuesFields = isForLineOrHvdcLineSubstation - ? [ - { name: PROPERTY_VALUES_1, label: 'PropertyValues1' }, - { name: PROPERTY_VALUES_2, label: 'PropertyValues2' }, - ] - : [{ name: PROPERTY_VALUES, label: 'PropertyValues' }]; - - const title = useMemo(() => { - return freePropertiesType === FreePropertiesTypes.FREE_FILTER_PROPERTIES ? 'FreeProps' : 'SubstationFreeProps'; - }, [freePropertiesType]); - - return ( - <> - - {(formattedTitle) =>

{formattedTitle}

}
-
- {filterProperties.map((prop, index) => ( - - - - ))} - - - - - - - - ); -} diff --git a/src/components/filter/criteriaBased/FilterProperties.tsx b/src/components/filter/criteriaBased/FilterProperties.tsx deleted file mode 100644 index fe25cea6..00000000 --- a/src/components/filter/criteriaBased/FilterProperties.tsx +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (c) 2024, 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 from '@mui/material/Grid'; -import { useEffect, useMemo } from 'react'; -import { useWatch } from 'react-hook-form'; -import { FormattedMessage } from 'react-intl'; -import { - Battery, - Generator, - Hvdc, - Line, - Load, - ShuntCompensator, - Substation, - TwoWindingTransfo, - VoltageLevel, -} from '../../../utils/types/equipmentTypes'; -import { areArrayElementsUnique } from '../../../utils/functions'; -import { FieldConstants } from '../../../utils/constants/fieldConstants'; -import yup from '../../../utils/yupConfig'; -import { FilterFreeProperties, FreePropertiesTypes } from './FilterFreeProperties'; -import { PROPERTY_NAME, PROPERTY_VALUES, PROPERTY_VALUES_1, PROPERTY_VALUES_2 } from './FilterProperty'; -import { usePredefinedProperties } from '../../../hooks/usePredefinedProperties'; -import { FilterType } from '../constants/FilterConstants'; -import { EquipmentType } from '../../../utils'; - -function propertyValuesTest( - values: (string | undefined)[] | undefined, - context: yup.TestContext, - doublePropertyValues: boolean -) { - // with context.from[length - 1], we can access to the root fields of the form - const rootLevelForm = context.from![context.from!.length - 1]; - const filterType = rootLevelForm.value[FieldConstants.FILTER_TYPE]; - if (filterType !== FilterType.CRITERIA_BASED.id) { - // we don't test if we are not in a criteria based form - return true; - } - const equipmentType = rootLevelForm.value[FieldConstants.EQUIPMENT_TYPE]; - const isForLineOrHvdcLine = equipmentType === Line.type || equipmentType === Hvdc.type; - if (doublePropertyValues) { - return isForLineOrHvdcLine ? values?.length! > 0 : true; - } - return isForLineOrHvdcLine || values?.length! > 0; -} - -export const filterPropertiesYupSchema = { - [FreePropertiesTypes.SUBSTATION_FILTER_PROPERTIES]: yup - .array() - .of( - yup.object().shape({ - [PROPERTY_NAME]: yup.string().required(), - [PROPERTY_VALUES]: yup - .array() - .of(yup.string()) - .test('can not be empty if not line', 'YupRequired', (values, context) => - propertyValuesTest(values, context, false) - ), - [PROPERTY_VALUES_1]: yup - .array() - .of(yup.string()) - .test('can not be empty if line', 'YupRequired', (values, context) => - propertyValuesTest(values, context, true) - ), - [PROPERTY_VALUES_2]: yup - .array() - .of(yup.string()) - .test('can not be empty if line', 'YupRequired', (values, context) => - propertyValuesTest(values, context, true) - ), - }) - ) - .test('distinct names', 'filterPropertiesNameUniquenessError', (properties, context) => { - // with context.from[length - 1], we can access to the root fields of the form - const rootLevelForm = context.from![context.from!.length - 1]; - const filterType = rootLevelForm.value[FieldConstants.FILTER_TYPE]; - if (filterType !== FilterType.CRITERIA_BASED.id) { - // we don't test if we are not in a criteria based form - return true; - } - const names = properties! // never null / undefined - .filter((prop) => !!prop[PROPERTY_NAME]) - .map((prop) => prop[PROPERTY_NAME]); - return areArrayElementsUnique(names); - }), - [FreePropertiesTypes.FREE_FILTER_PROPERTIES]: yup - .array() - .of( - yup.object().shape({ - [PROPERTY_NAME]: yup.string().required(), - [PROPERTY_VALUES]: yup - .array() - .of(yup.string()) - .test('can not be empty if not line', 'YupRequired', (values, context) => - propertyValuesTest(values, context, false) - ), - }) - ) - .test('distinct names', 'filterPropertiesNameUniquenessError', (properties, context) => { - // with context.from[length - 1], we can access to the root fields of the form - const rootLevelForm = context.from![context.from!.length - 1]; - const filterType = rootLevelForm.value[FieldConstants.FILTER_TYPE]; - if (filterType !== FilterType.CRITERIA_BASED.id) { - // we don't test if we are not in a criteria based form - return true; - } - const names = properties! // never null / undefined - .filter((prop) => !!prop[PROPERTY_NAME]) - .map((prop) => prop[PROPERTY_NAME]); - return areArrayElementsUnique(names); - }), -}; - -export function FilterProperties() { - const watchEquipmentType = useWatch({ - name: FieldConstants.EQUIPMENT_TYPE, - }); - const [equipmentPredefinedProps, setEquipmentType] = usePredefinedProperties(watchEquipmentType); - const [substationPredefinedProps, setSubstationType] = usePredefinedProperties(null); - - const displayEquipmentProperties = useMemo(() => { - return ( - watchEquipmentType === Substation.type || - watchEquipmentType === Load.type || - watchEquipmentType === Generator.type || - watchEquipmentType === Line.type || - watchEquipmentType === TwoWindingTransfo.type || - watchEquipmentType === Battery.type || - watchEquipmentType === ShuntCompensator.type || - watchEquipmentType === VoltageLevel.type - ); - }, [watchEquipmentType]); - - const displaySubstationProperties = useMemo(() => { - return watchEquipmentType !== Substation.type && watchEquipmentType !== null; - }, [watchEquipmentType]); - - useEffect(() => { - if (displayEquipmentProperties) { - setEquipmentType(watchEquipmentType); - } - }, [displayEquipmentProperties, watchEquipmentType, setEquipmentType]); - useEffect(() => { - if (displaySubstationProperties) { - setSubstationType(EquipmentType.SUBSTATION); - } - }, [displaySubstationProperties, setSubstationType]); - - return ( - watchEquipmentType && ( - - - {(txt) =>

{txt}

}
- {displayEquipmentProperties && ( - - )} - {displaySubstationProperties && ( - - )} -
-
- ) - ); -} diff --git a/src/components/filter/criteriaBased/FilterProperty.tsx b/src/components/filter/criteriaBased/FilterProperty.tsx deleted file mode 100644 index 12920cbd..00000000 --- a/src/components/filter/criteriaBased/FilterProperty.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) 2024, 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 { useCallback, useMemo } from 'react'; -import DeleteIcon from '@mui/icons-material/Delete'; -import IconButton from '@mui/material/IconButton'; -import Grid from '@mui/material/Grid'; -import { useFormContext, useWatch } from 'react-hook-form'; -import { AutocompleteInput } from '../../inputs/reactHookForm/autocompleteInputs/AutocompleteInput'; -import { MultipleAutocompleteInput } from '../../inputs/reactHookForm/autocompleteInputs/MultipleAutocompleteInput'; -import { FieldConstants } from '../../../utils/constants/fieldConstants'; - -import { PredefinedProperties } from '../../../utils/types/types'; - -export const PROPERTY_NAME = 'name_property'; -export const PROPERTY_VALUES = 'prop_values'; -export const PROPERTY_VALUES_1 = 'prop_values1'; -export const PROPERTY_VALUES_2 = 'prop_values2'; - -interface FilterPropertyProps { - index: number; - valuesFields: Array<{ name: string; label: string }>; - handleDelete: (index: number) => void; - predefined: PredefinedProperties; - propertyType: string; -} - -export function FilterProperty(props: FilterPropertyProps) { - const { propertyType, index, predefined, valuesFields, handleDelete } = props; - const { setValue } = useFormContext(); - - const watchName = useWatch({ - name: `${FieldConstants.CRITERIA_BASED}.${propertyType}[${index}].${PROPERTY_NAME}`, - }); - - const predefinedNames = useMemo(() => { - return Object.keys(predefined ?? []).sort(); - }, [predefined]); - - const predefinedValues = useMemo(() => { - const predefinedForName: string[] = predefined?.[watchName]; - if (!predefinedForName) { - return []; - } - return [...new Set(predefinedForName)].sort(); - }, [watchName, predefined]); - - // We reset values when name change - const onNameChange = useCallback(() => { - valuesFields.forEach((valuesField) => - setValue(`${FieldConstants.CRITERIA_BASED}.${propertyType}[${index}].${valuesField.name}`, []) - ); - }, [setValue, index, valuesFields, propertyType]); - - return ( - - - - - {valuesFields.map((valuesField) => ( - - - - ))} - - handleDelete(index)}> - - - - - ); -} diff --git a/src/components/filter/criteriaBased/criteriaBasedFilterUtils.ts b/src/components/filter/criteriaBased/criteriaBasedFilterUtils.ts deleted file mode 100644 index bd52cd6a..00000000 --- a/src/components/filter/criteriaBased/criteriaBasedFilterUtils.ts +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright (c) 2021, 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 ObjectSchema } from 'yup'; -import { FieldConstants } from '../../../utils/constants/fieldConstants'; -import { PROPERTY_NAME, PROPERTY_VALUES, PROPERTY_VALUES_1, PROPERTY_VALUES_2 } from './FilterProperty'; -import { FilterType } from '../constants/FilterConstants'; -import { PredefinedProperties } from '../../../utils/types/types'; -import yup from '../../../utils/yupConfig'; -import { - DEFAULT_RANGE_VALUE, - getRangeInputSchema, - type RangeInputData, -} from '../../inputs/reactHookForm/numbers/RangeInput'; -import { FreePropertiesTypes } from './FilterFreeProperties'; - -export type CriteriaBasedData = { - [FieldConstants.COUNTRIES]?: string[]; - [FieldConstants.COUNTRIES_1]?: string[]; - [FieldConstants.COUNTRIES_2]?: string[]; - [FieldConstants.NOMINAL_VOLTAGE]?: RangeInputData | null; - [FieldConstants.NOMINAL_VOLTAGE_1]?: RangeInputData | null; - [FieldConstants.NOMINAL_VOLTAGE_2]?: RangeInputData | null; - [FieldConstants.NOMINAL_VOLTAGE_3]?: RangeInputData | null; - [key: string]: any; -}; - -export function getCriteriaBasedSchema(extraFields: Record = {}) { - return { - [FieldConstants.CRITERIA_BASED]: yup.object().shape({ - [FieldConstants.COUNTRIES]: yup.array().of(yup.string().required()), - [FieldConstants.COUNTRIES_1]: yup.array().of(yup.string().required()), - [FieldConstants.COUNTRIES_2]: yup.array().of(yup.string().required()), - ...getRangeInputSchema(FieldConstants.NOMINAL_VOLTAGE), - ...getRangeInputSchema(FieldConstants.NOMINAL_VOLTAGE_1), - ...getRangeInputSchema(FieldConstants.NOMINAL_VOLTAGE_2), - ...getRangeInputSchema(FieldConstants.NOMINAL_VOLTAGE_3), - ...extraFields, - }), - } as const satisfies Record>; -} - -export function getCriteriaBasedFormData( - criteriaValues?: Record, - extraFields: Record = {} -) { - return { - [FieldConstants.CRITERIA_BASED]: { - [FieldConstants.COUNTRIES]: criteriaValues?.[FieldConstants.COUNTRIES] ?? [], - [FieldConstants.COUNTRIES_1]: criteriaValues?.[FieldConstants.COUNTRIES_1] ?? [], - [FieldConstants.COUNTRIES_2]: criteriaValues?.[FieldConstants.COUNTRIES_2] ?? [], - [FieldConstants.NOMINAL_VOLTAGE]: criteriaValues?.[FieldConstants.NOMINAL_VOLTAGE] ?? DEFAULT_RANGE_VALUE, - [FieldConstants.NOMINAL_VOLTAGE_1]: - criteriaValues?.[FieldConstants.NOMINAL_VOLTAGE_1] ?? DEFAULT_RANGE_VALUE, - [FieldConstants.NOMINAL_VOLTAGE_2]: - criteriaValues?.[FieldConstants.NOMINAL_VOLTAGE_2] ?? DEFAULT_RANGE_VALUE, - [FieldConstants.NOMINAL_VOLTAGE_3]: - criteriaValues?.[FieldConstants.NOMINAL_VOLTAGE_3] ?? DEFAULT_RANGE_VALUE, - ...extraFields, - }, - } as const; -} - -/** - * Transform - * from obj.equipmentFilterForm.{ - * freeProperties.{nameB:valuesB}, - * freeProperties1.{nameA:valuesA}, - * freeProperties2.{nameA:valuesC}} - * to a obj.criteriaBased.freeProperties.[ - * {name_property:nameA, prop_values1:valuesA, prop_values2:valuesC}, - * {name_property:namesB, prop_values:valuesB}] - * @author Laurent LAUGARN modified by Florent MILLOT - */ -export function backToFrontTweak(response: any) { - const subProps = response.equipmentFilterForm.substationFreeProperties; - const freeProps = response.equipmentFilterForm.freeProperties; - const props1 = response.equipmentFilterForm.freeProperties1; - const props2 = response.equipmentFilterForm.freeProperties2; - const allKeys = new Set(); - if (subProps) { - Object.keys(subProps).forEach((k) => allKeys.add(k)); - } - if (props1) { - Object.keys(props1).forEach((k) => allKeys.add(k)); - } - if (props2) { - Object.keys(props2).forEach((k) => allKeys.add(k)); - } - const filterSubstationProperties: PredefinedProperties[] = []; - const filterFreeProperties: PredefinedProperties[] = []; - allKeys.forEach((k: string) => { - const prop: any = { [PROPERTY_NAME]: k }; - const values = subProps?.[k]; - if (values) { - prop[PROPERTY_VALUES] = values; - } - const values1 = props1?.[k]; - if (values1) { - prop[PROPERTY_VALUES_1] = values1; - } - const values2 = props2?.[k]; - if (values2) { - prop[PROPERTY_VALUES_2] = values2; - } - filterSubstationProperties.push(prop); - }); - allKeys.clear(); - if (freeProps) { - Object.keys(freeProps).forEach((k) => allKeys.add(k)); - } - allKeys.forEach((k) => { - const prop: any = { [PROPERTY_NAME]: k }; - const values = freeProps?.[k]; - if (values) { - prop[PROPERTY_VALUES] = values; - } - filterFreeProperties.push(prop); - }); - - return { - [FieldConstants.EQUIPMENT_TYPE]: response[FieldConstants.EQUIPMENT_TYPE], - ...getCriteriaBasedFormData(response.equipmentFilterForm, { - [FieldConstants.ENERGY_SOURCE]: response.equipmentFilterForm[FieldConstants.ENERGY_SOURCE], - [FreePropertiesTypes.SUBSTATION_FILTER_PROPERTIES]: filterSubstationProperties, - [FreePropertiesTypes.FREE_FILTER_PROPERTIES]: filterFreeProperties, - }), - } as const; -} - -function isNominalVoltageEmpty(nominalVoltage: Record) { - return nominalVoltage[FieldConstants.VALUE_1] === null && nominalVoltage[FieldConstants.VALUE_2] === null; -} - -// The server expect them to be null if the user don't fill them, unlike contingency list -function cleanNominalVoltages(formValues: any) { - const cleanedFormValues = { ...formValues }; - if (isNominalVoltageEmpty(cleanedFormValues[FieldConstants.NOMINAL_VOLTAGE])) { - cleanedFormValues[FieldConstants.NOMINAL_VOLTAGE] = null; - } - if (isNominalVoltageEmpty(cleanedFormValues[FieldConstants.NOMINAL_VOLTAGE_1])) { - cleanedFormValues[FieldConstants.NOMINAL_VOLTAGE_1] = null; - } - if (isNominalVoltageEmpty(cleanedFormValues[FieldConstants.NOMINAL_VOLTAGE_2])) { - cleanedFormValues[FieldConstants.NOMINAL_VOLTAGE_2] = null; - } - if (isNominalVoltageEmpty(cleanedFormValues[FieldConstants.NOMINAL_VOLTAGE_3])) { - cleanedFormValues[FieldConstants.NOMINAL_VOLTAGE_3] = null; - } - return cleanedFormValues; -} - -/** - * Transform - * from obj.criteriaBased.freeProperties.[ - * {name_property:nameA, prop_values1:valuesA, prop_values2:valuesC}, - * {name_property:namesB, prop_values:valuesB}] - * to obj.equipmentFilterForm.{ - * freeProperties.{nameB:valuesB}, - * freeProperties1.{nameA:valuesA}, - * freeProperties2.{nameA:valuesC}} - * @author Laurent LAUGARN modified by Florent MILLOT - */ -export function frontToBackTweak(id?: string, filter?: any) { - const filterSubstationProperties = - filter[FieldConstants.CRITERIA_BASED][FreePropertiesTypes.SUBSTATION_FILTER_PROPERTIES]; - const eff = { - [FieldConstants.EQUIPMENT_TYPE]: filter[FieldConstants.EQUIPMENT_TYPE], - ...cleanNominalVoltages(filter[FieldConstants.CRITERIA_BASED]), - }; - delete eff[FreePropertiesTypes.SUBSTATION_FILTER_PROPERTIES]; - const props: any = {}; - const props1: any = {}; - const props2: any = {}; - filterSubstationProperties.forEach((prop: any) => { - const values = prop[PROPERTY_VALUES]; - const values1 = prop[PROPERTY_VALUES_1]; - const values2 = prop[PROPERTY_VALUES_2]; - if (values) { - props[prop[PROPERTY_NAME]] = values; - } - if (values1) { - props1[prop[PROPERTY_NAME]] = values1; - } - if (values2) { - props2[prop[PROPERTY_NAME]] = values2; - } - }); - eff.substationFreeProperties = props; - eff.freeProperties1 = props1; - eff.freeProperties2 = props2; - - const filterFreeProperties = filter[FieldConstants.CRITERIA_BASED][FreePropertiesTypes.FREE_FILTER_PROPERTIES]; - // in the back end we store everything in a field called equipmentFilterForm - delete eff[FreePropertiesTypes.FREE_FILTER_PROPERTIES]; - const freeProps: Record = {}; - filterFreeProperties.forEach((prop: any) => { - const values = prop[PROPERTY_VALUES]; - if (values) { - freeProps[prop[PROPERTY_NAME]] = values; - } - }); - eff.freeProperties = freeProps; - return { - id, - type: FilterType.CRITERIA_BASED.id, - // in the back end we store everything in a field called equipmentFilterForm - equipmentFilterForm: eff, - }; -} diff --git a/src/components/filter/index.ts b/src/components/filter/index.ts index fda67026..c7db167f 100644 --- a/src/components/filter/index.ts +++ b/src/components/filter/index.ts @@ -8,7 +8,7 @@ export * from './filter.type'; export * from './FilterCreationDialog'; export * from './FilterForm'; export * from './constants/FilterConstants'; -export * from './criteriaBased'; +export * from '../contingencyList/criteriaBased'; export * from './expert'; export * from './explicitNaming'; export * from './utils'; diff --git a/src/components/filter/utils/filterApi.ts b/src/components/filter/utils/filterApi.ts index 54411e7f..b49c1416 100644 --- a/src/components/filter/utils/filterApi.ts +++ b/src/components/filter/utils/filterApi.ts @@ -7,7 +7,6 @@ import { UUID } from 'crypto'; import { FieldConstants } from '../../../utils/constants/fieldConstants'; -import { frontToBackTweak } from '../criteriaBased/criteriaBasedFilterUtils'; import { Generator, Load } from '../../../utils/types/equipmentTypes'; import { exportExpertRules } from '../expert/expertFilterUtils'; import { DISTRIBUTION_KEY, FilterType } from '../constants/FilterConstants'; @@ -75,24 +74,6 @@ export const saveExplicitNamingFilter = ( }); } }; - -export const saveCriteriaBasedFilter = ( - filter: any, - activeDirectory: any, - onClose: () => void, - onError: (message: string) => void, - token?: string -) => { - const filterForBack = frontToBackTweak(undefined, filter); // no need ID for creation - createFilter(filterForBack, filter[FieldConstants.NAME], filter[FieldConstants.DESCRIPTION], activeDirectory, token) - .then(() => { - onClose(); - }) - .catch((error) => { - onError(error.message); - }); -}; - export const saveExpertFilter = ( id: string | null, query: any, diff --git a/src/translations/en/filterEn.ts b/src/translations/en/filterEn.ts index 222823d8..25c30ab6 100644 --- a/src/translations/en/filterEn.ts +++ b/src/translations/en/filterEn.ts @@ -26,8 +26,7 @@ export const filterEn = { betweenRule: "Left value of 'between' rule have to be lower than the right value", emptyGroup: 'Filter contains an empty group. Consider removing it or adding rules to this group', Hvdc: 'HVDC', - 'filter.criteriaBased': 'Criteria based', - 'filter.expert': 'Expert', + 'filter.expert': 'Criteria based', 'filter.explicitNaming': 'Explicit naming', nameEmpty: 'The name is empty', equipmentType: 'Equipment type', diff --git a/src/translations/fr/filterFr.ts b/src/translations/fr/filterFr.ts index 008b3578..51509ed1 100644 --- a/src/translations/fr/filterFr.ts +++ b/src/translations/fr/filterFr.ts @@ -26,8 +26,7 @@ export const filterFr = { betweenRule: "La valeur de gauche d'une règle 'entre' doit être inférieure à la valeur de droite", emptyGroup: 'Le filtre contient un groupe vide. Supprimez le ou ajoutez des règles à ce groupe', Hvdc: 'HVDC', - 'filter.criteriaBased': 'Par critères', - 'filter.expert': 'Expert', + 'filter.expert': 'Par critères', 'filter.explicitNaming': 'Par nommage', nameEmpty: 'Le nom est vide', equipmentType: "Type d'ouvrage",