'use client'; import { useCallback, useMemo } from 'react'; import { ColumnDef } from '@tanstack/react-table'; import ApprovalSteps, { formatGroupedApprovalsToApprovalSteps, } from '@/components/pages/ApprovalSteps'; import Table from '@/components/Table'; import Button from '@/components/Button'; import { Icon } from '@iconify/react'; import { useModal } from '@/components/Modal'; import Modal from '@/components/Modal'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import PurchaseOrderStaffApprovalForm from '@/components/pages/purchase/form/order/PurchaseOrderStaffApprovalForm'; import PurchaseOrderAcceptApprovalForm from '@/components/pages/purchase/form/order/PurchaseOrderAcceptApprovalForm'; import PurchaseOrderInvoice from '@/components/pages/purchase/order/PurchaseOrderInvoice'; import { BaseGroupedApproval } from '@/types/api/api-general'; import { PURCHASE_ORDER_APPROVAL_LINE } from '@/config/approval-line'; import Card from '@/components/Card'; import { CreateManagerApprovalRequestPayload, Purchase, PurchaseItem, } from '@/types/api/purchase/purchase'; import { createdUser, dummyAreas, dummyLocations, dummyProductWarehouses, dummyWarehouses, } from '@/dummy/marketing.dummy'; import { ManagerApprovalApi } from '@/services/api/purchase'; import { isResponseError } from '@/lib/api-helper'; import { toast } from 'react-hot-toast'; import { useSearchParams } from 'next/navigation'; const ItemPembelianDropdown = ({ onEdit }: { onEdit: () => void }) => { return ( ); }; interface PurchaseOrderDetailProps { type?: 'detail' | 'edit'; data?: Purchase; } const dummyPurchaseData: Purchase = { id: 1, pr_number: 'PR-MBU-01837', po_number: 'PO-MBU-01837', po_document_path: '/documents/po-mbu-01837.pdf', po_date: '2025-01-10T00:00:00Z', supplier: { id: 1, name: 'PT. CHAROEN POKPHAND JAYA FARM', alias: 'CP JAYA FARM', pic: 'Budi Santoso', type: 'Supplier', category: 'Feed', hatchery: 'Jawa Barat', phone: '+62-22-7563850', email: 'info@cp.co.id', address: 'Jl. Raya Bandung - Sumedang Km. 28, Desa Cisantana, Kec. Cigendel, Kabupaten Sumedang, Jawa Barat 45363', npwp: '01.938.451.6-433.000', account_number: '123-456-7890', due_date: 30, balance: 0, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z', created_user: createdUser, }, credit_term: 30, due_date: '2025-11-13T00:00:00Z', grand_total: 65000000, notes: null, area: dummyAreas[0], location: dummyLocations[0], items: [ { id: 1, purchase_id: 1, product: { id: 1, name: 'CP Vaksin', brand: '', sku: '', product_price: 0, selling_price: 0, tax: 0, expiry_period: 0, uom: { id: 1, name: 'Ekor', created_user: { id: 1, id_user: 1, email: 'hello@gmail.com', name: 'Admin', }, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z', }, product_category: { id: 1, code: 'DOC', name: 'DOC', created_user: { id: 1, id_user: 1, email: 'hello@gmail.com', name: 'Admin', }, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z', }, suppliers: [], flags: [], created_user: { id: 1, id_user: 1, email: 'hello@gmail.com', name: 'Admin', }, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z', }, product_warehouse: dummyProductWarehouses[0], quantity: 10000, sub_qty: 10000, total_qty: 10000, total_used: 0, price: 6500, total_price: 65000000, received_date: null, travel_number: null, travel_number_docs: null, vehicle_number: null, warehouse: dummyWarehouses[0], }, ], created_at: '2025-01-10T00:00:00Z', updated_at: '2025-01-10T00:00:00Z', created_by: 1, deleted_at: null, created_user: createdUser, warehouse: dummyWarehouses[0], }; // Goods Receipt data - using items from PurchaseItem with received data const dummyGoodsReceiptItems: PurchaseItem[] = [ { id: 1, purchase_id: 1, product: { id: 1, product_category: { id: 1, code: 'DOC', name: 'DOC', created_user: createdUser, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z', }, name: 'CP Vaksin', brand: '', sku: '', product_price: 0, selling_price: 0, tax: 0, expiry_period: 0, uom: { id: 1, name: 'Ekor', created_user: createdUser, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z', }, suppliers: [], flags: [], created_user: { id: 1, id_user: 1, email: 'hello@gmail.com', name: 'Admin', }, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z', }, product_warehouse: dummyProductWarehouses[0], quantity: 10000, sub_qty: 10000, total_qty: 10000, total_used: 0, price: 6500, total_price: 65000000, received_date: '2025-01-15T00:00:00Z', travel_number: 'NSJ-1', travel_number_docs: '/documents/nsj-1.pdf', vehicle_number: 'NAP-1', warehouse: dummyWarehouses[0], }, ]; const dummyGroupedApprovals: BaseGroupedApproval[] = [ { step_number: 1, step_name: 'Pengajuan', approvals: [ { step_number: 1, step_name: 'Pengajuan', action: 'submit', notes: 'Pengajuan purchase order dibuat', action_by: { id: 1, id_user: 1, email: 'user@company.com', name: 'User Pengajuan', }, action_at: '2025-01-10T08:00:00Z', }, ], }, { step_number: 2, step_name: 'Approval Purchasing', approvals: [ { step_number: 2, step_name: 'Approval Purchasing', action: 'approve', notes: 'Purchase order disetujui oleh purchasing', action_by: { id: 2, id_user: 2, email: 'purchasing@company.com', name: 'Staff Purchasing', }, action_at: '2025-01-10T10:30:00Z', }, ], }, { step_number: 3, step_name: 'Approval Manager Purchasing', approvals: [ { step_number: 3, step_name: 'Approval Manager Purchasing', action: 'approve', notes: 'Purchase order disetujui oleh manager purchasing', action_by: { id: 3, id_user: 3, email: 'manager.purchasing@company.com', name: 'Manager Purchasing', }, action_at: '2025-01-10T14:15:00Z', }, ], }, { step_number: 4, step_name: 'Produk Diterima', approvals: [ { step_number: 4, step_name: 'Produk Diterima', action: 'receive', notes: 'Produk telah diterima sesuai pesanan', action_by: { id: 4, id_user: 4, email: 'user@company.com', name: 'User Pengajuan', }, action_at: '2025-01-12T09:00:00Z', }, ], }, { step_number: 5, step_name: 'Selesai', approvals: [ { step_number: 5, step_name: 'Selesai', action: 'complete', notes: 'Purchase order telah selesai diproses', action_by: { id: 5, id_user: 5, email: 'user@company.com', name: 'User Pengajuan', }, action_at: '2025-01-12T16:00:00Z', }, ], }, ]; const PurchaseOrderDetail = ({ type = 'detail', data, }: PurchaseOrderDetailProps) => { // ===== MODAL HOOKS ===== const searchParams = useSearchParams(); const confirmationModalWithNotes = useModal(); const staffApprovalModal = useModal(); const acceptApprovalModal = useModal(); const editModal = useModal(); // ===== STATIC DATA ===== const purchaseData = data || dummyPurchaseData; const purchaseOrderItems = purchaseData.items || []; const goodsReceiptItems = dummyGoodsReceiptItems; const groupedApprovals = dummyGroupedApprovals; const latestApproval = groupedApprovals[groupedApprovals.length - 1]?.approvals[0]; // ===== SUBMISSION HANDLER ===== const createManagerApprovalHandler = useCallback( async (payload: CreateManagerApprovalRequestPayload) => { const purchaseRequestId = searchParams.get('purchaseId') ? parseInt(searchParams.get('purchaseId')!) : purchaseData?.id || 1; if (!purchaseRequestId) { toast.error('Purchase Request ID is required'); return; } const res = await ManagerApprovalApi.createManagerApproval( purchaseRequestId, payload ); if (isResponseError(res)) { toast.error(res.message); return; } toast.success(res?.message as string); }, [purchaseData?.id, searchParams] ); // ===== COMPUTED VALUES ===== const approvalSteps = useMemo(() => { if (!groupedApprovals.length || !latestApproval) return []; try { return formatGroupedApprovalsToApprovalSteps( PURCHASE_ORDER_APPROVAL_LINE, groupedApprovals, latestApproval ); } catch (error) { console.error('Error formatting approval steps:', error); return []; } }, [groupedApprovals, latestApproval]); const totalBeforeTax = useMemo(() => { return purchaseOrderItems.reduce( (sum, item) => sum + (item.total_price || 0), 0 ); }, [purchaseOrderItems]); const formatCurrency = (value: number) => { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 2, }).format(value); }; const formatNumber = (value: number) => { return new Intl.NumberFormat('id-ID').format(value); }; const purchaseOrderColumns: ColumnDef[] = [ { header: 'No', cell: (props) => props.row.index + 1, }, { accessorKey: 'product.name', header: 'Produk', cell: (props) => props.row.original.product?.name || '-', }, { accessorKey: 'product.product_category.name', header: 'Jenis Produk', cell: (props) => props.row.original.product?.product_category?.name || '-', }, { accessorKey: 'quantity', header: 'Jumlah', cell: (props) => formatNumber(props.getValue() as number), }, { accessorKey: 'product.uom.name', header: 'Satuan', cell: (props) => props.row.original.product?.uom?.name || '-', }, { accessorKey: 'price', header: 'Harga Satuan', cell: (props) => formatCurrency(props.getValue() as number), }, { accessorKey: 'total_price', header: 'Total (Rp.)', cell: (props) => formatCurrency(props.getValue() as number), }, ]; const goodsReceiptColumns: ColumnDef[] = [ { accessorKey: 'received_date', header: 'Tanggal Penerimaan', cell: (props) => props.row.original.received_date ? new Date(props.row.original.received_date).toLocaleDateString( 'id-ID' ) : '-', }, { accessorKey: 'product_warehouse.warehouse.name', header: 'Gudang Tujuan', cell: (props) => props.row.original.product_warehouse?.warehouse?.name || '-', }, { accessorKey: 'travel_number', header: 'No. Surat Jalan', cell: (props) => props.row.original.travel_number || '-', }, { accessorKey: 'travel_number_docs', header: 'Dokumen Surat Jalan', cell: (props) => { const documentPath = props.row.original.travel_number_docs; return documentPath ? ( ) : ( '-' ); }, }, { accessorKey: 'vehicle_number', header: 'No. Armada', cell: (props) => props.row.original.vehicle_number || '-', }, { accessorKey: 'pengangkut', header: 'Pengangkut', cell: (props) => props.row.original.product?.name || '-', }, { accessorKey: 'quantity', header: 'Jumlah Total', cell: (props) => formatNumber(props.getValue() as number), }, { accessorKey: 'sub_qty', header: 'Jumlah Diterima', cell: (props) => formatNumber(props.getValue() as number), }, { accessorKey: 'ekspedisi', header: 'Ekspedisi', cell: (props) => 'Ekspedisi 1', }, { accessorKey: 'price', header: 'Transport /Item', cell: (props) => formatCurrency(props.getValue() as number), }, { accessorKey: 'total_price', header: 'Transport Total', cell: (props) => formatCurrency(props.getValue() as number), }, ]; const summaryData = [ { label: 'Total Sebelum Pajak', value: totalBeforeTax, }, { label: 'Total Pembayaran', value: totalBeforeTax, }, ]; const summaryColumns: ColumnDef<(typeof summaryData)[0]>[] = [ { accessorKey: 'label', header: '', cell: (props) => ( {props.getValue() as string} ), }, { accessorKey: 'value', header: '', cell: (props) => ( {formatCurrency(props.getValue() as number)} ), }, ]; return (
{/* Approval Action Buttons */}
{/* Steps */} {approvalSteps.length > 0 ? (
) : (

Status approval tidak tersedia

)} {/* Detail Purchase Order */} {/* Order Information */}

