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

  1. Composição Completa em Padrão Ouro: Combina reatividade granular de ref e computed com filtragem reativa multicritério.
  2. Exportação de Dados Nativos no Navegador: Uso da API padrão Blob e URL.createObjectURL para download direto de arquivos estruturados sem depender de bibliotecas externas.
  3. Internacionalização Numérica (Intl.NumberFormat): Formatação monetária precisa no padrão brasileiro (pt-BR) garantindo legibilidade corporativa.