From 8fc62aeb0fcd3806831c3e3ae599d748a30b1f5c Mon Sep 17 00:00:00 2001 From: admtracksteel Date: Tue, 23 Jun 2026 00:05:22 +0000 Subject: [PATCH] Deploy Inicial do SteelCheck com Docker Build Automatizado --- components/ReportDisplay.tsx | 50 +++--- services/pdfService.ts | 307 +++-------------------------------- 2 files changed, 52 insertions(+), 305 deletions(-) diff --git a/components/ReportDisplay.tsx b/components/ReportDisplay.tsx index d254fd0..7f836e5 100644 --- a/components/ReportDisplay.tsx +++ b/components/ReportDisplay.tsx @@ -118,20 +118,20 @@ export const ReportDisplay: React.FC = ({ report }) => {
{/* Propriedades Mecânicas (Esquerda) */} -
+

2. Propriedades Mecânicas

-
- +
+
- - - - + + + + @@ -140,17 +140,17 @@ export const ReportDisplay: React.FC = ({ report }) => { const diffColor = isPositive ? 'text-emerald-600 dark:text-emerald-400' : 'text-rose-600 dark:text-rose-400'; return ( - - - + - @@ -172,30 +172,30 @@ export const ReportDisplay: React.FC = ({ report }) => { {/* Composição Química (Direita) */} -
+

3. Composição Química (%)

-
-
PropriedadeCertificado% Rel.StatusPropriedadeCertif.% Rel.Status
- {item.property} + + {item.property} {item.certificate} + {item.certificate} {item.differencePercentage ? ( {item.differencePercentage} ) : -} - + + {statusIcon(item.status)} {item.status}
+
+
- - - - + + + + {compliance.chemical.map((item, i) => ( - - - - + + + diff --git a/services/pdfService.ts b/services/pdfService.ts index 13f5021..951e5ab 100644 --- a/services/pdfService.ts +++ b/services/pdfService.ts @@ -1,291 +1,38 @@ import { jsPDF } from 'jspdf'; -import type { ReportData } from '../types'; +import html2canvas from 'html2canvas'; -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'); +export const exportAsPdf = async (elementId: string, fileName: string, action: 'preview' | 'download'): Promise => { + const element = document.getElementById(elementId); - 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) { + if (!element) { 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; - } + // We temporarily make the element visible to html2canvas if it's hidden by CSS + // by cloning it or just capturing it directly (html2canvas can capture off-screen if it's positioned absolute) + + const canvas = await html2canvas(element, { + scale: 2, // Higher resolution + useCORS: true, + logging: false, + backgroundColor: '#ffffff' // Ensure white background + }); - const report: ReportData = JSON.parse(jsonData); - const pdf = generatePdfReport(report); + const imgData = canvas.toDataURL('image/jpeg', 0.95); + + // A4 dimensions in mm: 210 x 297 + const pdf = new jsPDF({ + orientation: 'portrait', + unit: 'mm', + format: 'a4', + }); + + const pdfWidth = pdf.internal.pageSize.getWidth(); + const pdfHeight = (canvas.height * pdfWidth) / canvas.width; + + pdf.addImage(imgData, 'JPEG', 0, 0, pdfWidth, pdfHeight); if (action === 'download') { pdf.save(`${fileName}.pdf`); @@ -296,6 +43,6 @@ export const exportAsPdf = (elementId: string, fileName: string, action: 'previe URL.revokeObjectURL(pdfUrl); } } catch (error) { - console.error('Error generating PDF:', error); + console.error('Error generating PDF with html2canvas:', error); } }; \ No newline at end of file
Elem.Norma de ReferênciaValor CertificadoStatusElem.Norma ReferênciaCertificadoStatus
{item.element}{item.norm}{item.certificate} - + {item.element}{item.norm}{item.certificate} + {item.status}