diff --git a/src/components/pages/report/DailyMarketingReportPDF.tsx b/src/components/pages/report/DailyMarketingReportPDF.tsx index 86ee29bc..53b36b80 100644 --- a/src/components/pages/report/DailyMarketingReportPDF.tsx +++ b/src/components/pages/report/DailyMarketingReportPDF.tsx @@ -1,275 +1,226 @@ 'use client'; -import { - Document, - Image, - Page, - StyleSheet, - Text, - View, -} from '@react-pdf/renderer'; - +import { Page, View, Document, StyleSheet, Font } from '@react-pdf/renderer'; import { DailyMarketingReport, SalesSummary, } from '@/types/api/report/marketing'; -import { formatCurrency, formatDate, formatNumber } from '@/lib/helper'; +import { + formatCurrency, + formatDate, + formatNumber, + formatTitleCase, +} from '@/lib/helper'; +import { + PdfTable, + PdfColumn, + PdfTbodyCell, + PdfTfootCell, +} from '@/components/helper/pdf/table'; +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 { PdfPageNumber } from '@/components/helper/pdf/layout/PdfPageNumber'; + +Font.register({ + family: 'Helvetica', + src: 'helvetica', +}); + +const pdfStyles = StyleSheet.create({ + page: { + fontSize: 10, + fontFamily: 'Helvetica', + padding: 20, + backgroundColor: '#FFFFFF', + }, + titleSection: { + marginBottom: 10, + }, + parameterContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + marginBottom: 8, + }, +}); interface DailyMarketingReportPDFProps { data?: DailyMarketingReport; total?: SalesSummary; } -const DailyMarketingReportPDFStyle = StyleSheet.create({ - page: { - paddingTop: 24, - paddingBottom: 64, - paddingHorizontal: 16, // Reduce padding to fit more columns - orientation: 'landscape', +const getTableColumns = (): PdfColumn[] => [ + { key: 'no', header: 'No', flex: 0.5, align: 'center' }, + { key: 'so_date', header: 'Tanggal Sales Order', flex: 1.3, align: 'center' }, + { + key: 'do_date', + header: 'Tanggal Delivery Order', + flex: 1.3, + align: 'center', }, + { key: 'aging', header: 'Aging (Hari)', flex: 0.7, align: 'center' }, + { key: 'warehouse', header: 'Gudang', flex: 1.2, align: 'left' }, + { key: 'customer', header: 'Pelanggan', flex: 1.5, align: 'left' }, + { key: 'sales', header: 'Sales', flex: 1, align: 'left' }, + { key: 'product', header: 'Produk', flex: 1.3, align: 'left' }, + { key: 'do_number', header: 'Nomor DO', flex: 1.2, align: 'left' }, + { key: 'vehicle', header: 'Nomor Polisi', flex: 1, align: 'left' }, + { key: 'marketing_type', header: 'Tipe Marketing', flex: 1, align: 'center' }, + { key: 'qty', header: 'Quantity', flex: 0.7, align: 'right' }, + { key: 'avg_weight', header: 'Rata-Rata (Kg)', flex: 0.8, align: 'right' }, + { + key: 'total_weight', + header: 'Total Berat (Kg)', + flex: 0.9, + align: 'right', + }, + { key: 'sales_price', header: 'Harga Jual (Rp)', flex: 0.9, align: 'right' }, + { key: 'hpp_price', header: 'HPP (Rp)', flex: 1.3, align: 'right' }, + { key: 'sales_amount', header: 'Total Jual (Rp)', flex: 1, align: 'right' }, + { key: 'hpp_amount', header: 'Total HPP (Rp)', flex: 1.3, align: 'right' }, +]; - companyInfoHeader: { - width: '100%', - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-start', - marginBottom: 8, - }, - companyLogo: { - width: 64, - height: 'auto', - }, - companyInfoHeaderDate: { - paddingTop: 8, - fontSize: 10, - }, - companyName: { - fontSize: 12, - fontWeight: 'bold', - marginBottom: 4, - }, - companyAddress: { - fontSize: 8, - maxWidth: 400, - marginBottom: 10, - }, +const getTableData = (rows: DailyMarketingReport): PdfTbodyCell[][] => { + return rows.map((row, index) => [ + { key: 'no', value: index + 1 }, + { + key: 'so_date', + value: row.so_date ? formatDate(row.so_date, 'DD MMM YY') : '-', + }, + { + key: 'do_date', + value: row.realization_date + ? formatDate(row.realization_date, 'DD MMM YY') + : '-', + }, + { key: 'aging', value: row.aging_days ?? '-' }, + { key: 'warehouse', value: row.warehouse?.name ?? '-' }, + { key: 'customer', value: row.customer?.name ?? '-' }, + { key: 'sales', value: row.sales?.name ?? '-' }, + { key: 'product', value: row.product?.name ?? '-' }, + { key: 'do_number', value: row.do_number ?? '-' }, + { key: 'vehicle', value: row.vehicle_number ?? '-' }, + { + key: 'marketing_type', + value: row.marketing_type ? ( + + + {formatTitleCase(row.marketing_type)} + + + ) : ( + '-' + ), + }, + { key: 'qty', value: formatNumber(row.qty ?? 0), align: 'right' }, + { + key: 'avg_weight', + value: formatNumber(row.average_weight_kg ?? 0), + align: 'right', + }, + { + key: 'total_weight', + value: formatNumber(row.total_weight_kg ?? 0), + align: 'right', + }, + { + key: 'sales_price', + value: formatCurrency(row.sales_price_per_kg ?? 0), + align: 'right', + }, + { + key: 'hpp_price', + value: formatCurrency(row.hpp_price_per_kg ?? 0), + align: 'right', + }, + { + key: 'sales_amount', + value: formatCurrency(row.sales_amount ?? 0), + align: 'right', + }, + { + key: 'hpp_amount', + value: formatCurrency(row.hpp_amount ?? 0), + align: 'right', + }, + ]); +}; - title: { - marginTop: 16, - fontSize: 14, - lineHeight: '150%', - textAlign: 'center', - fontFamily: 'Times-Roman', - fontWeight: 'bold', - }, +const getTableFooter = (summary?: SalesSummary): PdfTfootCell[] => { + if (!summary) return []; - footer: { - width: '100%', - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 16, - - position: 'absolute', - fontSize: 8, - bottom: 30, - left: 0, - right: 0, - textAlign: 'center', - color: 'grey', - }, - - // Table Styles - table: { - width: '100%', - marginTop: 16, - borderWidth: 1, - borderColor: '#000000', - borderBottomWidth: 0, - fontSize: 7, // Smaller font for report - }, - tableRow: { - flexDirection: 'row', - borderBottomWidth: 1, - borderBottomColor: '#000000', - alignItems: 'center', - minHeight: 20, - }, - tableHeader: { - backgroundColor: '#f0f0f0', - fontWeight: 'bold', - }, - - // Columns definition (Total 100%) - colNo: { - width: '3%', - padding: 2, - textAlign: 'center', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colSoDate: { - width: '6%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colDoDate: { - width: '6%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colAging: { - width: '3%', - padding: 2, - textAlign: 'center', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colWarehouse: { - width: '7%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colCustomer: { - width: '9%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, // Reduced slightly - colSales: { - width: '6%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colProduct: { - width: '8%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, // Reduced slightly - colDoNumber: { - width: '7%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colVehicle: { - width: '5%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colMarketingType: { - width: '5%', - padding: 2, - textAlign: 'left', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colQty: { - width: '4%', - padding: 2, - textAlign: 'right', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colAvgWeight: { - width: '4%', - padding: 2, - textAlign: 'right', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colTotalWeight: { - width: '5%', - padding: 2, - textAlign: 'right', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colSalesPrice: { - width: '5%', - padding: 2, - textAlign: 'right', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colHppPrice: { - width: '5%', - padding: 2, - textAlign: 'right', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colSalesAmount: { - width: '6%', - padding: 2, - textAlign: 'right', - borderRightWidth: 1, - borderRightColor: '#000000', - }, - colHppAmount: { width: '6%', padding: 2, textAlign: 'right' }, // Last column - - // Text inside columns - cellText: { - fontSize: 6, - }, - headerText: { - fontSize: 7, - fontWeight: 'bold', - textAlign: 'center', - }, - - // Utils - doubleDivider: { - width: '100%', - height: 6, - borderTop: '2px solid black', - borderBottom: '2px solid black', - }, - - // Summary - summaryContainer: { - marginTop: 12, - flexDirection: 'row', - justifyContent: 'flex-end', - width: '100%', - }, - summaryTable: { - width: '30%', - borderWidth: 1, - borderColor: '#000000', - fontSize: 8, - }, - summaryRow: { - flexDirection: 'row', - padding: 2, - borderBottomWidth: 1, - borderBottomColor: '#eee', - }, - summaryLabel: { - width: '50%', - fontWeight: 'bold', - }, - summaryValue: { - width: '50%', - textAlign: 'right', - }, -}); + return [ + { key: 'no', value: 'TOTAL' }, + { key: 'so_date', value: '' }, + { key: 'do_date', value: '' }, + { key: 'aging', value: '' }, + { key: 'warehouse', value: '' }, + { key: 'customer', value: '' }, + { key: 'sales', value: '' }, + { key: 'product', value: '' }, + { key: 'do_number', value: '' }, + { key: 'vehicle', value: '' }, + { key: 'marketing_type', value: '' }, + { + key: 'qty', + value: formatNumber(summary.total_qty ?? 0), + align: 'right', + }, + { + key: 'avg_weight', + value: formatNumber(summary.total_weight_kg ?? 0), + align: 'right', + }, + { + key: 'total_weight', + value: formatNumber(summary.total_weight_kg ?? 0), + align: 'right', + }, + { key: 'sales_price', value: '' }, + { + key: 'hpp_price', + value: formatCurrency(summary.total_hpp_price_per_kg ?? 0), + align: 'right', + }, + { + key: 'sales_amount', + value: formatCurrency(summary.total_sales_amount ?? 0), + align: 'right', + }, + { + key: 'hpp_amount', + value: formatCurrency(summary.total_hpp_amount ?? 0), + align: 'right', + }, + ]; +}; const DailyMarketingReportPDF = ({ data, @@ -280,288 +231,31 @@ const DailyMarketingReportPDF = ({ return ( - - - - - - - {formatDate(Date.now(), 'DD MMMM YYYY')} - - - - - - PT LUMBUNG TELUR INDONESIA - - - SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel. - Cipedes, Kec. Sukajadi, Kota Bandung 40162 - - - + + {/* Title and Parameters */} + + + Laporan > Penjualan Harian + + + + Tanggal: {formatDate(Date.now(), 'DD MMMM YYYY')} + + + Dicetak: {formatDate(new Date(), 'DD MMM YYYY HH:mm')} + - - Laporan Penjualan Harian - + {/* Table */} + - {/* Data Table */} - - {/* Header */} - - - No - - - - Tgl SO - - - - - Tgl DO - - - - Aging - - - - Gudang - - - - - Pelanggan - - - - Sales - - - - Produk - - - - No DO - - - - Plat No - - - - Tipe - - - Qty - - - - Rerata - - - - Berat - - - - Hrg Jual - - - - - HPP/kg - - - - - Total Jual - - - - - Total HPP - - - - - {/* Rows */} - {rows.map((row, index) => ( - - - - {index + 1} - - - - - {formatDate(row.so_date, 'DD/MM/YYYY')} - - - - - {formatDate(row.realization_date, 'DD/MM/YYYY')} - - - - - {row.aging_days} - - - - - {row.warehouse?.name} - - - - - {row.customer?.name} - - - - - {row.sales.name} - - - - - {row.product?.name} - - - - - {row.do_number} - - - - - {row.vehicle_number} - - - - - {row.marketing_type} - - - - - {formatNumber(row.qty)} - - - - - {formatNumber(row.average_weight_kg)} - - - - - {formatNumber(row.total_weight_kg)} - - - - - {formatCurrency(row.sales_price_per_kg)} - - - - - {formatCurrency(row.hpp_price_per_kg)} - - - - - {formatCurrency(row.sales_amount)} - - - - - {formatCurrency(row.hpp_amount)} - - - - ))} - - - {/* Summary */} - - - - - Total Qty: - - - {formatNumber(summary?.total_qty ?? 0)} - - - - - Total Berat (kg): - - - {formatNumber(summary?.total_weight_kg ?? 0)} - - - - - Total Penjualan: - - - {formatCurrency(summary?.total_sales_amount ?? 0)} - - - - - Total HPP Per KG: - - - {formatCurrency(summary?.total_hpp_price_per_kg ?? 0)} - - - - - Total HPP: - - - {formatCurrency(summary?.total_hpp_amount ?? 0)} - - - - - - - - `${pageNumber} / ${totalPages}` - } - fixed - /> - + );