@@ -30,13 +30,18 @@ export const MetricForm: FC<{ metric: Metric<any>; onClose?: () => void }> = ({
3030 edges : keyBy ( edgeFields , "id" ) ,
3131 } ;
3232 const [ success , setSuccess ] = useState < { date : number ; message : string } | null > ( null ) ;
33+ const [ errors , setErrors ] = useState < { [ fieldId : string ] : string } | null > ( null ) ;
3334 const [ submitCount , setSubmitCount ] = useState ( 0 ) ;
3435 // get metric config from the preference if it exists
3536 const [ session , setSession ] = useAtom ( sessionAtom ) ;
36- const metricConfig = session . metrics [ metric . id ] || {
37- parameters : { } ,
38- attributeNames : { } ,
39- } ;
37+ const metricConfig = useMemo (
38+ ( ) =>
39+ session . metrics [ metric . id ] || {
40+ parameters : { } ,
41+ attributeNames : { } ,
42+ } ,
43+ [ metric . id , session . metrics ] ,
44+ ) ;
4045
4146 // default metric config
4247 const metricDefaultConfig = useMemo (
@@ -81,6 +86,33 @@ export const MetricForm: FC<{ metric: Metric<any>; onClose?: () => void }> = ({
8186 } ) ;
8287 } , [ metric , metricDefaultConfig , setSession ] ) ;
8388
89+ /**
90+ * When the metric params changed
91+ * => we check for form errors
92+ */
93+ useEffect ( ( ) => {
94+ const errors : { [ fieldId : string ] : string } = { } ;
95+ // Checking parameters
96+ metric . parameters . forEach ( ( param ) => {
97+ const name = t ( `${ prefix } .parameters.${ param . id } .title` ) ;
98+ const value = metricConfig . parameters [ param . id ] ;
99+
100+ if ( param . required === true && isNil ( value ) ) errors [ param . id ] = t ( `error.form.required` , { ...param , name } ) ;
101+ else if ( "min" in param && param . min && ! isNil ( value ) && ( value as unknown as number ) < param . min )
102+ errors [ param . id ] = t ( `error.form.min` , { ...param , name } ) ;
103+ else if ( "max" in param && param . max && ! isNil ( value ) && ( value as unknown as number ) < param . max )
104+ errors [ param . id ] = t ( `error.form.max` , { ...param , name } ) ;
105+ } ) ;
106+ // Checking output, they are required
107+ flatMap ( metric . outputs , ( outputs , _itemType : ItemType ) =>
108+ map ( outputs , ( _type , name ) => {
109+ const value = metricConfig . attributeNames [ name ] ;
110+ if ( isNil ( value ) || value === "" ) errors [ name ] = t ( `error.form.required` , { name } ) ;
111+ } ) ,
112+ ) ;
113+ setErrors ( Object . keys ( errors ) . length > 0 ? errors : null ) ;
114+ } , [ metric , metricConfig , prefix , t ] ) ;
115+
84116 /**
85117 * OnChange function for parameters
86118 */
@@ -260,6 +292,21 @@ export const MetricForm: FC<{ metric: Metric<any>; onClose?: () => void }> = ({
260292 { success && (
261293 < MessageAlert key = { success . date } message = { < p className = "gl-m-0" > { success . message } </ p > } type = "success" />
262294 ) }
295+ { errors && (
296+ < MessageAlert
297+ key = { JSON . stringify ( errors ) }
298+ message = {
299+ < ul className = "list-unstyled" >
300+ { Object . keys ( errors ) . map ( ( fieldId ) => (
301+ < li key = { fieldId } className = "gl-my-2" >
302+ { errors [ fieldId ] }
303+ </ li >
304+ ) ) }
305+ </ ul >
306+ }
307+ type = "error"
308+ />
309+ ) }
263310 < div className = "gl-actions" >
264311 < button
265312 type = "reset"
@@ -269,7 +316,7 @@ export const MetricForm: FC<{ metric: Metric<any>; onClose?: () => void }> = ({
269316 >
270317 < ResetIcon />
271318 </ button >
272- < button type = "submit" className = "gl-btn gl-btn-fill" >
319+ < button type = "submit" className = "gl-btn gl-btn-fill" disabled = { errors !== null } >
273320 { t ( "metrics.compute" , { count : Object . keys ( metricConfig . attributeNames ) . length } ) }
274321 </ button >
275322 </ div >
0 commit comments