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:
@@ -0,0 +1,56 @@
|
||||
// Andamento medio dello studio, con selettore di periodo. L'aggregazione media stress e
|
||||
// recupero di tutti i clienti sugli ultimi N rilievi settimanali.
|
||||
import { useState } from 'react';
|
||||
import DataChart from './DataChart';
|
||||
import type { Measurement } from '../../../lib/stress-index/data';
|
||||
|
||||
const PERIODS = [
|
||||
{ key: '30g', label: '30 giorni', points: 5 },
|
||||
{ key: '90g', label: '90 giorni', points: 13 },
|
||||
{ key: '6m', label: '6 mesi', points: 20 },
|
||||
{ key: 'tutto', label: 'Tutto', points: 20 },
|
||||
] as const;
|
||||
|
||||
interface Props { measurementsByClient: Record<string, Measurement[]> }
|
||||
|
||||
function aggregate(byClient: Record<string, Measurement[]>, points: number) {
|
||||
const out: { date: string; stress: number; recovery: number }[] = [];
|
||||
for (let fromEnd = points; fromEnd >= 1; fromEnd--) {
|
||||
let stress = 0, recovery = 0, count = 0, date = '';
|
||||
for (const series of Object.values(byClient)) {
|
||||
const point = series[series.length - fromEnd];
|
||||
if (!point) continue;
|
||||
stress += point.stress;
|
||||
recovery += point.recovery;
|
||||
count += 1;
|
||||
date = point.date;
|
||||
}
|
||||
if (count) out.push({ date, stress: Math.round(stress / count), recovery: Math.round(recovery / count) });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export default function AndamentoChart({ measurementsByClient }: Props) {
|
||||
const [period, setPeriod] = useState<string>('90g');
|
||||
const active = PERIODS.find((p) => p.key === period) ?? PERIODS[1];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="si-chips">
|
||||
{PERIODS.map((p) => (
|
||||
<button key={p.key} type="button" className="si-chip" aria-pressed={p.key === period} onClick={() => setPeriod(p.key)}>
|
||||
{p.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<DataChart
|
||||
data={aggregate(measurementsByClient, active.points)}
|
||||
xKey="date"
|
||||
series={[
|
||||
{ key: 'stress', label: 'Stress medio' },
|
||||
{ key: 'recovery', label: 'Recupero medio' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user