feat(web+api): cockpit gerente — painel, equipe, políticas e positivação
## API - GET /dashboard/manager: KPIs agregados (faturamento, pedidos, ticket médio, promoções ativas), meta total do período, ranking top 10 com clientes atendidos e % meta, positivação por representante, venda necessária/dia - Parâmetros opcionais ?mes&ano para filtrar período - GET /equipe: lista de reps com pedidos, faturamento, ticket médio e % meta (deduplicação de vw_representantes) - GET/POST /politicas/descontos: alçada de desconto por rep (AlcadaDesconto) - GET/POST/PATCH/DELETE /politicas/promocoes: CRUD de promoções com validade - Metas lidas de sar.vw_metas (tipo=GR), não de vw_metas do ERP ## Schema - Novo model Promocao (sar.promocoes) criado via prisma db execute ## Frontend - Cockpit /ger: GerPainel, EquipePage, PoliticasPage - GerPainel: filtro mes/ano, cards KPI, cards de meta vs realizado (atingimento, falta, venda/dia), ranking com clientes atendidos, positivação de clientes por representante (paginada) - Sidebar e BottomNav role-aware: rep / supervisor / gerente (manager+admin) - Clientes acessível ao gerente (carteira completa de todos os reps) - Rotas: HomeRoute redireciona por role; /ger, /ger/equipe, /ger/politicas Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,3 +7,5 @@ export * from './lib/dashboard.contract';
|
||||
export * from './lib/notifications.contract';
|
||||
export * from './lib/company.contract';
|
||||
export * from './lib/report.contract';
|
||||
export * from './lib/politicas.contract';
|
||||
export * from './lib/equipe.contract';
|
||||
|
||||
@@ -75,3 +75,35 @@ export const SupervisorDashboardSchema = z.object({
|
||||
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>;
|
||||
|
||||
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(),
|
||||
rankingReps: z.array(RankingRepSchema),
|
||||
positivacaoReps: z.array(PositivacaoRepSchema),
|
||||
syncedAt: z.iso.datetime(),
|
||||
});
|
||||
export type ManagerDashboard = z.infer<typeof ManagerDashboardSchema>;
|
||||
|
||||
18
libs/shared/api-interface/src/lib/equipe.contract.ts
Normal file
18
libs/shared/api-interface/src/lib/equipe.contract.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const RepStatsSchema = z.object({
|
||||
codVendedor: z.number().int(),
|
||||
nomeVendedor: z.string().nullable(),
|
||||
pedidosMes: z.number().int(),
|
||||
faturamentoMes: z.number(),
|
||||
ticketMedio: z.number(),
|
||||
pctMeta: z.number(),
|
||||
});
|
||||
export type RepStats = z.infer<typeof RepStatsSchema>;
|
||||
|
||||
export const EquipeResponseSchema = z.object({
|
||||
reps: z.array(RepStatsSchema),
|
||||
totalReps: z.number().int(),
|
||||
syncedAt: z.iso.datetime(),
|
||||
});
|
||||
export type EquipeResponse = z.infer<typeof EquipeResponseSchema>;
|
||||
55
libs/shared/api-interface/src/lib/politicas.contract.ts
Normal file
55
libs/shared/api-interface/src/lib/politicas.contract.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const AlcadaDescontoItemSchema = z.object({
|
||||
codVendedor: z.number().int(),
|
||||
codGrupo: z.number().int(),
|
||||
limitePerc: z.number(),
|
||||
nomeVendedor: z.string().nullable(),
|
||||
});
|
||||
export type AlcadaDescontoItem = z.infer<typeof AlcadaDescontoItemSchema>;
|
||||
|
||||
export const AlcadaDescontosResponseSchema = z.object({
|
||||
descontos: z.array(AlcadaDescontoItemSchema),
|
||||
});
|
||||
export type AlcadaDescontosResponse = z.infer<typeof AlcadaDescontosResponseSchema>;
|
||||
|
||||
export const UpsertDescontoBodySchema = z.object({
|
||||
codVendedor: z.number().int().positive(),
|
||||
codGrupo: z.number().int().default(0),
|
||||
limitePerc: z.number().min(0).max(100),
|
||||
});
|
||||
export type UpsertDescontoBody = z.infer<typeof UpsertDescontoBodySchema>;
|
||||
|
||||
export const PromocaoSchema = z.object({
|
||||
id: z.number().int(),
|
||||
descricao: z.string(),
|
||||
codProduto: z.string().nullable(),
|
||||
grpProd: z.string().nullable(),
|
||||
descPct: z.number(),
|
||||
dataInicio: z.string(),
|
||||
dataFim: z.string(),
|
||||
ativa: z.boolean(),
|
||||
createdAt: z.string(),
|
||||
createdBy: z.string(),
|
||||
});
|
||||
export type Promocao = z.infer<typeof PromocaoSchema>;
|
||||
|
||||
export const PromocoesResponseSchema = z.object({
|
||||
promocoes: z.array(PromocaoSchema),
|
||||
});
|
||||
export type PromocoesResponse = z.infer<typeof PromocoesResponseSchema>;
|
||||
|
||||
export const CreatePromocaoBodySchema = z.object({
|
||||
descricao: z.string().min(1).max(255),
|
||||
codProduto: z.string().optional(),
|
||||
grpProd: z.string().optional(),
|
||||
descPct: z.number().min(0).max(100),
|
||||
dataInicio: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
dataFim: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
});
|
||||
export type CreatePromocaoBody = z.infer<typeof CreatePromocaoBodySchema>;
|
||||
|
||||
export const UpdatePromocaoBodySchema = CreatePromocaoBodySchema.partial().extend({
|
||||
ativa: z.boolean().optional(),
|
||||
});
|
||||
export type UpdatePromocaoBody = z.infer<typeof UpdatePromocaoBodySchema>;
|
||||
Reference in New Issue
Block a user