refactor(FE): Tighten product form validation and layout

This commit is contained in:
rstubryan
2026-01-07 14:21:37 +07:00
parent 8b7ed9e46b
commit d049f6c34f
2 changed files with 195 additions and 179 deletions
@@ -29,36 +29,38 @@ export const ProductFormSchema: Yup.ObjectSchema<ProductFormSchemaType> =
sku: Yup.string().required('SKU wajib diisi!'), sku: Yup.string().required('SKU wajib diisi!'),
uom: Yup.object({ uom: Yup.object({
value: Yup.number().min(1).required(), value: Yup.number().min(1, 'Satuan wajib dipilih!').required(),
label: Yup.string().required(), label: Yup.string().required(),
}) })
.nullable() .nullable()
.required('Satuan wajib diisi!'), .required('Satuan wajib diisi!'),
uom_id: Yup.number() uom_id: Yup.number()
.min(1, 'Satuan wajib dipilih!')
.required('Satuan wajib diisi!') .required('Satuan wajib diisi!')
.typeError('Satuan wajib diisi!'), .typeError('Satuan wajib diisi!'),
product_category: Yup.object({ product_category: Yup.object({
value: Yup.number().min(1).required(), value: Yup.number().min(1, 'Kategori produk wajib dipilih!').required(),
label: Yup.string().required(), label: Yup.string().required(),
}) })
.nullable() .nullable()
.required('Kategori produk wajib diisi!'), .required('Kategori produk wajib diisi!'),
product_category_id: Yup.number() product_category_id: Yup.number()
.min(1, 'Kategori produk wajib dipilih!')
.required('Kategori produk wajib diisi!') .required('Kategori produk wajib diisi!')
.typeError('Kategori produk wajib diisi!'), .typeError('Kategori produk wajib diisi!'),
product_price: Yup.number() product_price: Yup.number()
.required('Harga produk wajib diisi!') .required('Harga produk wajib diisi!')
.typeError('Harga produk wajib diisi!') .typeError('Harga produk wajib diisi!')
.min(0, 'Harga produk tidak boleh kurang dari 0!'), .min(1, 'Harga produk tidak boleh kurang dari 1!'),
selling_price: Yup.number() selling_price: Yup.number()
.required('Harga jual wajib diisi!') .required('Harga jual wajib diisi!')
.typeError('Harga jual wajib diisi!') .typeError('Harga jual wajib diisi!')
.min(0, 'Harga jual tidak boleh kurang dari 0!'), .min(1, 'Harga jual tidak boleh kurang dari 1!'),
tax: Yup.number() tax: Yup.number()
.required('Pajak wajib diisi!') .required('Pajak wajib diisi!')
@@ -69,7 +71,7 @@ export const ProductFormSchema: Yup.ObjectSchema<ProductFormSchemaType> =
expiry_period: Yup.number() expiry_period: Yup.number()
.required('Periode kadaluarsa wajib diisi!') .required('Periode kadaluarsa wajib diisi!')
.typeError('Periode kadaluarsa wajib diisi!') .typeError('Periode kadaluarsa wajib diisi!')
.min(0, 'Periode kadaluarsa tidak boleh kurang dari 0!'), .min(1, 'Periode kadaluarsa tidak boleh kurang dari 1 hari!'),
supplier_ids: Yup.array() supplier_ids: Yup.array()
.of(Yup.number().required().typeError('Supplier tidak valid!')) .of(Yup.number().required().typeError('Supplier tidak valid!'))
@@ -234,7 +234,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
<span>{productFormErrorMessage}</span> <span>{productFormErrorMessage}</span>
</div> </div>
)} )}
<div className='flex flex-col gap-4'> <div className='grid grid-cols-1 gap-4'>
<TextInput <TextInput
required required
label='Nama' label='Nama'
@@ -247,6 +247,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
errorMessage={formik.errors.name} errorMessage={formik.errors.name}
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
<div className='grid grid-cols-2 gap-4'>
<TextInput <TextInput
required required
label='Merek' label='Merek'
@@ -271,6 +272,8 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
errorMessage={formik.errors.sku} errorMessage={formik.errors.sku}
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
</div>
<div className='grid grid-cols-2 gap-4'>
<SelectInput <SelectInput
required required
label='Satuan' label='Satuan'
@@ -280,7 +283,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
options={uomOptions} options={uomOptions}
onInputChange={setUomSelectInputValue} onInputChange={setUomSelectInputValue}
isLoading={isLoadingUoms} isLoading={isLoadingUoms}
isError={formik.touched.uom_id && Boolean(formik.errors.uom_id)} isError={
(formik.touched.uom || formik.touched.uom_id) &&
Boolean(formik.errors.uom_id)
}
errorMessage={formik.errors.uom_id as string} errorMessage={formik.errors.uom_id as string}
isDisabled={type === 'detail'} isDisabled={type === 'detail'}
isClearable isClearable
@@ -295,13 +301,16 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
onInputChange={setCategorySelectInputValue} onInputChange={setCategorySelectInputValue}
isLoading={isLoadingCategories} isLoading={isLoadingCategories}
isError={ isError={
formik.touched.product_category_id && (formik.touched.product_category ||
formik.touched.product_category_id) &&
Boolean(formik.errors.product_category_id) Boolean(formik.errors.product_category_id)
} }
errorMessage={formik.errors.product_category_id as string} errorMessage={formik.errors.product_category_id as string}
isDisabled={type === 'detail'} isDisabled={type === 'detail'}
isClearable isClearable
/> />
</div>
<div className='grid grid-cols-2 gap-4'>
<NumberInput <NumberInput
required required
label='Harga Produk' label='Harga Produk'
@@ -342,6 +351,8 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
errorMessage={formik.errors.selling_price as string} errorMessage={formik.errors.selling_price as string}
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
</div>
<div className='grid grid-cols-2 gap-4'>
<NumberInput <NumberInput
required required
label='Pajak (%)' label='Pajak (%)'
@@ -379,6 +390,8 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
errorMessage={formik.errors.expiry_period as string} errorMessage={formik.errors.expiry_period as string}
readOnly={type === 'detail'} readOnly={type === 'detail'}
/> />
</div>
<div className='grid grid-cols-2 gap-4'>
<SelectInput <SelectInput
required required
label='Supplier' label='Supplier'
@@ -421,6 +434,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
isClearable isClearable
/> />
</div> </div>
</div>
<div className='flex flex-row justify-between gap-2 flex-wrap'> <div className='flex flex-row justify-between gap-2 flex-wrap'>
{type !== 'add' && ( {type !== 'add' && (
<div className='flex flex-row justify-start gap-2'> <div className='flex flex-row justify-start gap-2'>