// Modulo Sport: tre viste (dashboard, elenco sessioni con filtri, schede atleta) in un'unica // isola, perché condividono lo stesso insieme di sessioni e i filtri agiscono su quello. import { useMemo, useState } from 'react'; import DataChart from './charts/DataChart'; import type { Athlete, Session } from '../../lib/stress-index/data'; import { dataBreve, dataOra, durata, kpiSport, perSettimana, riepilogoAtleti } from '../../lib/stress-index/sport'; interface Props { sessions: Session[]; athletes: Athlete[]; /** Ultimo giorno coperto dai dati: tutte le finestre temporali partono da qui. */ riferimento: string; } const TABS = [ { key: 'dashboard', label: 'Dashboard' }, { key: 'sessioni', label: 'Sessioni' }, { key: 'atleti', label: 'Atleti' }, ]; const PERIODI = [ { key: 'tutto', label: 'Tutto', giorni: 0 }, { key: '7', label: 'Ultimi 7 giorni', giorni: 7 }, { key: '30', label: 'Ultimi 30 giorni', giorni: 30 }, { key: '90', label: 'Ultimi 90 giorni', giorni: 90 }, ]; export default function SportView({ sessions, athletes, riferimento }: Props) { const [tab, setTab] = useState('dashboard'); const [atleta, setAtleta] = useState('tutti'); const [periodo, setPeriodo] = useState('tutto'); const [sport, setSport] = useState('tutti'); const sports = useMemo( () => [...new Set(sessions.map((s) => s.sport).filter((s): s is string => Boolean(s)))], [sessions] ); const filtrate = useMemo(() => { const giorni = PERIODI.find((p) => p.key === periodo)?.giorni ?? 0; const limite = new Date(`${riferimento}T00:00:00`); limite.setDate(limite.getDate() - giorni + 1); const dal = limite.toISOString().slice(0, 10); return sessions.filter((s) => (atleta === 'tutti' || s.athleteId === atleta) && (sport === 'tutti' || s.sport === sport) && (!giorni || s.startedAt.slice(0, 10) >= dal) ); }, [sessions, atleta, periodo, sport, riferimento]); const kpi = useMemo(() => kpiSport(sessions, riferimento), [sessions, riferimento]); const settimane = useMemo(() => perSettimana(sessions, 12, riferimento), [sessions, riferimento]); const atletiRiepilogo = useMemo(() => riepilogoAtleti(sessions, athletes, riferimento), [sessions, athletes, riferimento]); const nome = (id: string) => athletes.find((a) => a.clientId === id)?.name ?? id; return (
Sessioni di allenamento, zone DFA Alpha1, carico e recupero degli atleti
{k.label}
{k.value}
{k.note &&{k.note}
}Ultime 12 settimane
| Atleta | Data | Sport | Durata | TRIMP | HR medio | DFA A1 |
|---|---|---|---|---|---|---|
| {nome(s.athleteId)} | {dataBreve(s.startedAt)} | {s.sport ?? '—'} | {durata(s.durationSec)} | {s.trimp} | {s.hrAvg} bpm | {s.dfaA1 ?? '—'} |
{filtrate.length} {filtrate.length === 1 ? 'sessione' : 'sessioni'}
| Data e ora | Atleta | Sport | Durata | HR medio | HR max | TRIMP | DFA A1 | RPE |
|---|---|---|---|---|---|---|---|---|
| {dataOra(s.startedAt)} | {nome(s.athleteId)} | {s.sport ?? '—'} | {durata(s.durationSec)} | {s.hrAvg} | {s.hrMax} | {s.trimp} | {s.dfaA1 ?? '—'} | {s.rpe ? `${s.rpe}/10` : '—'} |
Nessuna sessione con questi filtri.
}{a.sport ?? 'Sport non specificato'}
{a.sessioni30}
Sessioni 30gg
{a.trimp7}
TRIMP 7gg
{a.ultimoTrimp}
Ultimo TRIMP