refactor(FE-208,212): enhance PurchaseRequestForm with location and warehouse handling, update validation and reset logic for dependent fields

This commit is contained in:
rstubryan
2025-11-03 14:06:32 +07:00
parent 7b19cd4a21
commit ae5a57277b
@@ -164,12 +164,7 @@ const PurchaseRequestForm = ({
isLoadingOptions: isLoadingAreas, isLoadingOptions: isLoadingAreas,
} = useSelect(AreaApi.basePath, 'id', 'name', 'search'); } = useSelect(AreaApi.basePath, 'id', 'name', 'search');
const { const [locationSelectInputValue, setLocationSelectInputValue] = useState('');
inputValue: locationSelectInputValue,
setInputValue: setLocationSelectInputValue,
options: locationOptions,
isLoadingOptions: isLoadingLocations,
} = useSelect(LocationApi.basePath, 'id', 'name', 'search');
const { const {
inputValue: warehouseSelectInputValue, inputValue: warehouseSelectInputValue,
@@ -177,34 +172,12 @@ const PurchaseRequestForm = ({
isLoadingOptions: isLoadingWarehouses, isLoadingOptions: isLoadingWarehouses,
} = useSelect(WarehouseApi.basePath, 'id', 'name', 'search'); } = useSelect(WarehouseApi.basePath, 'id', 'name', 'search');
const warehousesUrl = `${WarehouseApi.basePath}?${new URLSearchParams({ search: warehouseSelectInputValue }).toString()}`;
const { data: warehouses } = useSWR(
warehousesUrl,
WarehouseApi.getAllFetcher
);
// ===== DATA PROCESSING =====
const warehouseOptions = useMemo(() => {
if (!isResponseSuccess(warehouses)) return [];
return (
warehouses?.data.map((w) => ({
value: w.id,
label: w.name,
area: w.area?.name,
location:
'type' in w && (w.type === 'LOKASI' || w.type === 'KANDANG')
? w.location?.name
: undefined,
})) || []
);
}, [warehouses]);
// ===== PRODUCT WAREHOUSE FETCHING ===== // ===== PRODUCT WAREHOUSE FETCHING =====
const getProductWarehousesUrl = useCallback(() => { const getProductWarehousesUrl = useCallback(() => {
const productWarehouseParams = new URLSearchParams({ const productWarehouseParams = new URLSearchParams({
search: productWarehouseSelectInputValue, search: productWarehouseSelectInputValue,
}); });
return `${ProductWarehouseApi.basePath}?${productWarehouseParams.toString()}`; return `${ProductWarehouseApi.basePath}?${productWarehouseParams.toString()}`;
}, [productWarehouseSelectInputValue]); }, [productWarehouseSelectInputValue]);
@@ -212,6 +185,33 @@ const PurchaseRequestForm = ({
const { data: productWarehouses, isLoading: isLoadingProductWarehouses } = const { data: productWarehouses, isLoading: isLoadingProductWarehouses } =
useSWR(productWarehousesUrl, ProductWarehouseApi.getAllFetcher); useSWR(productWarehousesUrl, ProductWarehouseApi.getAllFetcher);
// Filter product warehouses per item based on selected warehouse
const getProductWarehouseOptionsForItem = useCallback(
(warehouseId: number | string) => {
if (!isResponseSuccess(productWarehouses)) return [];
const warehouseIdNum =
typeof warehouseId === 'string'
? parseInt(warehouseId) || 0
: warehouseId;
if (warehouseIdNum === 0) return [];
return (
productWarehouses?.data
.filter((pw) => pw.warehouse.id === warehouseIdNum)
.map((pw) => ({
value: pw.product.id,
label: pw.product.name,
product_id: pw.product.id,
warehouse_id: pw.warehouse.id,
warehouse_name: pw.warehouse.name,
quantity: pw.quantity,
})) || []
);
},
[productWarehouses]
);
const productWarehouseOptions = isResponseSuccess(productWarehouses) const productWarehouseOptions = isResponseSuccess(productWarehouses)
? productWarehouses?.data.map((pw) => ({ ? productWarehouses?.data.map((pw) => ({
value: pw.product.id, value: pw.product.id,
@@ -281,6 +281,71 @@ const PurchaseRequestForm = ({
}, },
}); });
// ===== API DATA FETCHING =====
const locationsUrl = useMemo(() => {
const params = new URLSearchParams({
search: locationSelectInputValue,
...(formik.values.area_id && formik.values.area_id > 0
? { area_id: formik.values.area_id.toString() }
: {}),
});
return `${LocationApi.basePath}?${params.toString()}`;
}, [locationSelectInputValue, formik.values.area_id]);
const { data: locations, isLoading: isLoadingLocations } = useSWR(
locationsUrl,
LocationApi.getAllFetcher
);
const locationOptions = useMemo(() => {
if (!isResponseSuccess(locations)) return [];
return (
locations?.data.map((location) => ({
value: location.id,
label: location.name,
})) || []
);
}, [locations]);
const warehousesUrl = useMemo(() => {
const params = new URLSearchParams({ search: warehouseSelectInputValue });
if (formik.values.area_id && formik.values.area_id > 0) {
params.append('area_id', formik.values.area_id.toString());
}
if (formik.values.location_id && formik.values.location_id > 0) {
params.append('location_id', formik.values.location_id.toString());
}
return `${WarehouseApi.basePath}?${params.toString()}`;
}, [
warehouseSelectInputValue,
formik.values.area_id,
formik.values.location_id,
]);
const { data: warehouses } = useSWR(
warehousesUrl,
WarehouseApi.getAllFetcher
);
const warehouseOptions = useMemo(() => {
if (!isResponseSuccess(warehouses)) return [];
return (
warehouses?.data.map((w) => ({
value: w.id,
label: w.name,
area: w.area?.name,
location:
'type' in w && (w.type === 'LOKASI' || w.type === 'KANDANG')
? w.location?.name
: undefined,
})) || []
);
}, [warehouses]);
// ===== EVENT HANDLERS ===== // ===== EVENT HANDLERS =====
const supplierChangeHandler = (val: OptionType | OptionType[] | null) => { const supplierChangeHandler = (val: OptionType | OptionType[] | null) => {
const supplier = val as OptionType | null; const supplier = val as OptionType | null;
@@ -296,6 +361,25 @@ const PurchaseRequestForm = ({
formik.setFieldValue('area', area); formik.setFieldValue('area', area);
formik.setFieldTouched('area_id', true); formik.setFieldTouched('area_id', true);
formik.setFieldValue('area_id', (area as OptionType)?.value || 0); formik.setFieldValue('area_id', (area as OptionType)?.value || 0);
// Reset area dependent fields
formik.setFieldValue('location', null);
formik.setFieldValue('location_id', 0);
setLocationSelectInputValue('');
// Reset area dependent fields in all purchase items
if (formik.values.purchase_items) {
formik.values.purchase_items.forEach((_, idx) => {
formik.setFieldValue(`purchase_items.${idx}.warehouse`, null);
formik.setFieldValue(`purchase_items.${idx}.warehouse_id`, '');
formik.setFieldValue(`purchase_items.${idx}.product_warehouse`, null);
formik.setFieldValue(
`purchase_items.${idx}.product_warehouse_id`,
null
);
formik.setFieldValue(`purchase_items.${idx}.product_id`, '');
});
}
}; };
const locationChangeHandler = (val: OptionType | OptionType[] | null) => { const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
@@ -304,6 +388,20 @@ const PurchaseRequestForm = ({
formik.setFieldValue('location', location); formik.setFieldValue('location', location);
formik.setFieldTouched('location_id', true); formik.setFieldTouched('location_id', true);
formik.setFieldValue('location_id', (location as OptionType)?.value || 0); formik.setFieldValue('location_id', (location as OptionType)?.value || 0);
// Reset location dependent fields in all purchase items
if (formik.values.purchase_items) {
formik.values.purchase_items.forEach((_, idx) => {
formik.setFieldValue(`purchase_items.${idx}.warehouse`, null);
formik.setFieldValue(`purchase_items.${idx}.warehouse_id`, '');
formik.setFieldValue(`purchase_items.${idx}.product_warehouse`, null);
formik.setFieldValue(
`purchase_items.${idx}.product_warehouse_id`,
null
);
formik.setFieldValue(`purchase_items.${idx}.product_id`, '');
});
}
}; };
// Purchase Items Handlers // Purchase Items Handlers
@@ -455,7 +553,11 @@ const PurchaseRequestForm = ({
<SelectInput <SelectInput
required required
label='Lokasi' label='Lokasi'
placeholder='Pilih Lokasi...' placeholder={
!formik.values.area_id
? 'Pilih Area terlebih dahulu'
: 'Pilih Lokasi...'
}
value={formik.values.location} value={formik.values.location}
onChange={locationChangeHandler} onChange={locationChangeHandler}
options={locationOptions} options={locationOptions}
@@ -466,8 +568,8 @@ const PurchaseRequestForm = ({
Boolean(formik.errors.location_id) Boolean(formik.errors.location_id)
} }
errorMessage={formik.errors.location_id as string} errorMessage={formik.errors.location_id as string}
isDisabled={type === 'detail'} isDisabled={type === 'detail' || !formik.values.area_id}
isClearable isClearable={type !== 'detail' && !!formik.values.area_id}
/> />
<div className={type === 'detail' ? 'col-span-1' : 'col-span-2'}> <div className={type === 'detail' ? 'col-span-1' : 'col-span-2'}>
@@ -577,6 +679,18 @@ const PurchaseRequestForm = ({
`purchase_items.${idx}.warehouse_id`, `purchase_items.${idx}.warehouse_id`,
(warehouse as OptionType)?.value || 0 (warehouse as OptionType)?.value || 0
); );
formik.setFieldValue(
`purchase_items.${idx}.product_warehouse`,
null
);
formik.setFieldValue(
`purchase_items.${idx}.product_warehouse_id`,
null
);
formik.setFieldValue(
`purchase_items.${idx}.product_id`,
''
);
}} }}
options={warehouseOptions} options={warehouseOptions}
onInputChange={setWarehouseSelectInputValue} onInputChange={setWarehouseSelectInputValue}
@@ -618,7 +732,9 @@ const PurchaseRequestForm = ({
?.product_id || 0 ?.product_id || 0
); );
}} }}
options={productWarehouseOptions} options={getProductWarehouseOptionsForItem(
item.warehouse_id
)}
onInputChange={setProductWarehouseSelectInputValue} onInputChange={setProductWarehouseSelectInputValue}
isLoading={isLoadingProductWarehouses} isLoading={isLoadingProductWarehouses}
isError={ isError={
@@ -629,9 +745,13 @@ const PurchaseRequestForm = ({
getPurchaseItemError(idx, 'product_warehouse_id') getPurchaseItemError(idx, 'product_warehouse_id')
.errorMessage .errorMessage
} }
isDisabled={type === 'detail'} isDisabled={type === 'detail' || !item.warehouse_id}
isClearable isClearable={type !== 'detail' && !!item.warehouse_id}
placeholder='Pilih Produk' placeholder={
!item.warehouse_id
? 'Pilih Gudang terlebih dahulu'
: 'Pilih Produk'
}
className={{ className={{
wrapper: 'min-w-32', wrapper: 'min-w-32',
}} }}