feat(FE-208,212): enhance PurchaseOrder forms with onCancel functionality and UI improvements

This commit is contained in:
rstubryan
2025-11-11 15:05:05 +07:00
parent 8c17367fb6
commit ecb497430a
3 changed files with 544 additions and 505 deletions
@@ -10,6 +10,7 @@ import Table from '@/components/Table';
import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import DebouncedTextInput from '@/components/input/DebouncedTextInput';
import Button from '@/components/Button'; import Button from '@/components/Button';
import { useModal } from '@/components/Modal'; import { useModal } from '@/components/Modal';
import Modal from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, { import SelectInput, {
OptionType, OptionType,
@@ -21,6 +22,8 @@ import TextInput from '@/components/input/TextInput';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import { cn, formatDate, formatCurrency } from '@/lib/helper'; import { cn, formatDate, formatCurrency } from '@/lib/helper';
import PurchaseOrderStaffApprovalForm from '@/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm';
import PurchaseOrderAcceptApprovalForm from '@/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm';
import { isResponseSuccess } from '@/lib/api-helper'; import { isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter'; import { useTableFilter } from '@/services/hooks/useTableFilter';
import { ROWS_OPTIONS } from '@/config/constant'; import { ROWS_OPTIONS } from '@/config/constant';
@@ -108,6 +111,8 @@ const PurchaseTable = () => {
// Modal hooks // Modal hooks
const deleteModal = useModal(); const deleteModal = useModal();
const staffApprovalModal = useModal();
const acceptApprovalModal = useModal();
// Supplier modal // Supplier modal
const { const {
@@ -282,6 +287,30 @@ const PurchaseTable = () => {
Tambah Tambah
</Button> </Button>
<Button
onClick={() => staffApprovalModal.openModal()}
variant='outline'
color='info'
className='w-full sm:w-fit'
>
<Icon icon='mdi:account-check-outline' width={24} height={24} />
Staff Approval
</Button>
<Button
onClick={() => acceptApprovalModal.openModal()}
variant='outline'
color='success'
className='w-full sm:w-fit'
>
<Icon
icon='mdi:package-variant-closed-check'
width={24}
height={24}
/>
Accept Approval
</Button>
{selectedRowIds.length > 0 && ( {selectedRowIds.length > 0 && (
<Button <Button
variant='outline' variant='outline'
@@ -418,6 +447,34 @@ const PurchaseTable = () => {
onClick: confirmationModalDeleteClickHandler, onClick: confirmationModalDeleteClickHandler,
}} }}
/> />
{/* Staff Approval Modal */}
<Modal
ref={staffApprovalModal.ref}
closeOnBackdrop
className={{
modalBox: 'w-full max-w-6xl max-h-[90vh] overflow-y-auto'
}}
>
<PurchaseOrderStaffApprovalForm
type='add'
onCancel={staffApprovalModal.closeModal}
/>
</Modal>
{/* Accept Approval Modal */}
<Modal
ref={acceptApprovalModal.ref}
closeOnBackdrop
className={{
modalBox: 'w-full max-w-6xl max-h-[90vh] overflow-y-auto'
}}
>
<PurchaseOrderAcceptApprovalForm
type='add'
onCancel={acceptApprovalModal.closeModal}
/>
</Modal>
</> </>
); );
}; };
@@ -23,16 +23,16 @@ import {
Purchase, Purchase,
} from '@/types/api/purchase/purchase'; } from '@/types/api/purchase/purchase';
import Card from '@/components/Card';
interface PurchaseOrderAcceptApprovalFormProps { interface PurchaseOrderAcceptApprovalFormProps {
type?: 'add' | 'edit'; type?: 'add' | 'edit';
initialValues?: Purchase; initialValues?: Purchase;
onCancel?: () => void;
} }
const PurchaseOrderAcceptApprovalForm = ({ const PurchaseOrderAcceptApprovalForm = ({
type = 'add', type = 'add',
initialValues, initialValues,
onCancel,
}: PurchaseOrderAcceptApprovalFormProps) => { }: PurchaseOrderAcceptApprovalFormProps) => {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const [purchaseOrderFormErrorMessage, setPurchaseOrderFormErrorMessage] = const [purchaseOrderFormErrorMessage, setPurchaseOrderFormErrorMessage] =
@@ -121,6 +121,7 @@ const PurchaseOrderAcceptApprovalForm = ({
return; return;
} }
toast.success(res?.message as string); toast.success(res?.message as string);
onCancel?.();
}, },
[initialValues?.id, searchParams] [initialValues?.id, searchParams]
); );
@@ -136,6 +137,7 @@ const PurchaseOrderAcceptApprovalForm = ({
return; return;
} }
toast.success(res?.message as string); toast.success(res?.message as string);
onCancel?.();
window.location.href = '/purchase'; window.location.href = '/purchase';
}, },
[] []
@@ -330,10 +332,7 @@ const PurchaseOrderAcceptApprovalForm = ({
formik.setFieldTouched(`items.${idx}.warehouse`, true); formik.setFieldTouched(`items.${idx}.warehouse`, true);
formik.setFieldValue(`items.${idx}.warehouse`, warehouse); formik.setFieldValue(`items.${idx}.warehouse`, warehouse);
formik.setFieldTouched(`items.${idx}.warehouse_id`, true); formik.setFieldTouched(`items.${idx}.warehouse_id`, true);
formik.setFieldValue( formik.setFieldValue(`items.${idx}.warehouse_id`, warehouse?.value || 0);
`items.${idx}.warehouse_id`,
warehouse?.value || 0
);
}; };
const expeditionVendorChangeHandler = ( const expeditionVendorChangeHandler = (
@@ -394,19 +393,11 @@ const PurchaseOrderAcceptApprovalForm = ({
}; };
return ( return (
<> <form onSubmit={formik.handleSubmit} className='w-full flex flex-col gap-6'>
<section className='w-full'> <div className='w-full'>
<form <h2 className='text-lg font-semibold mb-4'>
onSubmit={formik.handleSubmit} Konfirmasi Penerimaan Produk
className='w-full mt-8 flex flex-col gap-6' </h2>
>
<Card
title='Konfirmasi Penerimaan Produk'
className={{
wrapper: 'w-full mb-4 shadow',
title: 'mb-4',
}}
>
<div className='overflow-x-auto'> <div className='overflow-x-auto'>
<table className='table'> <table className='table'>
<thead> <thead>
@@ -471,13 +462,10 @@ const PurchaseOrderAcceptApprovalForm = ({
isClearable={true} isClearable={true}
value={item.purchase_item} value={item.purchase_item}
key={`purchase-item-${idx}`} key={`purchase-item-${idx}`}
onChange={(val) => onChange={(val) => purchaseItemChangeHandler(idx, val)}
purchaseItemChangeHandler(idx, val)
}
options={getPurchaseItemOptions()} options={getPurchaseItemOptions()}
isError={ isError={
getPurchaseItemError(idx, 'purchase_item_id') getPurchaseItemError(idx, 'purchase_item_id').isError
.isError
} }
errorMessage={ errorMessage={
getPurchaseItemError(idx, 'purchase_item_id') getPurchaseItemError(idx, 'purchase_item_id')
@@ -523,8 +511,8 @@ const PurchaseOrderAcceptApprovalForm = ({
typeof selectedPurchaseItem?.product typeof selectedPurchaseItem?.product
?.product_category === 'string' ?.product_category === 'string'
? selectedPurchaseItem.product.product_category ? selectedPurchaseItem.product.product_category
: selectedPurchaseItem?.product : selectedPurchaseItem?.product?.product_category
?.product_category?.name || '' ?.name || ''
} }
readOnly={true} readOnly={true}
placeholder='Pilih item terlebih dahulu' placeholder='Pilih item terlebih dahulu'
@@ -557,9 +545,7 @@ const PurchaseOrderAcceptApprovalForm = ({
<TextInput <TextInput
name={`items.${idx}.uom`} name={`items.${idx}.uom`}
type='text' type='text'
value={ value={selectedPurchaseItem?.product?.uom?.name || ''}
selectedPurchaseItem?.product?.uom?.name || ''
}
readOnly={true} readOnly={true}
placeholder='Pilih item terlebih dahulu' placeholder='Pilih item terlebih dahulu'
className={{ className={{
@@ -605,8 +591,7 @@ const PurchaseOrderAcceptApprovalForm = ({
getPurchaseItemError(idx, 'warehouse_id').isError getPurchaseItemError(idx, 'warehouse_id').isError
} }
errorMessage={ errorMessage={
getPurchaseItemError(idx, 'warehouse_id') getPurchaseItemError(idx, 'warehouse_id').errorMessage
.errorMessage
} }
placeholder='Pilih Gudang...' placeholder='Pilih Gudang...'
className={{ className={{
@@ -681,8 +666,7 @@ const PurchaseOrderAcceptApprovalForm = ({
} }
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
isError={ isError={
getPurchaseItemError(idx, 'vehicle_number') getPurchaseItemError(idx, 'vehicle_number').isError
.isError
} }
errorMessage={ errorMessage={
getPurchaseItemError(idx, 'vehicle_number') getPurchaseItemError(idx, 'vehicle_number')
@@ -740,8 +724,7 @@ const PurchaseOrderAcceptApprovalForm = ({
getPurchaseItemError(idx, 'received_qty').isError getPurchaseItemError(idx, 'received_qty').isError
} }
errorMessage={ errorMessage={
getPurchaseItemError(idx, 'received_qty') getPurchaseItemError(idx, 'received_qty').errorMessage
.errorMessage
} }
className={{ className={{
wrapper: 'min-w-32', wrapper: 'min-w-32',
@@ -800,8 +783,7 @@ const PurchaseOrderAcceptApprovalForm = ({
decimalSeparator='.' decimalSeparator='.'
inputPrefix={'Rp'} inputPrefix={'Rp'}
isError={ isError={
getPurchaseItemError(idx, 'transport_total') getPurchaseItemError(idx, 'transport_total').isError
.isError
} }
errorMessage={ errorMessage={
getPurchaseItemError(idx, 'transport_total') getPurchaseItemError(idx, 'transport_total')
@@ -834,11 +816,14 @@ const PurchaseOrderAcceptApprovalForm = ({
{/* Action buttons */} {/* Action buttons */}
<div className='flex flex-row justify-between gap-2 flex-wrap mt-5'> <div className='flex flex-row justify-between gap-2 flex-wrap mt-5'>
<div className='flex flex-row justify-end gap-2 w-full'> <div className='flex flex-row justify-end gap-2 w-full'>
<Link href='/purchase'> <Button
<Button color='warning' className='px-4'> type='button'
color='warning'
className='px-4'
onClick={onCancel}
>
Cancel Cancel
</Button> </Button>
</Link>
<Button <Button
type='submit' type='submit'
@@ -862,10 +847,8 @@ const PurchaseOrderAcceptApprovalForm = ({
<span>{purchaseOrderFormErrorMessage}</span> <span>{purchaseOrderFormErrorMessage}</span>
</div> </div>
)} )}
</Card> </div>
</form> </form>
</section>
</>
); );
}; };
@@ -23,16 +23,17 @@ import {
Purchase, Purchase,
} from '@/types/api/purchase/purchase'; } from '@/types/api/purchase/purchase';
import Card from '@/components/Card';
interface PurchaseOrderStaffApprovalFormProps { interface PurchaseOrderStaffApprovalFormProps {
type?: 'add' | 'edit'; type?: 'add' | 'edit';
initialValues?: Purchase; initialValues?: Purchase;
onCancel?: () => void;
} }
const PurchaseOrderStaffApprovalForm = ({ const PurchaseOrderStaffApprovalForm = ({
type = 'add', type = 'add',
initialValues, initialValues,
onCancel,
}: PurchaseOrderStaffApprovalFormProps) => { }: PurchaseOrderStaffApprovalFormProps) => {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const [purchaseOrderFormErrorMessage, setPurchaseOrderFormErrorMessage] = const [purchaseOrderFormErrorMessage, setPurchaseOrderFormErrorMessage] =
@@ -99,6 +100,7 @@ const PurchaseOrderStaffApprovalForm = ({
return; return;
} }
toast.success(res?.message as string); toast.success(res?.message as string);
onCancel?.();
}, },
[initialValues?.id, searchParams] [initialValues?.id, searchParams]
); );
@@ -117,6 +119,7 @@ const PurchaseOrderStaffApprovalForm = ({
return; return;
} }
toast.success(res?.message as string); toast.success(res?.message as string);
onCancel?.();
window.location.href = '/purchase'; window.location.href = '/purchase';
}, },
[] []
@@ -299,18 +302,12 @@ const PurchaseOrderStaffApprovalForm = ({
return ( return (
<> <>
<section className='w-full'>
<form <form
onSubmit={formik.handleSubmit} onSubmit={formik.handleSubmit}
className='w-full mt-8 flex flex-col gap-6' className='w-full flex flex-col gap-6'
>
<Card
title='Konfirmasi Approve Pembelian'
className={{
wrapper: 'w-full mb-4 shadow',
title: 'mb-4',
}}
> >
<div className='w-full'>
<h2 className='text-lg font-semibold mb-4'>Konfirmasi Approve Pembelian</h2>
<div className='overflow-x-auto'> <div className='overflow-x-auto'>
<table className='table'> <table className='table'>
<thead> <thead>
@@ -525,11 +522,14 @@ const PurchaseOrderStaffApprovalForm = ({
{/* Action buttons */} {/* Action buttons */}
<div className='flex flex-row justify-between gap-2 flex-wrap mt-5'> <div className='flex flex-row justify-between gap-2 flex-wrap mt-5'>
<div className='flex flex-row justify-end gap-2 w-full'> <div className='flex flex-row justify-end gap-2 w-full'>
<Link href='/purchase'> <Button
<Button color='warning' className='px-4'> type='button'
color='warning'
className='px-4'
onClick={onCancel}
>
Cancel Cancel
</Button> </Button>
</Link>
<Button <Button
type='submit' type='submit'
@@ -553,9 +553,8 @@ const PurchaseOrderStaffApprovalForm = ({
<span>{purchaseOrderFormErrorMessage}</span> <span>{purchaseOrderFormErrorMessage}</span>
</div> </div>
)} )}
</Card> </div>
</form> </form>
</section>
</> </>
); );
}; };