feat(audit): correcao layout pdf e auto-exportacao para markdown detalhado com chain of thought

This commit is contained in:
2026-07-11 22:19:43 +00:00
parent ef3651db95
commit ad98a2a4eb
3 changed files with 72 additions and 6 deletions
+15 -5
View File
@@ -85,12 +85,16 @@ export default function AuditPanel() {
config, config,
scenarios, scenarios,
(p) => { (p) => {
setProgress(p); if (typeof p === 'number') setProgress(p);
}, },
controller.signal controller.signal
); );
setReport(reportResult); setReport(reportResult);
setStatus('done'); setStatus('done');
// Auto-download MD
import('@/lib/audit/export-audit-md').then(m => m.downloadAuditMarkdown(reportResult));
} catch (e) { } catch (e) {
if (e instanceof Error && e.message.includes('Abortado')) { if (e instanceof Error && e.message.includes('Abortado')) {
setStatus('idle'); setStatus('idle');
@@ -186,10 +190,16 @@ export default function AuditPanel() {
</Button> </Button>
)} )}
{status === 'done' && report && ( {status === 'done' && report && (
<Button variant="outline" size="sm" onClick={handleExportPDF}> <>
<FileDown className="w-3.5 h-3.5 mr-1.5" /> <Button variant="outline" size="sm" onClick={() => import('@/lib/audit/export-audit-md').then(m => m.downloadAuditMarkdown(report))}>
Exportar PDF <FileDown className="w-3.5 h-3.5 mr-1.5" />
</Button> Exportar MD
</Button>
<Button variant="outline" size="sm" onClick={handleExportPDF}>
<FileDown className="w-3.5 h-3.5 mr-1.5" />
Exportar PDF
</Button>
</>
)} )}
</div> </div>
+56
View File
@@ -0,0 +1,56 @@
import type { AuditReport } from './types';
export function generateAuditMarkdown(report: AuditReport): string {
let md = `# Relatório de Auditoria — BrainWind\n\n`;
md += `**Provedor:** ${report.provider} (${report.model})\n`;
md += `**Data:** ${new Date(report.timestamp).toLocaleString('pt-BR')}\n\n`;
md += `## Resumo Geral\n`;
md += `- **Total de Cenários:** ${report.totalScenarios}\n`;
md += `- **PASS:** ${report.totalPassed}\n`;
md += `- **WARN:** ${report.totalWarnings} ⚠️\n`;
md += `- **FAIL:** ${report.totalFailed}\n\n`;
for (const mod of report.modules) {
md += `## Módulo: ${mod.moduleLabel}\n`;
md += `**Total:** ${mod.totalScenarios} | **PASS:** ${mod.passed} | **WARN:** ${mod.warnings} | **FAIL:** ${mod.failed}\n\n`;
for (const sc of mod.scenarios) {
const icon = sc.verdict === 'PASS' ? '✅' : sc.verdict === 'WARN' ? '⚠️' : '❌';
md += `### ${icon} Cenário: ${sc.scenarioId}\n`;
md += `**Veredito:** ${sc.verdict} — *${sc.summary}*\n\n`;
if (sc.llmNotes) {
md += `**Observações da IA:** ${sc.llmNotes}\n\n`;
}
md += `**Raciocínio Matemático (Chain of Thought):**\n`;
const calcSteps = sc.calculation_steps || 'Sem raciocínio fornecido pela IA.';
md += `> ${calcSteps.split('\n').join('\n> ')}\n\n`;
md += `**Verificações Individuais:**\n`;
for (const chk of sc.checks) {
const chkIcon = chk.status === 'PASS' ? '✅' : chk.status === 'WARN' ? '⚠️' : '❌';
md += `- ${chkIcon} **${chk.name}:** Calculado IA = \`${chk.expected}\` | App = \`${chk.actual}\`\n`;
if (chk.explanation) {
md += ` - *Nota:* ${chk.explanation}\n`;
}
}
md += `\n---\n\n`;
}
}
return md;
}
export function downloadAuditMarkdown(report: AuditReport) {
const md = generateAuditMarkdown(report);
const blob = new Blob([md], { type: 'text/markdown;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', `auditoria-${report.id}.md`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
+1 -1
View File
@@ -101,7 +101,7 @@ function AuditReportDocument({ report }: ReportPDFProps) {
</View> </View>
{report.modules.map((mod: ModuleAuditResult) => ( {report.modules.map((mod: ModuleAuditResult) => (
<View key={mod.module} wrap={false} style={styles.moduleCard}> <View key={mod.module} style={styles.moduleCard}>
<Text style={styles.moduleTitle}> <Text style={styles.moduleTitle}>
{mod.moduleLabel} {mod.passed}/{mod.totalScenarios} (W:{mod.warnings} F:{mod.failed}) {mod.moduleLabel} {mod.passed}/{mod.totalScenarios} (W:{mod.warnings} F:{mod.failed})
</Text> </Text>