refactor(FE-208,212,213): add action field to staff approval request payloads and implement rejection handling in PurchaseOrderDetail

This commit is contained in:
rstubryan
2025-11-20 23:05:12 +07:00
parent 24e2bcf35d
commit a6d187a8b3
3 changed files with 77 additions and 1 deletions
@@ -141,6 +141,7 @@ const PurchaseOrderStaffApprovalForm = ({
if (type === 'add') { if (type === 'add') {
const createPayload: CreateStaffApprovalRequestPayload = { const createPayload: CreateStaffApprovalRequestPayload = {
action: 'APPROVED',
notes: values.notes || '', notes: values.notes || '',
items: purchaseItems.map((purchaseItem, idx) => { items: purchaseItems.map((purchaseItem, idx) => {
const formItem = values.items?.[idx]; const formItem = values.items?.[idx];
@@ -163,6 +164,7 @@ const PurchaseOrderStaffApprovalForm = ({
await createStaffApprovalHandler(createPayload); await createStaffApprovalHandler(createPayload);
} else if (type === 'edit') { } else if (type === 'edit') {
const updatePayload: UpdateStaffApprovalRequestPayload = { const updatePayload: UpdateStaffApprovalRequestPayload = {
action: 'APPROVED',
notes: values.notes || null, notes: values.notes || null,
items: purchaseItems.map((purchaseItem, idx) => { items: purchaseItems.map((purchaseItem, idx) => {
const formItem = values.items?.[idx]; const formItem = values.items?.[idx];
@@ -23,6 +23,7 @@ import PurchaseOrderInvoice from '@/components/pages/purchase/order/PurchaseOrde
import Card from '@/components/Card'; import Card from '@/components/Card';
import { import {
CreateManagerApprovalRequestPayload, CreateManagerApprovalRequestPayload,
CreateStaffApprovalRequestPayload,
Purchase, Purchase,
PurchaseItem, PurchaseItem,
} from '@/types/api/purchase/purchase'; } from '@/types/api/purchase/purchase';
@@ -78,6 +79,7 @@ const PurchaseOrderDetail = ({
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const confirmationModalWithNotes = useModal(); const confirmationModalWithNotes = useModal();
const staffApprovalModal = useModal(); const staffApprovalModal = useModal();
const staffRejectionModal = useModal();
const acceptApprovalModal = useModal(); const acceptApprovalModal = useModal();
const editModal = useModal(); const editModal = useModal();
const penerimaanBarangModal = useModal(); const penerimaanBarangModal = useModal();
@@ -172,6 +174,18 @@ const PurchaseOrderDetail = ({
} }
}; };
const handleRejectionClick = () => {
if (!approvalStep) return;
switch (approvalStep) {
case 1:
staffRejectionModal.openModal();
break;
default:
break;
}
};
useMemo(() => { useMemo(() => {
if (!initialValues?.approval) return false; if (!initialValues?.approval) return false;
@@ -216,6 +230,32 @@ const PurchaseOrderDetail = ({
}, [purchaseOrderItems]); }, [purchaseOrderItems]);
// ===== SUBMISSION HANDLER ===== // ===== SUBMISSION HANDLER =====
const createStaffApprovalHandler = useCallback(
async (payload: CreateStaffApprovalRequestPayload) => {
const purchaseRequestId = searchParams.get('purchaseId')
? parseInt(searchParams.get('purchaseId')!)
: initialValues?.id || 1;
if (!purchaseRequestId) {
toast.error('Purchase Request ID is required');
return;
}
const res = await PurchaseApi.staffApproval.create(
purchaseRequestId,
payload
);
if (isResponseError(res)) {
toast.error(res.message);
return;
}
toast.success(res?.message as string);
refreshApprovals();
},
[initialValues?.id, searchParams, refreshApprovals]
);
const createManagerApprovalHandler = useCallback( const createManagerApprovalHandler = useCallback(
async (payload: CreateManagerApprovalRequestPayload) => { async (payload: CreateManagerApprovalRequestPayload) => {
const purchaseRequestId = searchParams.get('purchaseId') const purchaseRequestId = searchParams.get('purchaseId')
@@ -541,7 +581,12 @@ const PurchaseOrderDetail = ({
Approve Approve
</Button> </Button>
<Button variant='outline' color='error' className='w-full sm:w-fit'> <Button
variant='outline'
color='error'
className='w-full sm:w-fit'
onClick={handleRejectionClick}
>
<Icon icon='material-symbols:close' width={24} height={24} /> <Icon icon='material-symbols:close' width={24} height={24} />
Reject Reject
</Button> </Button>
@@ -934,6 +979,33 @@ const PurchaseOrderDetail = ({
/> />
</Modal> </Modal>
{/* Staff Rejection Modal */}
<ConfirmationModalWithNotes
ref={staffRejectionModal.ref}
type='error'
text='Apakah Anda yakin ingin menolak (reject) permintaan pembelian ini?'
placeholder='(Opsional) Masukkan alasan penolakan...'
rows={4}
closeOnBackdrop
primaryButton={{
text: 'Ya, Tolak',
color: 'error',
onClick: async (notes) => {
const payload: CreateStaffApprovalRequestPayload = {
action: 'REJECTED',
notes: notes || null,
items: [],
};
await createStaffApprovalHandler(payload);
staffRejectionModal.closeModal();
},
}}
secondaryButton={{
text: 'Batal',
}}
/>
{/* Delete Confirmation Modal */} {/* Delete Confirmation Modal */}
<ConfirmationModal <ConfirmationModal
ref={deleteModal.ref} ref={deleteModal.ref}
+2
View File
@@ -79,6 +79,7 @@ export type CreatePurchaseRequestPayload = {
}; };
export type CreateStaffApprovalRequestPayload = { export type CreateStaffApprovalRequestPayload = {
action: 'APPROVED' | 'REJECTED';
notes?: string | null; notes?: string | null;
items: { items: {
product_id: number; product_id: number;
@@ -90,6 +91,7 @@ export type CreateStaffApprovalRequestPayload = {
}; };
export type UpdateStaffApprovalRequestPayload = { export type UpdateStaffApprovalRequestPayload = {
action: 'APPROVED' | 'REJECTED';
notes?: string | null; notes?: string | null;
items: { items: {
purchase_item_id: number; purchase_item_id: number;