Stress Index: Analytics e Sport rifatti sulle viste reali

Le due sezioni erano ferme al prototipo locale, più povero della
piattaforma vera. Analytics ha ora l'intervallo di date, i KPI con la
nota sotto il valore, la distribuzione dello score a quattro serie per
fascia, le classifiche per recupero medio col numero di misurazioni e il
confronto segmenti con selettore di dimensione. Sport diventa Dashboard,
Sessioni con filtri atleta/periodo/sport e schede atleta con carico e
tendenza.

I numeri non sono più costanti scritte a mano: si calcolano dai dati nel
periodo scelto. La logica sta in lib/stress-index/analytics.ts e sport.ts,
coperta da test — che hanno subito trovato uno slittamento delle settimane
dovuto alle date costruite in ora locale invece che in UTC.

Le sessioni hanno ora ora di inizio, HR max, RPE e durata al secondo, che
il prototipo non prevedeva.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 16:20:40 +02:00
parent c4eb598a3a
commit 3cda76b4c4
17 changed files with 907 additions and 409 deletions
+1 -14
View File
@@ -17,8 +17,6 @@ interface Props {
currentLesson?: string;
}
const { title, nav, currentArea, currentLesson } = Astro.props;
// Solo l'admin gestisce gli utenti: dalla sezione ci arriva senza passare dal pannello.
const isAdmin = Astro.locals.user?.role === 'admin';
---
<!doctype html>
<html lang="it">
@@ -38,7 +36,7 @@ const isAdmin = Astro.locals.user?.role === 'admin';
<span class="eyebrow">InsanityLab</span>
Biohacking
</a>
<a class="cp-home" href="/">← Torna alla home</a>
<a class="cp-home" href="/piattaforme">Piattaforme</a>
<nav class="cp-nav">
{nav.areas.map((area) => (
@@ -65,11 +63,6 @@ const isAdmin = Astro.locals.user?.role === 'admin';
</div>
))}
</nav>
<div class="cp-foot">
{isAdmin && <a href="/admin/users">Gestione utenti</a>}
<a href="/admin/logout">Esci</a>
</div>
</aside>
<main class="cp-main"><slot /></main>
@@ -108,12 +101,6 @@ const isAdmin = Astro.locals.user?.role === 'admin';
.cp-chapters ul li.on a { color: #fff; background: rgba(184,169,140,.22); }
.cp-label { font-family: var(--font-heading); font-size: .68rem; letter-spacing: .06em; color: var(--c-accent); }
.cp-foot { display: flex; flex-direction: column; gap: 10px; margin-top: 30px;
padding-top: 18px; border-top: 1px solid rgba(255,255,255,.12); }
.cp-foot a { font-size: .78rem; letter-spacing: .1em; text-transform: uppercase;
color: #8d8379; text-decoration: none; }
.cp-foot a:hover { color: #fff; }
/* --- contenuto --- */
.cp-main { flex: 1; min-width: 0; max-width: 920px; margin: 0 auto; padding: 56px 44px 100px; }
.cp-main h1 { font-size: clamp(1.9rem, 3.4vw, 2.8rem); text-transform: none; letter-spacing: .02em; }