@@ -5,15 +5,18 @@ import {
55 Button ,
66 CloseButton ,
77 Flex ,
8+ FormControl ,
9+ FormErrorMessage ,
810 Menu ,
911 MenuButton ,
1012 MenuItem ,
1113 MenuList ,
1214 Stack ,
1315 Text ,
1416} from "@chakra-ui/react" ;
15- import { useFieldArray , useFormContext , useWatch } from "react-hook-form" ;
17+ import { FieldError , useFieldArray , useFormContext , useWatch } from "react-hook-form" ;
1618import { useTranslation } from "react-i18next" ;
19+ import { useEffect } from "react" ;
1720import {
1821 FilterExpression ,
1922 FilterExpressionType ,
@@ -26,6 +29,7 @@ import {
2629} from "../../types" ;
2730import { AnyAllSelector } from "./AnyAllSelector" ;
2831import { Condition } from "./Condition" ;
32+ import { getNestedField } from "../../../../utils/getNestedField" ;
2933
3034interface Props {
3135 onDeleted : ( ) => void ;
@@ -34,15 +38,34 @@ interface Props {
3438}
3539
3640export const LogicalExpressionForm = ( { onDeleted, prefix = "" , containerProps } : Props ) => {
37- const { control, setValue } = useFormContext ( ) ;
41+ const {
42+ control,
43+ setValue,
44+ setError,
45+ clearErrors,
46+ formState : { errors } ,
47+ } = useFormContext ( ) ;
48+ const childrenName = `${ prefix } children` ;
49+ const childrenError = getNestedField < FieldError > ( errors , childrenName ) ;
3850 const { fields, append, remove, insert } = useFieldArray ( {
3951 control,
40- name : ` ${ prefix } children` ,
52+ name : childrenName ,
4153 } ) ;
4254 const operator : LogicalExpressionOperator = useWatch ( {
4355 control,
4456 name : `${ prefix } op` ,
4557 } ) ;
58+ // console.log("🚀 ~ LogicalExpressionForm ~ errors:", errors);
59+ // console.log("🚀 ~ useEffect ~ childrenName:", childrenName);
60+ useEffect ( ( ) => {
61+ if ( fields . length === 0 ) {
62+ setError ( childrenName , {
63+ type : "required" ,
64+ } ) ;
65+ } else if ( childrenError ?. type === "required" ) {
66+ clearErrors ( childrenName ) ;
67+ }
68+ } , [ fields . length , childrenError ?. type ] ) ;
4669
4770 const { t } = useTranslation ( ) ;
4871
@@ -111,15 +134,6 @@ export const LogicalExpressionForm = ({ onDeleted, prefix = "", containerProps }
111134 append ( toInsert ) ;
112135 } ;
113136
114- const numberOfRelational =
115- (
116- fields as Array <
117- FilterExpression & {
118- id : string ;
119- }
120- >
121- ) ?. filter ( ( child ) => child ?. type === FilterExpressionType . Relational ) . length || 0 ;
122-
123137 return (
124138 < Stack { ...containerProps } >
125139 < Box
@@ -144,35 +158,40 @@ export const LogicalExpressionForm = ({ onDeleted, prefix = "", containerProps }
144158 < CloseButton aria-label = "Delete condition group" onClick = { onDeleted } />
145159 </ Flex >
146160 < Stack >
147- < Stack spacing = { 2 } marginTop = { 4 } width = "100%" >
148- { ( fields as Array < FilterExpression & { id : string } > ) ?. map ( ( child , childIndex ) => {
149- if ( child ?. type === FilterExpressionType . Logical ) {
150- return (
151- < LogicalExpressionForm
152- key = { child . id }
153- onDeleted = { ( ) => onChildDeleted ( childIndex ) }
154- prefix = { `${ prefix } children.${ childIndex } .` }
155- />
156- ) ;
157- }
158-
159- if ( child ?. type === FilterExpressionType . Relational ) {
160- return (
161- < Condition
162- key = { child . id }
163- onDelete = { ( ) => onChildDeleted ( childIndex ) }
164- prefix = { `${ prefix } children.${ childIndex } .` }
165- deletable = { numberOfRelational > 1 }
166- />
167- ) ;
168- }
169-
170- return null ;
171- } ) }
172- </ Stack >
161+ { ! ! fields . length && (
162+ < Stack spacing = { 2 } marginTop = { 4 } width = "100%" >
163+ { ( fields as Array < FilterExpression & { id : string } > ) ?. map ( ( child , childIndex ) => {
164+ if ( child ?. type === FilterExpressionType . Logical ) {
165+ return (
166+ < LogicalExpressionForm
167+ key = { child . id }
168+ onDeleted = { ( ) => onChildDeleted ( childIndex ) }
169+ prefix = { `${ prefix } children.${ childIndex } .` }
170+ />
171+ ) ;
172+ }
173+
174+ if ( child ?. type === FilterExpressionType . Relational ) {
175+ return (
176+ < Condition
177+ key = { child . id }
178+ onDelete = { ( ) => onChildDeleted ( childIndex ) }
179+ prefix = { `${ prefix } children.${ childIndex } .` }
180+ deletable
181+ />
182+ ) ;
183+ }
184+
185+ return null ;
186+ } ) }
187+ </ Stack >
188+ ) }
189+ < FormControl isInvalid = { childrenError ?. type === "required" } >
190+ < FormErrorMessage my = { 4 } > At least one condition is required.</ FormErrorMessage >
191+ </ FormControl >
173192 < Box >
174193 < Menu >
175- < MenuButton as = { Button } rightIcon = { < ChevronDownIcon /> } variant = "ghost" >
194+ < MenuButton as = { Button } rightIcon = { < ChevronDownIcon /> } >
176195 { t ( "features.feedConnections.components.filtersForm.addButtonText" ) }
177196 </ MenuButton >
178197 < MenuList >
0 commit comments