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';
}
}
function sanitizeForPDF(text: string | undefined): string {
if (!text) return '';
return text
.replace(/φ/g, 'phi')
.replace(/ϕ/g, 'phi')
.replace(/α/g, 'alpha')
.replace(/θ/g, 'theta')
.replace(/β/g, 'beta')
.replace(/γ/g, 'gamma')
.replace(/Δ/g, 'Delta')
.replace(/—/g, '-')
.replace(/–/g, '-')
.replace(/“/g, '"')
.replace(/”/g, '"')
.replace(/‘/g, "'")
.replace(/’/g, "'")
.replace(/Æ/g, 'phi') // Fix specifically the one seen in user screenshot
.replace(/≤/g, '<=')
.replace(/≥/g, '>=')
.replace(/≈/g, '~')
.replace(/×/g, 'x')
.replace(/÷/g, '/')
.replace(/²/g, '^2')
.replace(/³/g, '^3')
.replace(/°/g, ' deg');
}
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 (
{sanitizeForPDF(sc.scenarioId)} — {sc.verdict === 'PASS' ? '✓' : sc.verdict === 'WARN' ? '⚠' : '✗'} {sanitizeForPDF(summaryTranslated)}
{sc.enunciado_problema && (
Enunciado: {sanitizeForPDF(sc.enunciado_problema)}
)}
{sc.tabelas_nbr_consultadas && (
Tabelas: {sanitizeForPDF(sc.tabelas_nbr_consultadas)}
)}
{sc.resultado_app && (
Avaliação do App: {sanitizeForPDF(sc.resultado_app)}
)}
{sc.calculation_steps && (
CoT: {sanitizeForPDF(sc.calculation_steps.substring(0, 300))}{sc.calculation_steps.length > 300 ? '...' : ''}
)}
{sc.checks.map((chk, i) => (
{chk.status}
{sanitizeForPDF(chk.name)}
{sanitizeForPDF(chk.actual)} ({sanitizeForPDF(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);
}