From 9c114628c7e749ad2dd108fcf17565f405328f7b Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 22 Oct 2025 10:54:20 +0700 Subject: [PATCH] refactor(FE-114,136): improve form validation handling and set touched state asynchronously --- .../recording/form/RecordingForm.tsx | 102 ++++++++++-------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 6d4f90a7..98fd0f0b 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -512,44 +512,43 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }; // HELPER FUNCTIONS - const isRepeaterInputError = ( + const isRepeaterInputError = < + T extends 'feed_data' | 'body_weight' | 'vaccination' | 'mortality', + >( arrayName: T, - field: T extends 'feed_data' - ? keyof CreateRecordingPayload['feed_data'][0] + column: T extends 'feed_data' + ? keyof RecordingFormValues['feed_data'][0] : T extends 'body_weight' - ? keyof CreateRecordingPayload['body_weight'][0] + ? keyof RecordingFormValues['body_weight'][0] : T extends 'vaccination' - ? keyof CreateRecordingPayload['vaccination'][0] + ? keyof RecordingFormValues['vaccination'][0] : T extends 'mortality' - ? keyof CreateRecordingPayload['mortality'][0] + ? keyof RecordingFormValues['mortality'][0] : never, idx: number ) => { - const touched = formik.touched[arrayName] as - | { - [key: string]: boolean | undefined; - }[] - | undefined; - - const errors = formik.errors[arrayName] as - | { - [key: string]: string | undefined; - }[] - | undefined; - - if (!touched || !Array.isArray(touched)) { + if ( + !formik.touched[arrayName] || + !Array.isArray(formik.touched[arrayName]) + ) { return { isError: false, - errorMessage: undefined, + errorMessage: '', }; } - const touchedField = touched[idx]?.[field as string]; - const errorField = errors?.[idx]?.[field as string]; + const touchedField = formik.touched[arrayName]?.[idx]?.[column as string]; + const errorField = formik.errors[arrayName]?.[idx] as Record< + string, + string + >; return { - isError: touchedField && Boolean(errorField), - errorMessage: touchedField ? errorField : undefined, + isError: touchedField && Boolean(errorField?.[column as string]), + errorMessage: + touchedField && errorField?.[column as string] + ? errorField[column as string] + : '', }; }; @@ -576,9 +575,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { label='Lokasi' value={formik.values.location ?? undefined} onChange={(val) => { - formik.setFieldTouched('location', true); - formik.setFieldTouched('location_id', true); locationChangeHandler(val); + setTimeout(() => { + formik.setFieldTouched('location', true); + formik.setFieldTouched('location_id', true); + }, 0); }} options={locationOptions} onInputChange={setLocationSelectInputValue} @@ -625,9 +626,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { label='Flock' value={formik.values.flock ?? undefined} onChange={(val) => { - formik.setFieldTouched('flock', true); - formik.setFieldTouched('flock_id', true); flockChangeHandler(val); + setTimeout(() => { + formik.setFieldTouched('flock', true); + formik.setFieldTouched('flock_id', true); + }, 0); }} options={flockOptions} onInputChange={setFlockSelectInputValue} @@ -651,9 +654,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { label='Kandang' value={formik.values.coop ?? undefined} onChange={(val) => { - formik.setFieldTouched('coop', true); - formik.setFieldTouched('coop_id', true); coopChangeHandler(val); + setTimeout(() => { + formik.setFieldTouched('coop', true); + formik.setFieldTouched('coop_id', true); + }, 0); }} options={coopOptions} isError={ @@ -756,14 +761,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { ) ?? '') : ''; - formik.setFieldTouched( - `feed_data.${idx}.feed`, - true - ); - formik.setFieldTouched( - `feed_data.${idx}.feed_id`, - true - ); formik.setFieldValue( `feed_data.${idx}.feed`, val @@ -780,6 +777,16 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { `feed_data.${idx}.feed_stock`, 0 ); + setTimeout(() => { + formik.setFieldTouched( + `feed_data.${idx}.feed`, + true + ); + formik.setFieldTouched( + `feed_data.${idx}.feed_id`, + true + ); + }, 0); }} options={pakanOptions} isLoading={isLoadingPakan} @@ -1220,14 +1227,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { productWarehouseId as number ) ?? '') : ''; - formik.setFieldTouched( - `vaccination.${idx}.vaccine`, - true - ); - formik.setFieldTouched( - `vaccination.${idx}.vaccine_id`, - true - ); formik.setFieldValue( `vaccination.${idx}.vaccine`, val @@ -1244,6 +1243,17 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { `vaccination.${idx}.used_stock`, 0 ); + // Set touched after setting values to trigger validation + setTimeout(() => { + formik.setFieldTouched( + `vaccination.${idx}.vaccine`, + true + ); + formik.setFieldTouched( + `vaccination.${idx}.vaccine_id`, + true + ); + }, 0); }} options={ovkOptions} isLoading={isLoadingOvk}