mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE): Add utility for PDF badge styles and integrate into
reports
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
export type StatusColor = {
|
||||
bg: string;
|
||||
text: string;
|
||||
border: string;
|
||||
};
|
||||
|
||||
// Due status colors (for debt supplier reports)
|
||||
export const dueStatusColors: Record<string, StatusColor> = {
|
||||
'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<string, StatusColor> = {
|
||||
'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;
|
||||
};
|
||||
@@ -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 ? (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<PdfStatusBadge
|
||||
backgroundColor={item.status === 'LUNAS' ? '#DBEAFE' : '#FEE2E2'}
|
||||
textColor={item.status === 'LUNAS' ? '#1E40AF' : '#991B1B'}
|
||||
borderColor={item.status === 'LUNAS' ? '#60A5FA' : '#F87171'}
|
||||
backgroundColor={getPDFBadgeStyle(item.status, 'payment').bg}
|
||||
textColor={getPDFBadgeStyle(item.status, 'payment').text}
|
||||
borderColor={getPDFBadgeStyle(item.status, 'payment').border}
|
||||
>
|
||||
{item.status === 'LUNAS' ? 'Lunas' : 'Belum Lunas'}
|
||||
{formatTitleCase(item.status)}
|
||||
</PdfStatusBadge>
|
||||
</View>
|
||||
) : (
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 !== '-' ? (
|
||||
<PdfStatusBadge
|
||||
backgroundColor={getPDFBadgeStyle(item.due_status, 'due').bg}
|
||||
textColor={getPDFBadgeStyle(item.due_status, 'due').text}
|
||||
borderColor={getPDFBadgeStyle(item.due_status, 'due').border}
|
||||
>
|
||||
{item.due_status}
|
||||
</PdfStatusBadge>
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<PdfStatusBadge
|
||||
backgroundColor={getPDFBadgeStyle(item.due_status, 'due').bg}
|
||||
textColor={getPDFBadgeStyle(item.due_status, 'due').text}
|
||||
borderColor={getPDFBadgeStyle(item.due_status, 'due').border}
|
||||
>
|
||||
{item.due_status}
|
||||
</PdfStatusBadge>
|
||||
</View>
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
@@ -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: '' },
|
||||
|
||||
Reference in New Issue
Block a user