// Scheda cliente: è l'unica vista davvero interattiva della piattaforma (tab, filtri sul
// grafico, note, messaggi, tag), quindi vive come singola isola React. Lo stile arriva dalle
// classi `.si-*` del foglio globale: qui non ci sono classi Tailwind del prototipo originale.
import { useMemo, useState } from 'react';
import DataChart from './charts/DataChart';
import type { Alert, Client, Measurement, Note } from '../../lib/stress-index/data';
interface Props {
client: Client;
measurements: Measurement[];
alerts: Alert[];
notes: Note[];
}
const TABS = [
{ key: 'panoramica', label: 'Panoramica' },
{ key: 'misurazioni', label: 'Misurazioni' },
{ key: 'analytics', label: 'Analytics' },
{ key: 'report', label: 'Report' },
{ key: 'note', label: 'Note' },
{ key: 'messaggi', label: 'Messaggi' },
{ key: 'impostazioni', label: 'Impostazioni' },
];
const METRICHE = [
{ key: 'stress', label: 'Stress', color: '#b05a4e' },
{ key: 'recovery', label: 'Recupero', color: '#6f8f6a' },
{ key: 'balance', label: 'Equilibrio', color: '#c08a3e' },
{ key: 'energy', label: 'Energia', color: '#9c8b70' },
];
const PERIODI = [
{ key: '4', label: 'Ultime 4' },
{ key: '8', label: 'Ultime 8' },
{ key: 'all', label: 'Tutte' },
];
const REPORT = [
{ date: '2026-07-01', label: 'Report mensile — Giugno 2026' },
{ date: '2026-06-01', label: 'Report mensile — Maggio 2026' },
{ date: '2026-05-01', label: 'Report mensile — Aprile 2026' },
];
const MESSAGGI_INIZIALI = [
{ from: 'cliente', text: "Buongiorno dottore, le invio l'esito della misurazione di stamattina.", when: '09:12' },
{ from: 'professionista', text: 'Grazie, la vedo. I valori di recupero sono in miglioramento rispetto alla scorsa settimana.', when: '09:20' },
{ from: 'cliente', text: 'Sì, mi sento meglio, dormo di più.', when: '09:22' },
{ from: 'professionista', text: 'Continui così, ci sentiamo al prossimo controllo.', when: '09:25' },
];
const qualitativa = (v: number) => (v >= 70 ? 'Alto' : v >= 40 ? 'Moderato' : 'Basso');
const livello = (v: number) => (v >= 70 ? 'bad' : v >= 45 ? 'warn' : 'ok');
/** Indicatore ad arco: il valore riempie un semicerchio da sinistra a destra. */
function Gauge({ value, label }: { value: number; label: string }) {
const ratio = Math.max(0, Math.min(1, value / 100));
const R = 56;
const len = Math.PI * R;
return (
= 70 ? '#b05a4e' : value >= 45 ? '#c08a3e' : '#6f8f6a'}
strokeDasharray={`${len * ratio} ${len}`}
/>
{value}/100
{label} · {qualitativa(value)}
);
}
export default function ClientDetail({ client, measurements, alerts, notes }: Props) {
const [tab, setTab] = useState('panoramica');
const [periodo, setPeriodo] = useState('8');
const [metriche, setMetriche] = useState(METRICHE.map((m) => m.key));
const [noteCliente, setNoteCliente] = useState(notes);
const [nuovaNota, setNuovaNota] = useState('');
const [messaggi, setMessaggi] = useState(MESSAGGI_INIZIALI);
const [bozza, setBozza] = useState('');
const [tag, setTag] = useState(client.tags);
const [nuovoTag, setNuovoTag] = useState('');
const [promemoria, setPromemoria] = useState(true);
const [reportSemestrale, setReportSemestrale] = useState(false);
const ultima = measurements[measurements.length - 1];
const stress = ultima?.stress ?? client.stress;
const recovery = ultima?.recovery ?? 0;
const balance = ultima?.balance ?? 0;
const energy = ultima?.energy ?? 0;
const datiGrafico = useMemo(
() => (periodo === 'all' ? measurements : measurements.slice(-Number(periodo))),
[measurements, periodo]
);
const serieAttive = METRICHE.filter((m) => metriche.includes(m.key));
const ultimeTre = [...measurements].slice(-3).reverse();
const storico = [...measurements].reverse();
const toggleMetrica = (key: string) =>
setMetriche((prev) => (prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]));
const aggiungiNota = () => {
const testo = nuovaNota.trim();
if (!testo) return;
setNoteCliente((prev) => [{ clientId: client.id, text: testo, when: client.lastMeasurement }, ...prev]);
setNuovaNota('');
};
const invia = () => {
const testo = bozza.trim();
if (!testo) return;
setMessaggi((prev) => [...prev, { from: 'professionista', text: testo, when: 'ora' }]);
setBozza('');
};
const aggiungiTag = () => {
const t = nuovoTag.trim();
if (!t || tag.includes(t)) return;
setTag((prev) => [...prev, t]);
setNuovoTag('');
};
return (
{client.initials}
{client.name}
{client.age} anni · {client.sex} · in carico dal {client.since}
{[
{ label: 'Stress', value: stress, note: qualitativa(stress) },
{ label: 'Recupero', value: recovery, note: qualitativa(recovery) },
{ label: 'Misurazioni', value: measurements.length, note: 'totali' },
{ label: 'In carico da', value: client.since, note: 'data iscrizione' },
].map((k) => (
{k.label}
{k.value}
{k.note}
))}
{TABS.map((t) => (
setTab(t.key)}
>
{t.label}
))}
{tab === 'panoramica' && (
Andamento clinico
{PERIODI.map((p) => (
setPeriodo(p.key)}>
{p.label}
))}
{METRICHE.map((m) => (
toggleMetrica(m.key)}>
{m.label}
))}
Alert attivi
{alerts.length === 0 ? (
Nessun alert attivo per questo cliente.
) : (
alerts.map((a, i) => (
))
)}
Ultime 3 misurazioni
{ultimeTre.map((m, i) => (
{m.date}
· {m.type} · stress {m.stress}
{qualitativa(m.stress)}
))}
Sintesi
{stress >= 70
? `${client.name} mostra un livello di stress elevato nelle ultime misurazioni, con recupero notturno ridotto. Consigliata una revisione del carico settimanale e un lavoro sulla respirazione.`
: `${client.name} mantiene un equilibrio complessivamente stabile. Continuare il monitoraggio periodico e mantenere le abitudini di recupero attuali.`}
)}
{tab === 'misurazioni' && (
Storico misurazioni
Data Tipo Stress Recupero Equilibrio Energia Durata
{storico.map((m, i) => (
{m.date}
{m.type}
{m.stress}
{m.recovery}
{m.balance}
{m.energy}
{m.durationMin} min
))}
)}
{tab === 'analytics' && (
Trend dei quattro parametri
Stress e recupero, ultime misurazioni
)}
{tab === 'report' && (
Report generati
Genera report
{REPORT.map((r) => (
{r.date} · {r.label}
Scarica
))}
)}
{tab === 'note' && (
Note cliente
Aggiungi nota
Aggiungi nota
{noteCliente.length === 0 ? (
Nessuna nota registrata per questo cliente.
) : (
{noteCliente.map((n, i) => (
{n.when}
{n.text}
))}
)}
)}
{tab === 'messaggi' && (
)}
{tab === 'impostazioni' && (
Tag cliente
{tag.map((t) => (
{t}
setTag((prev) => prev.filter((x) => x !== t))}>✕
))}
Nuovo tag
setNuovoTag(e.target.value)} placeholder="Nuovo tag…" />
Aggiungi
)}
);
}