refactor(FE-208,212): enhance form validation and streamline change handlers in PurchaseRequestForm

This commit is contained in:
rstubryan
2025-11-20 14:13:57 +07:00
parent 343cc7c4e7
commit 00e18d6d0d
2 changed files with 92 additions and 159 deletions
@@ -12,12 +12,12 @@ type PurchaseRequestFormSchemaType = {
value: number;
label: string;
} | null;
area_id: number;
area_id: number | undefined;
location?: {
value: number;
label: string;
} | null;
location_id: number;
location_id: number | undefined;
notes: string | null;
items: {
warehouse?: {
@@ -91,17 +91,15 @@ export const PurchaseRequestFormSchema: Yup.ObjectSchema<PurchaseRequestFormSche
label: Yup.string().required(),
}).nullable(),
area_id: Yup.number()
.required('Area wajib dipilih!')
.min(1, 'Area wajib dipilih!')
.typeError('Area wajib dipilih!'),
.min(0, 'Area tidak boleh kurang dari 0!')
.typeError('Area harus berupa angka!'),
location: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
location_id: Yup.number()
.required('Lokasi wajib dipilih!')
.min(1, 'Lokasi wajib dipilih!')
.typeError('Lokasi wajib dipilih!'),
.min(0, 'Lokasi tidak boleh kurang dari 0!')
.typeError('Lokasi harus berupa angka!'),
notes: Yup.string().nullable().default(null),
items: Yup.array()
.of(PurchaseItemObjectSchema)
@@ -133,14 +131,14 @@ export const getPurchaseRequestFormInitialValues = (
label: initialValues.area.name,
}
: null,
area_id: initialValues?.area?.id ?? 0,
area_id: initialValues?.area?.id ?? undefined,
location: initialValues?.location
? {
value: initialValues.location.id,
label: initialValues.location.name,
}
: null,
location_id: initialValues?.location?.id ?? 0,
location_id: initialValues?.location?.id ?? undefined,
notes: initialValues?.notes ?? null,
items: [],
});
@@ -330,6 +330,79 @@ const PurchaseRequestForm = ({
setSelectedPurchaseItems([]);
};
// ===== FORM HANDLERS =====
const handleSupplierChange = useCallback(
(val: OptionType | OptionType[] | null) => {
const supplier = val as OptionType | null;
const supplierId = Number(supplier?.value);
formik.setFieldTouched('supplier', true);
formik.setFieldValue('supplier', supplier);
formik.setFieldTouched('supplier_id', true);
formik.setFieldValue('supplier_id', supplierId);
if (supplierId > 0 && isResponseSuccess(supplierRawData)) {
const supplierData = supplierRawData.data.find(
(s: Supplier) => s.id === supplierId
);
if (supplierData?.due_date) {
formik.setFieldTouched('credit_term', true);
formik.setFieldValue('credit_term', supplierData.due_date.toString());
} else {
formik.setFieldTouched('credit_term', false);
formik.setFieldValue('credit_term', '');
}
} else {
formik.setFieldTouched('credit_term', false);
formik.setFieldValue('credit_term', '');
}
if (formik.values.items) {
formik.values.items.forEach((_, idx) => {
formik.setFieldValue(`items.${idx}.product`, null);
formik.setFieldValue(`items.${idx}.product_id`, 0);
formik.setFieldValue(`items.${idx}.qty`, 0);
});
}
},
[, supplierRawData]
);
const handleAreaChange = useCallback(
(val: OptionType | OptionType[] | null) => {
const area = val as OptionType | null;
formik.setFieldTouched('area_id', true);
formik.setFieldValue('area_id', (area as OptionType)?.value || 0);
formik.setFieldTouched('area', true);
formik.setFieldValue('area', area);
},
[]
);
const handleLocationChange = useCallback(
(val: OptionType | OptionType[] | null) => {
const location = val as OptionType | null;
formik.setFieldTouched('location_id', true);
formik.setFieldValue('location_id', (location as OptionType)?.value || 0);
formik.setFieldTouched('location', true);
formik.setFieldValue('location', location);
},
[]
);
const handleWarehouseChange = useCallback(
(idx: number, val: OptionType | OptionType[] | null) => {
const warehouse = val as OptionType | null;
const warehouseId = (warehouse as OptionType)?.value || 0;
formik.setFieldTouched(`items.${idx}.warehouse`, true);
formik.setFieldValue(`items.${idx}.warehouse`, warehouse);
formik.setFieldTouched(`items.${idx}.warehouse_id`, true);
formik.setFieldValue(`items.${idx}.warehouse_id`, warehouseId);
},
[]
);
// ===== PURCHASE ITEM OPERATIONS =====
const handlePurchaseItemChange = (
idx: number,
@@ -381,51 +454,7 @@ const PurchaseRequestForm = ({
label='Vendor'
placeholder='Pilih Vendor...'
value={formik.values.supplier}
onChange={(val) => {
const supplier = val as OptionType | null;
const supplierId = supplier?.value
? typeof supplier.value === 'string'
? parseInt(supplier.value)
: supplier.value
: 0;
formik.setFieldTouched('supplier_id', true);
formik.setFieldValue('supplier_id', supplierId);
formik.setFieldTouched('supplier', true);
formik.setFieldValue('supplier', supplier);
if (supplierId > 0 && isResponseSuccess(supplierRawData)) {
const supplierData = supplierRawData.data.find(
(s: Supplier) => s.id === supplierId
);
if (supplierData?.due_date) {
formik.setFieldTouched('credit_term', true);
formik.setFieldValue(
'credit_term',
supplierData.due_date.toString()
);
}
if (formik.values.items) {
formik.values.items.forEach((_, idx) => {
formik.setFieldValue(`items.${idx}.product`, null);
formik.setFieldValue(`items.${idx}.product_id`, 0);
formik.setFieldValue(`items.${idx}.qty`, 0);
});
}
} else {
formik.setFieldTouched('credit_term', false);
formik.setFieldValue('credit_term', '');
if (formik.values.items) {
formik.values.items.forEach((_, idx) => {
formik.setFieldValue(`items.${idx}.product`, null);
formik.setFieldValue(`items.${idx}.product_id`, 0);
formik.setFieldValue(`items.${idx}.qty`, 0);
});
}
}
}}
onChange={handleSupplierChange}
options={supplierOptions}
onInputChange={setSupplierSelectInputValue}
isLoading={isLoadingSuppliers}
@@ -462,81 +491,27 @@ const PurchaseRequestForm = ({
/>
<SelectInput
required
label='Area'
placeholder='Pilih Area...'
value={formik.values.area}
onChange={(val) => {
const area = val as OptionType | null;
formik.setFieldTouched('area_id', true);
formik.setFieldValue(
'area_id',
(area as OptionType)?.value || 0
);
formik.setFieldTouched('area', true);
formik.setFieldValue('area', area);
formik.setFieldTouched('location', false);
formik.setFieldValue('location', undefined);
formik.setFieldTouched('location_id', false);
formik.setFieldValue('location_id', 0);
setLocationSelectInputValue('');
if (formik.values.items) {
formik.values.items.forEach((_, idx) => {
formik.setFieldValue(`items.${idx}.product`, null);
formik.setFieldValue(`items.${idx}.product_id`, 0);
});
}
}}
onChange={handleAreaChange}
options={areaOptions}
onInputChange={setAreaSelectInputValue}
isLoading={isLoadingAreas}
isError={
formik.touched.area_id && Boolean(formik.errors.area_id)
}
errorMessage={formik.errors.area_id as string}
isDisabled={type === 'detail'}
isClearable
/>
<SelectInput
required
label='Lokasi'
placeholder={
!formik.values.area_id
? 'Pilih Area terlebih dahulu'
: 'Pilih Lokasi...'
}
value={formik.values.area_id ? formik.values.location : null}
onChange={(val) => {
const location = val as OptionType | null;
formik.setFieldTouched('location_id', true);
formik.setFieldValue(
'location_id',
(location as OptionType)?.value || 0
);
formik.setFieldTouched('location', true);
formik.setFieldValue('location', location);
if (formik.values.items) {
formik.values.items.forEach((_, idx) => {
formik.setFieldValue(`items.${idx}.product`, null);
formik.setFieldValue(`items.${idx}.product_id`, 0);
});
}
}}
placeholder='Pilih Lokasi...'
value={formik.values.location}
onChange={handleLocationChange}
options={locationOptions}
onInputChange={setLocationSelectInputValue}
isLoading={isLoadingLocations}
isError={
formik.touched.location_id &&
Boolean(formik.errors.location_id)
}
errorMessage={formik.errors.location_id as string}
isDisabled={type === 'detail' || !formik.values.area_id}
isClearable={type !== 'detail' && !!formik.values.area_id}
key={`location-${formik.values.area_id}`}
isDisabled={type === 'detail'}
isClearable={type !== 'detail'}
/>
<div className={'col-span-2'}>
@@ -643,40 +618,9 @@ const PurchaseRequestForm = ({
)}
<td>
<SelectInput
required
placeholder={
!formik.values.area_id
? 'Pilih Area terlebih dahulu'
: formik.values.location_id
? 'Pilih Gudang...'
: 'Pilih Area dan Lokasi terlebih dahulu'
}
placeholder='Pilih Gudang...'
value={item.warehouse}
onChange={(val) => {
const warehouse = val as OptionType | null;
const warehouseId =
(warehouse as OptionType)?.value || 0;
formik.setFieldTouched(
`items.${idx}.warehouse`,
true
);
formik.setFieldValue(
`items.${idx}.warehouse`,
warehouse
);
formik.setFieldTouched(
`items.${idx}.warehouse_id`,
true
);
formik.setFieldValue(
`items.${idx}.warehouse_id`,
warehouseId
);
formik.setFieldValue(`items.${idx}.product`, null);
formik.setFieldValue(`items.${idx}.product_id`, 0);
}}
onChange={(val) => handleWarehouseChange(idx, val)}
options={warehouseOptions}
onInputChange={setWarehouseSelectInputValue}
isLoading={isLoadingWarehouses}
@@ -687,17 +631,8 @@ const PurchaseRequestForm = ({
isRepeaterInputError(idx, 'warehouse_id')
.errorMessage
}
isDisabled={
type === 'detail' ||
!formik.values.area_id ||
!formik.values.location_id
}
isClearable={
type !== 'detail' &&
!!formik.values.area_id &&
!!formik.values.location_id
}
key={`warehouse-${formik.values.area_id}-${formik.values.location_id}`}
isDisabled={type === 'detail'}
isClearable={type !== 'detail'}
/>
</td>
<td>