From 760e9ccd89c3ed69c810c1c74475daefe323353f Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 4 Feb 2026 14:13:59 +0700 Subject: [PATCH 1/2] refactor(FE): Add invoice download and PO table columns --- .../pages/purchase/PurchaseTable.tsx | 99 ++++++++++++++++++- .../purchase/order/PurchaseOrderInvoice.tsx | 30 +++++- src/types/api/purchase/purchase.d.ts | 18 +++- 3 files changed, 137 insertions(+), 10 deletions(-) diff --git a/src/components/pages/purchase/PurchaseTable.tsx b/src/components/pages/purchase/PurchaseTable.tsx index 11543eca..732541eb 100644 --- a/src/components/pages/purchase/PurchaseTable.tsx +++ b/src/components/pages/purchase/PurchaseTable.tsx @@ -17,6 +17,7 @@ import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RequirePermission from '@/components/helper/RequirePermission'; import StatusBadge from '@/components/helper/StatusBadge'; +import PurchaseOrderInvoice from '@/components/pages/purchase/order/PurchaseOrderInvoice'; import { cn, formatDate } from '@/lib/helper'; import { isResponseSuccess } from '@/lib/api-helper'; @@ -158,6 +159,27 @@ const PurchaseTable = () => { PurchaseApi.getAllFetcher ); + const [isDownloadingInvoice, setIsDownloadingInvoice] = useState(false); + const [invoicePurchaseData, setInvoicePurchaseData] = + useState(null); + + const handleDownloadInvoice = async (purchaseId: number) => { + setIsDownloadingInvoice(true); + try { + const response = await PurchaseApi.getSingle(purchaseId); + if (isResponseSuccess(response) && response.data) { + setInvoicePurchaseData(response.data); + setTimeout(() => { + setInvoicePurchaseData(null); + }, 1000); + } + } catch { + toast.error('Gagal mengambil data purchase order.'); + } finally { + setIsDownloadingInvoice(false); + } + }; + // ===== TABLE COLUMNS DEFINITION ===== const purchaseColumns: ColumnDef[] = [ { @@ -168,10 +190,66 @@ const PurchaseTable = () => { }, }, { - accessorKey: 'supplier', + accessorKey: 'po_expedition', + header: 'PO Ekspedisi', + cell: (props) => { + const purchase = props.row.original; + + if (!purchase.po_number || purchase.po_number === 'Belum dibuat') { + return -; + } + + return ( + + ); + }, + }, + { + accessorKey: 'supplier.name', header: 'Vendor', cell: (props) => props.row.original.supplier.name, }, + { + accessorKey: 'requester_name', + header: 'Nama Pengaju', + cell: (props) => props.row.original.requester_name || '-', + }, + { + accessorKey: 'products.name', + header: 'Produk', + cell: (props) => { + const products = props.row.original.products; + if (!products || products.length === 0) return '-'; + return ( +
    + {products.map((product, index) => ( +
  • {product.name}
  • + ))} +
+ ); + }, + }, + { + accessorKey: 'location.name', + header: 'Lokasi', + cell: (props) => props.row.original.location?.name || '-', + }, { accessorKey: 'po_date', header: 'Tgl. PO', @@ -180,6 +258,14 @@ const PurchaseTable = () => { ? formatDate(props.row.original.po_date, 'DD MMM YYYY') : '-', }, + { + accessorKey: 'due_date', + header: 'Jatuh Tempo', + cell: (props) => + props.row.original.due_date + ? formatDate(props.row.original.due_date, 'DD MMM YYYY') + : '-', + }, { header: 'Aging', cell: (props) => { @@ -231,7 +317,7 @@ const PurchaseTable = () => { color={statusColor} text={statusText} className={{ - badge: 'whitespace-nowrap', + badge: 'whitespace-nowrap max-w-max w-fit', }} /> ); @@ -409,6 +495,15 @@ const PurchaseTable = () => { onClick: confirmationModalDeleteClickHandler, }} /> + + {invoicePurchaseData && ( +
+ +
+ )} ); }; diff --git a/src/components/pages/purchase/order/PurchaseOrderInvoice.tsx b/src/components/pages/purchase/order/PurchaseOrderInvoice.tsx index aed154d0..4ad093e1 100644 --- a/src/components/pages/purchase/order/PurchaseOrderInvoice.tsx +++ b/src/components/pages/purchase/order/PurchaseOrderInvoice.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useMemo, useState } from 'react'; +import { useMemo, useState, useEffect, useCallback, useRef } from 'react'; import { Page, Text, @@ -235,11 +235,16 @@ const pdfStyles = StyleSheet.create({ interface PurchaseOrderInvoiceProps { data?: Purchase; className?: string; + triggerDownloadOnMount?: boolean; } -const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => { +const PurchaseOrderInvoice = ({ + data, + triggerDownloadOnMount, +}: PurchaseOrderInvoiceProps) => { const [, setIsGeneratingPDF] = useState(false); const purchaseData = data; + const hasDownloadedRef = useRef(false); const grandTotal = useMemo(() => { return ( @@ -250,7 +255,7 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => { ); }, [purchaseData?.items]); - const handleDownloadPDF = async () => { + const handleDownloadPDF = useCallback(async () => { if (!purchaseData) { toast.error('No purchase order data available'); return; @@ -510,7 +515,20 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => { } finally { setIsGeneratingPDF(false); } - }; + }, [purchaseData]); + + useEffect(() => { + if (triggerDownloadOnMount && purchaseData && !hasDownloadedRef.current) { + hasDownloadedRef.current = true; + handleDownloadPDF(); + } + }, [triggerDownloadOnMount, purchaseData]); + + useEffect(() => { + if (!triggerDownloadOnMount) { + hasDownloadedRef.current = false; + } + }, [triggerDownloadOnMount]); if (!purchaseData) { return ( @@ -520,6 +538,10 @@ const PurchaseOrderInvoice = ({ data }: PurchaseOrderInvoiceProps) => { ); } + if (triggerDownloadOnMount) { + return null; + } + return purchaseData?.po_number && purchaseData.po_number !== 'Belum dibuat' ? (