'use client' import { useProductSalesAnalytics, useProfitLossAnalytics, useSalesAnalytics, usePaymentAnalytics, useCategoryAnalytics } 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' | 'range'>('single') const [isGeneratingPDF, setIsGeneratingPDF] = useState(false) 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 { data: category } = useCategoryAnalytics(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 } const categorySummary = { totalRevenue: category?.data?.reduce((sum, item) => sum + (item?.total_revenue || 0), 0) || 0, orderCount: category?.data?.reduce((sum, item) => sum + (item?.order_count || 0), 0) || 0, productCount: category?.data?.reduce((sum, item) => sum + (item?.product_count || 0), 0) || 0, totalQuantity: category?.data?.reduce((sum, item) => sum + (item?.total_quantity || 0), 0) || 0 } useEffect(() => { setNow(new Date()) }, []) const formatDateForInput = (date: Date) => { return date.toISOString().split('T')[0] } const getReportPeriodText = () => { if (filterType === 'single') { return `${formatDateDDMMYYYY(selectedDate)} - ${formatDateDDMMYYYY(selectedDate)}` } else { return `${formatDateDDMMYYYY(dateRange.startDate)} - ${formatDateDDMMYYYY(dateRange.endDate)}` } } const getReportTitle = () => { if (filterType === 'single') { return 'Laporan Transaksi' } else { return `Laporan Transaksi` } } const handleGeneratePDF = async () => { const reportElement = reportRef.current if (!reportElement) { alert('Report element tidak ditemukan') return } setIsGeneratingPDF(true) try { const jsPDF = (await import('jspdf')).default const html2canvas = (await import('html2canvas')).default const originalOverflow = reportElement.style.overflow reportElement.style.overflow = 'visible' await new Promise(resolve => setTimeout(resolve, 300)) const canvas = await html2canvas(reportElement, { scale: 1.5, useCORS: true, allowTaint: true, backgroundColor: '#ffffff', logging: false, removeContainer: true, imageTimeout: 0, height: reportElement.scrollHeight, width: reportElement.scrollWidth, scrollX: 0, scrollY: 0, windowWidth: Math.max(reportElement.scrollWidth, window.innerWidth), windowHeight: Math.max(reportElement.scrollHeight, window.innerHeight) }) reportElement.style.overflow = originalOverflow const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4', compress: true }) const pdfWidth = 210 const pdfHeight = 297 const margin = 5 const availableWidth = pdfWidth - 2 * margin const availableHeight = pdfHeight - 2 * margin const imgWidth = availableWidth const imgHeight = (canvas.height * availableWidth) / canvas.width let yPosition = margin let sourceY = 0 let pageCount = 1 let remainingHeight = imgHeight while (remainingHeight > 0) { if (pageCount > 1) { pdf.addPage() yPosition = margin } const heightForThisPage = Math.min(remainingHeight, availableHeight) const sourceHeight = (heightForThisPage * canvas.height) / imgHeight const tempCanvas = document.createElement('canvas') const tempCtx = tempCanvas.getContext('2d') if (!tempCtx) { throw new Error('Unable to get 2D context from canvas') } tempCanvas.width = canvas.width tempCanvas.height = sourceHeight tempCtx.drawImage(canvas, 0, sourceY, canvas.width, sourceHeight, 0, 0, canvas.width, sourceHeight) const pageImageData = tempCanvas.toDataURL('image/jpeg', 0.9) pdf.addImage(pageImageData, 'JPEG', margin, yPosition, imgWidth, heightForThisPage) sourceY += sourceHeight remainingHeight -= heightForThisPage pageCount++ if (pageCount > 20) { console.warn('Too many pages, breaking loop') break } } pdf.setProperties({ title: getReportTitle(), subject: 'Transaction Report', author: 'Apskel POS System', creator: 'Apskel' }) const fileName = filterType === 'single' ? `laporan-transaksi-${formatDateForInput(selectedDate)}.pdf` : `laporan-transaksi-${formatDateForInput(dateRange.startDate)}-to-${formatDateForInput(dateRange.endDate)}.pdf` pdf.save(fileName) } catch (error) { console.error('Error generating PDF:', error) alert(`Terjadi kesalahan saat membuat PDF: ${error}`) } finally { setIsGeneratingPDF(false) } } const LoadingOverlay = ({ isVisible, message = 'Generating PDF...' }: { isVisible: boolean; message?: string }) => { if (!isVisible) return null return (

{message}

Mohon tunggu, proses ini mungkin membutuhkan beberapa detik...

Capturing report content
Processing pages
Finalizing PDF
) } // Group products by category const groupedProducts = products?.data?.reduce( (acc, item) => { const categoryName = item.category_name || 'Tidak Berkategori' if (!acc[categoryName]) { acc[categoryName] = [] } acc[categoryName].push(item) return acc }, {} as Record ) || {} return (
{/* Performance Summary */}

Ringkasan

Total Penjualan {formatCurrency(profitLoss?.summary.total_revenue ?? 0)}
Total Diskon {formatCurrency(profitLoss?.summary.total_discount ?? 0)}
Total Pajak {formatCurrency(profitLoss?.summary.total_tax ?? 0)}
Total {formatCurrency(profitLoss?.summary.total_revenue ?? 0)}
{/* Invoice */}

Invoice

Total Invoice {profitLoss?.summary.total_orders ?? 0}
{/* Payment Method Summary */}

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)}
{/* Category Summary */}

Ringkasan Kategori

{category?.data?.map((c, index) => ( )) || []}
Nama Qty Pendapatan
{c.category_name} {c.total_quantity} {formatCurrency(c.total_revenue)}
TOTAL {categorySummary?.totalQuantity ?? 0} {formatCurrency(categorySummary?.totalRevenue ?? 0)}
{/* Product Summary - Dipisah per kategori dengan tabel terpisah */}

Ringkasan Item Per Kategori

{Object.keys(groupedProducts) .sort() .map(categoryName => { const categoryProducts = groupedProducts[categoryName] const categoryTotalQty = categoryProducts.reduce((sum, item) => sum + (item.quantity_sold || 0), 0) const categoryTotalRevenue = categoryProducts.reduce((sum, item) => sum + (item.revenue || 0), 0) return (
{/* Category Title */}

{categoryName.toUpperCase()}

{/* Category Table */}
{categoryProducts.map((item, index) => ( ))}
Produk Qty Pendapatan
{item.product_name} {item.quantity_sold} {formatCurrency(item.revenue)}
Subtotal {categoryName} {categoryTotalQty} {formatCurrency(categoryTotalRevenue)}
) })} {/* Grand Total */}
TOTAL KESELURUHAN {productSummary.totalQuantitySold ?? 0} {formatCurrency(productSummary.totalRevenue ?? 0)}
{/* Footer */}

© 2025 Apskel - Sistem POS Terpadu

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

) } export default DailyPOSReport