From 39dbf57d7fced6a561c9d6aa9b9585ee19b87fb1 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Tue, 4 Nov 2025 09:20:39 +0700 Subject: [PATCH] refactor(FE-208,212): streamline PurchaseRequestForm structure, enhance state management, and improve field handling for purchase items --- .../purchase/form/PurchaseRequestForm.tsx | 152 +++++++----------- 1 file changed, 61 insertions(+), 91 deletions(-) diff --git a/src/components/pages/purchase/form/PurchaseRequestForm.tsx b/src/components/pages/purchase/form/PurchaseRequestForm.tsx index 14c0fc62..fd1a63f8 100644 --- a/src/components/pages/purchase/form/PurchaseRequestForm.tsx +++ b/src/components/pages/purchase/form/PurchaseRequestForm.tsx @@ -52,19 +52,16 @@ const PurchaseRequestForm = ({ }: PurchaseRequestFormProps) => { const router = useRouter(); const deleteModal = useModal(); + const [isDeleteLoading, setIsDeleteLoading] = useState(false); + const [locationSelectInputValue, setLocationSelectInputValue] = useState(''); const [selectedPurchaseItems, setSelectedPurchaseItems] = useState( [] ); const [purchaseRequestFormErrorMessage, setPurchaseRequestFormErrorMessage] = useState(''); - const [isDeleteLoading, setIsDeleteLoading] = useState(false); - const [ - productWarehouseSelectInputValue, - setProductWarehouseSelectInputValue, - ] = useState(''); - // ===== INTERFACES ===== + // ===== TYPE DEFINITIONS ===== interface ProductWarehouseOptionType extends OptionType { product_id: number; warehouse_id: number; @@ -72,7 +69,7 @@ const PurchaseRequestForm = ({ quantity: number; } - // ===== HELPER FUNCTIONS ===== + // ===== UTILITY FUNCTIONS ===== const getPurchaseItemError = ( idx: number, field: 'warehouse_id' | 'product_warehouse_id' | 'product_id' | 'sub_qty' @@ -104,7 +101,7 @@ const PurchaseRequestForm = ({ ); }; - // ===== FORM HANDLERS ===== + // ===== SUBMISSION HANDLERS ===== const createPurchaseRequestHandler = useCallback( async (payload: CreatePurchaseRequestPayload) => { const res = await PurchaseApi.create(payload); @@ -150,14 +147,7 @@ const PurchaseRequestForm = ({ router.push('/purchase'); }, [deleteModal, initialValues?.id, router]); - // ===== API DATA FETCHING ===== - const allProductWarehousesUrl = `${ProductWarehouseApi.basePath}`; - const { data: allProductWarehouses } = useSWR( - allProductWarehousesUrl, - ProductWarehouseApi.getAllFetcher - ); - - // ===== USE SELECT HOOKS ===== + // ===== SELECT INPUT DATA ===== const { inputValue: supplierSelectInputValue, setInputValue: setSupplierSelectInputValue, @@ -173,65 +163,13 @@ const PurchaseRequestForm = ({ isLoadingOptions: isLoadingAreas, } = useSelect(AreaApi.basePath, 'id', 'name', 'search'); - const [locationSelectInputValue, setLocationSelectInputValue] = useState(''); - const { inputValue: warehouseSelectInputValue, setInputValue: setWarehouseSelectInputValue, isLoadingOptions: isLoadingWarehouses, } = useSelect(WarehouseApi.basePath, 'id', 'name', 'search'); - // ===== PRODUCT WAREHOUSE FETCHING ===== - const getProductWarehousesUrl = useCallback(() => { - const productWarehouseParams = new URLSearchParams({ - search: productWarehouseSelectInputValue, - }); - - return `${ProductWarehouseApi.basePath}?${productWarehouseParams.toString()}`; - }, [productWarehouseSelectInputValue]); - - const productWarehousesUrl = getProductWarehousesUrl(); - const { data: productWarehouses, isLoading: isLoadingProductWarehouses } = - 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) - ? productWarehouses?.data.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, - })) - : []; - + // ===== FORM CONFIGURATION ===== const formikInitialValues = useMemo( () => getPurchaseRequestFormInitialValues(initialValues), [initialValues] @@ -290,7 +228,37 @@ const PurchaseRequestForm = ({ }, }); - // ===== PRODUCT DATA FETCHING ===== + // ===== API DATA FETCHING ===== + const allProductWarehousesUrl = `${ProductWarehouseApi.basePath}`; + const { data: productWarehouses, isLoading: isLoadingProductWarehouses } = + useSWR(allProductWarehousesUrl, ProductWarehouseApi.getAllFetcher); + + 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 productUrl = useMemo(() => { const productIds = formik.values.purchase_items @@ -320,8 +288,6 @@ const PurchaseRequestForm = ({ }); return data; }, [productsResponse]); - - // ===== API DATA FETCHING ===== const locationsUrl = useMemo(() => { const params = new URLSearchParams({ search: locationSelectInputValue, @@ -386,7 +352,7 @@ const PurchaseRequestForm = ({ ); }, [warehouses]); - // ===== EVENT HANDLERS ===== + // ===== FIELD CHANGE HANDLERS ===== const supplierChangeHandler = (val: OptionType | OptionType[] | null) => { const supplier = val as OptionType | null; formik.setFieldTouched('supplier', true); @@ -494,28 +460,19 @@ const PurchaseRequestForm = ({ setSelectedPurchaseItems([]); }; + // ===== PURCHASE ITEM OPERATIONS ===== const handlePurchaseItemChange = ( idx: number, - field: string, + field: 'sub_qty' | 'price', value: string | number ) => { - const integerFields = [ - 'warehouse_id', - 'product_id', - 'product_warehouse_id', - 'total_qty', - ]; - const floatFields = ['price', 'sub_qty']; - - if (integerFields.includes(field)) { + if (field === 'sub_qty') { const numValue = typeof value === 'string' ? parseInt(value) || 0 : value; - formik.setFieldValue(`purchase_items.${idx}.${field}`, numValue); - } else if (floatFields.includes(field)) { + formik.setFieldValue(`purchase_items.${idx}.sub_qty`, numValue); + } else if (field === 'price') { const numValue = typeof value === 'string' ? parseFloat(value) || 0 : value; - formik.setFieldValue(`purchase_items.${idx}.${field}`, numValue); - } else { - formik.setFieldValue(`purchase_items.${idx}.${field}`, value); + formik.setFieldValue(`purchase_items.${idx}.price`, numValue); } }; @@ -731,6 +688,7 @@ const PurchaseRequestForm = ({ { const warehouse = val as OptionType | null; formik.setFieldValue( @@ -741,17 +699,29 @@ const PurchaseRequestForm = ({ `purchase_items.${idx}.warehouse_id`, (warehouse as OptionType)?.value || 0 ); + formik.setFieldTouched( + `purchase_items.${idx}.product_warehouse`, + false + ); formik.setFieldValue( `purchase_items.${idx}.product_warehouse`, null ); + formik.setFieldTouched( + `purchase_items.${idx}.product_warehouse_id`, + false + ); formik.setFieldValue( `purchase_items.${idx}.product_warehouse_id`, - null + 0 + ); + formik.setFieldTouched( + `purchase_items.${idx}.product_id`, + false ); formik.setFieldValue( `purchase_items.${idx}.product_id`, - '' + 0 ); }} options={warehouseOptions} @@ -776,6 +746,7 @@ const PurchaseRequestForm = ({ { const productWarehouse = val as ProductWarehouseOptionType | null; @@ -799,7 +770,6 @@ const PurchaseRequestForm = ({ options={getProductWarehouseOptionsForItem( item.warehouse_id )} - onInputChange={setProductWarehouseSelectInputValue} isLoading={isLoadingProductWarehouses} isError={ getPurchaseItemError(idx, 'product_warehouse_id')