feat: PWA instalable, cambio de contraseña y dashboards por rol
- POST /api/auth/cambiar-password (bcrypt 12, política ≥8+1 número) + modal en las 6 apps con auth - PWA: manifest + íconos de marca en las 7 apps (sin service worker a propósito, documentado); instalable en celular - Dashboards: 'Mi tablero' para vendedor (sus KPIs y funnel) y sección Equipo para coordinador/corporativo/admin - mi-dia: equipo.porVendedor gana leadsNuevosSemana - Tests: api 271, cotizador 88, alta 88, cobranza 81, agente-ia 19, dashboards 15, mi-dia 11, landing 4
This commit is contained in:
@@ -64,7 +64,13 @@ export interface ResumenMiDia {
|
||||
// Solo roles coordinador/corporativo/admin
|
||||
equipo?: {
|
||||
leadsNuevosSinAsignar: number
|
||||
porVendedor: { mondayUserId: number; nombre: string | null; leadsAsignados: number }[]
|
||||
porVendedor: {
|
||||
mondayUserId: number
|
||||
nombre: string | null
|
||||
leadsAsignados: number
|
||||
/** Leads del vendedor creados en los últimos 7 días naturales */
|
||||
leadsNuevosSemana: number
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +176,14 @@ export async function resumenMiDia(
|
||||
if (ROLES_CON_EQUIPO.includes(usuario.rol)) {
|
||||
const sinAsignar = leads.filter((l) => l.vendedorIds.length === 0 && !esEstadoTerminalLead(l.estado))
|
||||
const conteoPorId = new Map<number, number>()
|
||||
const nuevosPorId = new Map<number, number>()
|
||||
for (const l of leads) {
|
||||
if (esEstadoTerminalLead(l.estado)) continue
|
||||
for (const id of l.vendedorIds) conteoPorId.set(id, (conteoPorId.get(id) ?? 0) + 1)
|
||||
const esNuevoSemana = Date.parse(l.createdAt) >= hace7DiasMs
|
||||
for (const id of l.vendedorIds) {
|
||||
conteoPorId.set(id, (conteoPorId.get(id) ?? 0) + 1)
|
||||
if (esNuevoSemana) nuevosPorId.set(id, (nuevosPorId.get(id) ?? 0) + 1)
|
||||
}
|
||||
}
|
||||
resumen.equipo = {
|
||||
leadsNuevosSinAsignar: sinAsignar.filter((l) => l.estado.trim().toUpperCase() === 'NUEVO').length,
|
||||
@@ -181,6 +192,7 @@ export async function resumenMiDia(
|
||||
mondayUserId,
|
||||
nombre: nombresPorMondayId.get(mondayUserId) ?? null,
|
||||
leadsAsignados,
|
||||
leadsNuevosSemana: nuevosPorId.get(mondayUserId) ?? 0,
|
||||
}))
|
||||
.sort((a, b) => b.leadsAsignados - a.leadsAsignados),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user