feat: mis clientes en Mi dia + perfilamiento de la afluencia en dashboards
- Cliente.capturadoPorId: el alta (directa y progresiva) registra al usuario que la inicio; el panel Mi dia muestra 'Mis clientes' con su estado (En captura / Cliente) sin depender de Monday. - LeadMonday ahora lee las 11 columnas de perfil del tablero Leads (estado civil, edad, rango salarial, tipo de empleo/economia, califica, credito, modelo, etc.). - GET /api/reportes/resumen agrega leads.perfil con conteos por dimension (valores vacios se omiten; edad en buckets). - /dash/ gana la seccion 'Perfilamiento de la afluencia' con mini-graficas de barras por dimension.
This commit is contained in:
@@ -176,6 +176,20 @@ describe('iniciarProgreso (POST /api/clientes/progreso/iniciar)', () => {
|
||||
expect(status).toHaveBeenCalledWith(201)
|
||||
expect(json.mock.calls[0]?.[0]).toEqual({ id: 'cli-1', mondayContactoId: null })
|
||||
})
|
||||
|
||||
it('guarda capturadoPorId con el sub del JWT (null sin token)', async () => {
|
||||
const { iniciarProgreso } = await import('./clientes.controller.js')
|
||||
const req = reqFake({ datosGenerales: { nombreCompleto: 'Ana Demo' } })
|
||||
;(req as unknown as { usuarioToken: { sub: string } }).usuarioToken = { sub: 'usr-1' }
|
||||
await iniciarProgreso(req, resFake().res)
|
||||
expect(prismaMock.cliente.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ data: expect.objectContaining({ capturadoPorId: 'usr-1' }) }),
|
||||
)
|
||||
await iniciarProgreso(reqFake({ datosGenerales: { nombreCompleto: 'Ana Demo' } }), resFake().res)
|
||||
expect(prismaMock.cliente.create).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ data: expect.objectContaining({ capturadoPorId: null }) }),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('actualizarProgreso (PATCH /api/clientes/progreso/:id)', () => {
|
||||
|
||||
@@ -163,4 +163,16 @@ describe('crearCliente (POST /api/clientes) — leadMondayId del funnel', () =>
|
||||
expect(status).toHaveBeenCalledWith(201)
|
||||
expect(json.mock.calls[0]?.[0]).toMatchObject({ id: 'cli-1', mondayContactoId: 'cont-1' })
|
||||
})
|
||||
|
||||
it('guarda capturadoPorId con el sub del JWT (null sin token)', async () => {
|
||||
const { crearCliente } = await import('./clientes.controller.js')
|
||||
await crearCliente({ body: altaBase, usuarioToken: { sub: 'usr-1' } } as unknown as Request, resFake().res)
|
||||
expect(prismaMock.cliente.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ data: expect.objectContaining({ capturadoPorId: 'usr-1' }) }),
|
||||
)
|
||||
await crearCliente(reqFake(altaBase), resFake().res)
|
||||
expect(prismaMock.cliente.create).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ data: expect.objectContaining({ capturadoPorId: null }) }),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -211,6 +211,7 @@ export async function crearCliente(req: Request, res: Response): Promise<void> {
|
||||
celular: dg.celular ?? null,
|
||||
proyecto: vc.proyecto,
|
||||
unidadCodigo: vc.ubicacion ?? '', // la "ubicación" del formato es el código de unidad (U-101, etc.)
|
||||
capturadoPorId: req.usuarioToken?.sub ?? null,
|
||||
payloadJson: JSON.stringify(body),
|
||||
firmaPng: body.firma?.firmaDataUrl ?? null,
|
||||
},
|
||||
@@ -348,6 +349,7 @@ export async function iniciarProgreso(req: Request, res: Response): Promise<void
|
||||
email: dg.email ?? null,
|
||||
celular: dg.celular ?? null,
|
||||
proyecto: '', // se define al completar el alta
|
||||
capturadoPorId: req.usuarioToken?.sub ?? null,
|
||||
payloadJson: JSON.stringify(body),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -32,12 +32,19 @@ export async function resumenMiDiaEndpoint(req: Request, res: Response): Promise
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
|
||||
// Altas de clientes iniciadas por el usuario (registro local)
|
||||
const clientes = await prisma.cliente.findMany({
|
||||
where: { capturadoPorId: usuario.id },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
|
||||
res.json(
|
||||
await resumenMiDia(
|
||||
mondayService,
|
||||
{ nombre: usuario.nombre, rol: usuario.rol, mondayUserId: usuario.mondayUserId },
|
||||
nombresPorMondayId,
|
||||
capturas,
|
||||
clientes,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user