From 4a974048a7a37f5ca14480c2086360ad2b230f31 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 12 Nov 2025 21:52:02 +0700 Subject: [PATCH] feat(FE-208): refactor Purchase Order forms to use table layout for improved readability and maintainability --- .../order/PurchaseOrderAcceptApprovalForm.tsx | 778 +++++++----------- .../order/PurchaseOrderStaffApprovalForm.tsx | 485 +++++------ 2 files changed, 516 insertions(+), 747 deletions(-) diff --git a/src/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm.tsx b/src/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm.tsx index 48e0b2f3..bf8af4a4 100644 --- a/src/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm.tsx +++ b/src/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm.tsx @@ -9,9 +9,6 @@ import Button from '@/components/Button'; import TextInput from '@/components/input/TextInput'; import NumberInput from '@/components/input/NumberInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput'; -import Table from '@/components/Table'; -import Card from '@/components/Card'; -import * as TanStack from '@tanstack/react-table'; import { PurchaseRequisitionsAcceptApprovalFormDefaultValues, @@ -24,7 +21,6 @@ import { CreateAcceptApprovalRequisitionsPayload, Purchase, } from '@/types/api/purchase/purchase'; -import { PurchaseRequisitionsAcceptApprovalFormValues } from './PurchaseOrderForm.schema'; import DateInput from '@/components/input/DateInput'; interface PurchaseOrderAcceptApprovalFormProps { @@ -71,7 +67,7 @@ const PurchaseOrderAcceptApprovalForm = ({ } // ===== UTILITY FUNCTIONS ===== - const isRepeaterInputError = ( + const getPurchaseItemError = ( idx: number, field: | 'purchase_item_id' @@ -396,466 +392,330 @@ const PurchaseOrderAcceptApprovalForm = ({ } }; - // ===== TABLE COLUMNS DEFINITION ===== - const columns = useMemo( - () => [ - { - accessorKey: 'purchase_item', - header: () => ( -
- Item - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - purchaseItemChangeHandler(idx, val)} - options={getPurchaseItemOptions()} - isError={isRepeaterInputError(idx, 'purchase_item_id').isError} - errorMessage={ - isRepeaterInputError(idx, 'purchase_item_id').errorMessage - } - placeholder='Pilih Item...' - className={{ - wrapper: 'min-w-52 md:min-w-72 lg:min-w-80', - }} - /> - ); - }, - }, - { - accessorKey: 'warehouse_id', - header: () => ( -
- Gudang Tujuan - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - warehouseChangeHandler(idx, val)} - options={getWarehouseOptions()} - isError={isRepeaterInputError(idx, 'warehouse_id').isError} - errorMessage={ - isRepeaterInputError(idx, 'warehouse_id').errorMessage - } - placeholder='Pilih Gudang...' - className={{ - wrapper: 'min-w-52 md:min-w-72 lg:min-w-80', - }} - /> - ); - }, - }, - { - accessorKey: 'travel_number', - header: () => ( -
- No. Surat Jalan - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - formik.setFieldValue( - `items.${idx}.travel_number`, - e.target.value - ) - } - onBlur={formik.handleBlur} - isError={isRepeaterInputError(idx, 'travel_number').isError} - errorMessage={ - isRepeaterInputError(idx, 'travel_number').errorMessage - } - placeholder='Masukkan no. surat jalan' - className={{ - wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', - }} - /> - ); - }, - }, - { - accessorKey: 'travel_document_path', - header: () => ( -
- Dokumen Surat Jalan - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - formik.setFieldValue( - `items.${idx}.travel_document_path`, - e.target.value - ) - } - onBlur={formik.handleBlur} - isError={ - isRepeaterInputError(idx, 'travel_document_path').isError - } - errorMessage={ - isRepeaterInputError(idx, 'travel_document_path').errorMessage - } - placeholder='Masukkan path dokumen' - className={{ - wrapper: 'min-w-52 md:min-w-72 lg:min-w-80', - }} - /> - ); - }, - }, - { - accessorKey: 'vehicle_number', - header: () => ( -
- Nomor Kendaraan - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - formik.setFieldValue( - `items.${idx}.vehicle_number`, - e.target.value - ) - } - onBlur={formik.handleBlur} - isError={isRepeaterInputError(idx, 'vehicle_number').isError} - errorMessage={ - isRepeaterInputError(idx, 'vehicle_number').errorMessage - } - placeholder='Masukkan nomor kendaraan' - className={{ - wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', - }} - /> - ); - }, - }, - { - accessorKey: 'expedition_vendor_id', - header: () => ( -
- Vendor Ekspedisi - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - expeditionVendorChangeHandler(idx, val)} - options={getExpeditionVendorOptions()} - isError={ - isRepeaterInputError(idx, 'expedition_vendor_id').isError - } - errorMessage={ - isRepeaterInputError(idx, 'expedition_vendor_id').errorMessage - } - placeholder='Pilih Vendor...' - className={{ - wrapper: 'min-w-48 md:min-w-64 lg:min-w-72', - }} - /> - ); - }, - }, - { - accessorKey: 'received_qty', - header: () => ( -
- Jumlah Diterima - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - handlePurchaseItemChange(idx, 'received_qty', e.target.value) - } - onBlur={formik.handleBlur} - placeholder='Masukkan jumlah diterima' - allowNegative={false} - decimalScale={0} - thousandSeparator=',' - decimalSeparator='.' - isError={isRepeaterInputError(idx, 'received_qty').isError} - errorMessage={ - isRepeaterInputError(idx, 'received_qty').errorMessage - } - className={{ - wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', - }} - /> - ); - }, - }, - { - accessorKey: 'transport_per_item', - header: () => ( -
- Transport/Item - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - handlePurchaseItemChange( - idx, - 'transport_per_item', - e.target.value - ) - } - onBlur={formik.handleBlur} - placeholder='Masukkan transport/item' - allowNegative={false} - decimalScale={2} - thousandSeparator=',' - decimalSeparator='.' - inputPrefix={'Rp'} - isError={isRepeaterInputError(idx, 'transport_per_item').isError} - errorMessage={ - isRepeaterInputError(idx, 'transport_per_item').errorMessage - } - className={{ - wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', - }} - /> - ); - }, - }, - { - accessorKey: 'transport_total', - header: () => ( -
- Total Transport - * -
- ), - cell: ( - props: TanStack.CellContext< - PurchaseRequisitionsAcceptApprovalFormValues['items'][0], - unknown - > - ) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - handlePurchaseItemChange(idx, 'transport_total', e.target.value) - } - onBlur={formik.handleBlur} - placeholder='Masukkan total transport' - allowNegative={false} - decimalScale={2} - thousandSeparator=',' - decimalSeparator='.' - inputPrefix={'Rp'} - isError={isRepeaterInputError(idx, 'transport_total').isError} - errorMessage={ - isRepeaterInputError(idx, 'transport_total').errorMessage - } - className={{ - wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', - }} - /> - ); - }, - }, - ], - [ - formik.values.items, - formik.handleBlur, - purchaseItemChangeHandler, - warehouseChangeHandler, - expeditionVendorChangeHandler, - handlePurchaseItemChange, - getPurchaseItemOptions, - getWarehouseOptions, - getExpeditionVendorOptions, - ] - ); - - const tableData = useMemo( - () => formik.values.items || [], - [formik.values.items] - ); - return (

Konfirmasi Penerimaan Produk

- - {/* Date Inputs Section - Above Table */} - -
- {formik.values.items?.map((item, idx) => ( -
- - formik.setFieldValue( - `items.${idx}.received_date`, - e.target.value - ) - } - onBlur={formik.handleBlur} - isError={isRepeaterInputError(idx, 'received_date').isError} - errorMessage={ - isRepeaterInputError(idx, 'received_date').errorMessage - } - placeholder='Pilih tanggal diterima' - className={{ - wrapper: 'w-full', - }} - isNestedModal={true} - /> -
- ))} -
-
- - - Belum ada data item... - - } - /> +
+
+ + + + + + + + + + + + + + + + {formik.values.items?.map((item, idx) => { + purchaseItems.find((p) => p.value === item.purchase_item_id); + return ( + + + + + + + + + + + + + ); + })} + +
+ Item + * + + Tanggal Diterima + * + + Gudang Tujuan + * + + No. Surat Jalan + * + + Dokumen Surat Jalan + * + + Nomor Kendaraan + * + + Vendor Ekspedisi + * + + Jumlah Diterima + * + + Transport/Item + * + + Total Transport + * +
+ purchaseItemChangeHandler(idx, val)} + options={getPurchaseItemOptions()} + isError={ + getPurchaseItemError(idx, 'purchase_item_id').isError + } + errorMessage={ + getPurchaseItemError(idx, 'purchase_item_id') + .errorMessage + } + placeholder='Pilih Item...' + className={{ + wrapper: 'min-w-52 md:min-w-72 lg:min-w-80', + }} + /> + + + formik.setFieldValue( + `items.${idx}.received_date`, + e.target.value + ) + } + onBlur={formik.handleBlur} + isError={ + getPurchaseItemError(idx, 'received_date').isError + } + errorMessage={ + getPurchaseItemError(idx, 'received_date') + .errorMessage + } + className={{ + wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', + }} + /> + + warehouseChangeHandler(idx, val)} + options={getWarehouseOptions()} + isError={ + getPurchaseItemError(idx, 'warehouse_id').isError + } + errorMessage={ + getPurchaseItemError(idx, 'warehouse_id').errorMessage + } + placeholder='Pilih Gudang...' + className={{ + wrapper: 'min-w-52 md:min-w-72 lg:min-w-80', + }} + /> + + + formik.setFieldValue( + `items.${idx}.travel_number`, + e.target.value + ) + } + onBlur={formik.handleBlur} + isError={ + getPurchaseItemError(idx, 'travel_number').isError + } + errorMessage={ + getPurchaseItemError(idx, 'travel_number') + .errorMessage + } + placeholder='Masukkan no. surat jalan' + className={{ + wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', + }} + /> + + + formik.setFieldValue( + `items.${idx}.travel_document_path`, + e.target.value + ) + } + onBlur={formik.handleBlur} + isError={ + getPurchaseItemError(idx, 'travel_document_path') + .isError + } + errorMessage={ + getPurchaseItemError(idx, 'travel_document_path') + .errorMessage + } + placeholder='Masukkan path dokumen' + className={{ + wrapper: 'min-w-52 md:min-w-72 lg:min-w-80', + }} + /> + + + formik.setFieldValue( + `items.${idx}.vehicle_number`, + e.target.value + ) + } + onBlur={formik.handleBlur} + isError={ + getPurchaseItemError(idx, 'vehicle_number').isError + } + errorMessage={ + getPurchaseItemError(idx, 'vehicle_number') + .errorMessage + } + placeholder='Masukkan nomor kendaraan' + className={{ + wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', + }} + /> + + + expeditionVendorChangeHandler(idx, val) + } + options={getExpeditionVendorOptions()} + isError={ + getPurchaseItemError(idx, 'expedition_vendor_id') + .isError + } + errorMessage={ + getPurchaseItemError(idx, 'expedition_vendor_id') + .errorMessage + } + placeholder='Pilih Vendor...' + className={{ + wrapper: 'min-w-48 md:min-w-64 lg:min-w-72', + }} + /> + + + handlePurchaseItemChange( + idx, + 'received_qty', + e.target.value + ) + } + onBlur={formik.handleBlur} + placeholder='Masukkan jumlah diterima' + allowNegative={false} + decimalScale={0} + thousandSeparator=',' + decimalSeparator='.' + isError={ + getPurchaseItemError(idx, 'received_qty').isError + } + errorMessage={ + getPurchaseItemError(idx, 'received_qty').errorMessage + } + className={{ + wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', + }} + /> + + + handlePurchaseItemChange( + idx, + 'transport_per_item', + e.target.value + ) + } + onBlur={formik.handleBlur} + placeholder='Masukkan transport/item' + allowNegative={false} + decimalScale={2} + thousandSeparator=',' + decimalSeparator='.' + inputPrefix={'Rp'} + isError={ + getPurchaseItemError(idx, 'transport_per_item') + .isError + } + errorMessage={ + getPurchaseItemError(idx, 'transport_per_item') + .errorMessage + } + className={{ + wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', + }} + /> + + + handlePurchaseItemChange( + idx, + 'transport_total', + e.target.value + ) + } + onBlur={formik.handleBlur} + placeholder='Masukkan total transport' + allowNegative={false} + decimalScale={2} + thousandSeparator=',' + decimalSeparator='.' + inputPrefix={'Rp'} + isError={ + getPurchaseItemError(idx, 'transport_total').isError + } + errorMessage={ + getPurchaseItemError(idx, 'transport_total') + .errorMessage + } + className={{ + wrapper: 'min-w-40 md:min-w-52 lg:min-w-64', + }} + /> +
+
{ @@ -301,268 +298,6 @@ const PurchaseOrderStaffApprovalForm = ({ } }; - // ===== TABLE COLUMNS DEFINITION ===== - const columns = useMemo( - () => [ - { - accessorKey: 'purchase_item', - header: () => ( -
- Item - * -
- ), - cell: (props: TanStack.CellContext) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - purchaseItemChangeHandler(idx, val) - } - options={getPurchaseItemOptions()} - isError={ - isRepeaterInputError(idx, 'purchase_item_id') - .isError - } - errorMessage={ - isRepeaterInputError(idx, 'purchase_item_id') - .errorMessage - } - placeholder='Pilih Item...' - className={{ - wrapper: 'min-w-52 md:min-w-72 lg:min-w-80', - }} - /> - ); - }, - }, - { - accessorKey: 'warehouse', - header: 'Gudang', - cell: (props: TanStack.CellContext) => { - const idx = props.row.index; - const selectedItem = purchaseItems.find( - (p) => p.value === formik.values.items?.[idx]?.purchase_item_id - ); - - return ( - - ); - }, - }, - { - accessorKey: 'product_name', - header: 'Produk', - cell: (props: TanStack.CellContext) => { - const idx = props.row.index; - const selectedItem = purchaseItems.find( - (p) => p.value === formik.values.items?.[idx]?.purchase_item_id - ); - - return ( - - ); - }, - }, - { - accessorKey: 'product_category', - header: 'Jenis Produk', - cell: (props: TanStack.CellContext) => { - const idx = props.row.index; - const selectedItem = purchaseItems.find( - (p) => p.value === formik.values.items?.[idx]?.purchase_item_id - ); - - return ( - - ); - }, - }, - { - accessorKey: 'quantity', - header: 'Jumlah', - cell: (props: TanStack.CellContext) => { - const idx = props.row.index; - const selectedItem = purchaseItems.find( - (p) => p.value === formik.values.items?.[idx]?.purchase_item_id - ); - - return ( - - ); - }, - }, - { - accessorKey: 'uom', - header: 'Satuan', - cell: (props: TanStack.CellContext) => { - const idx = props.row.index; - const selectedItem = purchaseItems.find( - (p) => p.value === formik.values.items?.[idx]?.purchase_item_id - ); - - return ( - - ); - }, - }, - { - accessorKey: 'price', - header: () => ( -
- Harga Satuan - * -
- ), - cell: (props: TanStack.CellContext) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - handlePurchaseItemChange( - idx, - 'price', - e.target.value - ) - } - onBlur={formik.handleBlur} - placeholder='Masukkan harga satuan' - allowNegative={false} - decimalScale={2} - thousandSeparator=',' - decimalSeparator='.' - inputPrefix={'Rp'} - isError={isRepeaterInputError(idx, 'price').isError} - errorMessage={ - isRepeaterInputError(idx, 'price').errorMessage - } - className={{ - wrapper: 'min-w-48 md:min-w-64 lg:min-w-72', - }} - /> - ); - }, - }, - { - accessorKey: 'total_price', - header: () => ( -
- Total (Rp.) - * -
- ), - cell: (props: TanStack.CellContext) => { - const idx = props.row.index; - const item = formik.values.items?.[idx]; - - return ( - - handlePurchaseItemChange( - idx, - 'total_price', - e.target.value - ) - } - onBlur={formik.handleBlur} - placeholder='Masukkan total harga' - allowNegative={false} - decimalScale={2} - thousandSeparator=',' - decimalSeparator='.' - inputPrefix={'Rp'} - isError={ - isRepeaterInputError(idx, 'total_price').isError - } - errorMessage={ - isRepeaterInputError(idx, 'total_price') - .errorMessage - } - className={{ - wrapper: 'min-w-48 md:min-w-64 lg:min-w-72', - }} - /> - ); - }, - }, - ], - [formik.values.items, formik.handleBlur, purchaseItemChangeHandler, handlePurchaseItemChange, getPurchaseItemOptions, purchaseItems] - ); - - const tableData = useMemo(() => formik.values.items || [], [formik.values.items]); - return ( <> Konfirmasi Approve Pembelian - - - Belum ada data item... - - - } - /> +
+
+ + + + + + + + + + + + + + {formik.values.items?.map((item, idx) => { + const selectedPurchaseItem = purchaseItems.find( + (p) => p.value === item.purchase_item_id + ); + return ( + + + + + + + + + + + ); + })} + +
+ Item + * + GudangProdukJenis ProdukJumlahSatuan + Harga Satuan + * + + Total (Rp.) + * +
+ + purchaseItemChangeHandler(idx, val) + } + options={getPurchaseItemOptions()} + isError={ + getPurchaseItemError(idx, 'purchase_item_id') + .isError + } + errorMessage={ + getPurchaseItemError(idx, 'purchase_item_id') + .errorMessage + } + placeholder='Pilih Item...' + className={{ + wrapper: 'min-w-52 md:min-w-72 lg:min-w-80', + }} + /> + + + + + + + + + + + + + handlePurchaseItemChange( + idx, + 'price', + e.target.value + ) + } + onBlur={formik.handleBlur} + placeholder='Masukkan harga satuan' + allowNegative={false} + decimalScale={2} + thousandSeparator=',' + decimalSeparator='.' + inputPrefix={'Rp'} + isError={getPurchaseItemError(idx, 'price').isError} + errorMessage={ + getPurchaseItemError(idx, 'price').errorMessage + } + className={{ + wrapper: 'min-w-48 md:min-w-64 lg:min-w-72', + }} + /> + + + handlePurchaseItemChange( + idx, + 'total_price', + e.target.value + ) + } + onBlur={formik.handleBlur} + placeholder='Masukkan total harga' + allowNegative={false} + decimalScale={2} + thousandSeparator=',' + decimalSeparator='.' + inputPrefix={'Rp'} + isError={ + getPurchaseItemError(idx, 'total_price').isError + } + errorMessage={ + getPurchaseItemError(idx, 'total_price') + .errorMessage + } + className={{ + wrapper: 'min-w-48 md:min-w-64 lg:min-w-72', + }} + /> +
+