From edd59598f9bfd20816f17b104a3ab011f4b84f7b Mon Sep 17 00:00:00 2001 From: rstubryan Date: Tue, 18 Nov 2025 13:50:54 +0700 Subject: [PATCH] refactor(FE-208,212): refine PurchaseRequestForm validation and state management --- .../request/PurchaseRequestForm.schema.ts | 2 +- .../form/request/PurchaseRequestForm.tsx | 216 ++++++++++-------- 2 files changed, 120 insertions(+), 98 deletions(-) diff --git a/src/components/pages/purchase/form/request/PurchaseRequestForm.schema.ts b/src/components/pages/purchase/form/request/PurchaseRequestForm.schema.ts index 0872884e..06c8606d 100644 --- a/src/components/pages/purchase/form/request/PurchaseRequestForm.schema.ts +++ b/src/components/pages/purchase/form/request/PurchaseRequestForm.schema.ts @@ -21,7 +21,7 @@ type PurchaseRequestFormSchemaType = { warehouse?: { value: number; label: string; - }; + } | null; warehouse_id: number; notes: string | null; items: { diff --git a/src/components/pages/purchase/form/request/PurchaseRequestForm.tsx b/src/components/pages/purchase/form/request/PurchaseRequestForm.tsx index 89879076..2b6fd5ee 100644 --- a/src/components/pages/purchase/form/request/PurchaseRequestForm.tsx +++ b/src/components/pages/purchase/form/request/PurchaseRequestForm.tsx @@ -74,35 +74,26 @@ const PurchaseRequestForm = ({ // ===== UTILITY FUNCTIONS ===== const getPurchaseItemError = ( idx: number, - field: 'warehouse_id' | 'product_warehouse_id' | 'product_id' | 'qty' + field: 'product_warehouse_id' | 'product_id' | 'qty' ): { isError: boolean; errorMessage: string } => { - const touchedItem = formik.touched.items?.[idx]; + if (!formik.touched.items || !Array.isArray(formik.touched.items)) { + return { + isError: false, + errorMessage: '', + }; + } + + const touchedField = formik.touched.items[idx]?.[field]; const errorItem = formik.errors.items?.[idx] as | Record | undefined; - if (!touchedItem) { - return { isError: false, errorMessage: '' }; - } - - const isTouched = (touchedItem as Record)?.[field]; - const errorMessage = errorItem?.[field] || ''; - return { - isError: Boolean(isTouched && errorMessage), - errorMessage: isTouched && errorMessage ? errorMessage : '', + isError: Boolean(touchedField && Boolean(errorItem?.[field])), + errorMessage: touchedField && errorItem?.[field] ? errorItem[field] : '', }; }; - const getSupplierById = (supplierId: number): Supplier | null => { - if (!isResponseSuccess(supplierRawData)) return null; - return ( - supplierRawData?.data.find( - (supplier: Supplier) => supplier.id === supplierId - ) || null - ); - }; - // ===== SUBMISSION HANDLERS ===== const createPurchaseRequestHandler = useCallback( async (payload: CreatePurchaseRequestPayload) => { @@ -349,74 +340,6 @@ const PurchaseRequestForm = ({ ); }, [warehouses]); - // ===== FIELD CHANGE HANDLERS ===== - const supplierChangeHandler = (val: OptionType | OptionType[] | null) => { - const supplier = val as OptionType | null; - formik.setFieldTouched('supplier', true); - formik.setFieldValue('supplier', supplier); - formik.setFieldTouched('supplier_id', true); - formik.setFieldValue('supplier_id', (supplier as OptionType)?.value || 0); - - if (supplier?.value) { - const supplierId = - typeof supplier.value === 'string' - ? parseInt(supplier.value) - : supplier.value; - const supplierData = getSupplierById(supplierId); - if (supplierData?.due_date) { - formik.setFieldTouched('credit_term', true); - formik.setFieldValue('credit_term', supplierData.due_date.toString()); - } - } else { - // Reset credit_term field and its touched state when supplier is cleared - formik.setFieldTouched('credit_term', false); - formik.setFieldValue('credit_term', ''); - } - }; - - const areaChangeHandler = (val: OptionType | OptionType[] | null) => { - const area = val as OptionType | null; - formik.setFieldTouched('area', true); - formik.setFieldValue('area', area); - formik.setFieldTouched('area_id', true); - formik.setFieldValue('area_id', (area as OptionType)?.value || 0); - - // Reset area dependent fields - formik.setFieldTouched('location', false); - formik.setFieldValue('location', undefined); - formik.setFieldTouched('location_id', false); - formik.setFieldValue('location_id', 0); - setLocationSelectInputValue(''); - - // Reset area dependent fields in all purchase items - if (formik.values.items) { - formik.values.items.forEach((_, idx) => { - formik.setFieldValue(`items.${idx}.product_warehouse`, null); - formik.setFieldValue(`items.${idx}.product_warehouse_id`, null); - formik.setFieldValue(`items.${idx}.product`, null); - formik.setFieldValue(`items.${idx}.product_id`, ''); - }); - } - }; - - const locationChangeHandler = (val: OptionType | OptionType[] | null) => { - const location = val as OptionType | null; - formik.setFieldTouched('location', true); - formik.setFieldValue('location', location); - formik.setFieldTouched('location_id', true); - formik.setFieldValue('location_id', (location as OptionType)?.value || 0); - - // Reset location dependent fields in all purchase items - if (formik.values.items) { - formik.values.items.forEach((_, idx) => { - formik.setFieldValue(`items.${idx}.product_warehouse`, null); - formik.setFieldValue(`items.${idx}.product_warehouse_id`, null); - formik.setFieldValue(`items.${idx}.product`, null); - formik.setFieldValue(`items.${idx}.product_id`, ''); - }); - } - }; - // Purchase Items Handlers const addPurchaseItem = () => { const newPurchaseItems = [ @@ -456,6 +379,7 @@ const PurchaseRequestForm = ({ if (field === 'qty') { const numValue = typeof value === 'string' ? parseFloat(value) || 0 : value; + formik.setFieldTouched(`items.${idx}.qty`, true); formik.setFieldValue(`items.${idx}.qty`, numValue); } }; @@ -497,12 +421,41 @@ const PurchaseRequestForm = ({ label='Vendor' placeholder='Pilih Vendor...' value={formik.values.supplier} - onChange={supplierChangeHandler} + 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() + ); + } + } else { + formik.setFieldTouched('credit_term', false); + formik.setFieldValue('credit_term', ''); + } + }} options={supplierOptions} onInputChange={setSupplierSelectInputValue} isLoading={isLoadingSuppliers} isError={ - formik.touched.supplier && Boolean(formik.errors.supplier_id) + formik.touched.supplier_id && + Boolean(formik.errors.supplier_id) } errorMessage={formik.errors.supplier_id as string} isDisabled={type === 'detail'} @@ -537,11 +490,43 @@ const PurchaseRequestForm = ({ label='Area' placeholder='Pilih Area...' value={formik.values.area} - onChange={areaChangeHandler} + 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_warehouse`, + null + ); + formik.setFieldValue( + `items.${idx}.product_warehouse_id`, + null + ); + formik.setFieldValue(`items.${idx}.product`, null); + formik.setFieldValue(`items.${idx}.product_id`, ''); + }); + } + }} options={areaOptions} onInputChange={setAreaSelectInputValue} isLoading={isLoadingAreas} - isError={formik.touched.area && Boolean(formik.errors.area_id)} + isError={ + formik.touched.area_id && Boolean(formik.errors.area_id) + } errorMessage={formik.errors.area_id as string} isDisabled={type === 'detail'} isClearable @@ -556,12 +541,37 @@ const PurchaseRequestForm = ({ : 'Pilih Lokasi...' } value={formik.values.area_id ? formik.values.location : null} - onChange={locationChangeHandler} + 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_warehouse`, + null + ); + formik.setFieldValue( + `items.${idx}.product_warehouse_id`, + null + ); + formik.setFieldValue(`items.${idx}.product`, null); + formik.setFieldValue(`items.${idx}.product_id`, ''); + }); + } + }} options={locationOptions} onInputChange={setLocationSelectInputValue} isLoading={isLoadingLocations} isError={ - formik.touched.location && Boolean(formik.errors.location_id) + formik.touched.location_id && + Boolean(formik.errors.location_id) } errorMessage={formik.errors.location_id as string} isDisabled={type === 'detail' || !formik.values.area_id} @@ -684,13 +694,13 @@ const PurchaseRequestForm = ({ value={formik.values.warehouse} onChange={(val) => { const warehouse = val as OptionType | null; - formik.setFieldTouched('warehouse', true); - formik.setFieldValue('warehouse', warehouse); formik.setFieldTouched('warehouse_id', true); formik.setFieldValue( 'warehouse_id', (warehouse as OptionType)?.value || 0 ); + formik.setFieldTouched('warehouse', true); + formik.setFieldValue('warehouse', warehouse); if (formik.values.items) { formik.values.items.forEach((_, idx) => { formik.setFieldValue( @@ -716,7 +726,7 @@ const PurchaseRequestForm = ({ onInputChange={setWarehouseSelectInputValue} isLoading={isLoadingWarehouses} isError={ - formik.touched.warehouse && + formik.touched.warehouse_id && Boolean(formik.errors.warehouse_id) } errorMessage={formik.errors.warehouse_id as string} @@ -741,10 +751,18 @@ const PurchaseRequestForm = ({ onChange={(val) => { const productWarehouse = val as ProductWarehouseOptionType | null; + formik.setFieldTouched( + `items.${idx}.product_warehouse`, + true + ); formik.setFieldValue( `items.${idx}.product_warehouse`, productWarehouse ); + formik.setFieldTouched( + `items.${idx}.product_warehouse_id`, + true + ); formik.setFieldValue( `items.${idx}.product_warehouse_id`, (productWarehouse as ProductWarehouseOptionType) @@ -754,6 +772,10 @@ const PurchaseRequestForm = ({ const productId = (productWarehouse as ProductWarehouseOptionType) ?.product_id || 0; + formik.setFieldTouched( + `items.${idx}.product_id`, + true + ); formik.setFieldValue( `items.${idx}.product_id`, productId