From ad98a2a4eb5bf3fba18bb87eb7200a816f84469e Mon Sep 17 00:00:00 2001 From: Marcos Date: Sat, 11 Jul 2026 22:19:43 +0000 Subject: [PATCH] feat(audit): correcao layout pdf e auto-exportacao para markdown detalhado com chain of thought --- app/src/components/AuditPanel.tsx | 20 ++++++--- app/src/lib/audit/export-audit-md.ts | 56 ++++++++++++++++++++++++++ app/src/lib/audit/export-audit-pdf.tsx | 2 +- 3 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 app/src/lib/audit/export-audit-md.ts diff --git a/app/src/components/AuditPanel.tsx b/app/src/components/AuditPanel.tsx index fc4d021..e7f830a 100644 --- a/app/src/components/AuditPanel.tsx +++ b/app/src/components/AuditPanel.tsx @@ -85,12 +85,16 @@ export default function AuditPanel() { config, scenarios, (p) => { - setProgress(p); + if (typeof p === 'number') setProgress(p); }, controller.signal ); setReport(reportResult); setStatus('done'); + + // Auto-download MD + import('@/lib/audit/export-audit-md').then(m => m.downloadAuditMarkdown(reportResult)); + } catch (e) { if (e instanceof Error && e.message.includes('Abortado')) { setStatus('idle'); @@ -186,10 +190,16 @@ export default function AuditPanel() { )} {status === 'done' && report && ( - + <> + + + )} diff --git a/app/src/lib/audit/export-audit-md.ts b/app/src/lib/audit/export-audit-md.ts new file mode 100644 index 0000000..cdf52c8 --- /dev/null +++ b/app/src/lib/audit/export-audit-md.ts @@ -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); +} diff --git a/app/src/lib/audit/export-audit-pdf.tsx b/app/src/lib/audit/export-audit-pdf.tsx index 3f13108..51e50da 100644 --- a/app/src/lib/audit/export-audit-pdf.tsx +++ b/app/src/lib/audit/export-audit-pdf.tsx @@ -101,7 +101,7 @@ function AuditReportDocument({ report }: ReportPDFProps) { {report.modules.map((mod: ModuleAuditResult) => ( - + {mod.moduleLabel} — {mod.passed}/{mod.totalScenarios} (W:{mod.warnings} F:{mod.failed})