Piattaforme riservate: ruolo dedicato e port della dashboard Stress Index

Il ruolo `campus` diventa `piattaforme` e non apre più solo la sezione
Biohacking: copre tutte le aree riservate, con migrazione guardata sugli
utenti esistenti. Nell'header la vecchia voce Biohacking lascia il posto a
un menu Piattaforme con i due link, visibile solo a chi ha il ruolo, e
/piattaforme diventa la pagina di atterraggio.

Stress Index è portata dentro il sito: le nove viste dell'area
professionisti diventano pagine Astro sotto /piattaforme/stress-index,
con lo stile del sito al posto del design system del prototipo. React
resta solo dove serve davvero — grafici recharts e scheda cliente — il
resto è Astro con un filo di JS. I dati sono ancora quelli dimostrativi,
raccolti in lib/stress-index/data.ts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 15:49:47 +02:00
parent e8e24e086b
commit c4eb598a3a
32 changed files with 2908 additions and 74 deletions
+83
View File
@@ -0,0 +1,83 @@
---
// Layout della piattaforma Stress Index. Come il Campus: token, font e palette del sito,
// senza Header e Footer pubblici — la navigazione è tutta interna alla piattaforma.
import '@fontsource/montserrat/400.css';
import '@fontsource/montserrat/500.css';
import '@fontsource/montserrat/600.css';
import '@fontsource/open-sans/400.css';
import '@fontsource/open-sans/600.css';
import '../styles/global.css';
import '../styles/stress-index.css';
import { professional } from '../lib/stress-index/data';
interface Props {
title: string;
/** Voci di percorso mostrate nella topbar, dopo "Stress Index". */
crumbs?: string[];
}
const { title, crumbs = [] } = Astro.props;
const path = Astro.url.pathname;
const isAdmin = Astro.locals.user?.role === 'admin';
const BASE = '/piattaforme/stress-index';
const NAV = [
{ href: BASE, label: 'Oggi' },
{ href: `${BASE}/clienti`, label: 'Clienti' },
{ href: `${BASE}/analytics`, label: 'Analytics' },
{ href: `${BASE}/sport`, label: 'Sport', badge: 'Pro' },
{ href: `${BASE}/sport/team-live`, label: 'Team live' },
{ href: `${BASE}/impostazioni`, label: 'Impostazioni' },
{ href: `${BASE}/organizzazione`, label: 'Organizzazione' },
];
---
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title} — Stress Index InsanityLab</title>
<meta name="robots" content="noindex, nofollow" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
</head>
<body class="si">
<input type="checkbox" id="si-menu" class="si-menu-toggle" hidden />
<label for="si-menu" class="si-menu-btn" aria-label="Apri il menu">☰ Menu</label>
<aside class="si-side">
<a class="si-brand" href={BASE}>
<span class="eyebrow">InsanityLab</span>
Stress Index
</a>
<a class="si-home" href="/piattaforme">← Piattaforme</a>
<nav class="si-nav">
{NAV.map((item) => (
<a
href={item.href}
class:list={['si-nav__link', { 'is-active': path === item.href }]}
>
{item.label}
{item.badge && <span class="si-nav__badge">{item.badge}</span>}
</a>
))}
</nav>
<div class="si-foot">
<span class="si-foot__role">{professional.role}</span>
{isAdmin && <a href="/admin/users">Gestione utenti</a>}
<a href="/admin/logout">Esci</a>
</div>
</aside>
<div class="si-body">
<header class="si-top">
<p class="si-top__crumbs">
<strong>Stress Index</strong>
{crumbs.map((c) => <span> / {c}</span>)}
</p>
<p class="si-top__studio">{professional.studio}</p>
</header>
<main class="si-main"><slot /></main>
</div>
</body>
</html>