feat(mejoras): 14 mejoras al CRM Frasa
Fase 1 - UI: dark mode, skeletons, toasts Fase 2 - Filtros: avanzados Mi día, búsqueda global Ctrl+K Fase 3 - Datos: export Excel, notas apartados, auditoría Fase 4 - Dashboard: gráficas vendedor + métricas uso Fase 5 - Responsive: tablets optimizadas Fase 6 - PWA: IndexedDB offline, push notifications Backend: 3 tablas nuevas, 6 services, 6 endpoints nuevos 721 API tests + fronts verdes.
This commit is contained in:
@@ -7,6 +7,7 @@ import { automatizacionesService, CLAVE_RR_SKIP, prismaStateStore } from '../ser
|
||||
import { prisma } from '../services/prisma.js'
|
||||
import { saludMonday } from '../services/salud-monday.service.js'
|
||||
import { crmLocal, encolarSyncSeguro, esIdLocal } from '../services/crm-local.service.js'
|
||||
import * as auditoria from '../services/auditoria.service.js'
|
||||
import { esFalloMonday } from '../services/sync-monday.service.js'
|
||||
import { MOTIVOS_NO_COMPRA, SUBCATEGORIAS_NO_COMPRA } from './oportunidades.controller.js'
|
||||
import { AppError } from '../utils/errors.js'
|
||||
@@ -500,6 +501,7 @@ export async function crearLeadEndpoint(req: Request, res: Response): Promise<vo
|
||||
}
|
||||
const asignacion = await asignarCapturistaSiAplica(usuarioSub, resultado.id, body.nombre, 'leads')
|
||||
res.status(201).json({ id: resultado.id, estado: 'NUEVO', oportunidadId: null, asignacion })
|
||||
auditoria.registrar(usuarioSub ?? 'sistema', 'CREAR_LEAD', 'lead', resultado.id)
|
||||
}
|
||||
|
||||
/** POST /api/afluencias { nombre, telefono?, ..., perfil completo } — Alta Afluencia (tablero Oportunidades) */
|
||||
@@ -620,6 +622,7 @@ export async function cambiarEstadoLeadEndpoint(req: Request, res: Response): Pr
|
||||
}
|
||||
}
|
||||
res.json({ itemId, estado: body.estado, ...(body.fecha !== undefined ? { fecha: body.fecha } : {}) })
|
||||
auditoria.registrar(req.usuarioToken?.sub ?? 'sistema', 'CAMBIAR_ESTADO', 'lead', itemId, body.estado)
|
||||
}
|
||||
|
||||
/** POST /api/leads/:itemId/descartar { motivo, subcategoria?, comentario?, fuente? } */
|
||||
@@ -651,6 +654,7 @@ export async function descartarLeadEndpoint(req: Request, res: Response): Promis
|
||||
}
|
||||
}
|
||||
res.json({ itemId, estado: body.fuente === 'lead' ? 'Descartado' : 'Perdido', motivo: body.motivo, motivoItemId })
|
||||
auditoria.registrar(req.usuarioToken?.sub ?? 'sistema', 'DESCARTAR_LEAD', 'lead', itemId, body.motivo)
|
||||
}
|
||||
|
||||
// ---------- Conversión lead → oportunidad (botón "Agendo" de Mis leads) ----------
|
||||
@@ -742,15 +746,18 @@ export async function convertirLeadEndpoint(req: Request, res: Response): Promis
|
||||
if (esIdLocal(itemId) || !saludMonday.estaDisponible()) {
|
||||
const oportunidad = await crmLocal.convertirLead(itemId, usuarioSub)
|
||||
res.status(201).json({ leadId: itemId, oportunidadId: oportunidad.leadId, etapa: 'Agendo Cita' })
|
||||
auditoria.registrar(usuarioSub ?? 'sistema', 'CONVERTIR_LEAD', 'lead', itemId)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const { oportunidadId } = await convertirLead(mondayService, itemId, usuarioSub, body.fecha)
|
||||
res.status(201).json({ leadId: itemId, oportunidadId, etapa: 'Agendo Cita' })
|
||||
auditoria.registrar(usuarioSub ?? 'sistema', 'CONVERTIR_LEAD', 'lead', itemId)
|
||||
} catch (err) {
|
||||
if (!esFalloMonday(err)) throw err
|
||||
const oportunidad = await crmLocal.convertirLead(itemId, usuarioSub)
|
||||
res.status(201).json({ leadId: itemId, oportunidadId: oportunidad.leadId, etapa: 'Agendo Cita' })
|
||||
auditoria.registrar(usuarioSub ?? 'sistema', 'CONVERTIR_LEAD', 'lead', itemId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user