Informasi Pesanan

{/* Kolom 1 */}
Area : {purchaseData.area?.name || '-'}
Lokasi : {purchaseData.location?.name || '-'}
Gudang : {purchaseData.warehouse?.name || '-'}
{/* Kolom 2 */}
Nama Supplier : {purchaseData.supplier?.name || '-'}
Alamat Supplier : {purchaseData.supplier?.address || '-'}
Tgl. Jatuh Tempo :{' '} {new Date(purchaseData.due_date).toLocaleDateString( 'id-ID', { day: 'numeric', month: 'short', year: 'numeric', } )}{' '} ({purchaseData.credit_term} hari)
Nomor : {purchaseData.pr_number}
Nomor PO
{purchaseData.po_number && purchaseData.po_number !== 'Belum dibuat' ? ( ) : ( )}
{/* Item Pembelian Section */}

Item Pembelian

{/* Product Table */}
data={purchaseOrderItems} columns={purchaseOrderColumns} isLoading={false} className={{ containerClassName: 'm-0', tableWrapperClassName: 'overflow-x-auto', tableClassName: 'w-full table-auto', headerRowClassName: 'bg-gray-50 border-b border-gray-200', headerColumnClassName: 'px-6 py-4 text-sm font-semibold text-gray-700 text-left', bodyRowClassName: 'border-b border-gray-100 hover:bg-gray-50 transition-colors', bodyColumnClassName: 'px-6 py-4 text-sm text-gray-900', paginationClassName: 'hidden', }} />
{/* Bottom Section - Catatan dan Total */}
{/* Catatan Section */}

