Ir al contenido

Dashboard

El Dashboard es la pantalla home que todo staff ve al entrar. Combina match badge (próximo partido + MD-N), fila de 5 KPIs (disponibles / wellness / ACWR / carga ayer / ánimo), session strip con la sesión del día, semáforo del plantel (lista priorizada por riesgo), alertas activas y feed de actividad. Todo se hidrata con streaming server-side y se deduplica entre componentes con cache() de React.

  • Problema que resuelve: un PF entra a las 8AM, necesita saber en 10 segundos quién está disponible, quién en riesgo, qué pasó desde ayer y cuál es la sesión del día. Sin Dashboard hay que abrir 5 pestañas distintas.
  • Casos de uso típicos [NEEDS_USER: CASE_STUDY feedback real Nacional / Tachira]:
    • Pre-entrenamiento: revisar semáforo y alertas para tomar decisiones de carga.
    • Briefing técnico: usar el match badge + KPIs como apertura.
    • Standby remoto: pantalla siempre abierta como “TV view” del cuerpo técnico.
  • Planes que lo incluyen: todos los planes. Sin gate. La feature custom_dashboard está prometida en pro / enterprise pero no existe layer de personalización (gap comercial documentado).
  • Diferenciador: ACWR del equipo en el header, semáforo priorizado por riesgo combinando ACWR + Hooper + lesiones (no es solo “lista de jugadores”), y streaming server-side para perceived-performance instantáneo aunque la DB tarde.

/dashboard es accesible para todos los roles staff autenticados (hop / dir / sc / ss / rtp / fisio / med / nut / psi / coord_form). El contenido se filtra por rol en los componentes individuales (ej. el feed de actividad puede ocultar eventos de áreas que el rol no consume).

Estructura vertical:

  1. Match badge — pill con MD-N + rival + fecha. Si no hay próximo partido, muestra “Sin próximo partido programado”.
  2. KPI row (5 cards):
    • Disponibles: X / total, breakdown (lesionados / RTP / ausentes / “plantel completo”).
    • Wellness: promedio Hooper Index (4-28, suma de 4 ítems 1-7), compliance, tendencia vs ayer (TrendArrow).
    • ACWR: ACWR equipo con badge óptimo / alto / bajo según acwrBadgeStyle (rangos 0.8–1.3 óptimo).
    • Carga ayer: distancia media + intensidad m/min.
    • Ánimo: motivación promedio /10 con tendencia.
  3. Session strip — sesión del día + base distance + media 7d.
  4. Main area (grid 65/35):
    • Semáforo del plantel (TeamSemaphore) — izquierda.
    • Alertas activas + Feed de actividad — derecha (stack).

Streaming: cada bloque se envuelve en <Suspense> con skeleton. KpiRow, SessionStrip, MainSection resuelven en paralelo. getDashboardLiveData está memoizado con cache() de React para que los tres bloques compartan una sola ejecución por request.

[NEEDS_USER: SCREENSHOT del Dashboard completo con datos reales]

  • organizations.primary_color / secondary_color afectan el match badge y el header global.
  • risk_acwr_* thresholds en la org afectan el cálculo del semáforo (consumidos desde risk_snapshots).
  • “El ACWR del equipo cambió pero ningún jugador entrenó” — es rolling 28d. Los días caen del denominador aunque no haya sesión.
  • “Veo skeleton infinito” — la DB no respondió. Mirar Workers logs con wrangler tail y Sentry. El streaming no rompe la página, solo los bloques que fallan.
  • “Mi rol no ve el feed completo” — esperado. El feed filtra por áreas que el rol consume.

Player surface: N/A. Los jugadores entran a /player que tiene su propio home (agenda + wellness propio + mensajes).

  • MD-N: diferencia en días entre próximo partido y hoy. Si el partido es hoy se muestra MD; si es mañana, hoy se muestra como MD-1 (N = días hasta el partido). El día después del partido, MD+1.
  • ACWR equipo: promedio de players.current_acwr ponderado por jugadores con datos.
  • Semáforo: combina ACWR + Hooper + lesiones activas; ranking desde risk_snapshots o cálculo en vivo si está en caliente.
  • Tendencias (TrendArrow): comparativa con yesterday para wellness y motivación. Verde si la métrica va en la dirección buena, rojo si va en contra.
  • Lee de: players, calendar_events, wellness_entries, risk_snapshots, gps_sessions, gps_player_data, mental_records, notifications.
  • Función central: getDashboardLiveData() en app/dashboard/actions.ts. Memoizada con cache() dentro de page.tsx.
  • Carga aporta ACWR y carga ayer.
  • Wellness aporta Hooper y compliance.
  • Plantel aporta total / disponibles / status.
  • Calendario aporta próximo partido y sesión del día.
  • Riesgo aporta semáforo y alertas.
  • Mensajería / Notificaciones alimentan el feed de actividad.
  • Sin personalización: custom_dashboard está prometido en pro / enterprise pero no existe layer (gap comercial).
  • Streaming requiere Workers: en local con next dev puede comportarse distinto. En prod va por @opennextjs/cloudflare.
  • Gotcha Next 16: revalidatePath se invoca solo en Server Actions, nunca durante el render del Server Component. Si se necesita refrescar el Dashboard tras una mutación, hacerlo desde la action que la disparó (ej. notifications-actions.ts). Sino Next.js lanza un error que en producción aparece como digest opaco.
  • cache() + cookies: usar cache() de React (no unstable_cache). unstable_cache + cookies() rompe con error E846 en Next 16.