feat(audit): adicionar módulo de auditoria + rebranding BrainWind
- AuditPanel, AuditDetailPopup, parser, runner, prompt-builder, serializer, scenarios, export-audit-pdf - i18n: strings novas para auditoria - Settings: integração com módulo de auditoria - types: tipos compartilhados do módulo audit
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import { Document, Page, Text, View, StyleSheet, pdf } 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 (
|
||||
<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>
|
||||
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Resumo</Text>
|
||||
<View style={styles.summaryRow}>
|
||||
<Text style={styles.summaryLabel}>Total de Cenários:</Text>
|
||||
<Text style={styles.summaryValue}>{report.totalScenarios}</Text>
|
||||
</View>
|
||||
<View style={styles.summaryRow}>
|
||||
<Text style={[styles.summaryLabel, { color: '#16a34a' }]}>Pass:</Text>
|
||||
<Text style={styles.summaryValue}>{report.totalPassed}</Text>
|
||||
</View>
|
||||
<View style={styles.summaryRow}>
|
||||
<Text style={[styles.summaryLabel, { color: '#d97706' }]}>Warn:</Text>
|
||||
<Text style={styles.summaryValue}>{report.totalWarnings}</Text>
|
||||
</View>
|
||||
<View style={styles.summaryRow}>
|
||||
<Text style={[styles.summaryLabel, { color: '#dc2626' }]}>Fail:</Text>
|
||||
<Text style={styles.summaryValue}>{report.totalFailed}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{report.modules.map((mod: ModuleAuditResult) => (
|
||||
<View key={mod.module} wrap={false} style={styles.moduleCard}>
|
||||
<Text style={styles.moduleTitle}>
|
||||
{mod.moduleLabel} — {mod.passed}/{mod.totalScenarios} (W:{mod.warnings} F:{mod.failed})
|
||||
</Text>
|
||||
{mod.scenarios.map((sc: ScenarioResult) => (
|
||||
<View key={sc.scenarioId} style={styles.scenarioItem}>
|
||||
<Text style={styles.scenarioId}>
|
||||
{sc.scenarioId} — {sc.verdict === 'PASS' ? '✓' : sc.verdict === 'WARN' ? '⚠' : '✗'} {sc.summary}
|
||||
</Text>
|
||||
{sc.checks.map((chk, i) => (
|
||||
<View key={i} style={styles.checkRow}>
|
||||
<Text style={[styles.statusBadge, { color: statusColor(chk.status) }]}>{chk.status}</Text>
|
||||
<Text style={styles.checkLabel}>{chk.name}</Text>
|
||||
<Text style={styles.checkValue}>{chk.actual} ({chk.expected})</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
))}
|
||||
|
||||
<Text style={styles.footer}>
|
||||
Gerado por BrainWind v1.0 — Ferramenta de Auditoria NBR 6123:2023
|
||||
</Text>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
}
|
||||
|
||||
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');
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', `auditoria-${report.id}.pdf`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
Reference in New Issue
Block a user