Skip to content

Dashboard

Ruta: /teacher/dashboard · Atajo: g d · Sidebar: Inicio

El Dashboard es la primera pantalla que ve el profesor al iniciar sesion. Muestra un resumen rapido del negocio, la agenda completa del dia, solicitudes de prueba pendientes y acciones sugeridas.

Dashboard page
┌──────────────────────────────────────────────────────────┐
│ Saludo + Streak badge [+ Clase rapida] │
├──────────────────────────────────────────────────────────┤
│ KPIs (2x2) │ Acciones sugeridas │
│ Ingresos · Clases │ (smart actions) │
│ Alumnos · Asistencia │ │
├──────────────────────────────────┼────────────────────────┤
│ Agenda de hoy (col-span-2) │ Solicitudes pendientes │
│ Timeline compacto, scroll │ (trials sin confirmar) │
├──────────────────────────────────┴────────────────────────┤
│ Esta semana (full width, solo si hay eventos no-hoy) │
└──────────────────────────────────────────────────────────┘

Mensaje de bienvenida que cambia segun la hora del dia:

Hora Saludo
6:00 – 11:59 Buenos dias, NAME
12:00 – 17:59 Buenas tardes, NAME
18:00 – 5:59 Buenas noches, NAME

Subtitulo fijo: “Tus alumnos tienen suerte de tenerte”

Incluye un boton “+ Clase rapida” a la derecha del saludo para programar sesiones sin salir del dashboard.

Si el profesor tiene un streak activo (dias consecutivos con clases completadas), se muestra un badge con icono de llama: “Llevas X dias consecutivos”.

KPIs configurables (4 tarjetas) con drag-and-drop

Section titled “KPIs configurables (4 tarjetas) con drag-and-drop”

Grid de 2x2 con metricas reordenables via drag-and-drop (dnd-kit). El profesor elige 4 de un catalogo de 9 metricas disponibles desde Ajustes > General > KPIs del dashboard. Por defecto se muestran las 4 primeras.

Metrica Key Color Datos Tendencia
Ingresos del mes month_revenue Verde Total enrollments.pricePaid del mes en curso % vs mes anterior
Clases este mes month_lessons Azul Sesiones scheduled + completed + pending_review % vs mes anterior
Alumnos activos active_students Morado Count de students con status active —
Asistencia attendance_rate Ambar completed / (completed + no_shows) * 100 o “—” si no hay datos —
Ingresos pendientes pending_revenue Esmeralda Suma de services.priceAmount para sesiones futuras scheduled —
Racha de ensenanza teaching_streak Naranja Dias consecutivos con clases completadas —
Nuevos alumnos new_students Cielo Students creados este mes —
Sesiones completadas completed_sessions Esmeralda Sesiones con status completed este mes —
Leads del directorio directory_leads Rosa Students con referralSource = 'directory' este mes —

La seleccion y orden se guardan en teachers.dashboardConfig (JSONB) con la estructura { kpis: KpiMetricKey[] }. NULL = defaults.

Drag-and-drop:

  • Componente SortableKpiCard envuelve cada KPI con useSortable() de dnd-kit
  • Grip handle (GripVertical) visible al hover, opacidad 0.5 durante drag
  • Sensores: PointerSensor (5px threshold) + KeyboardSensor, colision closestCenter
  • Al soltar: optimistic update en React Query cache + PATCH /teacher/settings/dashboard con { kpis: KpiMetricKey[] }
  • Toast confirma “Orden guardado”

La tarjeta de Ingresos del mes muestra un subtitulo con los ingresos pendientes cuando hay sesiones programadas.

API: GET /teacher/dashboard/summary

Panel lateral derecho con alertas priorizadas que enlazan a la pagina correspondiente:

Accion Prioridad Cuando aparece Enlaza a
Solicitudes de prueba pendientes Alta Hay trials con status pending_confirmation /teacher/calendar
Alumnos sin clase programada Media Students activos sin sesion scheduled futura /teacher/students
Creditos por expirar Alta Enrollments activos con validUntil < 7 dias /teacher/students
Alumnos en riesgo de abandono Alta Lifecycle events gap_detected en los ultimos 90 dias /teacher/students
Pruebas sin convertir Media Trials completados en ultimos 30 dias sin enrollment activo/completado /teacher/insights
Alumnos inactivos Baja Students con status inactive /teacher/students
Resenas sin responder Media Reviews con status approved y teacherResponse IS NULL /teacher/reviews
Content insight Variable Insights del content-analytics-worker (cache < 24h) /teacher/templates

