'use client'; import { Download, Loader2 } from 'lucide-react'; import { useCallback, useState } from 'react'; import { Button } from '@/components/ui/button'; interface ExportPdfButtonProps { /** CSS selector for the DOM element to capture */ targetSelector: string; /** Filename without extension */ filename?: string; } export function ExportPdfButton({ targetSelector, filename = 'dinh-gia-bat-dong-san', }: ExportPdfButtonProps) { const [isExporting, setIsExporting] = useState(false); const handleExport = useCallback(async () => { setIsExporting(true); try { const element = document.querySelector(targetSelector); if (!element) { console.error('Export target not found:', targetSelector); return; } // Dynamic imports for client-only PDF libraries const [html2canvasModule, jsPDFModule] = await Promise.all([ import('html2canvas'), import('jspdf'), ]); const html2canvas = html2canvasModule.default; const { jsPDF } = jsPDFModule; const canvas = await html2canvas(element as HTMLElement, { scale: 2, useCORS: true, logging: false, backgroundColor: '#ffffff', }); const imgData = canvas.toDataURL('image/png'); const imgWidth = canvas.width; const imgHeight = canvas.height; // A4 dimensions in mm const pdfWidth = 210; const pdfMargin = 10; const contentWidth = pdfWidth - 2 * pdfMargin; const contentHeight = (imgHeight / imgWidth) * contentWidth; const pdf = new jsPDF({ orientation: contentHeight > 297 - 2 * pdfMargin ? 'portrait' : 'portrait', unit: 'mm', format: 'a4', }); // Add header pdf.setFontSize(16); pdf.setTextColor(34, 139, 34); // Green pdf.text('GoodGo — Báo cáo định giá', pdfMargin, pdfMargin + 5); pdf.setFontSize(10); pdf.setTextColor(128, 128, 128); pdf.text( `Ngày: ${new Date().toLocaleDateString('vi-VN')}`, pdfMargin, pdfMargin + 12, ); const headerHeight = 20; const availableHeight = 297 - 2 * pdfMargin - headerHeight; if (contentHeight <= availableHeight) { // Fits on one page pdf.addImage( imgData, 'PNG', pdfMargin, pdfMargin + headerHeight, contentWidth, contentHeight, ); } else { // Multi-page: split the image let yOffset = 0; let isFirstPage = true; while (yOffset < contentHeight) { if (!isFirstPage) { pdf.addPage(); } const pageContentHeight = isFirstPage ? availableHeight : 297 - 2 * pdfMargin; const pageTopMargin = isFirstPage ? pdfMargin + headerHeight : pdfMargin; // Calculate source rectangle in image coordinates const srcY = (yOffset / contentHeight) * imgHeight; const srcHeight = (pageContentHeight / contentHeight) * imgHeight; // Create a temporary canvas for this page slice const pageCanvas = document.createElement('canvas'); pageCanvas.width = imgWidth; pageCanvas.height = Math.min(srcHeight, imgHeight - srcY); const ctx = pageCanvas.getContext('2d'); if (ctx) { ctx.drawImage( canvas, 0, srcY, imgWidth, pageCanvas.height, 0, 0, imgWidth, pageCanvas.height, ); } const pageImgData = pageCanvas.toDataURL('image/png'); const sliceHeight = (pageCanvas.height / imgWidth) * contentWidth; pdf.addImage( pageImgData, 'PNG', pdfMargin, pageTopMargin, contentWidth, sliceHeight, ); yOffset += pageContentHeight; isFirstPage = false; } } // Footer on last page pdf.setFontSize(8); pdf.setTextColor(180, 180, 180); pdf.text( 'Được tạo bởi GoodGo AI Valuation — goodgo.vn', pdfMargin, 297 - pdfMargin, ); pdf.save(`${filename}.pdf`); } catch (error) { console.error('PDF export failed:', error); } finally { setIsExporting(false); } }, [targetSelector, filename]); return ( ); }