Spaces:
Sleeping
Sleeping
| /** Server-computed statistics rendered as lean cards and CSS bars. */ | |
| import { useEffect, useState } from "preact/hooks"; | |
| import { getStats, type StatRow, type StatsData } from "../api"; | |
| const pct = (n = 0) => `${Math.round(n * 100)}%`; | |
| function Rows({ title, rows, metric }: { title: string; rows: StatRow[]; metric: "p_helped" | "p_failed" }) { | |
| return ( | |
| <section class="surface-card stats-card"> | |
| <h2>{title}</h2> | |
| {!rows.length ? <p class="muted">No qualifying data.</p> : rows.map((row) => { | |
| const value = row[metric] || 0; | |
| return <div class="stat-row" key={row.key}><div><span>{row.key}</span><span class="muted">n={row.n} · {pct(value)}</span></div><div class="bar-track"><span style={{ width: pct(value) }} /></div></div>; | |
| })} | |
| </section> | |
| ); | |
| } | |
| export function Stats() { | |
| const [data, setData] = useState<StatsData | null>(null); | |
| useEffect(() => { getStats().then(setData).catch(() => {}); }, []); | |
| if (!data) return <div class="gate-loading">Loading…</div>; | |
| const outcomes = Object.entries(data.outcomes); | |
| const dailyRows: StatRow[] = [ | |
| { key: "Brick done", n: 0, p_helped: data.daily.p_brick_done }, | |
| { key: "Corn okay", n: 0, p_helped: data.daily.p_corn_ok }, | |
| { key: "No FC", n: 0, p_helped: data.daily.p_no_fc }, | |
| { key: "Clean rerun", n: 0, p_helped: data.daily.p_rerun_clean }, | |
| { key: "Court closed", n: 0, p_helped: data.daily.p_court_closed }, | |
| ]; | |
| return ( | |
| <div class="app-shell"> | |
| <header class="top-bar material-bar"><h1>Stats</h1></header> | |
| <main class="page stack"> | |
| <section class="surface-card stats-card"><h2>Outcomes</h2><div class="outcome-grid">{outcomes.map(([key, count]) => <div key={key}><strong>{count}</strong><span class={`result-label result-${key}`}>{key}</span></div>)}</div><p class="muted">{data.n_entries_scored} scored · {data.n_entries_total} total</p></section> | |
| <Rows title="Remedies" rows={data.by_remedy} metric="p_helped" /> | |
| <Rows title="Tags · failed" rows={data.by_tag} metric="p_failed" /> | |
| <Rows title="Emotions" rows={data.by_emotion} metric="p_helped" /> | |
| <Rows title="Intensity" rows={data.by_intensity_bucket} metric="p_helped" /> | |
| <Rows title="Daily rates" rows={dailyRows} metric="p_helped" /> | |
| <section class="surface-card stats-card"><h2>Average daily points</h2><strong class="large-number">{data.daily.avg_points.toFixed(1)}/6</strong></section> | |
| </main> | |
| </div> | |
| ); | |
| } | |