77
88import { Box , Chip , FormControl , FormHelperText , Grid , IconButton , Tooltip } from '@mui/material' ;
99import { Folder as FolderIcon } from '@mui/icons-material' ;
10- import { useCallback , useMemo , useState } from 'react' ;
11- import { FieldValues , useController , useFieldArray } from 'react-hook-form' ;
10+ import { useCallback , useEffect , useMemo , useState } from 'react' ;
11+ import { FieldValues , useController , useFieldArray , useWatch } from 'react-hook-form' ;
1212import { FormattedMessage , useIntl } from 'react-intl' ;
1313import type { UUID } from 'node:crypto' ;
1414import { RawReadOnlyInput } from './RawReadOnlyInput' ;
@@ -100,7 +100,8 @@ export function DirectoryItemsInput({
100100 } ) ;
101101
102102 const formContext = useCustomFormContext ( ) ;
103- const { getValues, validationSchema } = formContext ;
103+ const { getValues, validationSchema, setError, clearErrors, getFieldState } = formContext ;
104+ const watchedElements = useWatch ( { name } ) as FieldValues [ ] | undefined ;
104105
105106 const {
106107 fieldState : { error } ,
@@ -172,6 +173,28 @@ export function DirectoryItemsInput({
172173 return allowMultiSelect === false && elements ?. length === 1 ;
173174 } , [ allowMultiSelect , elements ] ) ;
174175
176+ const hasElementsWithoutName = useMemo ( ( ) => {
177+ const elementsToCheck = ( watchedElements ?? elements ) as FieldValues [ ] | undefined ;
178+
179+ return ( elementsToCheck ?? [ ] ) . some ( ( item ) => ! item ?. [ NAME ] ) ;
180+ } , [ elements , watchedElements ] ) ;
181+
182+ useEffect ( ( ) => {
183+ const errorMessage = intl . formatMessage ( { id : 'elementNotFound' } ) ;
184+ const fieldState = getFieldState ( name ) ;
185+
186+ if ( hasElementsWithoutName ) {
187+ if ( fieldState . error ?. message !== errorMessage ) {
188+ setError ( name as any , {
189+ type : 'manual' ,
190+ message : errorMessage ,
191+ } ) ;
192+ }
193+ } else if ( fieldState . error ?. type === 'manual' && fieldState . error ?. message === errorMessage ) {
194+ clearErrors ( name as any ) ;
195+ }
196+ } , [ clearErrors , getFieldState , hasElementsWithoutName , intl , name , setError ] ) ;
197+
175198 return (
176199 < >
177200 < FormControl
@@ -190,46 +213,64 @@ export function DirectoryItemsInput({
190213 ) }
191214 { elements ?. length > 0 && (
192215 < FormControl sx = { styles . formDirectoryElements2 } >
193- { elements . map ( ( item , index ) => (
194- < Box
195- key = { item . id }
196- sx = { { display : 'flex' , alignItems : 'center' , flexDirection : 'column' , gap : 1 } }
197- >
198- < Chip
199- size = "small"
200- sx = { {
201- backgroundColor :
202- item ?. specificMetadata ?. equipmentType &&
203- equipmentColorsMap ?. get ( item ?. specificMetadata ?. equipmentType ) ,
204- } }
205- onDelete = { ( ) => removeElements ( index ) }
206- onClick = { ( ) => handleChipClick ( index ) }
207- label = {
208- < OverflowableText
209- text = {
210- getValues ( `${ name } .${ index } .${ NAME } ` ) ? (
211- < RawReadOnlyInput name = { `${ name } .${ index } .${ NAME } ` } />
212- ) : (
213- intl . formatMessage ( { id : 'elementNotFound' } )
214- )
215- }
216- sx = { { width : '100%' } }
217- />
218- }
219- />
220- { equipmentColorsMap && (
221- < FormHelperText >
222- { item ?. specificMetadata ?. equipmentType ? (
223- < FormattedMessage
224- id = { getFilterEquipmentTypeLabel ( item . specificMetadata . equipmentType ) }
225- />
226- ) : (
227- ''
216+ { elements . map ( ( item , index ) => {
217+ const elementName =
218+ watchedElements ?. [ index ] ?. [ NAME ] ??
219+ getValues ( `${ name } .${ index } .${ NAME } ` ) ??
220+ ( item as FieldValues ) ?. [ NAME ] ;
221+
222+ return (
223+ < Box
224+ key = { item . id }
225+ sx = { { display : 'flex' , alignItems : 'center' , flexDirection : 'column' , gap : 1 } }
226+ >
227+ < Chip
228+ size = "small"
229+ sx = { mergeSx (
230+ {
231+ backgroundColor :
232+ item ?. specificMetadata ?. equipmentType &&
233+ equipmentColorsMap ?. get ( item ?. specificMetadata ?. equipmentType ) ,
234+ } ,
235+ ! elementName
236+ ? ( theme ) => ( {
237+ backgroundColor : theme . palette . error . light ,
238+ borderColor : theme . palette . error . main ,
239+ color : theme . palette . error . contrastText ,
240+ } )
241+ : undefined
228242 ) }
229- </ FormHelperText >
230- ) }
231- </ Box >
232- ) ) }
243+ onDelete = { ( ) => removeElements ( index ) }
244+ onClick = { ( ) => handleChipClick ( index ) }
245+ label = {
246+ < OverflowableText
247+ text = {
248+ elementName ? (
249+ < RawReadOnlyInput name = { `${ name } .${ index } .${ NAME } ` } />
250+ ) : (
251+ intl . formatMessage ( { id : 'elementNotFound' } )
252+ )
253+ }
254+ sx = { { width : '100%' } }
255+ />
256+ }
257+ />
258+ { equipmentColorsMap && (
259+ < FormHelperText >
260+ { item ?. specificMetadata ?. equipmentType ? (
261+ < FormattedMessage
262+ id = { getFilterEquipmentTypeLabel (
263+ item . specificMetadata . equipmentType
264+ ) }
265+ />
266+ ) : (
267+ ''
268+ ) }
269+ </ FormHelperText >
270+ ) }
271+ </ Box >
272+ ) ;
273+ } ) }
233274 </ FormControl >
234275 ) }
235276 < Grid item xs >
0 commit comments