mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
feat(FE): Show unique form errors and improve product form
This commit is contained in:
@@ -11,6 +11,8 @@ import TextInput from '@/components/input/TextInput';
|
|||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
|
import { getUniqueFormikErrors } from '@/lib/formik-helper';
|
||||||
|
import AlertErrorList from '@/components/helper/form/FormErrors';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProductCategoryFormSchema,
|
ProductCategoryFormSchema,
|
||||||
@@ -39,6 +41,7 @@ const ProductCategoryForm = ({
|
|||||||
const deleteModal = useModal();
|
const deleteModal = useModal();
|
||||||
|
|
||||||
const [formErrorMessage, setFormErrorMessage] = useState('');
|
const [formErrorMessage, setFormErrorMessage] = useState('');
|
||||||
|
const [formErrorList, setFormErrorList] = useState<string[]>([]);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const createProductCategoryHandler = useCallback(
|
const createProductCategoryHandler = useCallback(
|
||||||
@@ -129,6 +132,22 @@ const ProductCategoryForm = ({
|
|||||||
formikSetValues(formikInitialValues);
|
formikSetValues(formikInitialValues);
|
||||||
}, [formikSetValues, formikInitialValues]);
|
}, [formikSetValues, formikInitialValues]);
|
||||||
|
|
||||||
|
const handleValidateForm = async () => {
|
||||||
|
const errors = await formik.validateForm();
|
||||||
|
|
||||||
|
if (Object.keys(errors).length > 0) {
|
||||||
|
const errorMessages = getUniqueFormikErrors(errors);
|
||||||
|
setFormErrorList(errorMessages);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleValidateForm();
|
||||||
|
formik.handleSubmit(e);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className='w-full max-w-2xl'>
|
<section className='w-full max-w-2xl'>
|
||||||
@@ -150,7 +169,7 @@ const ProductCategoryForm = ({
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
onSubmit={formik.handleSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
onReset={formik.handleReset}
|
onReset={formik.handleReset}
|
||||||
className='w-full mt-8 flex flex-col gap-6'
|
className='w-full mt-8 flex flex-col gap-6'
|
||||||
>
|
>
|
||||||
@@ -164,6 +183,15 @@ const ProductCategoryForm = ({
|
|||||||
<span>{formErrorMessage}</span>
|
<span>{formErrorMessage}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Error List Alert */}
|
||||||
|
{formErrorList.length > 0 && (
|
||||||
|
<AlertErrorList
|
||||||
|
formErrorList={formErrorList}
|
||||||
|
onClose={() => setFormErrorList([])}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
<TextInput
|
<TextInput
|
||||||
required
|
required
|
||||||
|
|||||||
@@ -29,28 +29,28 @@ 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, 'Satuan wajib dipilih!').required(),
|
value: Yup.number()
|
||||||
label: Yup.string().required(),
|
.min(1, 'Satuan wajib dipilih!')
|
||||||
})
|
.required('Satuan wajib dipilih!'),
|
||||||
.nullable()
|
label: Yup.string().required('Satuan wajib dipilih!'),
|
||||||
.required('Satuan wajib diisi!'),
|
}).nullable(),
|
||||||
|
|
||||||
uom_id: Yup.number()
|
uom_id: Yup.number()
|
||||||
.min(1, 'Satuan wajib dipilih!')
|
.min(1, 'Satuan wajib dipilih!')
|
||||||
.required('Satuan wajib diisi!')
|
.required('Satuan wajib dipilih!')
|
||||||
.typeError('Satuan wajib diisi!'),
|
.typeError('Satuan wajib dipilih!'),
|
||||||
|
|
||||||
product_category: Yup.object({
|
product_category: Yup.object({
|
||||||
value: Yup.number().min(1, 'Kategori produk wajib dipilih!').required(),
|
value: Yup.number()
|
||||||
label: Yup.string().required(),
|
.min(1, 'Kategori produk wajib dipilih!')
|
||||||
})
|
.required('Kategori produk wajib dipilih!'),
|
||||||
.nullable()
|
label: Yup.string().required('Kategori produk wajib dipilih!'),
|
||||||
.required('Kategori produk wajib diisi!'),
|
}).nullable(),
|
||||||
|
|
||||||
product_category_id: Yup.number()
|
product_category_id: Yup.number()
|
||||||
.min(1, 'Kategori produk wajib dipilih!')
|
.min(1, 'Kategori produk wajib dipilih!')
|
||||||
.required('Kategori produk wajib diisi!')
|
.required('Kategori produk wajib dipilih!')
|
||||||
.typeError('Kategori produk wajib diisi!'),
|
.typeError('Kategori produk wajib dipilih!'),
|
||||||
|
|
||||||
product_price: Yup.number()
|
product_price: Yup.number()
|
||||||
.required('Harga produk wajib diisi!')
|
.required('Harga produk wajib diisi!')
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import SelectInput, {
|
|||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
|
import { getUniqueFormikErrors } from '@/lib/formik-helper';
|
||||||
|
import AlertErrorList from '@/components/helper/form/FormErrors';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProductFormSchema,
|
ProductFormSchema,
|
||||||
@@ -48,6 +50,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
const deleteModal = useModal();
|
const deleteModal = useModal();
|
||||||
|
|
||||||
const [productFormErrorMessage, setProductFormErrorMessage] = useState('');
|
const [productFormErrorMessage, setProductFormErrorMessage] = useState('');
|
||||||
|
const [formErrorList, setFormErrorList] = useState<string[]>([]);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const createProductHandler = useCallback(
|
const createProductHandler = useCallback(
|
||||||
@@ -201,6 +204,22 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
formikSetValues(formikInitialValues);
|
formikSetValues(formikInitialValues);
|
||||||
}, [formikSetValues, formikInitialValues]);
|
}, [formikSetValues, formikInitialValues]);
|
||||||
|
|
||||||
|
const handleValidateForm = async () => {
|
||||||
|
const errors = await formik.validateForm();
|
||||||
|
|
||||||
|
if (Object.keys(errors).length > 0) {
|
||||||
|
const errorMessages = getUniqueFormikErrors(errors);
|
||||||
|
setFormErrorList(errorMessages);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleValidateForm();
|
||||||
|
formik.handleSubmit(e);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className='w-full max-w-2xl'>
|
<section className='w-full max-w-2xl'>
|
||||||
@@ -220,7 +239,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
</h1>
|
</h1>
|
||||||
</header>
|
</header>
|
||||||
<form
|
<form
|
||||||
onSubmit={formik.handleSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
onReset={formik.handleReset}
|
onReset={formik.handleReset}
|
||||||
className='w-full mt-8 flex flex-col gap-6'
|
className='w-full mt-8 flex flex-col gap-6'
|
||||||
>
|
>
|
||||||
@@ -234,6 +253,15 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
<span>{productFormErrorMessage}</span>
|
<span>{productFormErrorMessage}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Error List Alert */}
|
||||||
|
{formErrorList.length > 0 && (
|
||||||
|
<AlertErrorList
|
||||||
|
formErrorList={formErrorList}
|
||||||
|
onClose={() => setFormErrorList([])}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className='grid grid-cols-1 gap-4'>
|
<div className='grid grid-cols-1 gap-4'>
|
||||||
<TextInput
|
<TextInput
|
||||||
required
|
required
|
||||||
@@ -247,7 +275,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'>
|
<div className='grid sm:grid-cols-2 gap-4'>
|
||||||
<TextInput
|
<TextInput
|
||||||
required
|
required
|
||||||
label='Merek'
|
label='Merek'
|
||||||
@@ -273,7 +301,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
readOnly={type === 'detail'}
|
readOnly={type === 'detail'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-2 gap-4'>
|
<div className='grid sm:grid-cols-2 gap-4'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
label='Satuan'
|
label='Satuan'
|
||||||
@@ -310,7 +338,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-2 gap-4'>
|
<div className='grid sm:grid-cols-2 gap-4'>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
required
|
required
|
||||||
label='Harga Produk'
|
label='Harga Produk'
|
||||||
@@ -352,7 +380,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
readOnly={type === 'detail'}
|
readOnly={type === 'detail'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-2 gap-4'>
|
<div className='grid sm:grid-cols-2 gap-4'>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
required
|
required
|
||||||
label='Pajak (%)'
|
label='Pajak (%)'
|
||||||
@@ -391,7 +419,7 @@ const ProductForm = ({ type = 'add', initialValues }: ProductFormProps) => {
|
|||||||
readOnly={type === 'detail'}
|
readOnly={type === 'detail'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-2 gap-4'>
|
<div className='grid sm:grid-cols-2 gap-4'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
label='Supplier'
|
label='Supplier'
|
||||||
|
|||||||
Reference in New Issue
Block a user