diff --git a/src/app/[lang]/(dashboard)/(private)/dashboards/daily-report/page.tsx b/src/app/[lang]/(dashboard)/(private)/dashboards/daily-report/page.tsx index fd479ef..0d46ceb 100644 --- a/src/app/[lang]/(dashboard)/(private)/dashboards/daily-report/page.tsx +++ b/src/app/[lang]/(dashboard)/(private)/dashboards/daily-report/page.tsx @@ -23,6 +23,7 @@ const DailyPOSReport = () => { endDate: new Date() }) const [filterType, setFilterType] = useState<'single' | 'range'>('single') // 'single' or 'range' + const [isGeneratingPDF, setIsGeneratingPDF] = useState(false) // Use selectedDate for single date filter, or dateRange for range filter const getDateParams = () => { @@ -101,81 +102,212 @@ const DailyPOSReport = () => { const handleGeneratePDF = async () => { const reportElement = reportRef.current + if (!reportElement) { + alert('Report element tidak ditemukan') + return + } + + // Set loading state + setIsGeneratingPDF(true) + 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 + // Pastikan element terlihat penuh + const originalOverflow = reportElement.style.overflow + reportElement.style.overflow = 'visible' + + // Wait untuk memastikan rendering selesai + await new Promise(resolve => setTimeout(resolve, 300)) + + console.log('Starting PDF generation...') + + // Update loading message + console.log('Capturing content...') + + // Capture canvas dengan setting yang optimal + const canvas = await html2canvas(reportElement, { + scale: 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 + logging: false, + removeContainer: true, + imageTimeout: 0, + height: reportElement.scrollHeight, + width: reportElement.scrollWidth, + scrollX: 0, + scrollY: 0, + // Pastikan capture semua content + windowWidth: Math.max(reportElement.scrollWidth, window.innerWidth), + windowHeight: Math.max(reportElement.scrollHeight, window.innerHeight) }) - // Compress canvas using JPEG with quality setting - const imgData = canvas.toDataURL('image/jpeg', 0.85) // JPEG with 85% quality instead of PNG + console.log('Canvas captured:', canvas.width, 'x', canvas.height) + console.log('Generating PDF pages...') - // Create PDF with compression + // Restore overflow + reportElement.style.overflow = originalOverflow + + // Create PDF const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4', - compress: true // Enable built-in compression + compress: true }) - const imgWidth = 210 - const pageHeight = 295 - const imgHeight = (canvas.height * imgWidth) / canvas.width - let heightLeft = imgHeight - let position = 0 + // A4 dimensions + const pdfWidth = 210 + const pdfHeight = 297 + const margin = 5 // Small margin to prevent cutoff - // Add first page with compressed image - pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, '', 'FAST') // Use FAST compression - heightLeft -= pageHeight + // Calculate scaling + const availableWidth = pdfWidth - 2 * margin + const availableHeight = pdfHeight - 2 * margin - // Handle multiple pages if needed - while (heightLeft >= 0) { - position = heightLeft - imgHeight - pdf.addPage() - pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, '', 'FAST') - heightLeft -= pageHeight + const imgWidth = availableWidth + const imgHeight = (canvas.height * availableWidth) / canvas.width + + console.log('PDF dimensions - Canvas:', canvas.width, 'x', canvas.height) + console.log('PDF dimensions - Image:', imgWidth, 'x', imgHeight) + console.log('Available height per page:', availableHeight) + + // Split content across pages + let yPosition = margin + let sourceY = 0 + let pageCount = 1 + let remainingHeight = imgHeight + + while (remainingHeight > 0) { + console.log(`Processing page ${pageCount}, remaining height: ${remainingHeight}`) + + if (pageCount > 1) { + pdf.addPage() + yPosition = margin + } + + // Calculate how much content fits on this page + const heightForThisPage = Math.min(remainingHeight, availableHeight) + + // Calculate source dimensions for cropping + const sourceHeight = (heightForThisPage * canvas.height) / imgHeight + + // Create temporary canvas for this page portion + 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 + + // Draw the portion we need + tempCtx.drawImage( + canvas, + 0, + sourceY, + canvas.width, + sourceHeight, // Source rectangle + 0, + 0, + canvas.width, + sourceHeight // Destination rectangle + ) + + // Convert to image data + const pageImageData = tempCanvas.toDataURL('image/jpeg', 0.9) + + // Add to PDF + pdf.addImage(pageImageData, 'JPEG', margin, yPosition, imgWidth, heightForThisPage) + + // Update for next page + sourceY += sourceHeight + remainingHeight -= heightForThisPage + pageCount++ + + // Safety check to prevent infinite loop + if (pageCount > 20) { + console.warn('Too many pages, breaking loop') + break + } } - // Additional compression options + console.log(`Generated ${pageCount - 1} pages`) + console.log('Finalizing PDF...') + + // Add metadata pdf.setProperties({ - title: `${getReportTitle()}`, - subject: 'Daily Transaction Report', + title: getReportTitle(), + subject: 'Transaction Report', author: 'Apskel POS System', creator: 'Apskel' }) - // Save with optimized settings + // Generate filename const fileName = filterType === 'single' ? `laporan-transaksi-${formatDateForInput(selectedDate)}.pdf` : `laporan-transaksi-${formatDateForInput(dateRange.startDate)}-to-${formatDateForInput(dateRange.endDate)}.pdf` - pdf.save(fileName, { - returnPromise: true - }) + console.log('Saving PDF:', fileName) - // Clean up canvas to free memory - canvas.width = canvas.height = 0 + // Save PDF + pdf.save(fileName) + + console.log('PDF generated successfully!') } catch (error) { console.error('Error generating PDF:', error) - alert('Terjadi kesalahan saat membuat PDF. Pastikan jsPDF dan html2canvas sudah terinstall.') + alert(`Terjadi kesalahan saat membuat PDF: ${error}`) + } finally { + // Reset loading state + setIsGeneratingPDF(false) } } + const LoadingOverlay = ({ isVisible, message = 'Generating PDF...' }: { isVisible: boolean; message?: string }) => { + if (!isVisible) return null + + return ( +
+
+
+ {/* Animated Spinner */} +
+ + {/* Loading Message */} +

{message}

+ +

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

+ + {/* Progress Steps */} +
+
+
+ Capturing report content +
+
+
+ Processing pages +
+
+
+ Finalizing PDF +
+
+
+
+
+ ) + } + return (
+ {/* Control Panel */} = ({ sx={{ fontWeight: 600, mt: 1, - color: theme.palette.text.primary + color: '#222222' }} > {outlet.name} @@ -72,7 +72,7 @@ const ReportHeader: FC = ({ variant='body2' sx={{ mt: 1, - color: theme.palette.text.secondary + color: '#222222' }} > {outlet.address} @@ -88,21 +88,21 @@ const ReportHeader: FC = ({ component='h3' sx={{ fontWeight: 700, - color: theme.palette.text.primary + color: '#222222' }} > {reportTitle} {periode && ( - + {periode} )} {reportSubtitle && ( - + {reportSubtitle}