mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
refactor(FE): Tighten product form validation and layout
This commit is contained in:
@@ -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,179 +247,193 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
errorMessage={formik.errors.name}
|
errorMessage={formik.errors.name}
|
||||||
readOnly={type === 'detail'}
|
readOnly={type === 'detail'}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<div className='grid grid-cols-2 gap-4'>
|
||||||
required
|
<TextInput
|
||||||
label='Merek'
|
required
|
||||||
name='brand'
|
label='Merek'
|
||||||
placeholder='Masukkan merek...'
|
name='brand'
|
||||||
value={formik.values.brand}
|
placeholder='Masukkan merek...'
|
||||||
onChange={formik.handleChange}
|
value={formik.values.brand}
|
||||||
onBlur={formik.handleBlur}
|
onChange={formik.handleChange}
|
||||||
isError={formik.touched.brand && Boolean(formik.errors.brand)}
|
onBlur={formik.handleBlur}
|
||||||
errorMessage={formik.errors.brand}
|
isError={formik.touched.brand && Boolean(formik.errors.brand)}
|
||||||
readOnly={type === 'detail'}
|
errorMessage={formik.errors.brand}
|
||||||
/>
|
readOnly={type === 'detail'}
|
||||||
<TextInput
|
/>
|
||||||
required
|
<TextInput
|
||||||
label='SKU'
|
required
|
||||||
name='sku'
|
label='SKU'
|
||||||
placeholder='Masukkan SKU...'
|
name='sku'
|
||||||
value={formik.values.sku}
|
placeholder='Masukkan SKU...'
|
||||||
onChange={formik.handleChange}
|
value={formik.values.sku}
|
||||||
onBlur={formik.handleBlur}
|
onChange={formik.handleChange}
|
||||||
isError={formik.touched.sku && Boolean(formik.errors.sku)}
|
onBlur={formik.handleBlur}
|
||||||
errorMessage={formik.errors.sku}
|
isError={formik.touched.sku && Boolean(formik.errors.sku)}
|
||||||
readOnly={type === 'detail'}
|
errorMessage={formik.errors.sku}
|
||||||
/>
|
readOnly={type === 'detail'}
|
||||||
<SelectInput
|
/>
|
||||||
required
|
</div>
|
||||||
label='Satuan'
|
<div className='grid grid-cols-2 gap-4'>
|
||||||
placeholder='Pilih satuan...'
|
<SelectInput
|
||||||
value={formik.values.uom ?? undefined}
|
required
|
||||||
onChange={uomChangeHandler}
|
label='Satuan'
|
||||||
options={uomOptions}
|
placeholder='Pilih satuan...'
|
||||||
onInputChange={setUomSelectInputValue}
|
value={formik.values.uom ?? undefined}
|
||||||
isLoading={isLoadingUoms}
|
onChange={uomChangeHandler}
|
||||||
isError={formik.touched.uom_id && Boolean(formik.errors.uom_id)}
|
options={uomOptions}
|
||||||
errorMessage={formik.errors.uom_id as string}
|
onInputChange={setUomSelectInputValue}
|
||||||
isDisabled={type === 'detail'}
|
isLoading={isLoadingUoms}
|
||||||
isClearable
|
isError={
|
||||||
/>
|
(formik.touched.uom || formik.touched.uom_id) &&
|
||||||
<SelectInput
|
Boolean(formik.errors.uom_id)
|
||||||
required
|
}
|
||||||
label='Kategori Produk'
|
errorMessage={formik.errors.uom_id as string}
|
||||||
placeholder='Pilih kategori produk...'
|
isDisabled={type === 'detail'}
|
||||||
value={formik.values.product_category ?? undefined}
|
isClearable
|
||||||
onChange={categoryChangeHandler}
|
/>
|
||||||
options={categoryOptions}
|
<SelectInput
|
||||||
onInputChange={setCategorySelectInputValue}
|
required
|
||||||
isLoading={isLoadingCategories}
|
label='Kategori Produk'
|
||||||
isError={
|
placeholder='Pilih kategori produk...'
|
||||||
formik.touched.product_category_id &&
|
value={formik.values.product_category ?? undefined}
|
||||||
Boolean(formik.errors.product_category_id)
|
onChange={categoryChangeHandler}
|
||||||
}
|
options={categoryOptions}
|
||||||
errorMessage={formik.errors.product_category_id as string}
|
onInputChange={setCategorySelectInputValue}
|
||||||
isDisabled={type === 'detail'}
|
isLoading={isLoadingCategories}
|
||||||
isClearable
|
isError={
|
||||||
/>
|
(formik.touched.product_category ||
|
||||||
<NumberInput
|
formik.touched.product_category_id) &&
|
||||||
required
|
Boolean(formik.errors.product_category_id)
|
||||||
label='Harga Produk'
|
}
|
||||||
name='product_price'
|
errorMessage={formik.errors.product_category_id as string}
|
||||||
placeholder='Masukkan harga produk...'
|
isDisabled={type === 'detail'}
|
||||||
value={formik.values.product_price}
|
isClearable
|
||||||
onChange={formik.handleChange}
|
/>
|
||||||
onBlur={formik.handleBlur}
|
</div>
|
||||||
decimalScale={2}
|
<div className='grid grid-cols-2 gap-4'>
|
||||||
allowNegative={false}
|
<NumberInput
|
||||||
thousandSeparator=','
|
required
|
||||||
decimalSeparator='.'
|
label='Harga Produk'
|
||||||
inputPrefix='Rp '
|
name='product_price'
|
||||||
isError={
|
placeholder='Masukkan harga produk...'
|
||||||
formik.touched.product_price &&
|
value={formik.values.product_price}
|
||||||
Boolean(formik.errors.product_price)
|
onChange={formik.handleChange}
|
||||||
}
|
onBlur={formik.handleBlur}
|
||||||
errorMessage={formik.errors.product_price as string}
|
decimalScale={2}
|
||||||
readOnly={type === 'detail'}
|
allowNegative={false}
|
||||||
/>
|
thousandSeparator=','
|
||||||
<NumberInput
|
decimalSeparator='.'
|
||||||
required
|
inputPrefix='Rp '
|
||||||
label='Harga Jual'
|
isError={
|
||||||
name='selling_price'
|
formik.touched.product_price &&
|
||||||
placeholder='Masukkan harga jual...'
|
Boolean(formik.errors.product_price)
|
||||||
value={formik.values.selling_price}
|
}
|
||||||
onChange={formik.handleChange}
|
errorMessage={formik.errors.product_price as string}
|
||||||
onBlur={formik.handleBlur}
|
readOnly={type === 'detail'}
|
||||||
decimalScale={2}
|
/>
|
||||||
allowNegative={false}
|
<NumberInput
|
||||||
thousandSeparator=','
|
required
|
||||||
decimalSeparator='.'
|
label='Harga Jual'
|
||||||
inputPrefix='Rp '
|
name='selling_price'
|
||||||
isError={
|
placeholder='Masukkan harga jual...'
|
||||||
formik.touched.selling_price &&
|
value={formik.values.selling_price}
|
||||||
Boolean(formik.errors.selling_price)
|
onChange={formik.handleChange}
|
||||||
}
|
onBlur={formik.handleBlur}
|
||||||
errorMessage={formik.errors.selling_price as string}
|
decimalScale={2}
|
||||||
readOnly={type === 'detail'}
|
allowNegative={false}
|
||||||
/>
|
thousandSeparator=','
|
||||||
<NumberInput
|
decimalSeparator='.'
|
||||||
required
|
inputPrefix='Rp '
|
||||||
label='Pajak (%)'
|
isError={
|
||||||
name='tax'
|
formik.touched.selling_price &&
|
||||||
placeholder='Masukkan pajak...'
|
Boolean(formik.errors.selling_price)
|
||||||
value={formik.values.tax}
|
}
|
||||||
onChange={formik.handleChange}
|
errorMessage={formik.errors.selling_price as string}
|
||||||
onBlur={formik.handleBlur}
|
readOnly={type === 'detail'}
|
||||||
decimalScale={2}
|
/>
|
||||||
allowNegative={false}
|
</div>
|
||||||
thousandSeparator=','
|
<div className='grid grid-cols-2 gap-4'>
|
||||||
decimalSeparator='.'
|
<NumberInput
|
||||||
inputSuffix='%'
|
required
|
||||||
isError={formik.touched.tax && Boolean(formik.errors.tax)}
|
label='Pajak (%)'
|
||||||
errorMessage={formik.errors.tax as string}
|
name='tax'
|
||||||
readOnly={type === 'detail'}
|
placeholder='Masukkan pajak...'
|
||||||
/>
|
value={formik.values.tax}
|
||||||
<NumberInput
|
onChange={formik.handleChange}
|
||||||
required
|
onBlur={formik.handleBlur}
|
||||||
label='Periode Kadaluarsa (hari)'
|
decimalScale={2}
|
||||||
name='expiry_period'
|
allowNegative={false}
|
||||||
placeholder='Masukkan periode kadaluarsa...'
|
thousandSeparator=','
|
||||||
value={formik.values.expiry_period}
|
decimalSeparator='.'
|
||||||
onChange={formik.handleChange}
|
inputSuffix='%'
|
||||||
onBlur={formik.handleBlur}
|
isError={formik.touched.tax && Boolean(formik.errors.tax)}
|
||||||
decimalScale={0}
|
errorMessage={formik.errors.tax as string}
|
||||||
allowNegative={false}
|
readOnly={type === 'detail'}
|
||||||
thousandSeparator=','
|
/>
|
||||||
decimalSeparator='.'
|
<NumberInput
|
||||||
inputSuffix='hari'
|
required
|
||||||
isError={
|
label='Periode Kadaluarsa (hari)'
|
||||||
formik.touched.expiry_period &&
|
name='expiry_period'
|
||||||
Boolean(formik.errors.expiry_period)
|
placeholder='Masukkan periode kadaluarsa...'
|
||||||
}
|
value={formik.values.expiry_period}
|
||||||
errorMessage={formik.errors.expiry_period as string}
|
onChange={formik.handleChange}
|
||||||
readOnly={type === 'detail'}
|
onBlur={formik.handleBlur}
|
||||||
/>
|
decimalScale={0}
|
||||||
<SelectInput
|
allowNegative={false}
|
||||||
required
|
thousandSeparator=','
|
||||||
label='Supplier'
|
decimalSeparator='.'
|
||||||
placeholder='Pilih supplier...'
|
inputSuffix='hari'
|
||||||
isMulti
|
isError={
|
||||||
value={supplierOptions.filter((opt) =>
|
formik.touched.expiry_period &&
|
||||||
(formik.values.supplier_ids || []).includes(opt.value)
|
Boolean(formik.errors.expiry_period)
|
||||||
)}
|
}
|
||||||
onChange={supplierChangeHandler}
|
errorMessage={formik.errors.expiry_period as string}
|
||||||
options={supplierOptions}
|
readOnly={type === 'detail'}
|
||||||
onInputChange={setSupplierSelectInputValue}
|
/>
|
||||||
isLoading={isLoadingSuppliers}
|
</div>
|
||||||
isError={
|
<div className='grid grid-cols-2 gap-4'>
|
||||||
formik.touched.supplier_ids &&
|
<SelectInput
|
||||||
Boolean(formik.errors.supplier_ids)
|
required
|
||||||
}
|
label='Supplier'
|
||||||
errorMessage={formik.errors.supplier_ids as string}
|
placeholder='Pilih supplier...'
|
||||||
isDisabled={type === 'detail'}
|
isMulti
|
||||||
isClearable
|
value={supplierOptions.filter((opt) =>
|
||||||
/>
|
(formik.values.supplier_ids || []).includes(opt.value)
|
||||||
<SelectInput
|
)}
|
||||||
required
|
onChange={supplierChangeHandler}
|
||||||
label='Flags'
|
options={supplierOptions}
|
||||||
placeholder='Pilih flags...'
|
onInputChange={setSupplierSelectInputValue}
|
||||||
isMulti
|
isLoading={isLoadingSuppliers}
|
||||||
value={PRODUCT_FLAG_OPTIONS.filter((opt) =>
|
isError={
|
||||||
(formik.values.flags || []).includes(opt.value)
|
formik.touched.supplier_ids &&
|
||||||
)}
|
Boolean(formik.errors.supplier_ids)
|
||||||
onChange={(val) => {
|
}
|
||||||
const arr = Array.isArray(val) ? val : val ? [val] : [];
|
errorMessage={formik.errors.supplier_ids as string}
|
||||||
formik.setFieldValue(
|
isDisabled={type === 'detail'}
|
||||||
'flags',
|
isClearable
|
||||||
arr.map((v) => (v as OptionType).value)
|
/>
|
||||||
);
|
<SelectInput
|
||||||
}}
|
required
|
||||||
options={PRODUCT_FLAG_OPTIONS}
|
label='Flags'
|
||||||
isError={formik.touched.flags && Boolean(formik.errors.flags)}
|
placeholder='Pilih flags...'
|
||||||
errorMessage={formik.errors.flags as string}
|
isMulti
|
||||||
isDisabled={type === 'detail'}
|
value={PRODUCT_FLAG_OPTIONS.filter((opt) =>
|
||||||
isClearable
|
(formik.values.flags || []).includes(opt.value)
|
||||||
/>
|
)}
|
||||||
|
onChange={(val) => {
|
||||||
|
const arr = Array.isArray(val) ? val : val ? [val] : [];
|
||||||
|
formik.setFieldValue(
|
||||||
|
'flags',
|
||||||
|
arr.map((v) => (v as OptionType).value)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
options={PRODUCT_FLAG_OPTIONS}
|
||||||
|
isError={formik.touched.flags && Boolean(formik.errors.flags)}
|
||||||
|
errorMessage={formik.errors.flags as string}
|
||||||
|
isDisabled={type === 'detail'}
|
||||||
|
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' && (
|
||||||
|
|||||||
Reference in New Issue
Block a user