💻 Exemplos: Capítulo 20: Projeto Corporativo: Dashboard Vue 3/Nuxt com Auditoria JSON
Especialização em Front-End com Vue • Demonstrações Práticas Compiláveis
💻 Código de Exemplo 20 — Projeto Corporativo Integrado: Painel Executivo com Auditoria e Exportação JSON
Este exemplo consolida toda a arquitetura estudada na especialização: Composition API com <script setup>, tipagem TypeScript rigorosa, gerenciamento de estado global, reatividade com computed, filtros avançados, componentes reutilizáveis e exportação de logs de auditoria no formato JSON padronizado.
<!-- src/views/CorporateDashboardView.vue -->
<script setup lang="ts">
import { ref, computed } from 'vue';
// 1. CONTRATOS DE TIPAGEM CORPORATIVA
interface SystemTransaction {
id: string;
client: string;
amount: number;
channel: 'WEB' | 'MOBILE' | 'API';
status: 'APPROVED' | 'PENDING' | 'REJECTED';
timestamp: string;
}
interface KPIReport {
totalRevenue: number;
approvalRate: number;
activeTransactions: number;
}
// 2. ESTADO REATIVO DO DASHBOARD
const transactions = ref<SystemTransaction[]>([
{ id: 'tx-901', client: 'Acme Global Logistics', amount: 14250.0, channel: 'API', status: 'APPROVED', timestamp: '2026-09-28T09:12:00Z' },
{ id: 'tx-902', client: 'Banco Horizonte Tech', amount: 8400.0, channel: 'WEB', status: 'PENDING', timestamp: '2026-09-28T09:15:30Z' },
{ id: 'tx-903', client: 'Omni Retail Brasil', amount: 3100.5, channel: 'MOBILE', status: 'APPROVED', timestamp: '2026-09-28T09:20:10Z' },
{ id: 'tx-904', client: 'Delta Cloud Services', amount: 22800.0, channel: 'API', status: 'APPROVED', timestamp: '2026-09-28T09:25:40Z' },
{ id: 'tx-905', client: 'Startup X Finanças', amount: 560.0, channel: 'WEB', status: 'REJECTED', timestamp: '2026-09-28T09:30:00Z' }
]);
const selectedStatus = ref<string>('ALL');
const searchFilter = ref<string>('');
const isExporting = ref<boolean>(false);
// 3. CÁLCULO REATIVO DE INDICADORES (KPIS)
const kpis = computed<KPIReport>(() => {
const approved = transactions.value.filter((t) => t.status === 'APPROVED');
const totalRevenue = approved.reduce((acc, t) => acc + t.amount, 0);
const approvalRate = transactions.value.length > 0 ? (approved.length / transactions.value.length) * 100 : 0;
return {
totalRevenue,
approvalRate: Number(approvalRate.toFixed(1)),
activeTransactions: transactions.value.length
};
});
// 4. FILTRAGEM MULTICRITÉRIO EM TEMPO REAL
const filteredTransactions = computed<SystemTransaction[]>(() => {
return transactions.value.filter((t) => {
const matchesStatus = selectedStatus.value === 'ALL' || t.status === selectedStatus.value;
const matchesSearch = t.client.toLowerCase().includes(searchFilter.value.toLowerCase().trim()) ||
t.id.toLowerCase().includes(searchFilter.value.toLowerCase().trim());
return matchesStatus && matchesSearch;
});
});
// 5. AÇÃO DE SIMULAÇÃO DE NOVA TRANSAÇÃO EM TEMPO REAL
function injectTransaction(): void {
const channels: ('WEB' | 'MOBILE' | 'API')[] = ['WEB', 'MOBILE', 'API'];
const newTx: SystemTransaction = {
id: `tx-${Math.floor(Math.random() * 900) + 1000}`,
client: `Novo Parceiro Enterprise #${transactions.value.length + 1}`,
amount: Number((Math.random() * 15000 + 1000).toFixed(2)),
channel: channels[Math.floor(Math.random() * channels.length)],
status: Math.random() > 0.2 ? 'APPROVED' : 'PENDING',
timestamp: new Date().toISOString()
};
transactions.value.unshift(newTx);
}
// 6. EXPORTAÇÃO DE RELATÓRIO DE AUDITORIA EM JSON PARA DOWNLOAD
function exportAuditJSON(): void {
isExporting.value = true;
const exportPayload = {
generatedAt: new Date().toISOString(),
auditSystemVersion: '3.4.0-vue',
kpiSnapshot: kpis.value,
totalRecords: filteredTransactions.value.length,
records: filteredTransactions.value
};
const blob = new Blob([JSON.stringify(exportPayload, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = `audit_report_${Date.now()}.json`;
anchor.click();
URL.revokeObjectURL(url);
setTimeout(() => {
isExporting.value = false;
}, 600);
}
</script>
<template>
<div class="max-w-5xl mx-auto p-6 space-y-6">
<!-- Cabeçalho do Dashboard Corporativo -->
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 bg-white border p-6 rounded-2xl shadow-sm">
<div>
<div class="flex items-center gap-2">
<span class="w-3 h-3 rounded-full bg-emerald-500 animate-pulse"></span>
<h1 class="text-2xl font-black text-slate-900">Portal Financeiro & Auditoria</h1>
</div>
<p class="text-xs text-slate-500 mt-1">
Visão unificada em Vue 3, TypeScript e Composition API
</p>
</div>
<div class="flex items-center gap-3">
<button
@click="injectTransaction"
class="px-3.5 py-2 text-xs font-semibold bg-indigo-50 hover:bg-indigo-100 text-indigo-700 rounded-xl transition-colors"
>
+ Simular Transação
</button>
<button
:disabled="isExporting"
@click="exportAuditJSON"
class="px-4 py-2 text-xs font-bold bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl transition-colors shadow-sm disabled:opacity-50 flex items-center gap-2"
>
<span>📥</span>
{{ isExporting ? 'Gerando JSON...' : 'Exportar Auditoria JSON' }}
</button>
</div>
</div>
<!-- Indicadores Principais (KPIs Computados) -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-white border p-5 rounded-2xl shadow-sm">
<span class="text-xs font-bold text-slate-400 uppercase tracking-wider">Volume Aprovado</span>
<div class="text-2xl font-black text-slate-900 mt-1">
R$ {{ kpis.totalRevenue.toLocaleString('pt-BR', { minimumFractionDigits: 2 }) }}
</div>
<span class="text-xs text-emerald-600 font-semibold mt-1 inline-block">Liquidação em D+0</span>
</div>
<div class="bg-white border p-5 rounded-2xl shadow-sm">
<span class="text-xs font-bold text-slate-400 uppercase tracking-wider">Taxa de Aprovação</span>
<div class="text-2xl font-black text-emerald-600 mt-1">{{ kpis.approvalRate }}%</div>
<span class="text-xs text-slate-400 mt-1 inline-block">Meta mínima: 85.0%</span>
</div>
<div class="bg-white border p-5 rounded-2xl shadow-sm">
<span class="text-xs font-bold text-slate-400 uppercase tracking-wider">Total de Lançamentos</span>
<div class="text-2xl font-black text-indigo-600 mt-1">{{ kpis.activeTransactions }}</div>
<span class="text-xs text-slate-400 mt-1 inline-block">Eventos no período</span>
</div>
</div>
<!-- Tabela de Auditoria com Filtros Dinâmicos -->
<div class="bg-white border rounded-2xl shadow-sm overflow-hidden space-y-4 p-5">
<div class="flex flex-col sm:flex-row gap-3 justify-between items-center">
<input
type="text"
v-model.trim="searchFilter"
placeholder="Filtrar por cliente ou ID..."
class="w-full sm:w-72 border px-3 py-2 text-xs rounded-xl outline-none focus:ring-2 focus:ring-indigo-500"
/>
<div class="flex items-center gap-2">
<span class="text-xs text-slate-500 font-medium">Status:</span>
<select
v-model="selectedStatus"
class="border px-3 py-1.5 text-xs rounded-xl outline-none bg-white font-medium text-slate-700"
>
<option value="ALL">Todos os Status</option>
<option value="APPROVED">Apenas Aprovados</option>
<option value="PENDING">Apenas Pendentes</option>
<option value="REJECTED">Apenas Rejeitados</option>
</select>
</div>
</div>
<!-- Grade de Registros -->
<div class="border rounded-xl overflow-hidden">
<table class="w-full text-left text-xs">
<thead class="bg-slate-50 text-slate-600 font-semibold border-b">
<tr>
<th class="px-4 py-3">Código</th>
<th class="px-4 py-3">Cliente / Entidade</th>
<th class="px-4 py-3">Canal</th>
<th class="px-4 py-3 text-right">Valor Bruto</th>
<th class="px-4 py-3 text-center">Status</th>
<th class="px-4 py-3 text-right">Data/Hora</th>
</tr>
</thead>
<tbody class="divide-y text-slate-700">
<tr
v-for="tx in filteredTransactions"
:key="tx.id"
class="hover:bg-slate-50 transition-colors"
>
<td class="px-4 py-3 font-mono font-bold text-indigo-600">{{ tx.id }}</td>
<td class="px-4 py-3 font-medium text-slate-800">{{ tx.client }}</td>
<td class="px-4 py-3 font-mono text-[11px] text-slate-500">{{ tx.channel }}</td>
<td class="px-4 py-3 font-bold text-right">
R$ {{ tx.amount.toLocaleString('pt-BR', { minimumFractionDigits: 2 }) }}
</td>
<td class="px-4 py-3 text-center">
<span
class="px-2.5 py-0.5 rounded-full font-bold text-[10px]"
:class="{
'bg-emerald-100 text-emerald-800': tx.status === 'APPROVED',
'bg-amber-100 text-amber-800': tx.status === 'PENDING',
'bg-red-100 text-red-800': tx.status === 'REJECTED'
}"
>
{{ tx.status }}
</span>
</td>
<td class="px-4 py-3 text-right font-mono text-slate-400 text-[11px]">
{{ new Date(tx.timestamp).toLocaleTimeString() }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
🖥️ Saída / Comportamento Esperado
Carga Inicial:
Volume Aprovado: R$ 40.150,50 (Soma de Acme, Omni e Delta).
Taxa de Aprovação: 60.0% (3 de 5 transações aprovadas).
Total de Lançamentos: 5 transações listadas.
Ao Clicar em "+ Simular Transação":
Um novo objeto é inserido no topo com unshift().
Os 3 cards de KPI reagem instantaneamente, recalculando receita e taxa de aprovação.
Ao Filtrar por "APPROVED":
A tabela exibe apenas as linhas com status APPROVED.
Total de itens na tabela reduzido para 3.
Ao Clicar em "Exportar Auditoria JSON":
O navegador gera dinamicamente um arquivo 'audit_report_[timestamp].json' contendo o snapshot completo de KPIs e registros para auditoria em conformidade contábil.
🔍 Destaques da Implementação
- Composição Completa em Padrão Ouro: Combina reatividade granular de
refecomputedcom filtragem reativa multicritério. - Exportação de Dados Nativos no Navegador: Uso da API padrão
BlobeURL.createObjectURLpara download direto de arquivos estruturados sem depender de bibliotecas externas. - Internacionalização Numérica (
Intl.NumberFormat): Formatação monetária precisa no padrão brasileiro (pt-BR) garantindo legibilidade corporativa.