Mi día: cancelación con motivo, conteo escrituración real, cita sin retroceder, calendario completo Cobranza: MoraDashboard respeta alcance, hoy dinámico, cancelar con motivo Landing: contactos paginados, error vs vacío diferenciado Dashboards: KPI renombrado, drill-down planes activos Chat: historial recarga, preguntas rotativas, error solo toast Transversal: shadow-sm corrupto corregido en 9 archivos 553 API tests + 7 apps construidas
55 lines
2.4 KiB
TypeScript
55 lines
2.4 KiB
TypeScript
import type { Request, Response } from 'express'
|
|
import { detalleContactoLocal, listarContactosLocal, exportarCSV } from '../services/contactos-local.service.js'
|
|
import { perfilCompleto } from '../services/contactos-detalle.service.js'
|
|
import { NotFoundError } from '../utils/errors.js'
|
|
|
|
/**
|
|
* GET /api/contactos (Bearer) → contactos locales (tabla Cliente + LeadCaptura:
|
|
* nombre, teléfono, email, estado), lo más reciente primero, paginado de 100 en
|
|
* 100 (?pagina=N; el front muestra el límite). Filtra por usuario actual:
|
|
* clientes capturados por él + leads asignados a él.
|
|
*/
|
|
export async function listarContactosEndpoint(req: Request, res: Response): Promise<void> {
|
|
const paginaParam = Number(req.query['pagina'])
|
|
const pagina = Number.isFinite(paginaParam) && paginaParam >= 1 ? Math.floor(paginaParam) : 1
|
|
res.json(await listarContactosLocal(req.usuarioToken?.sub, pagina))
|
|
}
|
|
|
|
/**
|
|
* GET /api/contactos/:id/detalle (Bearer) → ficha completa del contacto armada
|
|
* 100% local: sus datos, la propiedad comprada/en proceso (apartado
|
|
* relacionado), quién atiende cada item relacionado (leads, oportunidades,
|
|
* apartados) y el historial de atención (capturas, altas, documentos y
|
|
* cotizaciones locales).
|
|
*/
|
|
export async function detalleContactoEndpoint(req: Request, res: Response): Promise<void> {
|
|
const detalle = await detalleContactoLocal(String(req.params['id']))
|
|
if (detalle === null) throw new NotFoundError('Contacto no encontrado')
|
|
res.json(detalle)
|
|
}
|
|
|
|
/**
|
|
* GET /api/contactos/:id/completo — perfil completo del contacto:
|
|
* datos, historial pagos, cotizaciones, documentos, lead score, última actividad
|
|
*/
|
|
export async function perfilCompletoEndpoint(req: Request, res: Response): Promise<void> {
|
|
const contactoId = String(req.params['id'])
|
|
const perfil = await perfilCompleto(contactoId)
|
|
if (perfil === null) throw new NotFoundError('Contacto no encontrado')
|
|
res.json(perfil)
|
|
}
|
|
|
|
/**
|
|
* GET /api/contactos/exportar — exportar contactos a CSV
|
|
* Query params: desarrollo, estatus
|
|
*/
|
|
export async function exportarContactosEndpoint(req: Request, res: Response): Promise<void> {
|
|
const csv = await exportarCSV({
|
|
desarrollo: (req.query['desarrollo'] as string) ?? undefined,
|
|
estatus: (req.query['estatus'] as string) ?? undefined,
|
|
})
|
|
res.setHeader('Content-Type', 'text/csv; charset=utf-8')
|
|
res.setHeader('Content-Disposition', 'attachment; filename="contactos.csv"')
|
|
res.send(csv)
|
|
}
|