refactor(FE-Storyless): enhance ProductForm schema and handling, add required fields and improve validation

This commit is contained in:
rstubryan
2025-11-02 21:15:41 +07:00
parent fc3b090da5
commit 39dd583e77
2 changed files with 79 additions and 46 deletions
@@ -1,53 +1,83 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
export const ProductFormSchema = Yup.object({ type ProductFormSchemaType = {
name: Yup.string().required('Nama wajib diisi!'), name: string;
brand: Yup.string().required('Merek wajib diisi!'), brand: string;
sku: Yup.string().required('SKU wajib diisi!'), sku: string;
uom: Yup.object({ uom?: {
value: Yup.number().min(1).required(), value: number;
label: Yup.string().required(), label: string;
}).nullable(), } | null;
uom_id: Yup.number().required('Satuan wajib diisi!').typeError('Satuan wajib diisi!'), uom_id: number;
product_category: Yup.object({ product_category?: {
value: Yup.number().min(1).required(), value: number;
label: Yup.string().required(), label: string;
}).nullable(), } | null;
product_category_id: Yup.number() product_category_id: number;
product_price: number | string;
selling_price: number | string;
tax: number | string;
expiry_period: number | string;
supplier_ids: number[];
flags: string[];
};
export const ProductFormSchema: Yup.ObjectSchema<ProductFormSchemaType> =
Yup.object({
name: Yup.string().required('Nama wajib diisi!'),
brand: Yup.string().required('Merek wajib diisi!'),
sku: Yup.string().required('SKU wajib diisi!'),
uom: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable().required('Satuan wajib diisi!'),
uom_id: Yup.number()
.required('Satuan wajib diisi!')
.typeError('Satuan wajib diisi!'),
product_category: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable().required('Kategori produk wajib diisi!'),
product_category_id: Yup.number()
.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(0, 'Harga produk tidak boleh kurang dari 0!'),
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(0, 'Harga jual tidak boleh kurang dari 0!'),
tax: Yup.number()
tax: Yup.number()
.required('Pajak wajib diisi!') .required('Pajak wajib diisi!')
.typeError('Pajak wajib diisi!') .typeError('Pajak wajib diisi!')
.min(0, 'Pajak tidak boleh kurang dari 0!') .min(0, 'Pajak tidak boleh kurang dari 0!')
.max(100, 'Pajak tidak boleh lebih dari 100%!'), .max(100, 'Pajak tidak boleh lebih dari 100%!'),
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(0, 'Periode kadaluarsa tidak boleh kurang dari 0!'),
supplier: Yup.object({
value: Yup.number().min(1).required(), supplier_ids: Yup.array()
label: Yup.string().required(), .of(Yup.number().required().typeError('Supplier tidak valid!'))
}).nullable(),
supplier_ids: Yup.array()
.of(Yup.number().typeError('Supplier tidak valid!'))
.min(1, 'Minimal harus ada 1 supplier!') .min(1, 'Minimal harus ada 1 supplier!')
.required('Supplier wajib diisi!'), .required('Supplier wajib diisi!'),
flags: Yup.array()
.of(Yup.string()) flags: Yup.array()
.of(Yup.string().required())
.min(1, 'Minimal harus ada 1 flag!') .min(1, 'Minimal harus ada 1 flag!')
.required('Flag wajib diisi!'), .required('Flag wajib diisi!'),
}); });
export const UpdateProductFormSchema = ProductFormSchema; export const UpdateProductFormSchema = ProductFormSchema;
export type ProductFormValues = Yup.InferType<typeof ProductFormSchema>; export type ProductFormValues = Yup.InferType<typeof ProductFormSchema>;
@@ -83,20 +83,19 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
sku: initialValues?.sku ?? '', sku: initialValues?.sku ?? '',
uom: initialValues?.uom uom: initialValues?.uom
? { value: initialValues.uom.id, label: initialValues.uom.name } ? { value: initialValues.uom.id, label: initialValues.uom.name }
: null, : undefined,
uom_id: initialValues?.uom?.id ?? 0, uom_id: initialValues?.uom?.id ?? 0,
product_category: initialValues?.product_category product_category: initialValues?.product_category
? { ? {
value: initialValues.product_category.id, value: initialValues.product_category.id,
label: initialValues.product_category.name, label: initialValues.product_category.name,
} }
: null, : undefined,
product_category_id: initialValues?.product_category?.id ?? 0, product_category_id: initialValues?.product_category?.id ?? 0,
product_price: initialValues?.product_price ?? 0, product_price: initialValues?.product_price ?? '',
selling_price: initialValues?.selling_price ?? 0, selling_price: initialValues?.selling_price ?? '',
tax: initialValues?.tax ?? 0, tax: initialValues?.tax ?? '',
expiry_period: initialValues?.expiry_period ?? 0, expiry_period: initialValues?.expiry_period ?? '',
supplier: null, // not used for payload, just for UI
supplier_ids: initialValues?.suppliers?.map((s) => s.id) ?? [], supplier_ids: initialValues?.suppliers?.map((s) => s.id) ?? [],
flags: initialValues?.flags ?? [], flags: initialValues?.flags ?? [],
}), }),
@@ -119,10 +118,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
selling_price: parseInt(values.selling_price.toString()) || 0, selling_price: parseInt(values.selling_price.toString()) || 0,
tax: parseInt(values.tax.toString()) || 0, tax: parseInt(values.tax.toString()) || 0,
expiry_period: parseInt(values.expiry_period.toString()) || 0, expiry_period: parseInt(values.expiry_period.toString()) || 0,
supplier_ids: (values.supplier_ids ?? []).filter( supplier_ids: values.supplier_ids.filter(
(id): id is number => typeof id === 'number' (id): id is number => typeof id === 'number'
), ),
flags: (values.flags ?? []).filter( flags: values.flags.filter(
(f): f is string => typeof f === 'string' (f): f is string => typeof f === 'string'
), ),
}; };
@@ -231,7 +230,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='Nama' label='Nama'
name='name' name='name'
placeholder='Masukkan nama produk' placeholder='Masukkan nama...'
value={formik.values.name} value={formik.values.name}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -243,7 +242,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='Merek' label='Merek'
name='brand' name='brand'
placeholder='Masukkan merek produk' placeholder='Masukkan merek...'
value={formik.values.brand} value={formik.values.brand}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -255,7 +254,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='SKU' label='SKU'
name='sku' name='sku'
placeholder='Masukkan SKU produk' placeholder='Masukkan SKU...'
value={formik.values.sku} value={formik.values.sku}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -266,6 +265,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
<SelectInput <SelectInput
required required
label='Satuan' label='Satuan'
placeholder='Pilih satuan...'
value={formik.values.uom ?? undefined} value={formik.values.uom ?? undefined}
onChange={uomChangeHandler} onChange={uomChangeHandler}
options={uomOptions} options={uomOptions}
@@ -279,6 +279,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
<SelectInput <SelectInput
required required
label='Kategori Produk' label='Kategori Produk'
placeholder='Pilih kategori produk...'
value={formik.values.product_category ?? undefined} value={formik.values.product_category ?? undefined}
onChange={categoryChangeHandler} onChange={categoryChangeHandler}
options={categoryOptions} options={categoryOptions}
@@ -296,7 +297,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='Harga Produk' label='Harga Produk'
name='product_price' name='product_price'
placeholder='Masukkan harga produk' placeholder='Masukkan harga produk...'
value={formik.values.product_price} value={formik.values.product_price}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -316,7 +317,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='Harga Jual' label='Harga Jual'
name='selling_price' name='selling_price'
placeholder='Masukkan harga jual' placeholder='Masukkan harga jual...'
value={formik.values.selling_price} value={formik.values.selling_price}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -336,7 +337,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='Pajak (%)' label='Pajak (%)'
name='tax' name='tax'
placeholder='Masukkan pajak' placeholder='Masukkan pajak...'
value={formik.values.tax} value={formik.values.tax}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -353,7 +354,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
required required
label='Periode Kadaluarsa (hari)' label='Periode Kadaluarsa (hari)'
name='expiry_period' name='expiry_period'
placeholder='Masukkan periode kadaluarsa' placeholder='Masukkan periode kadaluarsa...'
value={formik.values.expiry_period} value={formik.values.expiry_period}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -372,9 +373,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
<SelectInput <SelectInput
required required
label='Supplier' label='Supplier'
placeholder='Pilih supplier...'
isMulti isMulti
value={supplierOptions.filter((opt) => value={supplierOptions.filter((opt) =>
formik.values.supplier_ids.includes(opt.value) (formik.values.supplier_ids || []).includes(opt.value)
)} )}
onChange={supplierChangeHandler} onChange={supplierChangeHandler}
options={supplierOptions} options={supplierOptions}
@@ -391,9 +393,10 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
<SelectInput <SelectInput
required required
label='Flags' label='Flags'
placeholder='Pilih flags...'
isMulti isMulti
value={PRODUCT_FLAG_OPTIONS.filter((opt) => value={PRODUCT_FLAG_OPTIONS.filter((opt) =>
formik.values.flags.includes(opt.value) (formik.values.flags || []).includes(opt.value)
)} )}
onChange={(val) => { onChange={(val) => {
const arr = Array.isArray(val) ? val : val ? [val] : []; const arr = Array.isArray(val) ? val : val ? [val] : [];