API: GET /teacher/dashboard/smart-actions

Timeline compacto de todas las sesiones del dia actual. Ocupa 2 columnas en desktop, con scroll vertical cuando hay 8+ sesiones (max-h-[420px]). Auto-scroll al evento “next up”.

Cada fila tiene 3 estados visuales:

Estado Visual
Pasada/completada Opacidad reducida (opacity-50), borde izquierdo muted
Siguiente/en curso Borde izquierdo 4px color del evento, fondo con tint, badge “En curso” o countdown
Futura Normal, borde izquierdo a opacidad reducida

Contenido de cada fila:

  • Hora de inicio (columna fija)
  • Nombre del alumno + badge de servicio + badge de templates vinculadas
  • Rango horario completo (inicio – fin)
  • Badge “En curso” si la sesion esta en progreso, o countdown si es la siguiente
  • Acciones al hover: ver notas, WhatsApp, marcar completada

Header con contador de clases del dia. Si hay completadas, muestra “X de Y completadas”.

Casos especiales:

Escenario Comportamiento
0 eventos hoy Estado vacio: icono calendario + “No hay clases hoy” + “Disfruta tu dia libre”
8+ eventos max-h-[420px] overflow-y-auto, auto-scroll al evento next-up
Todas completadas Todas dimmed, resumen “8 de 8 completadas”
En curso Badge “En curso” en vez de countdown
Cancelada/no-show Dimmed con opacity-50, se mantiene en su posicion del timeline

Lista de las proximas 4 sesiones que NO son de hoy (grid de 2 columnas en desktop). Solo se renderiza si hay eventos futuros no-hoy.

Cada tarjeta muestra:

  • Nombre del alumno + badge de servicio
  • Dia relativo (Hoy/Manana/Lun 3 mar) + rango horario
  • Acciones al hover (mismas que agenda de hoy)

Enlace “Ver todo” → navega a /teacher/calendar

API: GET /teacher/calendar?start={hoy}&end={+7dias}

Tarjetas para cada sesion de prueba sin confirmar:

  • Badge del servicio + duracion
  • Nombre del alumno
  • Fecha y hora solicitada
  • Boton “Confirmar” → session status scheduled, crea evento en Calendar, envia email
  • Boton “Rechazar” (X) → dialog con motivo opcional, envia email de rechazo

API: GET /teacher/sessions?status=pending_confirmation

Boton “+ Clase rapida” en la cabecera del dashboard que abre un Sheet lateral con un formulario minimo:

  • Servicio: Selector con servicios activos del profesor (nombre + duracion)
  • Alumno: Selector con todos los alumnos
  • Fecha: Input date (default: hoy)
  • Hora: Input time con step de 15 min (default: siguiente cuarto de hora)
  • Boton “Programar” → POST /teacher/sessions con serviceId, studentId, startUtc, timezone

Al confirmar, invalida queries de calendario y dashboard.

Badge motivacional junto al saludo que muestra dias consecutivos con al menos una clase completada. Calcula hacia atras desde hoy (o ayer si hoy aun no hay clase completada). Se oculta si el streak es 0.

Modo normal: Icono de llama naranja + texto: “Llevas X dias consecutivos”

Modo record: Cuando el streak actual supera el best_streak guardado, el badge cambia a icono de trofeo ambar con animacion animate-pulse y aparece un toast de celebracion (5 segundos, icono Trophy). El toast solo se dispara una vez por carga de datos gracias a streakToastShown ref.

Tarjeta KPI teaching_streak: Muestra el icono y etiqueta segun el estado:

  • Normal: llama + label “Racha de ensenanza”, subtitulo con el mejor record historico
  • Record: trofeo + label “Nuevo record”, subtitulo con el record anterior superado

Datos: Calculo en getSummary() — agrupa class_sessions completadas por fecha, ordena desc, cuenta dias consecutivos (maximo 365 dias atras). Lee teachers.best_streak al inicio para comparar. Si isNewRecord, actualiza best_streak en la DB (fire-and-forget) y crea una notificacion in-app via NotificationService con tipo streak_record.

API: Los campos bestStreak, isNewRecord se incluyen en la respuesta de GET /teacher/dashboard/summary.

