feat(mi-dia): app /hoy + CRM ligero para vendedores

- App Mi día: KPIs, mis actividades, mis leads con acciones rápidas
  (contactado/intento/agenda cita/descartar con motivo), captura rápida
  de lead (409 en duplicado por teléfono), vista equipo para coordinadores
- API: GET /api/mi-dia/resumen, POST /api/leads, /leads/:id/estado,
  /leads/:id/descartar; mapeo Usuario.mondayUserId (seed listo para
  cuando inviten a los vendedores a Monday)
- Landing: tarjeta Mi día; tests api 188, mi-dia 8, landing 4
This commit is contained in:
2026-08-28 06:23:15 +00:00
parent 1f5b6ee088
commit bc0f9da8fe
31 changed files with 5917 additions and 3 deletions

View File

@@ -0,0 +1,36 @@
import type { Request, Response } from 'express'
import { prisma } from '../services/prisma.js'
import { mondayService } from '../services/monday.service.js'
import { resumenMiDia } from '../services/mi-dia.service.js'
import { AppError } from '../utils/errors.js'
/**
* GET /api/mi-dia/resumen — agenda del vendedor logueado.
* El "mío" se resuelve por Usuario.mondayUserId (mapeo usuario↔Monday);
* sin mapeo, las listas personales van vacías con sinMapeoMonday: true.
*/
export async function resumenMiDiaEndpoint(req: Request, res: Response): Promise<void> {
// Con AUTH_ENABLED=false (arranque temprano/tests) no hay token: se usa el admin
const sub = req.usuarioToken?.sub
const usuario = sub
? await prisma.usuario.findUnique({ where: { id: sub } })
: await prisma.usuario.findUnique({ where: { username: 'admin' } })
if (!usuario || !usuario.activo) throw new AppError(401, 'Credenciales inválidas')
// Nombres de los usuarios ya mapeados a Monday (para la sección equipo)
const mapeados = await prisma.usuario.findMany({
where: { mondayUserId: { not: null }, activo: true },
select: { nombre: true, mondayUserId: true },
})
const nombresPorMondayId = new Map<number, string>(
mapeados.filter((m) => m.mondayUserId !== null).map((m) => [m.mondayUserId!, m.nombre]),
)
res.json(
await resumenMiDia(
mondayService,
{ nombre: usuario.nombre, rol: usuario.rol, mondayUserId: usuario.mondayUserId },
nombresPorMondayId,
),
)
}