diff --git a/src/app/report/finance/page.tsx b/src/app/report/finance/page.tsx new file mode 100644 index 00000000..ae2e85e0 --- /dev/null +++ b/src/app/report/finance/page.tsx @@ -0,0 +1,7 @@ +import FinanceTabs from '@/components/pages/report/finance/FinanceTabs'; + +const Finance = () => { + return ; +}; + +export default Finance; diff --git a/src/components/pages/report/finance/FinanceTabs.tsx b/src/components/pages/report/finance/FinanceTabs.tsx new file mode 100644 index 00000000..7a970c76 --- /dev/null +++ b/src/components/pages/report/finance/FinanceTabs.tsx @@ -0,0 +1,23 @@ +'use client'; + +import Tabs from '@/components/Tabs'; +import CustomerPaymentTab from '@/components/pages/report/finance/tab/CustomerPaymentTab'; + +const FinanceTabs = () => { + const tabs = [ + { + id: '1', + label: 'Kontrol Pembayaran Customer', + + content: , + }, + ]; + + return ( +
+ +
+ ); +}; + +export default FinanceTabs; diff --git a/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx b/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx new file mode 100644 index 00000000..e224d0f0 --- /dev/null +++ b/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx @@ -0,0 +1,425 @@ +'use client'; + +import { + Page, + Text, + View, + Document, + StyleSheet, + Font, + pdf, +} from '@react-pdf/renderer'; + +import { formatDate, formatCurrency, formatNumber } from '@/lib/helper'; +import { CustomerPaymentReport } from '@/types/api/report/customer-payment'; + +Font.register({ + family: 'Helvetica', + src: 'helvetica', +}); + +const pdfStyles = StyleSheet.create({ + page: { + fontSize: 10, + fontFamily: 'Helvetica', + padding: 20, + backgroundColor: '#FFFFFF', + }, + titleSection: { + marginBottom: 10, + }, + mainTitle: { + fontSize: 14, + fontWeight: 'bold', + marginBottom: 5, + color: '#1f74bf', + }, + supplierTitle: { + fontSize: 12, + fontWeight: 'bold', + marginBottom: 8, + color: '#1f74bf', + }, + supplierInfo: { + fontSize: 9, + marginBottom: 5, + color: '#333333', + }, + table: { + borderWidth: 1, + borderColor: '#000000', + marginBottom: 15, + }, + tableRow: { + flexDirection: 'row', + }, + tableHeader: { + backgroundColor: '#F5F5F5', + }, + tableCell: { + flex: 1, + borderRightWidth: 1, + borderRightColor: '#000000', + borderRightStyle: 'solid', + padding: 4, + fontSize: 7, + textAlign: 'left', + }, + tableCellNo: { + flex: 0.5, + borderRightWidth: 1, + borderRightColor: '#000000', + borderRightStyle: 'solid', + padding: 4, + fontSize: 7, + textAlign: 'center', + }, + tableCellLast: { + flex: 1, + padding: 4, + fontSize: 7, + }, + tableCellHeader: { + flex: 1, + borderRightWidth: 1, + borderRightColor: '#000000', + borderRightStyle: 'solid', + padding: 4, + fontSize: 7, + fontWeight: 'bold', + backgroundColor: '#F5F5F5', + borderBottomWidth: 1, + borderBottomColor: '#000000', + borderBottomStyle: 'solid', + paddingVertical: 12, + textAlign: 'center', + }, + tableCellHeaderRight: { + flex: 1, + borderRightWidth: 1, + borderRightColor: '#000000', + borderRightStyle: 'solid', + padding: 4, + fontSize: 7, + fontWeight: 'bold', + backgroundColor: '#F5F5F5', + textAlign: 'right', + borderBottomWidth: 1, + borderBottomColor: '#000000', + borderBottomStyle: 'solid', + paddingVertical: 12, + }, + tableCellRight: { + flex: 1, + borderRightWidth: 1, + borderRightColor: '#000000', + borderRightStyle: 'solid', + padding: 4, + fontSize: 7, + textAlign: 'right', + }, + tableCellCenter: { + flex: 1, + borderRightWidth: 1, + borderRightColor: '#000000', + borderRightStyle: 'solid', + padding: 4, + fontSize: 7, + textAlign: 'center', + }, + tableBorderBottom: { + borderBottomWidth: 1, + borderBottomColor: '#000000', + borderBottomStyle: 'solid', + }, + summaryRow: { + backgroundColor: '#F0F0F0', + fontWeight: 'bold', + }, +}); + +interface CustomerPaymentExportPDFParams { + data: CustomerPaymentReport[]; +} + +const createPDFDocument = (params: CustomerPaymentExportPDFParams) => { + return ( + + {params.data.map((customerReport, customerIndex) => ( + + {/* Title and Customer Info */} + + + Laporan > Kontrol Pembayaran Customer + + + {customerReport.customer.name} + + + {customerReport.customer_address || ''} + + + NPWP: {customerReport.customer_npwp || '-'} + + {customerReport.summary && ( + + Total Saldo Piutang:{' '} + {formatCurrency( + customerReport.summary.total_accounts_receivable + )} + + )} + + + {/* Table */} + + {/* Table Header */} + + + No + + + Tgl DO/Bayar + + + Tgl Realisasi + + + Aging + + + Referensi + + + No. Polisi + + + Qty + + + Berat (Kg) + + + AVG + + + Harga Awal + + + CN + + + Harga Akhir + + + PPN (%) + + + Total + + + Pembayaran + + + Saldo Piutang + + + Ket + + + Pengambilan + + + Sales + + + + {/* Table Body */} + {customerReport.rows.map((item, index) => ( + + + {index + 1} + + + + {item.do_date ? formatDate(item.do_date, 'DD MMM YY') : '-'} + + + + + {item.realization_date + ? formatDate(item.realization_date, 'DD MMM YY') + : '-'} + + + + {formatNumber(item.aging)} hari + + + {item.reference || '-'} + + + {item.vehicle_plate || '-'} + + + {formatNumber(item.qty)} + + + {formatNumber(item.weight)} + + + {formatNumber(item.average_weight)} + + + {formatCurrency(item.price)} + + + {formatCurrency(item.credit_note)} + + + {formatCurrency(item.final_price)} + + + {formatNumber(item.ppn)}% + + + {formatCurrency(item.total)} + + + {formatCurrency(item.payment)} + + + {formatCurrency(item.accounts_receivable)} + + + {item.notes || '-'} + + + {item.pickup_info || '-'} + + + {item.sales_marketing || '-'} + + + ))} + + {/* Summary Row */} + {customerReport.summary && ( + + + Total + + + + + + + + + + + + + + + + + + {formatNumber(customerReport.summary.total_qty)} + + + + {formatNumber(customerReport.summary.total_weight)} + + + + + + + + {formatCurrency( + customerReport.summary.total_initial_amount + )} + + + + + {formatCurrency(customerReport.summary.total_credit_note)} + + + + + {formatCurrency(customerReport.summary.total_final_amount)} + + + + + + + + {formatCurrency(customerReport.summary.total_grand_amount)} + + + + + {formatCurrency(customerReport.summary.total_payment)} + + + + + {formatCurrency( + customerReport.summary.total_accounts_receivable + )} + + + + + + + + + + + + + )} + + + ))} + + ); +}; + +export const generateCustomerPaymentPDF = async ( + params: CustomerPaymentExportPDFParams +): Promise => { + const PDFDocument = createPDFDocument(params); + + try { + const blob = await pdf(PDFDocument).toBlob(); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `laporan-kontrol-pembayaran-customer-${formatDate(new Date(), 'YYYY-MM-DD-HHmm')}.pdf`; + + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + } catch (error) { + throw error; + } +}; diff --git a/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx b/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx new file mode 100644 index 00000000..dc999f3c --- /dev/null +++ b/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx @@ -0,0 +1,109 @@ +'use client'; + +import * as XLSX from 'xlsx'; +import { formatDate, formatCurrency, formatNumber } from '@/lib/helper'; +import { CustomerPaymentReport } from '@/types/api/report/customer-payment'; + +interface CustomerPaymentExportExcelParams { + data: CustomerPaymentReport[]; +} + +export const generateCustomerPaymentExcel = ( + params: CustomerPaymentExportExcelParams +): void => { + if (!params.data || params.data.length === 0) { + return; + } + + const workbook = XLSX.utils.book_new(); + + params.data.forEach((customerReport) => { + const customerData = customerReport.rows; + const customerName = customerReport.customer.name || 'Unknown Customer'; + + const excelData: { [key: string]: string | number }[] = customerData.map( + (item, index) => ({ + No: index + 1, + 'Tanggal DO/Bayar': item.do_date + ? formatDate(item.do_date, 'DD MMM YYYY') + : '', + 'Tanggal Realisasi': item.realization_date + ? formatDate(item.realization_date, 'DD MMM YYYY') + : '', + Aging: item.aging || 0, + Referensi: item.reference || '', + 'Nomor Polisi': item.vehicle_plate || '', + 'Ekor/Qty': item.qty || 0, + 'Berat (Kg)': item.weight || 0, + AVG: item.average_weight || 0, + 'Harga Awal': item.price || 0, + CN: item.credit_note || 0, + 'Harga Akhir': item.final_price || 0, + 'PPN (%)': item.ppn || 0, + Total: item.total || 0, + Pembayaran: item.payment || 0, + 'Saldo Piutang': item.accounts_receivable || 0, + Keterangan: item.notes || '', + Pengambilan: item.pickup_info || '', + 'Sales/Marketing': item.sales_marketing || '', + }) + ); + + if (customerReport.summary) { + excelData.push({ + No: 'Total', + 'Tanggal DO/Bayar': '', + 'Tanggal Realisasi': '', + Aging: '', + Referensi: '', + 'Nomor Polisi': '', + 'Ekor/Qty': customerReport.summary.total_qty || 0, + 'Berat (Kg)': customerReport.summary.total_weight || 0, + AVG: '', + 'Harga Awal': customerReport.summary.total_initial_amount || 0, + CN: customerReport.summary.total_credit_note || 0, + 'Harga Akhir': customerReport.summary.total_final_amount || 0, + 'PPN (%)': '', + Total: customerReport.summary.total_grand_amount || 0, + Pembayaran: customerReport.summary.total_payment || 0, + 'Saldo Piutang': customerReport.summary.total_accounts_receivable || 0, + Keterangan: '', + Pengambilan: '', + 'Sales/Marketing': '', + }); + } + + const worksheet = XLSX.utils.json_to_sheet(excelData); + + const colWidths = [ + { wch: 5 }, // No + { wch: 15 }, // Tanggal DO/Bayar + { wch: 15 }, // Tanggal Realisasi + { wch: 8 }, // Aging + { wch: 12 }, // Referensi + { wch: 15 }, // Nomor Polisi + { wch: 10 }, // Ekor/Qty + { wch: 12 }, // Berat + { wch: 10 }, // AVG + { wch: 15 }, // Harga Awal + { wch: 10 }, // CN + { wch: 15 }, // Harga Akhir + { wch: 10 }, // PPN + { wch: 15 }, // Total + { wch: 15 }, // Pembayaran + { wch: 15 }, // Saldo Piutang + { wch: 20 }, // Keterangan + { wch: 15 }, // Pengambilan + { wch: 20 }, // Sales/Marketing + ]; + worksheet['!cols'] = colWidths; + + const sheetName = + customerName.length > 31 ? customerName.substring(0, 31) : customerName; + XLSX.utils.book_append_sheet(workbook, worksheet, sheetName); + }); + + const filename = `laporan-kontrol-pembayaran-customer-dicetak-pada-${formatDate(new Date(), 'YYYY-MM-DD-HHmm')}.xlsx`; + + XLSX.writeFile(workbook, filename); +}; diff --git a/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx b/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx new file mode 100644 index 00000000..b7bde263 --- /dev/null +++ b/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx @@ -0,0 +1,697 @@ +import { useState, useMemo, useCallback } from 'react'; +import { ChangeEventHandler } from 'react'; +import useSWR from 'swr'; +import Card from '@/components/Card'; +import SelectInput, { + useSelect, + OptionType, +} from '@/components/input/SelectInput'; +import DateInput from '@/components/input/DateInput'; +import { CustomerApi } from '@/services/api/master-data'; +import { FinanceApi } from '@/services/api/report/finance-report'; +import Table from '@/components/Table'; +import { ColumnDef } from '@tanstack/react-table'; +import { formatCurrency, formatDate, formatNumber } from '@/lib/helper'; +import { + CustomerPaymentReport, + CustomerPaymentSummary, +} from '@/types/api/report/customer-payment'; +import { isResponseSuccess } from '@/lib/api-helper'; +import { useTableFilter } from '@/services/hooks/useTableFilter'; +import Pagination from '@/components/Pagination'; +import Button from '@/components/Button'; +import Dropdown from '@/components/Dropdown'; +import MenuItem from '@/components/menu/MenuItem'; +import Menu from '@/components/menu/Menu'; +import toast from 'react-hot-toast'; +import { generateCustomerPaymentExcel } from '@/components/pages/report/finance/export/CustomerPaymentExportXLSX'; +import { generateCustomerPaymentPDF } from '@/components/pages/report/finance/export/CustomerPaymentExportPDF'; + +const CustomerPaymentTab = () => { + // ===== STATE MANAGEMENT ===== + const [isPdfExportLoading, setIsPdfExportLoading] = useState(false); + const [isExcelExportLoading, setIsExcelExportLoading] = useState(false); + const isAnyExportLoading = isPdfExportLoading || isExcelExportLoading; + + // ===== PAGINATION STATE ===== + const [currentPage, setCurrentPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + + // ===== SUBMISSION STATE ===== + const [isSubmitted, setIsSubmitted] = useState(false); + + // ===== TABLE FILTER STATE ===== + const { state: tableFilterState, updateFilter } = useTableFilter({ + initial: { + customer_id: [] as string[], + sales: [] as string[], + date_type: 'do_date' as 'do_date' | 'payment_date', + start_date: '', + end_date: '', + }, + paramMap: { + page: 'page', + pageSize: 'limit', + }, + }); + + const { options: customerOptions, isLoadingOptions: isLoadingCustomers } = + useSelect(CustomerApi.basePath, 'id', 'name', 'search'); + + const salesOptions = useMemo( + () => [ + { value: 'Sales A', label: 'Sales A' }, + { value: 'Sales B', label: 'Sales B' }, + { value: 'Sales C', label: 'Sales C' }, + // TODO: Fetch sales options from API + ], + [] + ); + + const dataTypeOptions = useMemo( + () => [ + { value: 'do_date', label: 'Tanggal Jual' }, + { value: 'payment_date', label: 'Tanggal Bayar' }, + ], + [] + ); + + const customerChangeHandler = useCallback( + (val: OptionType | OptionType[] | null) => { + const arr = Array.isArray(val) ? val : val ? [val] : []; + updateFilter( + 'customer_id', + arr.map((v) => String((v as OptionType).value)) + ); + setIsSubmitted(false); + }, + [updateFilter] + ); + + const salesChangeHandler = useCallback( + (val: OptionType | OptionType[] | null) => { + const arr = Array.isArray(val) ? val : val ? [val] : []; + updateFilter( + 'sales', + arr.map((v) => String((v as OptionType).value)) + ); + setIsSubmitted(false); + }, + [updateFilter] + ); + + const dataTypeChangeHandler = useCallback( + (val: OptionType | OptionType[] | null) => { + const newVal = val as OptionType; + const dateType = + (newVal?.value as 'do_date' | 'payment_date') || 'do_date'; + updateFilter('date_type', dateType); + setIsSubmitted(false); + }, + [updateFilter] + ); + + const startDateChangeHandler = useCallback< + ChangeEventHandler + >( + (e) => { + const val = e.target.value; + updateFilter('start_date', val || ''); + setIsSubmitted(false); + }, + [updateFilter] + ); + + const endDateChangeHandler = useCallback< + ChangeEventHandler + >( + (e) => { + const val = e.target.value; + updateFilter('end_date', val || ''); + setIsSubmitted(false); + }, + [updateFilter] + ); + + const resetFilters = useCallback(() => { + updateFilter('customer_id', []); + updateFilter('sales', []); + updateFilter('date_type', 'do_date'); + updateFilter('start_date', ''); + updateFilter('end_date', ''); + setIsSubmitted(false); + }, [updateFilter]); + + const handleSubmit = useCallback(() => { + setIsSubmitted(true); + setCurrentPage(1); + }, []); + + // ===== DATA FETCHING ===== + const { data: customerPayment, isLoading } = useSWR( + isSubmitted + ? () => { + const params = { + customer_id: + tableFilterState.customer_id.length > 0 + ? tableFilterState.customer_id.join(',') + : undefined, + sales: + tableFilterState.sales.length > 0 + ? tableFilterState.sales.join(',') + : undefined, + date_type: tableFilterState.date_type || undefined, + start_date: tableFilterState.start_date || undefined, + end_date: tableFilterState.end_date || undefined, + page: currentPage, + limit: pageSize, + }; + + return ['customer-payment-report', params]; + } + : null, + ([, params]) => + FinanceApi.getCustomerPaymentReport( + params.customer_id, + params.sales, + params.date_type, + params.start_date, + params.end_date, + params.page, + params.limit + ) + ); + + const data: CustomerPaymentReport[] = useMemo( + () => + isResponseSuccess(customerPayment) + ? (customerPayment?.data as unknown as CustomerPaymentReport[]) || [] + : [], + [customerPayment] + ); + + const meta = + isResponseSuccess(customerPayment) && customerPayment?.meta + ? customerPayment.meta + : null; + + // ===== EXPORT DATA FETCHER ===== + const customerPaymentExport = useCallback(async (): Promise< + CustomerPaymentReport[] | null + > => { + const params = { + customer_id: + tableFilterState.customer_id.length > 0 + ? tableFilterState.customer_id.join(',') + : undefined, + sales: + tableFilterState.sales.length > 0 + ? tableFilterState.sales.join(',') + : undefined, + date_type: tableFilterState.date_type || undefined, + start_date: tableFilterState.start_date || undefined, + end_date: tableFilterState.end_date || undefined, + limit: 100, + page: 1, + }; + + const response = await FinanceApi.getCustomerPaymentReport( + params.customer_id, + params.sales, + params.date_type, + params.start_date, + params.end_date, + params.page, + params.limit + ); + + return isResponseSuccess(response) + ? (response.data as unknown as CustomerPaymentReport[]) + : null; + }, [tableFilterState]); + + // ===== EXPORT HANDLERS ===== + const handleExportExcel = useCallback(async () => { + setIsExcelExportLoading(true); + try { + const allDataForExport = await customerPaymentExport(); + + if ( + !allDataForExport || + !Array.isArray(allDataForExport) || + allDataForExport.length === 0 + ) { + toast.error('Tidak ada data untuk diekspor.'); + return; + } + + generateCustomerPaymentExcel({ data: allDataForExport }); + toast.success('Excel berhasil dibuat dan diunduh.'); + } catch { + toast.error('Gagal membuat Excel. Silakan coba lagi.'); + } finally { + setIsExcelExportLoading(false); + } + }, [customerPaymentExport]); + + const handleExportPdf = useCallback(async () => { + setIsPdfExportLoading(true); + try { + const allDataForExport = await customerPaymentExport(); + + if ( + !allDataForExport || + !Array.isArray(allDataForExport) || + allDataForExport.length === 0 + ) { + toast.error('Tidak ada data untuk diekspor.'); + return; + } + + await generateCustomerPaymentPDF({ data: allDataForExport }); + toast.success('PDF berhasil dibuat dan diunduh.'); + } catch { + toast.error('Gagal membuat PDF. Silakan coba lagi.'); + } finally { + setIsPdfExportLoading(false); + } + }, [customerPaymentExport]); + + // ===== PAGINATION HANDLERS ===== + const handlePageChange = (page: number) => { + setCurrentPage(page); + }; + + const handleRowChange = (pageSize: number) => { + setPageSize(pageSize); + }; + + const handleNextPage = () => { + if (meta && currentPage < meta.total_pages) { + setCurrentPage(currentPage + 1); + } + }; + + const handlePrevPage = () => { + if (currentPage > 1) { + setCurrentPage(currentPage - 1); + } + }; + + const getTableColumns = ( + summary: CustomerPaymentSummary + ): ColumnDef[] => { + const tableColumns: ColumnDef[] = [ + { + id: 'no', + header: 'No', + cell: (props) => props.row.index + 1, + footer: () =>
Total
, + }, + { + id: 'do_date_or_payment_date', + header: 'Tanggal DO/Bayar', + accessorKey: 'do_date', + cell: (props) => { + const value = props.row.original.do_date; + return formatDate(value, 'DD MMM YYYY'); + }, + }, + { + id: 'realization_date', + header: 'Tanggal Realisasi', + accessorKey: 'realization_date', + cell: (props) => { + const value = props.row.original.realization_date; + return formatDate(value, 'DD MMM YYYY'); + }, + }, + { + id: 'aging', + header: 'Aging', + accessorKey: 'aging', + cell: (props) => { + const value = props.row.original.aging; + return
{formatNumber(value)} hari
; + }, + }, + { + id: 'reference', + header: 'Referensi', + accessorKey: 'reference', + cell: (props) => { + const value = props.row.original.reference; + return value || '-'; + }, + }, + { + id: 'vehicle_plate', + header: 'Nomor Polisi', + accessorKey: 'vehicle_plate', + cell: (props) => { + const value = props.row.original.vehicle_plate; + return value || '-'; + }, + }, + { + id: 'qty', + header: 'Ekor/Qty', + accessorKey: 'qty', + cell: (props) => { + const value = props.row.original.qty; + return
{formatNumber(value)}
; + }, + footer: () => ( +
+ {formatNumber(summary.total_qty) || '-'} +
+ ), + }, + { + id: 'weight', + header: 'Berat (Kg)', + accessorKey: 'weight', + cell: (props) => { + const value = props.row.original.weight; + return
{formatNumber(value)}
; + }, + footer: () => ( +
+ {formatNumber(summary.total_weight) || '-'} +
+ ), + }, + { + id: 'average_weight', + header: 'AVG', + accessorKey: 'average_weight', + cell: (props) => { + const value = props.row.original.average_weight; + return
{formatNumber(value)}
; + }, + footer: () => ( +
-
+ ), + }, + { + id: 'price', + header: 'Harga Awal', + accessorKey: 'price', + cell: (props) => { + const value = props.row.original.price; + return
{formatCurrency(value)}
; + }, + footer: () => ( +
+ {formatCurrency(summary.total_initial_amount) || '-'} +
+ ), + }, + { + id: 'credit_note', + header: 'CN', + accessorKey: 'credit_note', + cell: (props) => { + const value = props.row.original.credit_note; + return
{formatCurrency(value)}
; + }, + footer: () => ( +
+ {formatCurrency(summary.total_credit_note) || '-'} +
+ ), + }, + { + id: 'final_price', + header: 'Harga Akhir', + accessorKey: 'final_price', + cell: (props) => { + const value = props.row.original.final_price; + return
{formatCurrency(value)}
; + }, + footer: () => ( +
+ {formatCurrency(summary.total_final_amount) || '-'} +
+ ), + }, + { + id: 'ppn', + header: 'PPN (%)', + accessorKey: 'ppn', + cell: (props) => { + const value = props.row.original.ppn; + return
{formatNumber(value)}%
; + }, + footer: () => ( +
-
+ ), + }, + { + id: 'total', + header: 'Total', + accessorKey: 'total', + cell: (props) => { + const value = props.row.original.total; + return
{formatCurrency(value)}
; + }, + footer: () => ( +
+ {formatCurrency(summary.total_grand_amount) || '-'} +
+ ), + }, + { + id: 'payment', + header: 'Pembayaran', + accessorKey: 'payment', + cell: (props) => { + const value = props.row.original.payment; + return
{formatCurrency(value)}
; + }, + footer: () => ( +
+ {formatCurrency(summary.total_payment) || '-'} +
+ ), + }, + { + id: 'accounts_receivable', + header: 'Saldo Piutang', + accessorKey: 'accounts_receivable', + cell: (props) => { + const value = props.row.original.accounts_receivable; + return
{formatCurrency(value)}
; + }, + footer: () => ( +
+ {formatCurrency(summary.total_accounts_receivable) || '-'} +
+ ), + }, + { + id: 'notes', + header: 'Keterangan', + accessorKey: 'notes', + cell: (props) => { + const value = props.row.original.notes; + return value || '-'; + }, + }, + { + id: 'pickup_info', + header: 'Pengambilan', + accessorKey: 'pickup_info', + cell: (props) => { + const value = props.row.original.pickup_info; + return value || '-'; + }, + }, + { + id: 'sales_marketing', + header: 'Sales/Marketing', + accessorKey: 'sales_marketing', + cell: (props) => { + const value = props.row.original.sales_marketing; + return value || '-'; + }, + }, + ]; + return tableColumns; + }; + + return ( +
+ +
+ + + + Export + + } + align='end' + > + + + + + +
+
+ + (tableFilterState.customer_id || []) + .map(String) + .includes(String(opt.value)) + )} + onChange={customerChangeHandler} + isLoading={isLoadingCustomers} + isClearable + /> + + (tableFilterState.sales || []) + .map(String) + .includes(String(opt.value)) + )} + onChange={salesChangeHandler} + isLoading={false} + isClearable + /> + option.value === tableFilterState.date_type + ) || null + } + onChange={dataTypeChangeHandler} + isLoading={false} + isClearable={false} + /> +
+
+
+ + +
+
+ + {!isSubmitted ? ( +
+ Silakan pilih filter dan klik tombol Submit untuk menampilkan data. +
+ ) : isLoading ? ( +
+ +
+ ) : data.length === 0 ? ( +
+ Tidak ada data yang dapat ditampilkan... +
+ ) : ( + data.map((customerReport) => { + const summary = customerReport.summary || { + total_qty: 0, + total_weight: 0, + total_initial_amount: 0, + total_credit_note: 0, + total_final_amount: 0, + total_ppn: 0, + total_grand_amount: 0, + total_payment: 0, + total_accounts_receivable: 0, + }; + + const totalAccountsReceivable = summary.total_accounts_receivable; + const tableColumns = getTableColumns(summary); + + return ( + + 0} + className={{ + containerClassName: 'w-full', + tableWrapperClassName: 'overflow-x-auto mt-4', + tableClassName: 'w-full table-auto text-sm', + headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', + headerColumnClassName: + 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200', + bodyRowClassName: + 'hover:bg-gray-50 transition-colors border-b border-l border-r border-b-gray-200 border-l-gray-200 border-r-gray-200', + bodyColumnClassName: + 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap', + tableFooterClassName: + 'bg-gray-100 font-semibold border border-gray-200', + footerRowClassName: 'border-t-2 border-gray-300', + footerColumnClassName: + 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap', + paginationClassName: 'hidden', + }} + /> + + ); + }) + )} + + {meta && data.length > 0 && ( +
+ +
+ )} + + ); +}; + +export default CustomerPaymentTab;