From a41420d0fc40d41ca0512c0ee028f2dbaa45cedf Mon Sep 17 00:00:00 2001 From: dtzp555-max Date: Tue, 26 May 2026 17:20:49 +1000 Subject: [PATCH] =?UTF-8?q?feat:=20D82=20=E2=80=94=20dashboard=20UI=20Clau?= =?UTF-8?q?de.ai-style=20(Phase=205)=20(#54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: D82 — dashboard UI Claude.ai-style per-provider rows (Phase 5) Implements ADR 0012 D82: restructures dashboard.html to render quota_v2 data (produced by D81 / PR #53) in a Claude.ai-style per-provider row layout. Closes v1.x roadmap #8. ## What changed ### dashboard.html (A–G) A. New "Plan Usage" section at top (full-width, above the 2-col grid): - Per-provider rows rendered from `data.quota_v2` - Each row: provider badge (colored chip), status dot + chip (live/stale/unavailable), schema version tag - Two utilization bars (5h + 7d) with: - Rounded gradient bar (green <50% / amber 50-80% / red >80%) - Label "Current 5-hour session: 49%" / "Weekly all-models: 31%" - Right-side reset countdown (see B) - Bottom chips: representative-claim badge (purple), overage chip (amber/green), fallback-percentage chip, last-fresh "Updated N min ago" tag; stale rows show amber ⚠ stale data chip with tooltip - Unavailable rows: provider badge + reason text only; no bars B. formatResetCountdown(epochSeconds): - < 1 hour: "Resets in 23 min" - 1–24 hours: "Resets in 12hr 30min" - < 7 days: "Resets Sun 9:00 PM" (weekday + 12h time) - >= 7 days: "Resets May 31 9:00 PM" (month day + time) - past: "Resetting now…" Uses toLocaleString('en-US', { hour12: true }). C. 60s auto-refresh (quota_v2 only) with visibilityState guard: - Separate timer (quotaRefreshTimer); does NOT replace the 30s poll - Pauses on 'hidden'; resumes + immediate re-fetch on 'visible' - Other 3 panels (24h, 30d, top fallback) keep 30s cadence unchanged D. Manual refresh button (↻ Refresh) in Plan Usage header: - 2-second spam guard (button disables post-click) - Spinning ⟳ icon during fetch - Re-enables after fetch completes (success or error) E. Graceful quota_v2 / legacy quota fallback: - If data.quota_v2 is present and non-empty → render Plan Usage rows; hide legacy "Quota (per provider)" panel - If data.quota_v2 is absent/empty → show note in Plan Usage area; surface legacy data.quota in the original table panel - Guards operator running an older OLP build (pre-D81) F. Visual polish: rounded bars, gradient fills, airy whitespace, mobile- responsive (bars reflow on narrow viewports via flex-wrap). Color palette: #10b981 (green), #f59e0b (amber), #ef4444 (red) matching Tailwind emerald/amber/red-500 per spec. G. Other 3 panels (24h, 30d, top fallback) and their 30s poll cadence are IDENTICAL to D51. Only the Quota panel restructures. ### docs/v1x-roadmap.md (H) Marks entry #8 as "✅ CLOSED (D82, v0.5.0)". Adds closure status, PR ref, and a brief note inside the entry body. Updates reading-order header paragraph to include #8 in the closed list. ## Authority + citations - ADR 0012 D82 — Claude.ai-style restructure D-day spec (docs/adr/0012-phase-5-charter-quota-probes-dashboard.md § D-day table) - D81 PR #53 — quota_v2 shape producer (commit 5288493); ProviderQuotaEntry shape per ADR 0008 Amendment 1 § 3 - Maintainer reference 2026-05-26 — claude.ai/settings/usage screenshot; "Resets in 1hr 6min" / "Resets Sun 9:00 PM" string format - v1.x roadmap #8 — closed by this commit (docs/v1x-roadmap.md #8 — Dashboard enrichment) ## Test impact npm test: 727 pass / 0 fail (unchanged). dashboard.html is frontend-only; D83 ships Suite 38/39 (probe unit tests + dashboard smoke tests). Co-Authored-By: Claude Sonnet 4.6 * fix(dashboard): D82 reviewer Nit #4 — gate overage chip on real status When entry.overage = { status: null, disabled_reason: null } (audit-query's default shape when the provider doesn't supply overage info), the chip rendered as amber "Overage: —" which falsely suggests a warning state. Now: chip only renders when entry.overage.status is truthy (i.e., Anthropic actually returned an overage-status header). Truly-missing overage info shows no chip at all, matching the maintainer's intent. Identified by D82 fresh-context reviewer (PR #54 thread) as the only maintainer-visible nit worth folding in pre-merge. 1-line change. All 727 tests continue to pass. Co-Authored-By: Claude Opus 4.7 --------- Co-authored-by: dtzp555 Co-authored-by: Claude Sonnet 4.6 --- dashboard.html | 572 ++++++++++++++++++++++++++++++++++++++++++-- docs/v1x-roadmap.md | 5 +- 2 files changed, 551 insertions(+), 26 deletions(-) diff --git a/dashboard.html b/dashboard.html index 9302613..e91d3c0 100644 --- a/dashboard.html +++ b/dashboard.html @@ -1,16 +1,24 @@ +
+
+

Plan Usage

+
+ + +
+
+
Loading…
+
+
-
-

Quota (per provider)

+
@@ -67,15 +294,21 @@
Loading…
-
OLP Dashboard · poll every 30s · paused when tab hidden · v0.3.0-phase3
+
OLP Dashboard · Plan Usage: 60s refresh · other panels: 30s · paused when tab hidden · v0.5.0-phase5
diff --git a/docs/v1x-roadmap.md b/docs/v1x-roadmap.md index 55edab2..63dcb3b 100644 --- a/docs/v1x-roadmap.md +++ b/docs/v1x-roadmap.md @@ -8,7 +8,7 @@ 3. **Where** does the work live in the tree today (file + anchor). 4. **When** does it need to land (trigger: load profile, security event, governance amendment). -**Reading order for a v1.x sprint kickoff.** As of 2026-05-25, #1 (streaming SF, D57+D58) and #2 (multi-key auth, Phase 2) are CLOSED, and #4 and #7 closed in D56. Remaining v1.x scope: #3 (soft trigger reactivation), #5 (provider cacheKeyFields mask), #6 (streaming SPAWN_FAILED salvage — unbundled from #1 at #1 close). All three remaining items have explicit "trigger to start" gates that have not fired. +**Reading order for a v1.x sprint kickoff.** As of 2026-05-26, #1 (streaming SF, D57+D58), #2 (multi-key auth, Phase 2), #4 and #7 (closed in D56), and #8 (dashboard enrichment, D82 Phase 5) are CLOSED. Remaining v1.x scope: #3 (soft trigger reactivation), #5 (provider cacheKeyFields mask), #6 (streaming SPAWN_FAILED salvage — unbundled from #1 at #1 close). All three remaining items have explicit "trigger to start" gates that have not fired. --- @@ -88,8 +88,9 @@ - **Tracking.** Not a GitHub issue. Tracked here. - **Trigger to start.** First report of streaming-path SPAWN_FAILED mid-stream where partial-chunk salvage would have helped a downstream caller. Practically unlikely at family scale. -## #8 — Dashboard enrichment: per-provider subscription quota + reset times + 1-min refresh + manual refresh (D78 follow-up) +## #8 — Dashboard enrichment: per-provider subscription quota + reset times + 1-min refresh + manual refresh (D78 follow-up) — ✅ **CLOSED (D82, v0.5.0)** +- **Status.** Closed at D82 (Phase 5). `dashboard.html` restructured to Claude.ai-style per-provider rows rendering `quota_v2`. Closed by PR on branch `d82-dashboard-ui-claude-ai-style`; ships with v0.5.0. 60s quota auto-refresh + manual refresh button + visibilityState guard implemented. Graceful fallback to legacy `quota` field when server runs a pre-D81 build. - **What.** Phase 3 dashboard (D51 `dashboard.html`, v0.3.0) shows: per-provider quota (currently always "n/a — no quota api"), last-24h request count + cache hit + fallback rate, 30d request-count sparkline, top fallback chains. **Maintainer request 2026-05-26 post-D78**: extend to show what each enabled provider's subscription is actually consuming, with reset times visible, refresh once per minute (current 30s is OK but maintainer specified 1min target), and a manual refresh button. Reference design: Claude.ai's own `claude.ai/settings/usage` page — current session bar with "Resets in 1hr 6min", weekly all-models bar with "Resets Sun 9:00 PM", per-model bar (Sonnet only), additional features (routine runs), usage credits + monthly spend limit + auto-reload toggle. - **Why deferred.** v0.3.0/v0.4.x ships the dashboard frame but `provider.quotaStatus()` returns `null` in all three v0.1 plugins (anthropic / openai / mistral). The ratifying spec in ADR 0004 Amendment 2 punts `quotaStatus()` to v1.x ("soft trigger reactivation") — this dashboard ask is the **operator-facing reason** that work would land. - **What this requires.** Per-provider plugin work + dashboard.html UI work + audit-query.mjs aggregation: