diff --git a/src/components/pages/purchase/form/PurchaseRequestForm.schema.ts b/src/components/pages/purchase/form/PurchaseRequestForm.schema.ts index e15dae8d..ebd0792d 100644 --- a/src/components/pages/purchase/form/PurchaseRequestForm.schema.ts +++ b/src/components/pages/purchase/form/PurchaseRequestForm.schema.ts @@ -7,6 +7,16 @@ type PurchaseRequestFormSchemaType = { label: string; } | null; supplier_id: number; + area?: { + value: number; + label: string; + } | null; + area_id: number; + location?: { + value: number; + label: string; + } | null; + location_id: number; credit_term: number | string; notes: string | null; purchase_items: { @@ -90,6 +100,22 @@ export const PurchaseRequestFormSchema: Yup.ObjectSchema { + const touchedItem = formik.touched.purchase_items?.[idx]; + const errorItem = formik.errors.purchase_items?.[idx] as + | Record + | undefined; + + if (!touchedItem || !errorItem) { + return { isError: false, errorMessage: '' }; + } + + const isTouched = (touchedItem as Record)?.[field]; + const errorMessage = errorItem?.[field] || ''; + + return { + isError: Boolean(isTouched && errorMessage), + errorMessage: isTouched && errorMessage ? errorMessage : '', + }; + }; // ===== FORM HANDLERS ===== const createPurchaseRequestHandler = useCallback( @@ -94,37 +143,86 @@ const PurchaseRequestForm = ({ }, [deleteModal, initialValues?.id, router]); // ===== API DATA FETCHING ===== - const { data: suppliers, isLoading: isLoadingSuppliers } = useSWR( - SupplierApi.basePath, - SupplierApi.getAllFetcher + const allProductWarehousesUrl = `${ProductWarehouseApi.basePath}`; + const { data: allProductWarehouses } = useSWR( + allProductWarehousesUrl, + ProductWarehouseApi.getAllFetcher ); - const { data: warehouses, isLoading: isLoadingWarehouses } = useSWR( - WarehouseApi.basePath, + // ===== USE SELECT HOOKS ===== + const { + inputValue: supplierSelectInputValue, + setInputValue: setSupplierSelectInputValue, + options: supplierOptions, + isLoadingOptions: isLoadingSuppliers, + } = useSelect(SupplierApi.basePath, 'id', 'name', 'search'); + + const { + inputValue: areaSelectInputValue, + setInputValue: setAreaSelectInputValue, + options: areaOptions, + isLoadingOptions: isLoadingAreas, + } = useSelect(AreaApi.basePath, 'id', 'name', 'search'); + + const { + inputValue: locationSelectInputValue, + setInputValue: setLocationSelectInputValue, + options: locationOptions, + isLoadingOptions: isLoadingLocations, + } = useSelect(LocationApi.basePath, 'id', 'name', 'search'); + + const { + inputValue: warehouseSelectInputValue, + setInputValue: setWarehouseSelectInputValue, + isLoadingOptions: isLoadingWarehouses, + } = 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 supplierOptions = useMemo(() => { - if (!isResponseSuccess(suppliers)) return []; - return ( - suppliers?.data.map((supplier) => ({ - value: supplier.id, - label: supplier.name, - })) || [] - ); - }, [suppliers]); - const warehouseOptions = useMemo(() => { if (!isResponseSuccess(warehouses)) return []; + return ( - warehouses?.data.map((warehouse) => ({ - value: warehouse.id, - label: warehouse.name, + 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 ===== + 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); + + 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, + })) + : []; + const formikInitialValues = useMemo( () => getPurchaseRequestFormInitialValues(initialValues), [initialValues] @@ -184,18 +282,28 @@ const PurchaseRequestForm = ({ }); // ===== EVENT HANDLERS ===== - const supplierChangeHandler = (val: string) => { - const supplierId = parseInt(val) || 0; - formik.setFieldValue('supplier_id', supplierId); + 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); + }; - const selectedSupplier = supplierOptions.find( - (option) => option.value === supplierId - ); - if (selectedSupplier) { - formik.setFieldValue('supplier', selectedSupplier); - } else { - formik.setFieldValue('supplier', null); - } + 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); + }; + + 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); }; // Purchase Items Handlers @@ -293,21 +401,22 @@ const PurchaseRequestForm = ({ : 'grid grid-cols-1 md:grid-cols-2 gap-6' } > - supplierChangeHandler(e.target.value)} - onBlur={formik.handleBlur} + placeholder='Pilih Vendor...' + value={formik.values.supplier} + onChange={supplierChangeHandler} + options={supplierOptions} + onInputChange={setSupplierSelectInputValue} + isLoading={isLoadingSuppliers} isError={ formik.touched.supplier_id && Boolean(formik.errors.supplier_id) } errorMessage={formik.errors.supplier_id as string} - readOnly={type === 'detail'} - type='number' - placeholder='Masukkan Supplier ID' + isDisabled={type === 'detail'} + isClearable /> - {}} - onBlur={formik.handleBlur} - readOnly={type === 'detail'} - type='number' - placeholder='Pilih Area' + placeholder='Pilih Area...' + value={formik.values.area} + onChange={areaChangeHandler} + 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 /> - {}} - onBlur={formik.handleBlur} - readOnly={type === 'detail'} - type='number' - placeholder='Pilih Lokasi' + placeholder='Pilih Lokasi...' + value={formik.values.location} + onChange={locationChangeHandler} + 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'} + isClearable />
@@ -442,43 +564,76 @@ const PurchaseRequestForm = ({ )} - - handlePurchaseItemChange( - idx, - 'warehouse_id', - e.target.value - ) + value={item.warehouse} + onChange={(val) => { + const warehouse = val as OptionType | null; + formik.setFieldValue( + `purchase_items.${idx}.warehouse`, + warehouse + ); + formik.setFieldValue( + `purchase_items.${idx}.warehouse_id`, + (warehouse as OptionType)?.value || 0 + ); + }} + options={warehouseOptions} + onInputChange={setWarehouseSelectInputValue} + isLoading={isLoadingWarehouses} + isError={ + getPurchaseItemError(idx, 'warehouse_id').isError } - onBlur={formik.handleBlur} - type='number' - placeholder='Masukkan Warehouse ID' - readOnly={type === 'detail'} + errorMessage={ + getPurchaseItemError(idx, 'warehouse_id') + .errorMessage + } + isDisabled={type === 'detail'} + isClearable + placeholder='Pilih Gudang' className={{ - wrapper: 'min-w-24', + wrapper: 'min-w-32', }} /> - - handlePurchaseItemChange( - idx, - 'product_warehouse_id', - e.target.value - ) + { + const productWarehouse = + val as ProductWarehouseOptionType | null; + formik.setFieldValue( + `purchase_items.${idx}.product_warehouse`, + productWarehouse + ); + formik.setFieldValue( + `purchase_items.${idx}.product_warehouse_id`, + (productWarehouse as ProductWarehouseOptionType) + ?.value || 0 + ); + formik.setFieldValue( + `purchase_items.${idx}.product_id`, + (productWarehouse as ProductWarehouseOptionType) + ?.product_id || 0 + ); + }} + options={productWarehouseOptions} + onInputChange={setProductWarehouseSelectInputValue} + isLoading={isLoadingProductWarehouses} + isError={ + getPurchaseItemError(idx, 'product_warehouse_id') + .isError } - onBlur={formik.handleBlur} - type='number' - placeholder='Product Warehouse ID' - readOnly={type === 'detail'} + errorMessage={ + getPurchaseItemError(idx, 'product_warehouse_id') + .errorMessage + } + isDisabled={type === 'detail'} + isClearable + placeholder='Pilih Produk' className={{ - wrapper: 'min-w-24', + wrapper: 'min-w-32', }} />