Catatan

{purchaseData.notes || 'Tidak ada catatan'}
{/* Summary Section */}
{/* Penerimaan Barang */} {/* Detail Penerimaan Barang Section */}

Informasi Penerimaan Barang

data={goodsReceiptItems} columns={goodsReceiptColumns} isLoading={false} className={{ containerClassName: 'm-0', tableWrapperClassName: 'overflow-x-auto', tableClassName: 'w-full table-auto', headerRowClassName: 'bg-gray-50 border-b border-gray-200', headerColumnClassName: 'px-4 py-3 text-sm font-semibold text-gray-700 text-left whitespace-nowrap', bodyRowClassName: 'border-b border-gray-100 hover:bg-gray-50 transition-colors', bodyColumnClassName: 'px-4 py-3 text-sm text-gray-900 whitespace-nowrap', paginationClassName: 'hidden', }} />
{/* Confirmation Modal with Notes */} { const payload: CreateManagerApprovalRequestPayload = { notes: notes || null, }; await createManagerApprovalHandler(payload); confirmationModalWithNotes.closeModal(); }, }} secondaryButton={{ text: 'Batal', }} /> {/* Staff Approval Modal */} {/* Accept Approval Modal */} {/* Edit Modal */} ); }; export default PurchaseOrderDetail;