Files
sar/libs/shared/api-interface/src/lib/dashboard.contract.ts
julian 996eff8e65 feat(auth,ger): usuarios dev por papel, escopo do supervisor por equipe e positivacao diaria
- DevLogin com 3 usuarios reais: Pavei (rep 29), Sidnei (supervisor 191)
  e Lucas (gerente 156); guard deixa de forcar DEV_REP_CODE em dev e usa
  sub/id_empresa do JWT (fallback para os valores do .env); idEmpresa do
  token dev opcional, default DEV_EMPRESA_ID
- getTeamCodes (vw_representantes.cod_supervisor): supervisor ve so a
  equipe em pedidos SAR/ERP, detalhe, aprovar/recusar, clientes (lista e
  carteira), dashboard supervisor, chamados SAC e relatorios
  carteira/curva ABC; gerente/admin seguem vendo a empresa toda
- Painel gerencial: card "Positivacao Diaria de Clientes" (clientes
  distintos por dia via vw_pedidos_erp) com campo de meta/dia
  persistido em localStorage, linha de meta tracejada e contador de
  dias na meta

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:24:54 +00:00

128 lines
4.2 KiB
TypeScript

import { z } from 'zod';
import { PedidoSummarySchema } from './order.contract';
// ADR 0006 revogado: OrderSummary → PedidoSummary, ids numéricos.
export const ClienteNaoPositivadoSchema = z.object({
idCliente: z.number().int(),
nome: z.string(),
razao: z.string().nullable(),
diasSemPedido: z.number().int(), // 999 = nunca comprou
ultimoPedido: z.string().nullable(), // ISO date YYYY-MM-DD
comprouAntes: z.boolean(),
whatsapp: z.string().nullable(),
});
export type ClienteNaoPositivado = z.infer<typeof ClienteNaoPositivadoSchema>;
// Dimensão de meta. O ERP (vw_metas.tipo) define como o cliente acompanha metas:
// GL = global, GR = por grupo. Motor único; outras dimensões (marca/subgrupo/
// produto) entram aqui depois sem mudar a forma.
export const MetaDimensaoSchema = z.enum(['global', 'grupo']);
export type MetaDimensao = z.infer<typeof MetaDimensaoSchema>;
// Linha de meta vs realizado por grupo, multi-medida (codigo=null = rollup global).
// fator = R$/kg (valor/peso); o ERP guarda vl_fator na meta.
export const MetaItemSchema = z.object({
codigo: z.number().int().nullable(),
rotulo: z.string(),
pedidos: z.number().int(),
valorMeta: z.number(),
valorReal: z.number(),
qtdMeta: z.number(),
qtdReal: z.number(),
pesoMeta: z.number(),
pesoReal: z.number(),
fatorMeta: z.number(),
fatorReal: z.number(),
pct: z.number(),
falta: z.number(),
});
export type MetaItem = z.infer<typeof MetaItemSchema>;
export const MesAnoSchema = z.object({
mes: z.number().int().min(1).max(12),
valor: z.number(),
meta: z.number(),
atingiu: z.boolean(),
});
export type MesAno = z.infer<typeof MesAnoSchema>;
export const RepDashboardSchema = z.object({
meta: z.object({
atingido: z.number(),
total: z.number(),
pct: z.number(),
falta: z.number(),
}),
metaDimensao: MetaDimensaoSchema.default('global'),
metasPorGrupo: z.array(MetaItemSchema).default([]),
pedidosMes: z.number().int(),
pedidosRecentes: z.array(PedidoSummarySchema),
naoPositivados: z.array(ClienteNaoPositivadoSchema),
totalNaoPositivados: z.number().int(),
historicoMensal: z.array(MesAnoSchema).default([]),
syncedAt: z.iso.datetime(),
});
export type RepDashboard = z.infer<typeof RepDashboardSchema>;
export const RepInativosSummarySchema = z.object({
codVendedor: z.number().int(),
nomeVendedor: z.string().nullable().optional(),
inativosCount: z.number().int(),
});
export type RepInativosSummary = z.infer<typeof RepInativosSummarySchema>;
export const SupervisorDashboardSchema = z.object({
approvalQueue: z.array(PedidoSummarySchema),
pedidosDia: z.object({
count: z.number().int(),
total: z.number(),
countSemanaAnterior: z.number().int(),
totalSemanaAnterior: z.number(),
}),
inativosPorRep: z.array(RepInativosSummarySchema),
syncedAt: z.iso.datetime(),
});
export type SupervisorDashboard = z.infer<typeof SupervisorDashboardSchema>;
export const PositivacaoRepSchema = z.object({
codVendedor: z.number().int(),
nomeVendedor: z.string().nullable(),
totalClientes: z.number().int(),
clientesPositivados: z.number().int(),
pctPositivacao: z.number(),
});
export type PositivacaoRep = z.infer<typeof PositivacaoRepSchema>;
export const RankingRepSchema = z.object({
codVendedor: z.number().int(),
nomeVendedor: z.string().nullable(),
pedidos: z.number().int(),
clientesAtendidos: z.number().int(),
faturamento: z.number(),
pctMeta: z.number(),
});
export type RankingRep = z.infer<typeof RankingRepSchema>;
// Clientes distintos positivados por dia do período (YYYY-MM-DD)
export const PositivacaoDiaSchema = z.object({
dia: z.string(),
clientes: z.number().int(),
});
export type PositivacaoDia = z.infer<typeof PositivacaoDiaSchema>;
export const ManagerDashboardSchema = z.object({
faturamentoMes: z.number(),
pedidosMes: z.number().int(),
ticketMedio: z.number(),
totalReps: z.number().int(),
promocoesAtivas: z.number().int(),
metaTotal: z.number(),
metasPorGrupo: z.array(MetaItemSchema).default([]),
rankingReps: z.array(RankingRepSchema),
positivacaoReps: z.array(PositivacaoRepSchema),
positivacaoDiaria: z.array(PositivacaoDiaSchema).default([]),
syncedAt: z.iso.datetime(),
});
export type ManagerDashboard = z.infer<typeof ManagerDashboardSchema>;