feat(mejoras): 25 mejoras UX en todos los módulos + purga Monday

Backend: mi-dia siempre local, vocabulario canónico, AplicacionPago con usuario
Mi día: modal español real, cita desde modal, score falso eliminado
Cotizador: barra sticky, persistencia local con folio
Alta Clientes: redirect eliminado, foto conectada, asesor desde sesión, refs dinámicas
Cobranza: contrato Mora arreglado, link a Plan, abono próximo pago, WhatsApp
Landing: contactos locales, ModalContacto eliminado, ModoTrabajo eliminado
Dashboards: MiTablero unificado, funnel con descartados, drill-down, rango fechas
Chat FRASA: corpus 5 desarrollos, 6 herramientas, historial conversaciones

69 archivos, 7 apps construidas
This commit is contained in:
2026-09-11 06:52:37 +00:00
parent 075d47c304
commit 64b01246d6
69 changed files with 1600 additions and 1133 deletions

View File

@@ -12,22 +12,82 @@ async function getDB(): Promise<IDBPDatabase> {
})
}
// Todas las operaciones son tolerantes a fallos: sin IndexedDB (navegación
// privada, tests) no lanzan, para no romper el flujo que las usa.
export async function guardarUnidades(unidades: unknown[]) {
const db = await getDB()
await db.put('unidades', unidades, 'actual')
try {
const db = await getDB()
await db.put('unidades', unidades, 'actual')
} catch {
// Sin almacenamiento: el inventario se recarga del API en la próxima sesión
}
}
export async function cargarUnidadesLocal() {
const db = await getDB()
return db.get('unidades', 'actual')
export async function cargarUnidadesLocal(): Promise<unknown> {
try {
const db = await getDB()
return db.get('unidades', 'actual')
} catch {
return undefined
}
}
export async function guardarCotizaciones(cotizaciones: unknown[]) {
const db = await getDB()
await db.put('cotizaciones', cotizaciones, 'historial')
try {
const db = await getDB()
await db.put('cotizaciones', cotizaciones, 'historial')
} catch {
// Sin almacenamiento
}
}
export async function cargarCotizacionesLocal() {
const db = await getDB()
return db.get('cotizaciones', 'historial')
export async function cargarCotizacionesLocal(): Promise<unknown> {
try {
const db = await getDB()
return db.get('cotizaciones', 'historial')
} catch {
return undefined
}
}
// ---------- Cotizaciones guardadas localmente (fallback sin servidor) ----------
const COTIZACIONES_LS = 'frasa-cotizador:cotizaciones-locales'
const MAX_LOCALES = 50
/**
* Guarda una cotización local (id con prefijo 'local-') en IndexedDB.
* Si IDB no está disponible (p. ej. navegación privada) cae a localStorage;
* si no hay almacenamiento alguno, no lanza: la sesión sigue.
*/
export async function guardarCotizacionLocal(cotizacion: unknown): Promise<void> {
try {
const db = await getDB()
await db.put('cotizaciones', cotizacion, `local:${(cotizacion as { id: string }).id}`)
return
} catch {
// IndexedDB no disponible: intenta localStorage
}
try {
const prev = JSON.parse(localStorage.getItem(COTIZACIONES_LS) ?? '[]') as unknown[]
localStorage.setItem(COTIZACIONES_LS, JSON.stringify([cotizacion, ...prev].slice(0, MAX_LOCALES)))
} catch {
// Sin almacenamiento disponible
}
}
/** Lee las cotizaciones guardadas localmente (IndexedDB o localStorage) */
export async function cargarCotizacionesLocales(): Promise<unknown[]> {
try {
const db = await getDB()
const claves = (await db.getAllKeys('cotizaciones')).filter((k) => String(k).startsWith('local:'))
return Promise.all(claves.map((k) => db.get('cotizaciones', k)))
} catch {
// IndexedDB no disponible: intenta localStorage
}
try {
return JSON.parse(localStorage.getItem(COTIZACIONES_LS) ?? '[]') as unknown[]
} catch {
return []
}
}