feat(mejoras): 22 mejoras UX en todos los módulos

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
This commit is contained in:
2026-09-11 12:48:47 +00:00
parent 89d95eb53b
commit 1ba7aa49da
28 changed files with 518 additions and 160 deletions

View File

@@ -17,12 +17,25 @@ export interface Contacto {
updatedAt: string
}
/** GET /contactos — contactos locales (máx 100) */
export async function getContactos(): Promise<Contacto[]> {
const resp = await fetch(`${API_URL}/contactos`, conAuth())
/** Respuesta paginada de GET /contactos */
export interface RespuestaContactos {
contactos: Contacto[]
pagina: number
porPagina: number
hayMas: boolean
}
/** GET /contactos — contactos locales, paginados de 100 en 100 (?pagina=N) */
export async function getContactos(pagina = 1): Promise<RespuestaContactos> {
const resp = await fetch(`${API_URL}/contactos?pagina=${pagina}`, conAuth())
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
return (await resp.json()) as Contacto[]
if (!resp.ok) {
// Propagar el mensaje del API (p.ej. error upstream) para diferenciarlo
// del caso "sin contactos"
const body = (await resp.json().catch(() => null)) as { error?: string } | null
throw new Error(body?.error ?? `HTTP ${resp.status}`)
}
return (await resp.json()) as RespuestaContactos
}
// ---------- Detalle de contacto (modal del módulo Contactos) ----------

View File

@@ -64,7 +64,7 @@ export async function listarClientesMaestro(filtros: FiltrosClientesMaestro = {}
return (await resp.json()) as ResultadoListado
}
/** GET /api/clientes-maestro/exportar — array de objetos para xlsx */
/** GET /api/clientes-maestro/exportar — array de objetos para generar CSV */
export async function exportarClientesMaestro(filtros: Omit<FiltrosClientesMaestro, 'pagina' | 'limite'> = {}): Promise<ClienteMaestro[]> {
const params = new URLSearchParams()
if (filtros.busqueda) params.set('busqueda', filtros.busqueda)