Build dashboard shell and core panels

Task 5396 · Done · Coder · created · updated

## Original requirement (verbatim, epic) > Create Operator dashboard with high-level metrics of the entire system to analyze usage, efficiency, resources spent, results achieved - LOCs, docs - quantitative and qualitative. Some trends. Make it very robust - less words, more information that can be zoomed in. > > Do a web research to find out what competition does, but also come up with innovative ideas that will help observe and grow the business. Operator interview answers (verbatim): "Suggest other things too" · "When the runner merges a PR we can capture lines at that point" · "We are already calculating dollar cost from tokens and our price table in settings. BTW update the table with the latest data" Full settled inputs: PRD = Task 5386 `prd` artifact; architecture = Task 5387 `architecture` artifact (read with `task_read_artifact`). Section refs below (PRD §n / Arch §n) point there. Metric attribution rules (Arch §3), the pinned API contract (Arch §6) and the algorithms (Arch §7) are BINDING — do not invent alternatives. ## Requirements (PRD, verbatim) ### R1 — Access and placement - **AC1.1** Operator sees a "Dashboard" entry at the top of the sidebar and lands on it at `/`. → *Verify:* on dev, log in as operator, open `/` → the dashboard renders; sidebar's first entry is "Dashboard". - **AC1.2** Any non-operator (tenant owner/admin/member, superadmin not on operator plane) sees no Dashboard entry, `/` behaves exactly as today, and every dashboard API returns 403. - **AC1.3 Availability (R29)** — the dashboard is ON by default in dev and prod: no feature flag, plan flag or setting gates it for the operator. → *Verify:* a fresh operator login on dev with default settings reaches it with no configuration step. ### R2 — Presentation: dense, trend-bearing, sliceable - **AC2.1 Density / "less words"** — the top band shows every headline KPI as a number tile with a sparkline; no paragraph text anywhere; labels ≤3 words; above the fold at 1440×900 at least 12 KPIs are visible without scrolling. → *Verify:* screenshot at 1440×900; count tiles; grep rendered DOM for text nodes >60 chars outside tooltips (none). - **AC2.2 Legibility (quality bar)** — no KPI value, label or axis label is truncated or overlapping at 1440, 1024 and 390 px widths; numbers use compact units (1.2k, $3.4k, 45%) with full value on hover; charts are readable in light and dark theme. A layout that fits by shrinking text below 12px or truncating labels FAILS. → *Verify:* Playwright screenshots at the three widths in both themes; visual review + assertion no element has `scrollWidth > clientWidth` on labels/values. - **AC2.3 Trends** — every KPI tile has a trend series over the selected range; each section has at least one time-series chart. Range presets 7d/30d/90d/1y, default 30d. → *Verify:* change range on dev → every tile and chart re-renders with the matching bucket count. - **AC2.4 Slicing** — a global filter bar slices EVERY widget by tenant (org), project, persona, model, provider, node, user (multi-select each); each chart offers "group by" over the same dimensions where meaningful. Active filters persist in the URL (shareable link). → *Verify:* on dev filter to one tenant → every tile changes to that tenant's numbers (cross-check one tile against a SQL count); copy URL to new tab → same view. - **AC2.5 Honest gaps** — a value with no data shows "—" with a reason on hover ("no runs in range", "not captured before <date>", "model unpriced"), never a silent 0. → *Verify:* select a tenant with no activity → tiles show "—", not 0. - **AC2.6 Freshness** — data ≤15 min old; the page shows "as of HH:MM". Page loads ≤3 s p95 for the 1y range on dev data. ### R3 — Usage & throughput Metrics (each with trend, sliceable): tasks created; tasks done; tasks failed (`system_failed`); epics started / completed; sessions run; runner-hours (sum of session durations); active tenants, active projects, active users (≥1 task created or run in range); tasks by persona; tasks in each state now (current snapshot). ### R4 — Efficiency & resources Metrics: tokens by class (input/cache/output) and by model; cost $ = tokens × price table (same function as task run metrics); cost by tenant/project/persona/model; runner-hours; subscription account quota consumption (5h/7d) over time; node utilisation (CPU/RAM) and worktree occupancy (free/task/user) over time; retries (attempts > 1) and failure rate. ### R5 — Results: PRs, LOC, docs Metrics: merged PRs; no-PR terminals by reason; LOC added / removed / net and files changed (from R9); artifacts produced by kind (prd, architecture, report, file); tasks done per persona. ## Scope & approach (this child) R1 UI + R2 UI + Usage/Efficiency/Results sections (Arch §2 frontend, §9). Built against the real API-core endpoints (pinned contract Arch §6). - `apps/web/src/App.tsx:185`: `/` renders lazy-loaded `OperatorDashboard` when `admin.is_operator`, else today's `HomeLanding` unchanged; tenants never download the chart chunk. - `apps/web/src/components/Sidebar.tsx` `DESTINATIONS`: new FIRST entry `{key:'dashboard', to:'/', label:'Dashboard', operatorOnly:true}`; exact match on `/` (must not swallow other routes). - `apps/web/src/pages/dashboard/`: add `@mui/x-charts` (community/MIT) pinned to an exact version, themed from the console MUI theme (light/dark). Sticky filter bar: range presets 7d/30d/90d/1y (default 30d) + 7 multi-select pickers fed by `/dimensions`; state in URL via `useSearchParams` using the API's param names. KPI band of number tiles each with a `SparkLineChart`. Section grid (Usage, Efficiency, Results now; the other sections slot into the same grid in the follow-on child), each section its own React Query hook (progressive render, per-section skeleton/error), each ≥1 time-series chart (`LineChart`/`BarChart`) with a per-chart group-by select; `as of HH:MM` in the header. - Build REUSABLE pieces (tile, chart card, group-by select, status renderer, compact formatter) — the follow-on child adds Autonomy/Funnel/Tenants/Quality/Insights with them. Every tile/chart carries `data-metric-key=<key>`. - Quality bar (Arch §2): tiles in CSS grid `repeat(auto-fill, minmax(140px, 1fr))`; labels ≤3 words, NEVER truncated (wrap to 2 lines allowed); values via `Intl.NumberFormat({notation:'compact'})` with full value in tooltip; min font 12px; at 390px grid flows to 2 columns (scroll allowed, clipping not). Render "—" + reason tooltip from `status`, never from `value == null`. Group-by charts render the API's top-7 + "Other" with legend sorted by total. A layout that fits by shrinking text or truncating labels FAILS. Out of scope: - Do NOT change tenant `/` (`HomeLanding`) or any existing page/route; no new route other than operator `/`. - No brush-select, no period-over-period deltas, no drill-to-records. - No Autonomy/Funnel/Tenants/Quality/Insights panels here (follow-on child). No backend changes. Epic out-of-scope (PRD §5, verbatim): - No tenant-facing dashboard or data exposure: no new route, nav entry or API readable by any non-operator; tenant `/` (onboarding `HomeLanding`) unchanged. - Do NOT change Audit › Sessions › Efficiency, task run-metrics, `/clients` billing, account/node pages — reuse their definitions; any metric they already compute must not be re-implemented with a different formula. - Do NOT change cost arithmetic in `app/model_prices.py` (only its rate data, R10). - No LOC backfill, no git-clone-and-count on nodes, no LOC for non-merged work. - No doc-file LOC split (docs = artifacts only, D10). - No star-rating panels, no derived "quality signals" panels (D9 picked AI review only); existing ratings feature untouched. - No brush-select, no period-over-period deltas, no drill-down record lists (D8). Clicking an anomaly applies a slice — that is not a record list. - No Slack/email/push of insights, digests or anomalies (D11). - No change to task lifecycle semantics: recording state-transition times must be additive and must not alter transitions, guards or the `task_lifecycle.py` table. - No billing changes (plans, Stripe, wallets) — read only. - Never block a settle on LOC capture. Done means: - AC1.1/1.2: Playwright — operator at `/` sees the dashboard and "Dashboard" as first sidebar entry; tenant user at `/` sees `HomeLanding` unchanged and no Dashboard entry; the x-charts chunk is not requested for a tenant. - AC2.1: Playwright at 1440×900 — ≥12 KPI tiles fully in viewport; no rendered text node >60 chars outside tooltips; every tile label ≤3 words. - AC2.2: Playwright screenshots at 1440, 1024, 390 × light, dark (attached to the report); assertion no label/value element has `scrollWidth > clientWidth`; computed font-size ≥12px on tiles/axes; reviewer visually confirms charts legible in both themes. - AC2.3: Playwright — switching 7d/30d/90d/1y changes every sparkline/chart's point count to the bucket count. - AC2.4: Playwright — selecting one org updates every tile; reloading the copied URL reproduces the view; on dev cross-check one tile vs SQL (cite query + value). - AC2.5: vitest — a `no_data`/`not_sliceable` metric renders "—" with the reason tooltip, never 0. - AC2.6: header shows `as of HH:MM`; on dev 1y load measured ≤3 s (cite devtools timing). - Render completeness: Playwright walks EVERY metric and chart key of Arch §6.2 for `/usage`, `/efficiency`, `/results` and asserts a rendered `[data-metric-key=<key>]` element for each; asserts each section has ≥1 time-series chart and a group-by control on each grouped chart. - `./dc check full` green; PR merged to develop.

Outcome

Built the operator dashboard UI at / with URL filters, KPI sparkline tiles, and Usage/Efficiency/Results charts with group-by. Full gate green; PR #4727 merged; dev deploy succeeded (on the second run). E2E covers 1440/1024/448 per the repo's width standard, not 390.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Awaiting response
  • Awaiting response
  • Runner resumed
  • Runner ended (done)