import React, { useState, useEffect, useMemo, useCallback } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { Button } from '../ui/Button' import { FaArrowLeft, FaCalendarAlt, FaFileAlt, FaDownload, FaSearchPlus, FaSearchMinus, } from 'react-icons/fa' import { ReportGeneratedDto, ReportTemplateDto } from '@/proxy/reports/models' import { useReports } from '@/utils/hooks/useReports' import { ROUTES_ENUM } from '@/routes/route.constant' import { useLocalization } from '@/utils/hooks/useLocalization' export const ReportViewer: React.FC = () => { const { id } = useParams<{ id: string }>() const navigate = useNavigate() const [zoomLevel, setZoomLevel] = useState(100) const [report, setReport] = useState(null) const [template, setTemplate] = useState(null) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) const { translate } = useLocalization() const { getReportById, getTemplateById } = useReports() // İçeriği sayfalara bölen fonksiyon const splitContentIntoPages = (content: string) => { // Basit olarak içeriği paragraf ve tablo bazında bölelim const tempDiv = document.createElement('div') tempDiv.innerHTML = content const elements = Array.from(tempDiv.children) const pages: string[] = [] let currentPage = '' let currentPageHeight = 0 const maxPageHeight = 257 // 297mm - 40mm padding (top+bottom) elements.forEach((element) => { const elementHtml = element.outerHTML // Basit yükseklik tahmini (gerçek uygulamada daha karmaşık olabilir) let estimatedHeight = 20 // Default height if (element.tagName === 'TABLE') { const rows = element.querySelectorAll('tr') estimatedHeight = rows.length * 25 // Her satır için 25mm } else if (element.tagName.startsWith('H')) { estimatedHeight = 15 } else if (element.tagName === 'P') { estimatedHeight = 10 } if (currentPageHeight + estimatedHeight > maxPageHeight && currentPage) { pages.push(currentPage) currentPage = elementHtml currentPageHeight = estimatedHeight } else { currentPage += elementHtml currentPageHeight += estimatedHeight } }) if (currentPage) { pages.push(currentPage) } return pages.length > 0 ? pages : [content] } const preloadPdfLibs = useCallback(() => { // Hover’da ısıtma için (opsiyonel) import('jspdf') import('html2canvas') }, []) // YENİ: memoize edilmiş sayfalar const memoizedPages = useMemo(() => { return report ? splitContentIntoPages(report.generatedContent) : [] }, [report]) // Asenkron veri yükleme useEffect(() => { const loadReportData = async () => { if (!id) { setError("Rapor ID'si bulunamadı") setIsLoading(false) return } try { setIsLoading(true) setError(null) // Raporu yükle const reportData = await getReportById(id) if (!reportData) { setError('Rapor bulunamadı') setIsLoading(false) return } setReport(reportData) // Şablonu yükle if (reportData.templateId) { const templateData = await getTemplateById(reportData.templateId) setTemplate(templateData || null) } } catch (err) { console.error('Error loading report data:', err) setError('Rapor yüklenirken bir hata oluştu') } finally { setIsLoading(false) } } loadReportData() }, [id, getReportById, getTemplateById]) // Zoom fonksiyonları const handleZoomIn = () => { setZoomLevel((prev) => Math.min(prev + 25, 200)) // Maksimum %200 } const handleZoomOut = () => { setZoomLevel((prev) => Math.max(prev - 25, 50)) // Minimum %50 } // Loading durumu if (isLoading) { return (

{translate('::App.Reports.ReportViewer.LoadingTitle')}

{translate('::App.Reports.ReportViewer.LoadingSubtitle')}

) } // Error durumu if (error || !id) { return (

{error || translate('::App.Reports.ReportViewer.ErrorNotFound')}

) } if (!id) { return (

{translate('::App.Reports.ReportViewer.ErrorNotFound')}

) } // Report yüklenmemiş ise if (!report) { return (

{translate('::App.Reports.ReportViewer.ErrorNotFound')}

{translate('::App.Reports.ReportViewer.ErrorNotFoundDescription')}

) } const handlePrint = () => { // Yazdırma sırasında zoom seviyesini geçici olarak %100'e ayarla const currentZoom = zoomLevel setZoomLevel(100) // DOM'un güncellenmesi için kısa bir gecikme setTimeout(() => { window.print() // Yazdırma işlemi tamamlandıktan sonra orijinal zoom seviyesine geri dön // Print dialog kapandıktan sonra zoom'u eski haline getir setTimeout(() => { setZoomLevel(currentZoom) }, 100) }, 100) } // DEĞİŞTİR: handleDownloadPdf const handleDownloadPdf = async () => { // Ağır kütüphaneleri ihtiyaç anında indir const [jspdfMod, h2cMod] = await Promise.all([import('jspdf'), import('html2canvas')]) // jsPDF bazı dağıtımlarda default, bazılarında { jsPDF } olarak gelir const jsPDFCtor = (jspdfMod as any).default ?? (jspdfMod as any).jsPDF const html2canvas = (h2cMod as any).default ?? (h2cMod as any) const pages = memoizedPages // aşağıdaki 2. adımda tanımlayacağız try { const pdf = new jsPDFCtor({ orientation: 'portrait', unit: 'mm', format: 'a4' }) for (let i = 0; i < pages.length; i++) { const elementId = i === 0 ? 'report-content' : `report-content-page-${i + 1}` const element = document.getElementById(elementId) if (!element) continue // Yakalama öncesi zoom’u etkisizleştir (transform varsa kalite düşmesin) const container = element.parentElement as HTMLElement | null const prevTransform = container?.style.transform if (container) container.style.transform = 'none' const canvas = await html2canvas(element, { scale: 2, useCORS: true, allowTaint: true, backgroundColor: '#ffffff', }) if (container) container.style.transform = prevTransform ?? '' const imgData = canvas.toDataURL('image/png') const imgWidth = 210 const imgHeight = 297 if (i > 0) pdf.addPage() pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight) } pdf.save( `${report!.templateName}_${new Date(report!.generatedAt).toLocaleDateString('tr-TR')}.pdf`, ) } catch (error) { console.error('PDF oluşturma hatası:', error) alert('PDF oluşturulurken bir hata oluştu.') } } return (
{/* Header - Print edilmeyecek */}

{report.templateName}

{new Date(report.generatedAt).toLocaleDateString('tr-TR', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', })} {template && ( {template.categoryName} )}
{zoomLevel}%
{/* Rapor İçeriği */}
{memoizedPages.map((pageContent, index) => (
{index === 0 && ( )} {/* Sayfa Header - Tarih ve Saat */}
{new Date().toLocaleDateString('tr-TR', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', })}
{/* Sayfa İçeriği */}
{/* Sayfa Footer - Sayfa Numarası */}
{translate('::App.Reports.ReportViewer.Page')} {index + 1} / {memoizedPages.length}
))}
) }