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; value: number;
label: string; label: string;
} | null; } | null;
area_id: number; area_id: number | undefined;
location?: { location?: {
value: number; value: number;
label: string; label: string;
} | null; } | null;
location_id: number; location_id: number | undefined;
notes: string | null; notes: string | null;
items: { items: {
warehouse?: { warehouse?: {
@@ -91,17 +91,15 @@ export const PurchaseRequestFormSchema: Yup.ObjectSchema<PurchaseRequestFormSche
label: Yup.string().required(), label: Yup.string().required(),
}).nullable(), }).nullable(),
area_id: Yup.number() area_id: Yup.number()
.required('Area wajib dipilih!') .min(0, 'Area tidak boleh kurang dari 0!')
.min(1, 'Area wajib dipilih!') .typeError('Area harus berupa angka!'),
.typeError('Area wajib dipilih!'),
location: Yup.object({ location: Yup.object({
value: Yup.number().min(1).required(), value: Yup.number().min(1).required(),
label: Yup.string().required(), label: Yup.string().required(),
}).nullable(), }).nullable(),
location_id: Yup.number() location_id: Yup.number()
.required('Lokasi wajib dipilih!') .min(0, 'Lokasi tidak boleh kurang dari 0!')
.min(1, 'Lokasi wajib dipilih!') .typeError('Lokasi harus berupa angka!'),
.typeError('Lokasi wajib dipilih!'),
notes: Yup.string().nullable().default(null), notes: Yup.string().nullable().default(null),
items: Yup.array() items: Yup.array()
.of(PurchaseItemObjectSchema) .of(PurchaseItemObjectSchema)
@@ -133,14 +131,14 @@ export const getPurchaseRequestFormInitialValues = (
label: initialValues.area.name, label: initialValues.area.name,
} }
: null, : null,
area_id: initialValues?.area?.id ?? 0, area_id: initialValues?.area?.id ?? undefined,
location: initialValues?.location location: initialValues?.location
? { ? {
value: initialValues.location.id, value: initialValues.location.id,
label: initialValues.location.name, label: initialValues.location.name,
} }
: null, : null,
location_id: initialValues?.location?.id ?? 0, location_id: initialValues?.location?.id ?? undefined,
notes: initialValues?.notes ?? null, notes: initialValues?.notes ?? null,
items: [], items: [],
}); });
@@ -330,6 +330,79 @@ const PurchaseRequestForm = ({
setSelectedPurchaseItems([]); 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 ===== // ===== PURCHASE ITEM OPERATIONS =====
const handlePurchaseItemChange = ( const handlePurchaseItemChange = (
idx: number, idx: number,
@@ -381,51 +454,7 @@ const PurchaseRequestForm = ({
label='Vendor' label='Vendor'
placeholder='Pilih Vendor...' placeholder='Pilih Vendor...'
value={formik.values.supplier} value={formik.values.supplier}
onChange={(val) => { onChange={handleSupplierChange}
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);
});
}
}
}}
options={supplierOptions} options={supplierOptions}
onInputChange={setSupplierSelectInputValue} onInputChange={setSupplierSelectInputValue}
isLoading={isLoadingSuppliers} isLoading={isLoadingSuppliers}
@@ -462,81 +491,27 @@ const PurchaseRequestForm = ({
/> />
<SelectInput <SelectInput
required
label='Area' label='Area'
placeholder='Pilih Area...' placeholder='Pilih Area...'
value={formik.values.area} value={formik.values.area}
onChange={(val) => { onChange={handleAreaChange}
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);
});
}
}}
options={areaOptions} options={areaOptions}
onInputChange={setAreaSelectInputValue} onInputChange={setAreaSelectInputValue}
isLoading={isLoadingAreas} isLoading={isLoadingAreas}
isError={
formik.touched.area_id && Boolean(formik.errors.area_id)
}
errorMessage={formik.errors.area_id as string}
isDisabled={type === 'detail'} isDisabled={type === 'detail'}
isClearable isClearable
/> />
<SelectInput <SelectInput
required
label='Lokasi' label='Lokasi'
placeholder={ placeholder='Pilih Lokasi...'
!formik.values.area_id value={formik.values.location}
? 'Pilih Area terlebih dahulu' onChange={handleLocationChange}
: '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);
});
}
}}
options={locationOptions} options={locationOptions}
onInputChange={setLocationSelectInputValue} onInputChange={setLocationSelectInputValue}
isLoading={isLoadingLocations} isLoading={isLoadingLocations}
isError={ isDisabled={type === 'detail'}
formik.touched.location_id && isClearable={type !== 'detail'}
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}`}
/> />
<div className={'col-span-2'}> <div className={'col-span-2'}>
@@ -643,40 +618,9 @@ const PurchaseRequestForm = ({
)} )}
<td> <td>
<SelectInput <SelectInput
required placeholder='Pilih Gudang...'
placeholder={
!formik.values.area_id
? 'Pilih Area terlebih dahulu'
: formik.values.location_id
? 'Pilih Gudang...'
: 'Pilih Area dan Lokasi terlebih dahulu'
}
value={item.warehouse} value={item.warehouse}
onChange={(val) => { onChange={(val) => handleWarehouseChange(idx, 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);
}}
options={warehouseOptions} options={warehouseOptions}
onInputChange={setWarehouseSelectInputValue} onInputChange={setWarehouseSelectInputValue}
isLoading={isLoadingWarehouses} isLoading={isLoadingWarehouses}
@@ -687,17 +631,8 @@ const PurchaseRequestForm = ({
isRepeaterInputError(idx, 'warehouse_id') isRepeaterInputError(idx, 'warehouse_id')
.errorMessage .errorMessage
} }
isDisabled={ isDisabled={type === 'detail'}
type === 'detail' || isClearable={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}`}
/> />
</td> </td>
<td> <td>