Dashboard: /stats UI panel consuming GET /stats #129

Closed
opened 2026-04-20 11:33:35 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As the operator, I want a /stats panel on the dashboard that renders the data GET /stats (shipped in #123/#127) returns, so that I can see cost, turn, and success-rate trends per agent, repo, and day without curling the API.

Context

/stats returns:

{
  "window": { "from": "<iso>", "to": "<iso>", "days": 30 },
  "totals": { "tasks": N, "cost_usd": N, "turns": N, "success_rate": 0-1 },
  "by_agent": [{ name, type, tasks, cost_usd, turns, success_rate, avg_turns_per_task, avg_cost_per_task }],
  "by_repo": [{ repo, tasks, cost_usd, success_rate }],
  "by_day": [{ day, tasks, cost_usd, success_rate }]
}

The current dashboard has no consumer. This story bolts a /stats tab/panel onto src/dashboard.html that pulls + renders it.

Acceptance criteria

Panel

  • New tab or panel titled "Stats" reachable from the existing dashboard chrome. Keep the design language of the monitor rework (#70/#122) — same tokens, same type scale.
  • Window selector (chips or dropdown): 7d / 30d / 90d / all. Default 30d. Changing the window refetches.
  • Agent filter (dropdown or chip-strip) populated from the workers SSE event. ALL default. Filter re-passes ?agent=<name> to the endpoint.
  • Repo filter (dropdown). ALL default. Filter re-passes ?repo=<owner/name>.

Rendering

  • Totals card — tasks, cost_usd ($x.xx), turns, success_rate (xx%). Whole card grays out on empty window.
  • By-agent table — one row per agent. Columns: name (with type pill), tasks, cost (USD), avg cost/task, turns, avg turns/task, success. Sort by tasks desc.
  • By-day sparkline or bar strip — one bar/point per day of tasks count, colour-shaded by success rate. Same visual vocabulary as the turns timeline from #122.
  • By-repo list — compact list (not a full table): repo · tasks · cost · success%.
  • Empty-window state — "No tasks in this window" copy; matches the empty-state treatment from the monitor.

Polling

  • Refresh on window/filter change (no polling otherwise — stats are slow-moving; refetch on tab re-entry is fine).
  • Optional: subtle "refresh" button in the panel header.

Tests

  • Extend src/dashboard-browser.test.ts with happy-dom coverage: mount the panel, mock /stats → verify totals + by-agent table render; change window → verify refetch with the right query param; filter by agent → same.
  • src/dashboard-smoke.test.ts structural check for the new tab element + all three subcomponents.

Out of scope

  • Cost forecasting / anomaly detection (future ticket — raw aggregates only).
  • Drill-down from by-agent row → list of matching tasks (would need a new ?agent= filter on /history or equivalent; separate story).
  • CSV export.
  • Alerting on success-rate dips (#122 follow-up, not here).

References

  • Endpoint: src/main.ts::handleStats (shipped in #127).
  • task-store.ts for the shape of the underlying rows.
  • Dashboard style vocabulary: PR #126 (monitor rework).

Dependencies

  • Blocked by: nothing (endpoint already landed).
  • Blocks: nothing.
  • Branch off: main.
## User story As the **operator**, I want a `/stats` panel on the dashboard that renders the data `GET /stats` (shipped in #123/#127) returns, so that I can see cost, turn, and success-rate trends per agent, repo, and day without curling the API. ## Context `/stats` returns: ```json { "window": { "from": "<iso>", "to": "<iso>", "days": 30 }, "totals": { "tasks": N, "cost_usd": N, "turns": N, "success_rate": 0-1 }, "by_agent": [{ name, type, tasks, cost_usd, turns, success_rate, avg_turns_per_task, avg_cost_per_task }], "by_repo": [{ repo, tasks, cost_usd, success_rate }], "by_day": [{ day, tasks, cost_usd, success_rate }] } ``` The current dashboard has no consumer. This story bolts a `/stats` tab/panel onto `src/dashboard.html` that pulls + renders it. ## Acceptance criteria ### Panel - [ ] New tab or panel titled "Stats" reachable from the existing dashboard chrome. Keep the design language of the monitor rework (#70/#122) — same tokens, same type scale. - [ ] Window selector (chips or dropdown): `7d` / `30d` / `90d` / `all`. Default `30d`. Changing the window refetches. - [ ] Agent filter (dropdown or chip-strip) populated from the `workers` SSE event. `ALL` default. Filter re-passes `?agent=<name>` to the endpoint. - [ ] Repo filter (dropdown). `ALL` default. Filter re-passes `?repo=<owner/name>`. ### Rendering - [ ] **Totals card** — tasks, cost_usd (`$x.xx`), turns, success_rate (`xx%`). Whole card grays out on empty window. - [ ] **By-agent table** — one row per agent. Columns: name (with type pill), tasks, cost (USD), avg cost/task, turns, avg turns/task, success. Sort by tasks desc. - [ ] **By-day sparkline or bar strip** — one bar/point per day of tasks count, colour-shaded by success rate. Same visual vocabulary as the turns timeline from #122. - [ ] **By-repo list** — compact list (not a full table): `repo · tasks · cost · success%`. - [ ] Empty-window state — "No tasks in this window" copy; matches the empty-state treatment from the monitor. ### Polling - [ ] Refresh on window/filter change (no polling otherwise — stats are slow-moving; refetch on tab re-entry is fine). - [ ] Optional: subtle "refresh" button in the panel header. ### Tests - [ ] Extend `src/dashboard-browser.test.ts` with happy-dom coverage: mount the panel, mock `/stats` → verify totals + by-agent table render; change window → verify refetch with the right query param; filter by agent → same. - [ ] `src/dashboard-smoke.test.ts` structural check for the new tab element + all three subcomponents. ## Out of scope - Cost forecasting / anomaly detection (future ticket — raw aggregates only). - Drill-down from by-agent row → list of matching tasks (would need a new `?agent=` filter on `/history` or equivalent; separate story). - CSV export. - Alerting on success-rate dips (#122 follow-up, not here). ## References - Endpoint: `src/main.ts::handleStats` (shipped in #127). - `task-store.ts` for the shape of the underlying rows. - Dashboard style vocabulary: PR #126 (monitor rework). ## Dependencies - **Blocked by:** nothing (endpoint already landed). - **Blocks:** nothing. - **Branch off:** `main`.
claude-desktop removed their assignment 2026-04-20 11:39:04 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#129
No description provided.