diff --git a/src/components/helper/pdf/utils/pdf-badge.ts b/src/components/helper/pdf/utils/pdf-badge.ts new file mode 100644 index 00000000..4b26b4eb --- /dev/null +++ b/src/components/helper/pdf/utils/pdf-badge.ts @@ -0,0 +1,65 @@ +export type StatusColor = { + bg: string; + text: string; + border: string; +}; + +// Due status colors (for debt supplier reports) +export const dueStatusColors: Record = { + 'SUDAH JATUH TEMPO': { + bg: '#FEE2E2', + text: '#991B1B', + border: '#F87171', + }, // error/red + 'BELUM JATUH TEMPO': { + bg: '#D1FAE5', + text: '#065F46', + border: '#34D399', + }, // success/green + 'MENDEKATI JATUH TEMPO': { + bg: '#FEF3C7', + text: '#92400E', + border: '#FBBF24', + }, // warning/yellow +}; + +// Payment status colors (for customer payment & debt supplier reports) +export const paymentStatusColors: Record = { + 'BELUM LUNAS': { + bg: '#FEF3C7', + text: '#92400E', + border: '#FBBF24', + }, // warning/yellow + LUNAS: { + bg: '#DBEAFE', + text: '#1E40AF', + border: '#60A5FA', + }, // primary/blue + 'PEMBAYARAN SEBAGIAN': { bg: '#D1FAE5', text: '#065F46', border: '#34D399' }, // success/green + PEMBAYARAN: { + bg: '#D1FAE5', + text: '#065F46', + border: '#34D399', + }, // success/green +}; + +// Fallback color for unknown statuses +export const fallbackStatusColor: StatusColor = { + bg: '#F3F4F6', + text: '#374151', + border: '#D1D5DB', +}; // neutral + +export const getPDFBadgeStyle = ( + statusText: string, + type: 'due' | 'payment' = 'payment' +): StatusColor => { + const normalizedStatus = statusText.toUpperCase().trim(); + + const colors = + type === 'due' + ? dueStatusColors[normalizedStatus] + : paymentStatusColors[normalizedStatus]; + + return colors || fallbackStatusColor; +}; diff --git a/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx b/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx index 61e8792a..1c374e4b 100644 --- a/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx +++ b/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx @@ -9,7 +9,12 @@ import { pdf, } from '@react-pdf/renderer'; -import { formatDate, formatCurrency, formatNumber } from '@/lib/helper'; +import { + formatDate, + formatCurrency, + formatNumber, + formatTitleCase, +} from '@/lib/helper'; import { CustomerPaymentReport } from '@/types/api/report/customer-payment'; import { PdfTable, @@ -20,6 +25,7 @@ import { import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; +import { getPDFBadgeStyle } from '@/components/helper/pdf/utils/pdf-badge'; Font.register({ family: 'Helvetica', @@ -70,11 +76,21 @@ const getTableColumns = (): PdfColumn[] => [ { key: 'qty', header: 'Qty', flex: 0.8, align: 'right' }, { key: 'weight', header: 'Berat', flex: 1, align: 'right' }, { key: 'average_weight', header: 'Rata-Rata', flex: 0.8, align: 'right' }, - { key: 'unit_price', header: 'Harga/Unit', flex: 1.2, align: 'right' }, - { key: 'final_price', header: 'Harga Akhir', flex: 1.2, align: 'right' }, - { key: 'total_price', header: 'Total', flex: 1.2, align: 'right' }, - { key: 'payment_amount', header: 'Pembayaran', flex: 1.2, align: 'right' }, - { key: 'accounts_receivable', header: 'Saldo', flex: 1.2, align: 'right' }, + { key: 'unit_price', header: 'Harga/Unit (Rp)', flex: 1.2, align: 'right' }, + { key: 'final_price', header: 'Harga Akhir (Rp)', flex: 1.2, align: 'right' }, + { key: 'total_price', header: 'Total (Rp)', flex: 1.2, align: 'right' }, + { + key: 'payment_amount', + header: 'Pembayaran (Rp)', + flex: 1.2, + align: 'right', + }, + { + key: 'accounts_receivable', + header: 'Saldo (Rp)', + flex: 1.2, + align: 'right', + }, { key: 'status', header: 'Keterangan', flex: 1.5, align: 'center' }, { key: 'pickup_info', header: 'Pengambilan', flex: 1, align: 'left' }, { key: 'sales_person', header: 'Sales', flex: 1.5, align: 'left' }, @@ -139,18 +155,18 @@ const getTableData = ( key: 'accounts_receivable', value: formatCurrency(item.accounts_receivable), align: 'right', - color: item.accounts_receivable < 0 ? '#DC2626' : undefined, + color: item.accounts_receivable < 0 ? 'red' : undefined, }, { key: 'status', value: item.status ? ( - {item.status === 'LUNAS' ? 'Lunas' : 'Belum Lunas'} + {formatTitleCase(item.status)} ) : ( @@ -204,8 +220,7 @@ const getTableFooter = ( key: 'accounts_receivable', value: formatCurrency(summary?.total_accounts_receivable || 0), align: 'right', - color: - (summary?.total_accounts_receivable || 0) < 0 ? '#DC2626' : undefined, + color: (summary?.total_accounts_receivable || 0) < 0 ? 'red' : undefined, }, { key: 'status', value: '' }, { key: 'pickup_info', value: '' }, @@ -273,8 +288,7 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => { valueKey: 'accounts_receivable', value: customerReport.initial_balance, align: 'right', - color: - customerReport.initial_balance < 0 ? '#DC2626' : 'black', + color: customerReport.initial_balance < 0 ? 'red' : 'black', } : undefined } diff --git a/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx b/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx index 3238d46e..e8bfda5e 100644 --- a/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx +++ b/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx @@ -27,11 +27,11 @@ export const generateCustomerPaymentExcel = async ( { header: 'Ekor/Qty', key: 'qty', width: 10 }, { header: 'Berat (Kg)', key: 'weight', width: 12 }, { header: 'AVG', key: 'avgWeight', width: 10 }, - { header: 'Harga/Unit', key: 'unitPrice', width: 15 }, - { header: 'Harga Akhir', key: 'finalPrice', width: 15 }, - { header: 'Total', key: 'totalPrice', width: 15 }, - { header: 'Pembayaran', key: 'paymentAmount', width: 15 }, - { header: 'Saldo Piutang', key: 'accountsReceivable', width: 15 }, + { header: 'Harga/Unit (Rp)', key: 'unitPrice', width: 15 }, + { header: 'Harga Akhir (Rp)', key: 'finalPrice', width: 15 }, + { header: 'Total (Rp)', key: 'totalPrice', width: 15 }, + { header: 'Pembayaran (Rp)', key: 'paymentAmount', width: 15 }, + { header: 'Saldo Piutang (Rp)', key: 'accountsReceivable', width: 15 }, { header: 'Keterangan', key: 'status', width: 20 }, { header: 'Pengambilan', key: 'pickupInfo', width: 15 }, { header: 'Sales/Marketing', key: 'salesPerson', width: 20 }, diff --git a/src/components/pages/report/finance/export/DebtSupllierExportPDF.tsx b/src/components/pages/report/finance/export/DebtSupllierExportPDF.tsx index b44c8060..b5c26525 100644 --- a/src/components/pages/report/finance/export/DebtSupllierExportPDF.tsx +++ b/src/components/pages/report/finance/export/DebtSupllierExportPDF.tsx @@ -20,53 +20,13 @@ import { PdfTbodyCell, PdfTfootCell, } from '@/components/helper/pdf/table'; +import { getPDFBadgeStyle } from '@/components/helper/pdf/utils/pdf-badge'; Font.register({ family: 'Helvetica', src: 'helvetica', }); -// Status color mappings (same as in DebtSupplierTab) -const dueStatusColors: Record< - string, - { bg: string; text: string; border: string } -> = { - 'Sudah Jatuh Tempo': { bg: '#FEE2E2', text: '#991B1B', border: '#F87171' }, // error/red - 'Belum Jatuh Tempo': { bg: '#D1FAE5', text: '#065F46', border: '#34D399' }, // success/green - 'Mendekati Jatuh Tempo': { - bg: '#FEF3C7', - text: '#92400E', - border: '#FBBF24', - }, // warning/yellow -}; - -const paymentStatusColors: Record< - string, - { bg: string; text: string; border: string } -> = { - 'Belum Lunas': { bg: '#FEF3C7', text: '#92400E', border: '#FBBF24' }, // warning/yellow - Lunas: { bg: '#DBEAFE', text: '#1E40AF', border: '#60A5FA' }, // primary/blue - Pembayaran: { bg: '#D1FAE5', text: '#065F46', border: '#34D399' }, // success/green -}; - -/** - * Get badge style for PDF rendering - * @param statusText - The status text - * @param type - Type of status: 'due' or 'payment' - * @returns Style object with background and text colors - */ -const getPDFBadgeStyle = ( - statusText: string, - type: 'due' | 'payment' = 'payment' -) => { - const colors = - type === 'due' - ? dueStatusColors[statusText] - : paymentStatusColors[statusText]; - - return colors || { bg: '#F3F4F6', text: '#374151', border: '#D1D5DB' }; // neutral fallback -}; - const pdfStyles = StyleSheet.create({ page: { fontSize: 10, @@ -99,7 +59,7 @@ const getTableColumns = (): PdfColumn[] => [ { key: 'area', header: 'Area', flex: 1, align: 'left' }, { key: 'warehouse', header: 'Gudang', flex: 1, align: 'left' }, { key: 'due_date', header: 'Jatuh Tempo', flex: 1, align: 'center' }, - { key: 'due_status', header: 'Status Jatuh Tempo', flex: 2, align: 'left' }, + { key: 'due_status', header: 'Status Jatuh Tempo', flex: 2, align: 'center' }, { key: 'total_price', header: 'Nominal Pembelian (Rp)', @@ -151,13 +111,15 @@ const getTableData = (rows: DebtSupplier['rows']): PdfTbodyCell[][] => { key: 'due_status', value: item.due_status && item.due_status !== '-' ? ( - - {item.due_status} - + + + {item.due_status} + + ) : ( '-' ), @@ -226,6 +188,7 @@ const getTableFooter = (total: DebtSupplier['total']): PdfTfootCell[] => [ key: 'balance', value: formatCurrency(total?.debt_price || 0), align: 'right', + color: (total?.debt_price || 0) < 0 ? 'red' : undefined, }, { key: 'status', value: '' }, { key: 'travel_number', value: '' },