feat(api,web): funil de vendas do representante
Kanban de oportunidades por etapa (lead, proposta, negociacao, ganho, perdido) com CRUD completo. - API: `FunilModule` com GET/POST/PATCH/DELETE em /funil, escopado por id_empresa + cod_vendedor. - Modelo `Oportunidade` + migration `sar.oportunidades`. Uma oportunidade referencia um cliente do ERP (id_cliente) ou e um prospect livre (nome_prospect / empresa_prospect). - Contrato Zod compartilhado em `funil.contract.ts`. - Web: `FunilPage` com colunas por etapa, entrada no menu do rep e rota /funil. Nota: a UI ainda nao expoe seletor de cliente, entao toda oportunidade criada pela tela nasce como prospect livre; `idCliente` e `idPedido` ja sao suportados no backend, mas ficam inalcancaveis pela interface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,3 +9,4 @@ export * from './lib/company.contract';
|
||||
export * from './lib/report.contract';
|
||||
export * from './lib/politicas.contract';
|
||||
export * from './lib/equipe.contract';
|
||||
export * from './lib/funil.contract';
|
||||
|
||||
45
libs/shared/api-interface/src/lib/funil.contract.ts
Normal file
45
libs/shared/api-interface/src/lib/funil.contract.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ETAPAS_FUNIL = ['lead', 'proposta', 'negociacao', 'ganho', 'perdido'] as const;
|
||||
export type EtapaFunil = (typeof ETAPAS_FUNIL)[number];
|
||||
|
||||
export const OportunidadeSchema = z.object({
|
||||
id: z.number(),
|
||||
idCliente: z.number().nullable(),
|
||||
nomeProspect: z.string().nullable(),
|
||||
empresaProspect: z.string().nullable(),
|
||||
titulo: z.string(),
|
||||
etapa: z.enum(ETAPAS_FUNIL),
|
||||
valorEstimado: z.string().nullable(),
|
||||
observacoes: z.string().nullable(),
|
||||
idPedido: z.string().nullable(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
nomeCliente: z.string().nullable(),
|
||||
});
|
||||
export type Oportunidade = z.infer<typeof OportunidadeSchema>;
|
||||
|
||||
export const CreateOportunidadeSchema = z.object({
|
||||
idCliente: z.number().int().positive().nullable().optional(),
|
||||
nomeProspect: z.string().max(200).nullable().optional(),
|
||||
empresaProspect: z.string().max(200).nullable().optional(),
|
||||
titulo: z.string().min(1).max(200),
|
||||
etapa: z.enum(ETAPAS_FUNIL).default('lead'),
|
||||
valorEstimado: z.number().positive().nullable().optional(),
|
||||
observacoes: z.string().max(2000).nullable().optional(),
|
||||
});
|
||||
export type CreateOportunidadeDto = z.infer<typeof CreateOportunidadeSchema>;
|
||||
|
||||
export const UpdateOportunidadeSchema = CreateOportunidadeSchema.partial().extend({
|
||||
idPedido: z.string().nullable().optional(),
|
||||
});
|
||||
export type UpdateOportunidadeDto = z.infer<typeof UpdateOportunidadeSchema>;
|
||||
|
||||
export const FunilResponseSchema = z.object({
|
||||
lead: z.array(OportunidadeSchema),
|
||||
proposta: z.array(OportunidadeSchema),
|
||||
negociacao: z.array(OportunidadeSchema),
|
||||
ganho: z.array(OportunidadeSchema),
|
||||
perdido: z.array(OportunidadeSchema),
|
||||
});
|
||||
export type FunilResponse = z.infer<typeof FunilResponseSchema>;
|
||||
Reference in New Issue
Block a user