From 5e4628b20f18a190613a6e27c4634edeb549f4dc Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Thu, 27 Aug 2026 11:05:05 -0400 Subject: [PATCH] docs(docs): revamp progress dashboard UI and add week field comment --- apps/docs/islands/ProgressDashboard.tsx | 223 ++++++++++++++++-------- 1 file changed, 147 insertions(+), 76 deletions(-) diff --git a/apps/docs/islands/ProgressDashboard.tsx b/apps/docs/islands/ProgressDashboard.tsx index 1fb3e78..615415d 100644 --- a/apps/docs/islands/ProgressDashboard.tsx +++ b/apps/docs/islands/ProgressDashboard.tsx @@ -35,6 +35,7 @@ interface Stats { deferred_total: number; }[]; ladder: Record; + /** week is the 1-based campaign week — the Worker converts from ISO. */ gates: { week: number; issue: number | null; @@ -76,7 +77,12 @@ function dayLabel(iso: string): string { return `${weekday} ${iso.slice(8)}`; } -// ── shared styles (theme tokens only — no hand-rolled palette) ─── +// ── shared styles ──────────────────────────────────────────────── +// Theme tokens only, with two deliberate exceptions: the phase-bar sets get +// fixed hues (accent-blue core, yellow optional, red deferred — they must be +// distinguishable, not shades of one colour), and the last-7-days heatmap +// reuses the GREENS ramp from apps/api/src/charts.ts so the page matches the +// README/email heatmaps (level 0 stays a theme token so light mode works). const card: CSSProperties = { border: "1px solid var(--blume-border)", @@ -142,32 +148,78 @@ function Header({ stats }: { stats: Stats }) { ); } +// One stacked bar per phase: solved core / optional / deferred segments over +// a muted track sized by the phase's total problem count, so both phase size +// and progress read at a glance. +const SETS = [ + ["core", "var(--blume-accent)"], + ["optional", "#d29922"], + ["deferred", "#e5534b"], +] as const; + function Phases({ stats }: { stats: Stats }) { + const maxTotal = Math.max( + 1, + ...stats.phases.map((p) => p.core_total + p.optional_total + p.deferred_total), + ); return (
Phases
- - - {stats.phases.map((p) => ( - - - - - - ))} - -
{p.name} - {" "} - - core {p.core_done}/{p.core_total} - - - - optional {p.optional_done}/{p.optional_total} - {p.deferred_total > 0 && - ` · deferred ${p.deferred_done}/${p.deferred_total}`} - -
+
+ {SETS.map(([set, color]) => ( + + + {set} + + ))} +
+ {stats.phases.map((p) => { + const total = p.core_total + p.optional_total + p.deferred_total; + const done = p.core_done + p.optional_done + p.deferred_done; + return ( +
+ {p.name} + + + {SETS.map(([set, color]) => { + const solved = p[`${set}_done`]; + return solved > 0 ? ( + + ) : null; + })} + + + + {done}/{total} + +
+ ); + })}
); } @@ -224,76 +276,95 @@ function Gates({ stats }: { stats: Stats }) {
Gate log
{stats.gates.length === 0 ? ( - No gates yet. + No gates yet — the first weekly review opens Saturday night. ) : ( - - - - {["Week", "Issue", "Pass rate", "Closed"].map((h) => ( - - ))} - - - - {stats.gates.map((g) => ( - - - - - - - ))} - -
- {h} -
{g.week} - {g.issue === null ? ( - - ) : ( - - #{g.issue} - - )} - - {g.pass_rate === null ? ( - open - ) : ( - percent(g.pass_rate) - )} - - {g.closed_on ?? } -
+ stats.gates.map((g) => ( +
+ Week {g.week} + {g.issue === null ? ( + no review issue + ) : ( + + #{g.issue} + + )} + {g.pass_rate === null ? ( + in progress + ) : ( + + {percent(g.pass_rate)} passed + {g.closed_on && · closed {g.closed_on}} + + )} +
+ )) )} +
+ One gate per campaign week — scored when its review issue closes. +
); } +// GREENS ramp from apps/api/src/charts.ts; level 0 swapped for the theme's +// muted token so an empty day looks right in both colour schemes. +const GREENS = [ + "var(--blume-muted)", + "#0e4429", + "#006d32", + "#26a641", + "#39d353", +]; + function Recent({ stats }: { stats: Stats }) { return (
Last 7 days
-
+
{stats.recent.map((r) => ( - {dayLabel(r.date)} - {r.passes} - /{r.attempts} + + + {dayLabel(r.date)} + ))}
-
passes / attempts
+
+ Less + {GREENS.map((color) => ( + + ))} + More · colour = attempts, hover for passes +
); }