// 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 (

Modulo Sport Pro

Sessioni di allenamento, zone DFA Alpha1, carico e recupero degli atleti

{TABS.map((t) => ( ))}
{tab === 'dashboard' && (
{kpi.map((k) => (

{k.label}

{k.value}

{k.note &&

{k.note}

}
))}

Sessioni per settimana

Ultime 12 settimane

Ultime sessioni

{sessions.slice(0, 10).map((s) => ( ))}
AtletaDataSportDurataTRIMPHR medioDFA A1
{nome(s.athleteId)} {dataBreve(s.startedAt)} {s.sport ?? '—'} {durata(s.durationSec)} {s.trimp} {s.hrAvg} bpm {s.dfaA1 ?? '—'}
)} {tab === 'sessioni' && (

{filtrate.length} {filtrate.length === 1 ? 'sessione' : 'sessioni'}

{filtrate.map((s) => ( ))}
Data e oraAtletaSportDurataHR medioHR maxTRIMPDFA A1RPE
{dataOra(s.startedAt)} {nome(s.athleteId)} {s.sport ?? '—'} {durata(s.durationSec)} {s.hrAvg} {s.hrMax} {s.trimp} {s.dfaA1 ?? '—'} {s.rpe ? `${s.rpe}/10` : '—'}
{filtrate.length === 0 &&

Nessuna sessione con questi filtri.

}
)} {tab === 'atleti' && (
{atletiRiepilogo.map((a) => (

{a.name}

{a.sport ?? 'Sport non specificato'}

{a.trend}

{a.sessioni30}

Sessioni 30gg

{a.trimp7}

TRIMP 7gg

{a.ultimoTrimp}

Ultimo TRIMP

{a.ultimaSessione ? `Ultima sessione ${dataBreve(a.ultimaSessione)}` : 'Nessuna sessione registrata'}
))}
)}
); }