import { Document, Page, Text, View, StyleSheet, pdf, Image } from '@react-pdf/renderer'; import type { AuditReport, ModuleAuditResult, ScenarioResult, CheckStatus } from './types'; const styles = StyleSheet.create({ page: { flexDirection: 'column', padding: 40, fontSize: 10, fontFamily: 'Helvetica', color: '#333', }, header: { marginBottom: 20, borderBottom: '2pt solid #6b21a8', paddingBottom: 10, }, title: { fontSize: 20, fontWeight: 'bold', color: '#6b21a8' }, subtitle: { fontSize: 10, color: '#666', marginTop: 4 }, section: { marginTop: 15, marginBottom: 10 }, sectionTitle: { fontSize: 14, fontWeight: 'bold', marginBottom: 8, color: '#111' }, summaryRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 4, }, summaryLabel: { fontWeight: 'bold', width: 200 }, summaryValue: { flex: 1 }, moduleCard: { marginTop: 12, padding: 10, border: '1pt solid #ddd', borderRadius: 4, }, moduleTitle: { fontSize: 12, fontWeight: 'bold', marginBottom: 6, color: '#6b21a8' }, moduleStats: { fontSize: 9, color: '#555', marginBottom: 4 }, scenarioItem: { marginTop: 6, padding: 6, backgroundColor: '#f9fafb', borderRadius: 3, }, scenarioId: { fontSize: 9, fontWeight: 'bold', marginBottom: 3, color: '#111' }, checkRow: { flexDirection: 'row', marginBottom: 2, fontSize: 8 }, statusBadge: { width: 40, fontWeight: 'bold' }, checkLabel: { width: 120 }, checkValue: { flex: 1 }, footer: { position: 'absolute', bottom: 30, left: 40, right: 40, textAlign: 'center', color: '#999', fontSize: 8, borderTop: '1pt solid #eaeaea', paddingTop: 10, }, }); function statusColor(s: CheckStatus): string { switch (s) { case 'PASS': return '#16a34a'; case 'WARN': return '#d97706'; case 'FAIL': return '#dc2626'; } } interface ReportPDFProps { report: AuditReport; } function AuditReportDocument({ report }: ReportPDFProps) { return ( Relatório de Auditoria Auditor IA: {report.provider} ({report.model}) | {new Date(report.timestamp).toLocaleString('pt-BR')} {typeof window !== 'undefined' && window.location && ( )} Resumo Total de Cenários: {report.totalScenarios} Aprovado: {report.totalPassed} Atenção: {report.totalWarnings} Falha: {report.totalFailed} {report.modules.map((mod: ModuleAuditResult) => ( {mod.moduleLabel} — {mod.passed}/{mod.totalScenarios} (Alertas: {mod.warnings} | Falhas: {mod.failed}) {mod.scenarios.map((sc: ScenarioResult) => { const summaryTranslated = (sc.summary || '') .replace(/Verdict PASS\.?/gi, 'Veredito: APROVADO.') .replace(/Verdict WARN\.?/gi, 'Veredito: ATENÇÃO.') .replace(/Verdict FAIL\.?/gi, 'Veredito: FALHA.'); return ( {sc.scenarioId} — {sc.verdict === 'PASS' ? '✓' : sc.verdict === 'WARN' ? '⚠' : '✗'} {summaryTranslated} {sc.enunciado_problema && ( Enunciado: {sc.enunciado_problema} )} {sc.tabelas_nbr_consultadas && ( Tabelas: {sc.tabelas_nbr_consultadas} )} {sc.resultado_app && ( Avaliação do App: {sc.resultado_app} )} {sc.calculation_steps && ( CoT: {sc.calculation_steps.substring(0, 300)}{sc.calculation_steps.length > 300 ? '...' : ''} )} {sc.checks.map((chk, i) => ( {chk.status} {chk.name} {chk.actual} ({chk.expected}) ))} ); })} ))} Gerado por BrainWind v1.0 — Ferramenta de Auditoria NBR 6123:2023 ); } export async function exportAuditReportToPDF(report: AuditReport): Promise { const blob = await pdf().toBlob(); const url = URL.createObjectURL(blob); const link = document.createElement('a'); const dateStr = new Date(report.timestamp).toLocaleDateString('pt-BR').replace(/\//g, '-'); const safeModel = report.model.split('/').pop()?.replace(/[^a-zA-Z0-9_-]/g, '-') || 'LLM'; const filename = `Audit-${safeModel}_${dateStr}.pdf`; link.setAttribute('href', url); link.setAttribute('download', filename); document.body.appendChild(link); link.click(); document.body.removeChild(link); }