refactor(FE-114): improve validation messages and update layout for better responsiveness

This commit is contained in:
rstubryan
2025-10-20 13:10:41 +07:00
parent c64ff527dd
commit ba9ae07455
2 changed files with 105 additions and 94 deletions
@@ -52,7 +52,7 @@ export const RecordingFormSchema = Yup.object({
feed_qty: Yup.mixed<number | ''>().notRequired(), feed_qty: Yup.mixed<number | ''>().notRequired(),
feed_stock: Yup.number() feed_stock: Yup.number()
.required('Jumlah pakan yang digunakan wajib diisi!') .required('Jumlah pakan yang digunakan wajib diisi!')
.min(1, 'Jumlah pakan minimal 1 kg!') .min(1, 'Jumlah pakan minimal 1!')
.typeError('Jumlah pakan yang digunakan harus berupa angka!') .typeError('Jumlah pakan yang digunakan harus berupa angka!')
.test( .test(
'is-not-exceed-qty', 'is-not-exceed-qty',
@@ -60,7 +60,12 @@ export const RecordingFormSchema = Yup.object({
function (value) { function (value) {
const { feed_qty } = this.parent; const { feed_qty } = this.parent;
if (value === undefined) return true; if (value === undefined) return true;
if (feed_qty === undefined || feed_qty === '' || typeof feed_qty !== 'number') return true; if (
feed_qty === undefined ||
feed_qty === '' ||
typeof feed_qty !== 'number'
)
return true;
return value <= feed_qty; return value <= feed_qty;
} }
), ),
@@ -102,7 +107,12 @@ export const RecordingFormSchema = Yup.object({
function (value) { function (value) {
const { total_stock } = this.parent; const { total_stock } = this.parent;
if (value === undefined) return true; if (value === undefined) return true;
if (total_stock === undefined || total_stock === '' || typeof total_stock !== 'number') return true; if (
total_stock === undefined ||
total_stock === '' ||
typeof total_stock !== 'number'
)
return true;
return value <= total_stock; return value <= total_stock;
} }
), ),
@@ -477,7 +477,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
return ( return (
<> <>
<section className='w-full max-w-5xl'> <section className='w-full'>
<FormHeader type={type} title='Recording' backUrl='/flock/recording' /> <FormHeader type={type} title='Recording' backUrl='/flock/recording' />
<form <form
onSubmit={formik.handleSubmit} onSubmit={formik.handleSubmit}
@@ -488,96 +488,92 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
<div className='card-body flex flex-col gap-6'> <div className='card-body flex flex-col gap-6'>
<h2 className='card-title'>Recording Information</h2> <h2 className='card-title'>Recording Information</h2>
<div className='flex flex-col gap-6'> <div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
<div className='flex gap-4'> <SelectInput
<SelectInput required
required label='Lokasi'
label='Lokasi' value={formik.values.location ?? undefined}
value={formik.values.location ?? undefined} onChange={locationChangeHandler}
onChange={locationChangeHandler} options={locationOptions}
options={locationOptions} onInputChange={setLocationSelectInputValue}
onInputChange={setLocationSelectInputValue} isLoading={isLoadingLocations}
isLoading={isLoadingLocations} isError={
isError={ formik.touched.location_id &&
formik.touched.location_id && Boolean(formik.errors.location_id)
Boolean(formik.errors.location_id) }
} errorMessage={formik.errors.location_id as string}
errorMessage={formik.errors.location_id as string} isDisabled={type === 'detail'}
isDisabled={type === 'detail'} isClearable
isClearable placeholder='Pilih lokasi terlebih dahulu'
placeholder='Pilih lokasi terlebih dahulu' />
/>
<TextInput <TextInput
required required
label='Tanggal Recording' label='Tanggal Recording'
type='date' type='date'
name='recording_date' name='recording_date'
value={ value={
formik.values.recording_date instanceof Date formik.values.recording_date instanceof Date
? formik.values.recording_date ? formik.values.recording_date
.toISOString() .toISOString()
.substring(0, 10) .substring(0, 10)
: '' : ''
} }
onChange={(e) => { onChange={(e) => {
const date = e.target.value const date = e.target.value
? new Date(e.target.value) ? new Date(e.target.value)
: null; : null;
formik.setFieldValue('recording_date', date); formik.setFieldValue('recording_date', date);
}} }}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
isError={ isError={
formik.touched.recording_date && formik.touched.recording_date &&
Boolean(formik.errors.recording_date) Boolean(formik.errors.recording_date)
} }
errorMessage={formik.errors.recording_date as string} errorMessage={formik.errors.recording_date as string}
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
</div>
<div className='flex gap-4'> <SelectInput
<SelectInput required
required label='Flock'
label='Flock' value={formik.values.flock ?? undefined}
value={formik.values.flock ?? undefined} onChange={flockChangeHandler}
onChange={flockChangeHandler} options={flockOptions}
options={flockOptions} onInputChange={setFlockSelectInputValue}
onInputChange={setFlockSelectInputValue} isLoading={isLoadingFlocks}
isLoading={isLoadingFlocks} isError={
isError={ formik.touched.flock_id && Boolean(formik.errors.flock_id)
formik.touched.flock_id && Boolean(formik.errors.flock_id) }
} errorMessage={formik.errors.flock_id as string}
errorMessage={formik.errors.flock_id as string} isDisabled={type === 'detail' || !formik.values.location_id}
isDisabled={type === 'detail' || !formik.values.location_id} isClearable
isClearable placeholder={
placeholder={ !formik.values.location_id
!formik.values.location_id ? 'Pilih lokasi terlebih dahulu'
? 'Pilih lokasi terlebih dahulu' : 'Pilih Flock'
: 'Pilih Flock' }
} />
/>
<SelectInput <SelectInput
key={`coop-select-${formik.values.flock_id || 'no-flock'}`} key={`coop-select-${formik.values.flock_id || 'no-flock'}`}
required required
label='Kandang' label='Kandang'
value={formik.values.coop ?? undefined} value={formik.values.coop ?? undefined}
onChange={coopChangeHandler} onChange={coopChangeHandler}
options={coopOptions} options={coopOptions}
isError={ isError={
formik.touched.coop_id && Boolean(formik.errors.coop_id) formik.touched.coop_id && Boolean(formik.errors.coop_id)
} }
errorMessage={formik.errors.coop_id as string} errorMessage={formik.errors.coop_id as string}
isDisabled={type === 'detail' || !selectedProjectFlock} isDisabled={type === 'detail' || !selectedProjectFlock}
isClearable isClearable
placeholder={ placeholder={
!selectedProjectFlock !selectedProjectFlock
? 'Pilih flock terlebih dahulu' ? 'Pilih flock terlebih dahulu'
: 'Pilih Kandang' : 'Pilih Kandang'
} }
/> />
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -689,7 +685,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
isDisabled={type === 'detail'} isDisabled={type === 'detail'}
isClearable isClearable
className={{ className={{
wrapper: 'w-full min-w-24', wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
}} }}
/> />
</td> </td>
@@ -1074,7 +1071,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
productWarehouseId as number productWarehouseId as number
) ?? '') ) ?? '')
: ''; : '';
formik.setFieldValue( formik.setFieldValue(
`vaccination.${idx}.vaccine`, `vaccination.${idx}.vaccine`,
val val
@@ -1111,7 +1107,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
isDisabled={type === 'detail'} isDisabled={type === 'detail'}
isClearable isClearable
className={{ className={{
wrapper: 'w-full min-w-24', wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
}} }}
/> />
</td> </td>
@@ -1306,6 +1303,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
options={RECORDING_FLAG_OPTIONS} options={RECORDING_FLAG_OPTIONS}
isDisabled={type === 'detail'} isDisabled={type === 'detail'}
isClearable isClearable
className={{
wrapper:
'w-full min-w-52 md:min-w-72 lg:min-w-80',
}}
/> />
</td> </td>
<td> <td>