import { jsPDF } from 'jspdf'; import type { ReportData } from '../types'; const COLORS = { primary: '#1e40af', secondary: '#6366f1', success: '#059669', danger: '#dc2626', dark: '#1e293b', gray: '#64748b', light: '#f8fafc', }; const formatDate = (dateStr: string): string => { return dateStr || 'N/A'; }; const getStatusColor = (status: string): string => { if (status === 'CONFORME' || status === 'OK') return COLORS.success; if (status === 'NÃO CONFORME' || status === 'FALHA') return COLORS.danger; return COLORS.gray; }; export const generatePdfReport = (report: ReportData): jsPDF => { const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4', }); const pageWidth = doc.internal.pageSize.getWidth(); const pageHeight = doc.internal.pageSize.getHeight(); const margin = 15; let y = margin; const addHeader = () => { doc.setFillColor(COLORS.primary); doc.rect(0, 0, pageWidth, 30, 'F'); doc.setTextColor(255, 255, 255); doc.setFontSize(20); doc.setFont('helvetica', 'bold'); doc.text('SteelCheck', margin, 18); doc.setFontSize(10); doc.setFont('helvetica', 'normal'); doc.text('Relatório de Análise Técnica de Qualidade', margin, 25); y = 40; }; const addFooter = (pageNum: number) => { doc.setFillColor(COLORS.light); doc.rect(0, pageHeight - 15, pageWidth, 15, 'F'); doc.setFontSize(8); doc.setTextColor(COLORS.gray); doc.text(`Página ${pageNum}`, pageWidth / 2, pageHeight - 8, { align: 'center' }); doc.text('SteelCheck - Análise assistida por IA', margin, pageHeight - 8); }; addHeader(); doc.setTextColor(COLORS.dark); doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.text('Relatório Técnico de Qualidade', pageWidth / 2, y, { align: 'center' }); y += 8; doc.setFontSize(10); doc.setFont('helvetica', 'normal'); doc.setTextColor(COLORS.gray); doc.text('Análise de conformidade normativa assistida por IA', pageWidth / 2, y, { align: 'center' }); y += 12; doc.setFillColor(COLORS.light); doc.roundedRect(margin, y, pageWidth - 2 * margin, 25, 3, 3, 'F'); y += 5; doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor(COLORS.primary); doc.text('Grau de Confiança da Análise', pageWidth / 2, y, { align: 'center' }); y += 8; doc.setFontSize(36); doc.setTextColor(COLORS.secondary); doc.text(`${report.confidence}%`, pageWidth / 2, y + 5, { align: 'center' }); y += 20; const sectionTitle = (title: string) => { y += 5; doc.setFillColor(COLORS.primary); doc.rect(margin, y, 3, 10, 'F'); doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor(COLORS.dark); doc.text(title, margin + 8, y + 7); doc.setDrawColor(200, 200, 200); doc.line(margin + 8, y + 12, pageWidth - margin, y + 12); y += 15; }; const addKeyValue = (key: string, value: string) => { doc.setFontSize(9); doc.setFont('helvetica', 'bold'); doc.setTextColor(COLORS.gray); doc.text(key.toUpperCase(), margin, y); doc.setFont('helvetica', 'normal'); doc.setTextColor(COLORS.dark); const valueLines = doc.splitTextToSize(value || 'N/A', pageWidth - 2 * margin - 40); doc.text(valueLines, margin + 40, y); y += 6 * (valueLines.length || 1); }; sectionTitle('1. Dados de Identificação'); const ident = report.identification; addKeyValue('Produto', ident.product); addKeyValue('Norma(s)', ident.standards); addKeyValue('Fabricante', ident.manufacturer); addKeyValue('Nr. Certificado', ident.certificateNumber); addKeyValue('Data', ident.certificateDate); addKeyValue('Lotes', ident.batches); addKeyValue('Corridas (Heats)', ident.heats); addKeyValue('Quantidade', ident.quantity); sectionTitle('2. Verificação de Conformidade'); const complianceStatusColor = report.compliance.status === 'CONFORME' ? COLORS.success : COLORS.danger; doc.setFillColor(complianceStatusColor); doc.roundedRect(pageWidth - margin - 40, y - 12, 35, 10, 2, 2, 'F'); doc.setTextColor(255, 255, 255); doc.setFontSize(10); doc.setFont('helvetica', 'bold'); doc.text(report.compliance.status, pageWidth - margin - 22, y - 6, { align: 'center' }); const addTable = (title: string, items: { property?: string; element?: string; test?: string; norm: string; certificate: string; status: string }[]) => { doc.setFontSize(10); doc.setFont('helvetica', 'bold'); doc.setTextColor(COLORS.dark); doc.text(title, margin, y); y += 6; const colWidths = [45, 40, 40, 25]; const colX = [margin, margin + 45, margin + 85, margin + 125]; doc.setFillColor(240, 240, 240); doc.rect(margin, y, pageWidth - 2 * margin, 8, 'F'); doc.setFontSize(8); doc.setTextColor(COLORS.gray); doc.text('Propriedade/Norma', colX[0] + 10, y + 5); doc.text('Valor Norma', colX[1] + 5, y + 5); doc.text('Valor Certificado', colX[2] + 5, y + 5); doc.text('Status', colX[3] + 8, y + 5); y += 8; items.forEach((item) => { const label = item.property || item.element || item.test || ''; doc.setFont('helvetica', 'normal'); doc.setTextColor(COLORS.dark); doc.setFontSize(8); const labelLines = doc.splitTextToSize(label, colWidths[0] - 5); const normLines = doc.splitTextToSize(item.norm, colWidths[1] - 5); const certLines = doc.splitTextToSize(item.certificate, colWidths[2] - 5); const rowHeight = Math.max(labelLines.length, normLines.length, certLines.length) * 4 + 4; doc.text(labelLines, colX[0], y + 4); doc.text(normLines, colX[1], y + 4); doc.text(certLines, colX[2], y + 4); const statusColor = item.status === 'OK' ? COLORS.success : COLORS.danger; doc.setFillColor(statusColor); doc.roundedRect(colX[3], y, 20, 6, 1, 1, 'F'); doc.setTextColor(255, 255, 255); doc.setFontSize(7); doc.setFont('helvetica', 'bold'); doc.text(item.status, colX[3] + 10, y + 4, { align: 'center' }); y += rowHeight; }); y += 5; }; addTable('Propriedades Mecânicas', report.compliance.mechanical); addTable('Composição Química (%)', report.compliance.chemical); if (report.compliance.otherTests.length > 0) { addTable('Outros Testes', report.compliance.otherTests); } if (report.compliance.status === 'CONFORME' && report.overPerformance.length > 0) { sectionTitle('3. Destaques de Desempenho'); doc.setFontSize(9); doc.setTextColor(COLORS.gray); doc.text('Este material excede os requisitos mínimos normativos:', margin, y); y += 6; report.overPerformance.forEach((item) => { doc.setFillColor(240, 255, 240); doc.roundedRect(margin, y - 3, pageWidth - 2 * margin, 12, 2, 2, 'F'); doc.setFont('helvetica', 'bold'); doc.setTextColor(COLORS.dark); doc.text(item.property, margin + 5, y + 2); doc.setTextColor(COLORS.success); doc.text(item.value, margin + 5, y + 8); y += 15; }); } if (y > pageHeight - 50) { doc.addPage(); y = margin; addHeader(); } sectionTitle('4. Normas Equivalentes'); doc.setFontSize(9); doc.setTextColor(COLORS.gray); doc.text(`Equivalências internacionais para ${report.identification.standards}:`, margin, y); y += 8; const boxWidth = 40; const boxHeight = 15; const gap = 5; let x = margin; report.equivalents.forEach((item) => { if (x + boxWidth > pageWidth - margin) { x = margin; y += boxHeight + gap; } doc.setFillColor(248, 248, 255); doc.roundedRect(x, y, boxWidth, boxHeight, 2, 2, 'F'); doc.setFontSize(7); doc.setTextColor(COLORS.gray); doc.text(item.system, x + 2, y + 4); doc.setFontSize(9); doc.setFont('helvetica', 'bold'); doc.setTextColor(COLORS.dark); doc.text(item.norm, x + 2, y + 10); x += boxWidth + gap; }); const totalPages = doc.getNumberOfPages(); for (let i = 1; i <= totalPages; i++) { doc.setPage(i); addFooter(i); } return doc; }; export const exportAsPdf = (elementId: string, fileName: string, action: 'preview' | 'download'): void => { console.log('Using native jsPDF for better quality PDF generation'); const hiddenDiv = document.getElementById(elementId); if (!hiddenDiv) { console.error(`Element with id ${elementId} not found.`); return; } try { const jsonData = hiddenDiv.getAttribute('data-report'); if (!jsonData) { console.error('Report data not found in element'); return; } const report: ReportData = JSON.parse(jsonData); const pdf = generatePdfReport(report); if (action === 'download') { pdf.save(`${fileName}.pdf`); } else { const pdfBlob = pdf.output('blob'); const pdfUrl = URL.createObjectURL(pdfBlob); window.open(pdfUrl, '_blank'); URL.revokeObjectURL(pdfUrl); } } catch (error) { console.error('Error generating PDF:', error); } };