1+ import { typeboxResolver } from '@hookform/resolvers/typebox' ;
12import { TestRunLimitSettings } from '@openops/shared' ;
3+ import { Type } from '@sinclair/typebox' ;
24import { t } from 'i18next' ;
35import React , { useEffect , useMemo } from 'react' ;
46import { useForm } from 'react-hook-form' ;
@@ -96,6 +98,23 @@ type TestRunLimitsFormProps = {
9698 max ?: number ;
9799} ;
98100
101+ function createTestRunLimitsSchema ( min : number , max : number ) {
102+ return Type . Object ( {
103+ isEnabled : Type . Boolean ( ) ,
104+ limits : Type . Array (
105+ Type . Object ( {
106+ blockName : Type . String ( ) ,
107+ actionName : Type . String ( ) ,
108+ isEnabled : Type . Boolean ( ) ,
109+ limit : Type . Number ( {
110+ minimum : min ,
111+ maximum : max ,
112+ } ) ,
113+ } ) ,
114+ ) ,
115+ } ) ;
116+ }
117+
99118function TestRunLimitsForm ( {
100119 value,
101120 onSave,
@@ -105,14 +124,22 @@ function TestRunLimitsForm({
105124 min = 1 ,
106125 max = 99 ,
107126} : TestRunLimitsFormProps ) {
127+ const schema = useMemo ( ( ) => createTestRunLimitsSchema ( min , max ) , [ min , max ] ) ;
128+
108129 const form = useForm < TestRunLimitSettings > ( {
130+ resolver : typeboxResolver ( schema ) ,
109131 defaultValues : {
110132 isEnabled : value ?. isEnabled ?? false ,
111133 limits : value ?. limits ?? [ ] ,
112134 } ,
113135 mode : 'onChange' ,
114136 } ) ;
115137
138+ useEffect ( ( ) => {
139+ form . trigger ( ) ;
140+ // eslint-disable-next-line react-hooks/exhaustive-deps
141+ } , [ schema ] ) ;
142+
116143 useEffect ( ( ) => {
117144 form . reset ( {
118145 isEnabled : value ?. isEnabled ?? false ,
@@ -293,23 +320,35 @@ function TestRunLimitsForm({
293320 < FormField
294321 control = { form . control }
295322 name = { `limits.${ index } .limit` as const }
296- render = { ( { field } ) => (
297- < FormItem >
298- < FormControl >
299- < NumericInput
300- min = { min }
301- max = { max }
302- value = { field . value }
303- onChange = { ( number ) => {
304- field . onChange ( number ?? min ) ;
305- } }
306- disabled = { ! isEnabled }
307- integerOnly = { true }
308- className = "h-8 w-[64px] outline-none"
309- />
310- </ FormControl >
311- </ FormItem >
312- ) }
323+ render = { ( { field } ) => {
324+ const isInvalid =
325+ ! ! form . formState . errors . limits ?. [ index ]
326+ ?. limit ;
327+
328+ return (
329+ < FormItem >
330+ < FormControl >
331+ < NumericInput
332+ value = { field . value }
333+ onChange = { ( number ) => {
334+ field . onChange ( number ?? min ) ;
335+ } }
336+ disabled = { ! isEnabled }
337+ integerOnly = { true }
338+ min = { min }
339+ max = { max }
340+ className = { cn (
341+ 'h-8 w-[64px] outline-none' ,
342+ {
343+ 'border-red-500 focus-visible:ring-red-500' :
344+ isInvalid ,
345+ } ,
346+ ) }
347+ />
348+ </ FormControl >
349+ </ FormItem >
350+ ) ;
351+ } }
313352 />
314353 </ div >
315354 ) ) }
@@ -326,7 +365,7 @@ function TestRunLimitsForm({
326365 < Button
327366 size = { 'lg' }
328367 loading = { ! ! isLoading }
329- disabled = { ! limits . length }
368+ disabled = { ! limits . length || ! form . formState . isValid }
330369 onClick = { ( ) => onSave ( form . getValues ( ) ) }
331370 >
332371 { t ( 'Save' ) }
0 commit comments