Subtitulo en la tarjeta de Ingresos del mes que muestra la suma de services.priceAmount para sesiones futuras con status scheduled.

Formato: “Ingresos pendientes: $X”. Solo se muestra si hay sesiones programadas (pendingRevenue > 0).

Cuando el profesor tiene pagos en multiples monedas, el dashboard agrupa los ingresos pendientes por moneda y muestra cada importe formateado de forma independiente. El campo pendingRevenueByCurrency es un array de objetos { currency: string, amount: number } ordenado por importe desc. La tarjeta KPI itera el array y muestra una linea por moneda.


No hay items pendientes.


Bug Descripcion Estado Corregido
Moneda hardcodeada a EUR El dashboard ahora lee teachers.defaultCurrency (configurable en Ajustes > General) ✅ Batch 1
Ingresos mezclan monedas La moneda del profesor (defaultCurrency) se usa como referencia para el simbolo y el formato ✅ Batch 1
Asistencia 100% con 0 sesiones Devuelve null y muestra “—” cuando no hay datos ✅ Batch 1
Key i18n incorrecta para templates Usa key dedicada dashboard.linkedTemplates ✅ Batch 1
Content insights no renderizados El tipo content_insight ahora se renderiza en SmartActionCard ✅ Batch 1

Mejora Descripcion Dificultad Estado Implementado
KPIs con drag-and-drop para reordenar Las tarjetas KPI se reordenan con dnd-kit. Grip handle al hover, optimistic update, persiste en dashboardConfig.kpis ✅ Batch 5
Leads del directorio KPI Nueva metrica directory_leads — cuenta alumnos con referralSource = 'directory' del mes en curso ✅ Batch 5

Archivo Proposito
apps/app/src/routes/teacher/dashboard.lazy.tsx Pagina completa (componente React)
apps/api/src/services/teacher/dashboard-service.ts Calculo de KPIs y smart actions
apps/api/src/routes/teacher/dashboard.ts Rutas thin (GET summary, GET smart-actions)
apps/app/src/components/ui/kpi-card.tsx Componente reutilizable de tarjeta KPI
Endpoint Metodo Respuesta
/teacher/dashboard/summary GET { monthRevenue, monthRevenueChange, monthLessons, monthLessonsChange, activeStudents, attendanceRate: number | null, currency, pendingRevenue, pendingRevenueByCurrency: { currency: string, amount: number }[], teachingStreak, bestStreak, isNewRecord, newStudents, completedSessions, directoryLeads }
/teacher/dashboard/smart-actions GET [{ type, count, priority, insight? }] — tipos: pending_trials, expiring_credits, no_upcoming, inactive_students, at_risk, unconverted_trials, unanswered_reviews, content_insight
/teacher/sessions?status=pending_confirmation GET [{ id, startsAt, student, service, ... }]
/teacher/calendar?start=X&end=Y GET [{ id, type, title, startsAt, endsAt, status, ... }]
/teacher/sessions POST Crea sesion manual. Body: { serviceId, studentId, startUtc, timezone? }
/teacher/settings/dashboard PATCH Actualizar config de KPIs. Body: { kpis: KpiMetricKey[] } (min 1, max 9)
/teacher/services GET Lista de servicios (usada por Quick add)
/teacher/students GET Lista de alumnos (usada por Quick add)
// KPIs
useQuery({ queryKey: ['teacher-dashboard-summary'], queryFn: ... })
// Smart actions
useQuery({ queryKey: ['teacher-dashboard-smart-actions'], queryFn: ... })
// Teacher settings (for dashboardConfig.kpis)
useQuery({ queryKey: ['teacher', 'settings'], queryFn: ... })
// Trials pendientes
useQuery({ queryKey: ['teacher-sessions-pending'], queryFn: ... })
// Proximas 7 dias
useQuery({ queryKey: ['teacher-calendar', start, end], queryFn: ... })
// Servicios (lazy, solo cuando se abre Quick add)
useQuery({ queryKey: ['teacher-services'], enabled: open, queryFn: ... })
// Alumnos (lazy, solo cuando se abre Quick add)
useQuery({ queryKey: ['teacher-students-for-scheduling'], enabled: open, queryFn: ... })
Accion Hook Optimista
Confirmar trial useMutation No (server-side, espera confirmacion)
Rechazar trial useMutation No
Completar sesion useCompleteSession Si (actualiza cache calendario)
Clase rapida useMutation No (server-side, espera confirmacion)