diff --git a/src/components/dialogs/descriptionModificationDialog/DescriptionModificationDialog.tsx b/src/components/dialogs/descriptionModificationDialog/DescriptionModificationDialog.tsx index 37663851..28f86bfa 100644 --- a/src/components/dialogs/descriptionModificationDialog/DescriptionModificationDialog.tsx +++ b/src/components/dialogs/descriptionModificationDialog/DescriptionModificationDialog.tsx @@ -14,6 +14,7 @@ import { FieldConstants } from '../../../utils/constants/fieldConstants'; import { useSnackMessage } from '../../../hooks/useSnackMessage'; import { CustomMuiDialog } from '../customMuiDialog/CustomMuiDialog'; import { ExpandingTextField } from '../../inputs/reactHookForm/text/ExpandingTextField'; +import { MAX_CHAR_DESCRIPTION } from '../../../utils/constants/uiConstants'; export interface DescriptionModificationDialogProps { elementUuid: string; @@ -24,7 +25,7 @@ export interface DescriptionModificationDialogProps { } const schema = yup.object().shape({ - [FieldConstants.DESCRIPTION]: yup.string().max(500, 'descriptionLimitError'), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION, 'descriptionLimitError'), }); type SchemaType = yup.InferType; diff --git a/src/components/filter/FilterCreationDialog.tsx b/src/components/filter/FilterCreationDialog.tsx index 348cf702..8207860b 100644 --- a/src/components/filter/FilterCreationDialog.tsx +++ b/src/components/filter/FilterCreationDialog.tsx @@ -23,6 +23,7 @@ import { FilterForm } from './FilterForm'; import { EXPERT_FILTER_QUERY, expertFilterSchema, getExpertFilterEmptyFormData } from './expert/ExpertFilterForm'; import { FilterType } from './constants/FilterConstants'; import { ElementExistsType } from '../../utils/types/elementType'; +import { MAX_CHAR_DESCRIPTION } from '../../utils/constants/uiConstants'; const emptyFormData = { [FieldConstants.NAME]: '', @@ -38,7 +39,7 @@ const formSchema = yup .object() .shape({ [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), - [FieldConstants.DESCRIPTION]: yup.string().max(500, 'descriptionLimitError'), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION, 'descriptionLimitError'), [FieldConstants.FILTER_TYPE]: yup.string().required(), [FieldConstants.EQUIPMENT_TYPE]: yup.string().required(), ...explicitNamingFilterSchema, diff --git a/src/components/filter/HeaderFilterForm.tsx b/src/components/filter/HeaderFilterForm.tsx index d6f64410..0867660a 100644 --- a/src/components/filter/HeaderFilterForm.tsx +++ b/src/components/filter/HeaderFilterForm.tsx @@ -13,6 +13,8 @@ import { UniqueNameInput } from '../inputs/reactHookForm/text/UniqueNameInput'; import { ElementExistsType, ElementType } from '../../utils/types/elementType'; import { DescriptionField } from '../inputs/reactHookForm/text/DescriptionField'; import { RadioInput } from '../inputs/reactHookForm/booleans/RadioInput'; +import yup from '../../utils/yupConfig'; +import { MAX_CHAR_DESCRIPTION } from '../../utils/constants/uiConstants'; export interface FilterFormProps { creation?: boolean; @@ -25,6 +27,13 @@ export interface FilterFormProps { handleFilterTypeChange?: (event: React.ChangeEvent, value: string) => void; } +export const HeaderFilterSchema = { + [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), + [FieldConstants.FILTER_TYPE]: yup.string().required(), + [FieldConstants.EQUIPMENT_TYPE]: yup.string().required(), + [FieldConstants.DESCRIPTION]: yup.string().max(MAX_CHAR_DESCRIPTION, 'descriptionLimitError'), +}; + export function HeaderFilterForm({ sourceFilterForExplicitNamingConversion, creation, @@ -46,22 +55,20 @@ export function HeaderFilterForm({ elementExists={elementExists} /> - {creation && ( - <> - - + <> + + + + {creation && !sourceFilterForExplicitNamingConversion && ( + + - {!sourceFilterForExplicitNamingConversion && ( - - - - )} - - )} + )} + ); } diff --git a/src/components/filter/expert/ExpertFilterEditionDialog.tsx b/src/components/filter/expert/ExpertFilterEditionDialog.tsx index de0a0110..42ec88a5 100644 --- a/src/components/filter/expert/ExpertFilterEditionDialog.tsx +++ b/src/components/filter/expert/ExpertFilterEditionDialog.tsx @@ -6,47 +6,29 @@ */ import { yupResolver } from '@hookform/resolvers/yup'; -import { UUID } from 'crypto'; import { useCallback, useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useSnackMessage } from '../../../hooks/useSnackMessage'; import { FetchStatus } from '../../../utils/constants/fetchStatus'; import { FieldConstants } from '../../../utils/constants/fieldConstants'; -import { ElementExistsType } from '../../../utils/types/elementType'; import yup from '../../../utils/yupConfig'; import { CustomMuiDialog } from '../../dialogs/customMuiDialog/CustomMuiDialog'; import { FilterType, NO_ITEM_SELECTION_FOR_COPY } from '../constants/FilterConstants'; -import { ItemSelectionForCopy } from '../filter.type'; +import { FilterEditionProps } from '../filter.type'; import { FilterForm } from '../FilterForm'; import { saveExpertFilter } from '../utils/filterApi'; import { EXPERT_FILTER_QUERY, expertFilterSchema } from './ExpertFilterForm'; import { importExpertRules } from './expertFilterUtils'; +import { HeaderFilterSchema } from '../HeaderFilterForm'; const formSchema = yup .object() .shape({ - [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), - [FieldConstants.FILTER_TYPE]: yup.string().required(), - [FieldConstants.EQUIPMENT_TYPE]: yup.string().required(), + ...HeaderFilterSchema, ...expertFilterSchema, }) .required(); -export interface ExpertFilterEditionDialogProps { - id: string; - name: string; - titleId: string; - open: boolean; - onClose: () => void; - broadcastChannel: BroadcastChannel; - itemSelectionForCopy: ItemSelectionForCopy; - setItemSelectionForCopy: (selection: ItemSelectionForCopy) => void; - getFilterById: (id: string) => Promise<{ [prop: string]: any }>; - activeDirectory?: UUID; - elementExists?: ElementExistsType; - language?: string; -} - export function ExpertFilterEditionDialog({ id, name, @@ -60,7 +42,8 @@ export function ExpertFilterEditionDialog({ activeDirectory, elementExists, language, -}: Readonly) { + description, +}: Readonly) { const { snackError } = useSnackMessage(); const [dataFetchStatus, setDataFetchStatus] = useState(FetchStatus.IDLE); @@ -86,6 +69,7 @@ export function ExpertFilterEditionDialog({ setDataFetchStatus(FetchStatus.FETCH_SUCCESS); reset({ [FieldConstants.NAME]: name, + [FieldConstants.DESCRIPTION]: description, [FieldConstants.FILTER_TYPE]: FilterType.EXPERT.id, [FieldConstants.EQUIPMENT_TYPE]: response[FieldConstants.EQUIPMENT_TYPE], [EXPERT_FILTER_QUERY]: importExpertRules(response[EXPERT_FILTER_QUERY]), @@ -99,7 +83,7 @@ export function ExpertFilterEditionDialog({ }); }); } - }, [id, name, open, reset, snackError, getFilterById]); + }, [id, name, open, reset, snackError, getFilterById, description]); const onSubmit = useCallback( (filterForm: { [prop: string]: any }) => { @@ -108,7 +92,7 @@ export function ExpertFilterEditionDialog({ filterForm[EXPERT_FILTER_QUERY], filterForm[FieldConstants.EQUIPMENT_TYPE], filterForm[FieldConstants.NAME], - '', // The description can not be edited from this dialog + filterForm[FieldConstants.DESCRIPTION] ?? '', false, null, onClose, diff --git a/src/components/filter/explicitNaming/ExplicitNamingFilterEditionDialog.tsx b/src/components/filter/explicitNaming/ExplicitNamingFilterEditionDialog.tsx index 43b539f0..5c0399be 100644 --- a/src/components/filter/explicitNaming/ExplicitNamingFilterEditionDialog.tsx +++ b/src/components/filter/explicitNaming/ExplicitNamingFilterEditionDialog.tsx @@ -6,7 +6,6 @@ */ import { yupResolver } from '@hookform/resolvers/yup'; -import { UUID } from 'crypto'; import PropTypes from 'prop-types'; import { useCallback, useEffect, useState } from 'react'; import { SubmitHandler, useForm, UseFormReturn } from 'react-hook-form'; @@ -19,36 +18,20 @@ import { saveExplicitNamingFilter } from '../utils/filterApi'; import { explicitNamingFilterSchema, FILTER_EQUIPMENTS_ATTRIBUTES } from './ExplicitNamingFilterForm'; import { FetchStatus } from '../../../utils/constants/fetchStatus'; -import { ElementExistsType } from '../../../utils/types/elementType'; import { FilterForm } from '../FilterForm'; import { FilterType, NO_ITEM_SELECTION_FOR_COPY } from '../constants/FilterConstants'; -import { ItemSelectionForCopy } from '../filter.type'; +import { FilterEditionProps } from '../filter.type'; +import { HeaderFilterSchema } from '../HeaderFilterForm'; const formSchema = yup .object() .shape({ - [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), - [FieldConstants.FILTER_TYPE]: yup.string().required(), - [FieldConstants.EQUIPMENT_TYPE]: yup.string().required(), + ...HeaderFilterSchema, ...explicitNamingFilterSchema, }) .required(); type FormSchemaType = yup.InferType; -export interface ExplicitNamingFilterEditionDialogProps { - id: string; - name: string; - titleId: string; - open: boolean; - onClose: () => void; - broadcastChannel: BroadcastChannel; - itemSelectionForCopy: ItemSelectionForCopy; - setItemSelectionForCopy: (selection: ItemSelectionForCopy) => void; - getFilterById: (id: string) => Promise; - activeDirectory?: UUID; - elementExists?: ElementExistsType; - language?: string; -} export function ExplicitNamingFilterEditionDialog({ id, @@ -63,7 +46,8 @@ export function ExplicitNamingFilterEditionDialog({ activeDirectory, elementExists, language, -}: Readonly) { + description, +}: Readonly) { const { snackError } = useSnackMessage(); const [dataFetchStatus, setDataFetchStatus] = useState(FetchStatus.IDLE); @@ -88,6 +72,7 @@ export function ExplicitNamingFilterEditionDialog({ setDataFetchStatus(FetchStatus.FETCH_SUCCESS); reset({ [FieldConstants.NAME]: name, + [FieldConstants.DESCRIPTION]: description, [FieldConstants.FILTER_TYPE]: FilterType.EXPLICIT_NAMING.id, [FieldConstants.EQUIPMENT_TYPE]: response[FieldConstants.EQUIPMENT_TYPE], [FILTER_EQUIPMENTS_ATTRIBUTES]: response[FILTER_EQUIPMENTS_ATTRIBUTES].map((row: any) => ({ @@ -104,7 +89,7 @@ export function ExplicitNamingFilterEditionDialog({ }); }); } - }, [id, name, open, reset, snackError, getFilterById]); + }, [id, name, open, reset, snackError, getFilterById, description]); const onSubmit = useCallback>( (filterForm) => { @@ -113,7 +98,7 @@ export function ExplicitNamingFilterEditionDialog({ false, filterForm[FieldConstants.EQUIPMENT_TYPE], filterForm[FieldConstants.NAME], - '', // The description can not be edited from this dialog + filterForm[FieldConstants.DESCRIPTION] ?? '', id, (error) => { snackError({ diff --git a/src/components/filter/filter.type.ts b/src/components/filter/filter.type.ts index 361dffa7..3ba8e434 100644 --- a/src/components/filter/filter.type.ts +++ b/src/components/filter/filter.type.ts @@ -6,6 +6,7 @@ */ import { UUID } from 'crypto'; +import { ElementExistsType } from '../../utils'; /** * Represent an item/object in directories. @@ -18,3 +19,19 @@ export type ItemSelectionForCopy = { parentDirectoryUuid: UUID | null; specificTypeItem: string | null; }; + +export interface FilterEditionProps { + id: string; + name: string; + titleId: string; + open: boolean; + onClose: () => void; + broadcastChannel: BroadcastChannel; + itemSelectionForCopy: ItemSelectionForCopy; + setItemSelectionForCopy: (selection: ItemSelectionForCopy) => void; + getFilterById: (id: string) => Promise<{ [prop: string]: any }>; + activeDirectory?: UUID; + elementExists?: ElementExistsType; + language?: string; + description?: string; +} diff --git a/src/components/filter/utils/filterApi.ts b/src/components/filter/utils/filterApi.ts index b49c1416..960fb208 100644 --- a/src/components/filter/utils/filterApi.ts +++ b/src/components/filter/utils/filterApi.ts @@ -64,6 +64,7 @@ export const saveExplicitNamingFilter = ( filterEquipmentsAttributes: cleanedTableValues, }, name, + description, token ) .then(() => { @@ -113,6 +114,7 @@ export const saveExpertFilter = ( rules: exportExpertRules(query), }, name, + description, token ) .then(() => { diff --git a/src/components/inputs/reactHookForm/text/DescriptionField.tsx b/src/components/inputs/reactHookForm/text/DescriptionField.tsx index 2ed75cf2..882d5545 100644 --- a/src/components/inputs/reactHookForm/text/DescriptionField.tsx +++ b/src/components/inputs/reactHookForm/text/DescriptionField.tsx @@ -15,8 +15,9 @@ import { FieldConstants } from '../../../../utils/constants/fieldConstants'; import { ExpandingTextField } from './ExpandingTextField'; export function DescriptionField() { - const [isDescriptionFieldVisible, setIsDescriptionFieldVisible] = useState(false); - const { setValue } = useFormContext(); + const { setValue, getValues } = useFormContext(); + const description = getValues(FieldConstants.DESCRIPTION); + const [isDescriptionFieldVisible, setIsDescriptionFieldVisible] = useState(!!description); const handleOpenDescription = () => { setIsDescriptionFieldVisible(true); @@ -24,7 +25,7 @@ export function DescriptionField() { const handleCloseDescription = () => { setIsDescriptionFieldVisible(false); - setValue(FieldConstants.DESCRIPTION, ''); + setValue(FieldConstants.DESCRIPTION, '', { shouldDirty: true }); }; return ( diff --git a/src/services/explore.ts b/src/services/explore.ts index ca25a230..0e0fc8e5 100644 --- a/src/services/explore.ts +++ b/src/services/explore.ts @@ -35,9 +35,10 @@ export function createFilter( ); } -export function saveFilter(filter: any, name: string, token?: string) { +export function saveFilter(filter: any, name: string, description: string, token?: string) { const urlSearchParams = new URLSearchParams(); urlSearchParams.append('name', name); + urlSearchParams.append('description', description); return backendFetch( `${PREFIX_EXPLORE_SERVER_QUERIES}/v1/explore/filters/${filter.id}?${urlSearchParams.toString()}`, { diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts index 49d6858a..cd7e9d00 100644 --- a/src/utils/constants/index.ts +++ b/src/utils/constants/index.ts @@ -7,3 +7,4 @@ export * from './browserConstants'; export * from './fetchStatus'; export * from './fieldConstants'; +export * from './uiConstants'; diff --git a/src/utils/constants/uiConstants.ts b/src/utils/constants/uiConstants.ts new file mode 100644 index 00000000..8d373de8 --- /dev/null +++ b/src/utils/constants/uiConstants.ts @@ -0,0 +1,8 @@ +/* + * Copyright © 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/. + */ + +export const MAX_CHAR_DESCRIPTION = 500;