Services de Relatório — Vona
Os relatórios do Vona expõem três services de servidor, todos seguindo o padrão factory (createXService()):
| Service | Relatório | Fonte de dados |
|---|---|---|
RankingService | Por Ranking | Data Warehouse (dw_emplacamento) |
TerritoryService | Por Território | Data Warehouse (dw_emplacamento) |
AccessService | De Acessos | Supabase (report_access_events) |
Os services de DW (RankingService e TerritoryService) são documentados nesta página. O AccessService está documentado em Relatório De Acessos.
Todos são usados exclusivamente em Server Components e nunca exportam código para o cliente.
Criar a instância do service
Ambos os services usam o padrão factory — crie a instância no início do Server Component ou Route Handler.
import { createRankingService } from './_lib/server/ranking-service';
import { createTerritoryService } from './_lib/server/territory-service';
// Por Ranking
const rankingService = createRankingService();
// Por Território
const territoryService = createTerritoryService();
Os services importam server-only no topo. Importar em Client Components causa erro de build. Use sempre em page.tsx, layout.tsx ou Route Handlers.
Buscar os dados com getReportData
RankingService
const data = await rankingService.getReportData(tab, filters);
// data: RankingPageData
Parâmetros:
| Parâmetro | Tipo | Descrição |
|---|---|---|
tab | RankingTab | Aba ativa: 'por-marcas' | 'cidades' | 'concessionarias' | 'modelos' |
filters | RankingFilters | Filtros aplicados pelo usuário |
Tipo RankingFilters:
interface RankingFilters {
intervalStart?: string; // YYYY-MM-DD — mês/ano de referência
state?: string; // Sigla do estado (ex: 'SP')
group?: string; // Região ABRACAF (ex: 'ABRACAF')
personType?: string; // 'fisica' | 'juridica' | undefined (= todos)
}
Retorno RankingPageData:
interface RankingPageData {
card: { variant: 'pivot'; data: PivotCard }
| { variant: 'standard'; data: StandardCard }
| null; // null quando não há dados no DW para o período
lastUpdated: string | null; // Data mais recente com dados (ISO)
}
Abas e seu retorno:
| Tab | Variante | Descrição |
|---|---|---|
por-marcas | pivot | Ranking anual por fabricante com acumulados e últimos 5 dias úteis |
cidades | standard | Top 100 cidades por volume de emplacamento |
concessionarias | standard | Top 100 concessionárias por volume |
modelos | standard | Top 100 modelos com pivot mensal (últimos 12 meses) |
TerritoryService
const data = await territoryService.getReportData(grupoId, tab, subtab, filters);
// data: TerritoryPageData
Parâmetros:
| Parâmetro | Tipo | Descrição |
|---|---|---|
grupoId | string | ID do grupo concessionário (reservado — não usado nas queries ainda) |
tab | TerritoryTab | Aba principal |
subtab | CapitalsSubTab | Sub-aba de capitais |
filters | TerritoryFilters | Filtros aplicados |
Tipo TerritoryFilters:
interface TerritoryFilters {
intervalStart?: string; // YYYY-MM-DD — mês/ano de referência
state?: string; // Sigla do estado
group?: string; // Região ABRACAF
dealerships?: string; // CNPJ da concessionária
}
Abas disponíveis (TerritoryTab):
| Tab | Descrição |
|---|---|
da-sua-concessionaria | Emplacamentos dentro e fora da área de influência da concessionária |
por-area-influencia | Market share por fabricante × área de influência |
por-capitais | Dados das 27 capitais brasileiras (3 sub-abas) |
por-cidade-concessionaria | Market share por fabricante × cidade/concessionária |
por-regioes | Market share por fabricante × região ABRACAF |
Sub-abas de capitais (CapitalsSubTab):
| Subtab | Descrição |
|---|---|
anual | Acumulado anual por capital, colunas = meses |
mensal | Emplacamentos do mês selecionado por capital, colunas = dias |
e-marcas | Emplacamentos do mês por capital × fabricante |
Retorno TerritoryPageData:
interface TerritoryPageData {
cards: PivotCard[]; // Um ou mais cards pivot, dependendo da aba
lastUpdated: string | null;
}
Buscar opções de filtro com getFilterOptions
Ambos os services expõem getFilterOptions() para popular os selects do modal de filtros. Deve ser chamado no Server Component da página e passado como props para o header da página.
RankingService
const { stateOptions, groupOptions } = await rankingService.getFilterOptions();
// stateOptions: FilterOption[] — estados distintos do DW
// groupOptions: FilterOption[] — regiões ABRACAF distintas
TerritoryService
const { stateOptions, groupOptions, dealershipOptions } = await territoryService.getFilterOptions();
// dealershipOptions: FilterOption[] — { value: cnpj, label: razao_social }
Referência ao Data Warehouse
Os services se conectam ao DW via getSupabaseDwClient(), uma conexão tagged-template SQL para o schema dw_emplacamento. As queries sempre usam a coluna _ano_particao como filtro de partição para performance.
Tabelas consultadas:
| Tabela | Alias | Descrição |
|---|---|---|
fato_emplacamentos | f | Tabela fato central — uma linha por emplacamento |
dim_veiculo | dv | Dimensão veículo (fabricante, modelo) |
dim_local | dl | Dimensão local (município, estado, região ABRACAF) |
dim_concessionaria | dc | Dimensão concessionária (razão social, CNPJ, região) |
O TerritoryService usa a constante CAPITAIS_CODIGOS_DW (array de 27 inteiros) para filtrar as capitais estaduais. Esses IDs são do DW interno e não correspondem a códigos IBGE. Para adicionar ou corrigir uma capital, edite CAPITAIS_CODIGOS_DW em territory-service.ts consultando dw_emplacamento.dim_local diretamente.
Exportar dados de tabela
Todos os cards de relatório (StandardCard e AccessCard) suportam exportação via o utilitário compartilhado exportCardData, localizado em reports/_lib/export-utils.ts.
import { exportCardData } from '../../_lib/export-utils';
// Dentro de um componente cliente:
exportCardData(card, 'csv'); // CSV com separador vírgula
exportCardData(card, 'xlsx'); // CSV com BOM + separador ponto-e-vírgula (compatível com Excel)
Interface aceita pelo utilitário:
interface ExportableCard {
id: string; // Usado como nome do arquivo: `${id}.csv`
title: string; // Primeira coluna do cabeçalho
columns: ReportColumn[];
rows: ReportRow[];
}
O formato 'xlsx' na verdade gera um arquivo .csv com BOM UTF-8 e separador ponto-e-vírgula. Isso faz o Excel abrir o arquivo diretamente sem precisar configurar a importação — o nome do botão é "Excel (CSV)" por conveniência.
O StandardTableCard e o AccessTableCard já incluem os botões de exportação pré-configurados; não é necessário nenhum código adicional para ativar essa funcionalidade ao criar um novo relatório baseado nesses componentes.