'use client' import { useProductSalesAnalytics, useProfitLossAnalytics, useSalesAnalytics, usePaymentAnalytics } from '@/services/queries/analytics' import { useOutletById } from '@/services/queries/outlets' import { formatCurrency, formatDate, formatDateDDMMYYYY, formatDatetime } from '@/utils/transform' import ReportGeneratorComponent from '@/views/dashboards/daily-report/report-generator' import ReportHeader from '@/views/dashboards/daily-report/report-header' import React, { useEffect, useRef, useState } from 'react' const DailyPOSReport = () => { const reportRef = useRef(null) const [now, setNow] = useState(new Date()) const [selectedDate, setSelectedDate] = useState(new Date()) const [dateRange, setDateRange] = useState({ startDate: new Date(), endDate: new Date() }) const [filterType, setFilterType] = useState('single') // 'single' or 'range' // Use selectedDate for single date filter, or dateRange for range filter const getDateParams = () => { if (filterType === 'single') { return { date_from: formatDateDDMMYYYY(selectedDate), date_to: formatDateDDMMYYYY(selectedDate) } } else { return { date_from: formatDateDDMMYYYY(dateRange.startDate), date_to: formatDateDDMMYYYY(dateRange.endDate) } } } const dateParams = getDateParams() const { data: outlet } = useOutletById() const { data: sales } = useSalesAnalytics(dateParams) const { data: profitLoss } = useProfitLossAnalytics(dateParams) const { data: products } = useProductSalesAnalytics(dateParams) const { data: paymentAnalytics } = usePaymentAnalytics(dateParams) const productSummary = { totalQuantitySold: products?.data?.reduce((sum, item) => sum + (item?.quantity_sold || 0), 0) || 0, totalRevenue: products?.data?.reduce((sum, item) => sum + (item?.revenue || 0), 0) || 0, totalOrders: products?.data?.reduce((sum, item) => sum + (item?.order_count || 0), 0) || 0 } // Calculate profit loss product summary const profitLossProductSummary = { totalRevenue: profitLoss?.product_data?.reduce((sum, item) => sum + (item?.revenue || 0), 0) || 0, totalCost: profitLoss?.product_data?.reduce((sum, item) => sum + (item?.cost || 0), 0) || 0, totalGrossProfit: profitLoss?.product_data?.reduce((sum, item) => sum + (item?.gross_profit || 0), 0) || 0, totalQuantity: profitLoss?.product_data?.reduce((sum, item) => sum + (item?.quantity_sold || 0), 0) || 0 } useEffect(() => { setNow(new Date()) }, []) // Format date for input field (YYYY-MM-DD) const formatDateForInput = (date: Date) => { return date.toISOString().split('T')[0] } // Get display text for the report period const getReportPeriodText = () => { if (filterType === 'single') { return `${formatDateDDMMYYYY(selectedDate)} - ${formatDateDDMMYYYY(selectedDate)}` } else { return `${formatDateDDMMYYYY(dateRange.startDate)} - ${formatDateDDMMYYYY(dateRange.endDate)}` } } // Get report title based on filter type const getReportTitle = () => { if (filterType === 'single') { return 'Laporan Transaksi' } else { const daysDiff = Math.ceil((dateRange.endDate.getTime() - dateRange.startDate.getTime()) / (1000 * 3600 * 24)) + 1 // return `Laporan Transaksi ${daysDiff} Hari` return `Laporan Transaksi` } } const handleGeneratePDF = async () => { const reportElement = reportRef.current try { // Import jsPDF dan html2canvas const jsPDF = (await import('jspdf')).default const html2canvas = (await import('html2canvas')).default // Optimized canvas capture dengan scale lebih rendah const canvas = await html2canvas(reportElement!, { scale: 1.5, // Reduced from 2 to 1.5 useCORS: true, allowTaint: true, backgroundColor: '#ffffff', logging: false, // Disable logging for performance removeContainer: true, // Clean up after capture imageTimeout: 0, // No timeout for image loading height: reportElement!.scrollHeight, width: reportElement!.scrollWidth }) // Compress canvas using JPEG with quality setting const imgData = canvas.toDataURL('image/jpeg', 0.85) // JPEG with 85% quality instead of PNG // Create PDF with compression const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4', compress: true // Enable built-in compression }) const imgWidth = 210 const pageHeight = 295 const imgHeight = (canvas.height * imgWidth) / canvas.width let heightLeft = imgHeight let position = 0 // Add first page with compressed image pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, '', 'FAST') // Use FAST compression heightLeft -= pageHeight // Handle multiple pages if needed while (heightLeft >= 0) { position = heightLeft - imgHeight pdf.addPage() pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, '', 'FAST') heightLeft -= pageHeight } // Additional compression options pdf.setProperties({ title: `${getReportTitle()}`, subject: 'Daily Transaction Report', author: 'Apskel POS System', creator: 'Apskel' }) // Save with optimized settings const fileName = filterType === 'single' ? `laporan-transaksi-${formatDateForInput(selectedDate)}.pdf` : `laporan-transaksi-${formatDateForInput(dateRange.startDate)}-to-${formatDateForInput(dateRange.endDate)}.pdf` pdf.save(fileName, { returnPromise: true }) // Clean up canvas to free memory canvas.width = canvas.height = 0 } catch (error) { console.error('Error generating PDF:', error) alert('Terjadi kesalahan saat membuat PDF. Pastikan jsPDF dan html2canvas sudah terinstall.') } } return (
{/* Control Panel */} {/* Report Template */}
{/* Header */} {/* Performance Summary */}

1. Ringkasan Kinerja

TOTAL PENJUALAN (termasuk rasik) {formatCurrency(profitLoss?.summary.total_revenue ?? 0)}
Total Terjual {productSummary.totalQuantitySold}
HPP {formatCurrency(profitLoss?.summary.total_cost ?? 0)} |{' '} {(((profitLoss?.summary.total_cost ?? 0) / (profitLoss?.summary.total_revenue || 1)) * 100).toFixed( 0 )} %
Laba Kotor {formatCurrency(profitLoss?.summary.gross_profit ?? 0)} |{' '} {(profitLoss?.summary.gross_profit_margin ?? 0).toFixed(0)}%
Biaya lain² {formatCurrency(profitLoss?.summary.total_tax ?? 0)} |{' '} {(((profitLoss?.summary.total_tax ?? 0) / (profitLoss?.summary.total_revenue || 1)) * 100).toFixed(0)} %
Laba/Rugi {formatCurrency(profitLoss?.summary.net_profit ?? 0)} |{' '} {(profitLoss?.summary.net_profit_margin ?? 0).toFixed(0)}%
{/* Profit Loss Product Table */}

Laba Rugi Per Produk

{profitLoss?.product_data?.map((item, index) => ( )) || []}
Produk Qty Pendapatan HPP Laba Kotor Margin (%)
{item.product_name} {item.quantity_sold} {formatCurrency(item.revenue)} {formatCurrency(item.cost)} {formatCurrency(item.gross_profit)} {(item.gross_profit_margin ?? 0).toFixed(1)}%
TOTAL {profitLossProductSummary.totalQuantity} {formatCurrency(profitLossProductSummary.totalRevenue)} {formatCurrency(profitLossProductSummary.totalCost)} {formatCurrency(profitLossProductSummary.totalGrossProfit)} {profitLossProductSummary.totalRevenue > 0 ? ( (profitLossProductSummary.totalGrossProfit / profitLossProductSummary.totalRevenue) * 100 ).toFixed(1) : 0} %
{/* Payment Method Summary */}

2. Ringkasan Metode Pembayaran

{paymentAnalytics?.data?.map((payment, index) => ( )) || []}
Metode Pembayaran Tipe Jumlah Order Total Amount Persentase
{payment.payment_method_name} {payment.payment_method_type.toUpperCase()} {payment.order_count} {formatCurrency(payment.total_amount)} {(payment.percentage ?? 0).toFixed(1)}%
TOTAL {paymentAnalytics?.summary.total_orders ?? 0} {formatCurrency(paymentAnalytics?.summary.total_amount ?? 0)} 100.0%
{/* Transaction Summary */}

3. Ringkasan Transaksi

{products?.data?.map((item, index) => ( )) || []}
Produk Kategori Qty Order Pendapatan Rata Rata
{item.product_name} {item.category_name} {item.quantity_sold} {item.order_count ?? 0} {formatCurrency(item.revenue)} {formatCurrency(item.average_price)}
TOTAL {productSummary.totalQuantitySold ?? 0} {productSummary.totalOrders ?? 0} {formatCurrency(productSummary.totalRevenue ?? 0)}
{/* Profit Loss Product Table */} {/* Footer */}

© 2025 Apskel - Sistem POS Terpadu

Dicetak pada: {now.toLocaleDateString('id-ID')}

) } export default DailyPOSReport