🚀 Auto-deploy: BrainWind atualizado em 15/07/2026 21:36:38
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Document, Page, Text, View, StyleSheet, pdf } from '@react-pdf/renderer';
|
||||
import { Document, Page, Text, View, StyleSheet, pdf, Image } from '@react-pdf/renderer';
|
||||
import type { AuditReport, ModuleAuditResult, ScenarioResult, CheckStatus } from './types';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -73,11 +73,16 @@ function AuditReportDocument({ report }: ReportPDFProps) {
|
||||
return (
|
||||
<Document>
|
||||
<Page size="A4" style={styles.page}>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.title}>Relatório de Auditoria — BrainWind</Text>
|
||||
<Text style={styles.subtitle}>
|
||||
Provedor: {report.provider} ({report.model}) | {new Date(report.timestamp).toLocaleString('pt-BR')}
|
||||
</Text>
|
||||
<View style={[styles.header, { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }]}>
|
||||
<View>
|
||||
<Text style={styles.title}>Relatório de Auditoria</Text>
|
||||
<Text style={styles.subtitle}>
|
||||
Auditor IA: {report.provider} ({report.model}) | {new Date(report.timestamp).toLocaleString('pt-BR')}
|
||||
</Text>
|
||||
</View>
|
||||
{typeof window !== 'undefined' && window.location && (
|
||||
<Image src={`${window.location.origin}/logo_brainwind.png`} style={{ width: 120, height: 40, objectFit: 'contain' }} />
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View style={styles.section}>
|
||||
@@ -87,15 +92,15 @@ function AuditReportDocument({ report }: ReportPDFProps) {
|
||||
<Text style={styles.summaryValue}>{report.totalScenarios}</Text>
|
||||
</View>
|
||||
<View style={styles.summaryRow}>
|
||||
<Text style={[styles.summaryLabel, { color: '#16a34a' }]}>Pass:</Text>
|
||||
<Text style={[styles.summaryLabel, { color: '#16a34a' }]}>Aprovado:</Text>
|
||||
<Text style={styles.summaryValue}>{report.totalPassed}</Text>
|
||||
</View>
|
||||
<View style={styles.summaryRow}>
|
||||
<Text style={[styles.summaryLabel, { color: '#d97706' }]}>Warn:</Text>
|
||||
<Text style={[styles.summaryLabel, { color: '#d97706' }]}>Atenção:</Text>
|
||||
<Text style={styles.summaryValue}>{report.totalWarnings}</Text>
|
||||
</View>
|
||||
<View style={styles.summaryRow}>
|
||||
<Text style={[styles.summaryLabel, { color: '#dc2626' }]}>Fail:</Text>
|
||||
<Text style={[styles.summaryLabel, { color: '#dc2626' }]}>Falha:</Text>
|
||||
<Text style={styles.summaryValue}>{report.totalFailed}</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -103,13 +108,33 @@ function AuditReportDocument({ report }: ReportPDFProps) {
|
||||
{report.modules.map((mod: ModuleAuditResult) => (
|
||||
<View key={mod.module} style={styles.moduleCard}>
|
||||
<Text style={styles.moduleTitle}>
|
||||
{mod.moduleLabel} — {mod.passed}/{mod.totalScenarios} (W:{mod.warnings} F:{mod.failed})
|
||||
{mod.moduleLabel} — {mod.passed}/{mod.totalScenarios} (Alertas: {mod.warnings} | Falhas: {mod.failed})
|
||||
</Text>
|
||||
{mod.scenarios.map((sc: ScenarioResult) => (
|
||||
{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 (
|
||||
<View key={sc.scenarioId} style={styles.scenarioItem}>
|
||||
<Text style={styles.scenarioId}>
|
||||
{sc.scenarioId} — {sc.verdict === 'PASS' ? '✓' : sc.verdict === 'WARN' ? '⚠' : '✗'} {sc.summary}
|
||||
{sc.scenarioId} — {sc.verdict === 'PASS' ? '✓' : sc.verdict === 'WARN' ? '⚠' : '✗'} {summaryTranslated}
|
||||
</Text>
|
||||
{sc.enunciado_problema && (
|
||||
<Text style={{ fontSize: 8, color: '#444', marginBottom: 2 }}>Enunciado: {sc.enunciado_problema}</Text>
|
||||
)}
|
||||
{sc.tabelas_nbr_consultadas && (
|
||||
<Text style={{ fontSize: 8, color: '#444', marginBottom: 2 }}>Tabelas: {sc.tabelas_nbr_consultadas}</Text>
|
||||
)}
|
||||
{sc.resultado_app && (
|
||||
<Text style={{ fontSize: 8, color: '#444', marginBottom: 2 }}>Avaliação do App: {sc.resultado_app}</Text>
|
||||
)}
|
||||
{sc.calculation_steps && (
|
||||
<Text style={{ fontSize: 8, color: '#444', marginBottom: 4, fontStyle: 'italic' }}>
|
||||
CoT: {sc.calculation_steps.substring(0, 300)}{sc.calculation_steps.length > 300 ? '...' : ''}
|
||||
</Text>
|
||||
)}
|
||||
{sc.checks.map((chk, i) => (
|
||||
<View key={i} style={styles.checkRow}>
|
||||
<Text style={[styles.statusBadge, { color: statusColor(chk.status) }]}>{chk.status}</Text>
|
||||
@@ -118,7 +143,8 @@ function AuditReportDocument({ report }: ReportPDFProps) {
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
))}
|
||||
|
||||
@@ -134,8 +160,13 @@ export async function exportAuditReportToPDF(report: AuditReport): Promise<void>
|
||||
const blob = await pdf(<AuditReportDocument report={report} />).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', `auditoria-${report.id}.pdf`);
|
||||
link.setAttribute('download', filename);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
Reference in New Issue
Block a user