From 61eb442df56af93dd5ad3a19bd72098658fee223 Mon Sep 17 00:00:00 2001 From: Pierre Brisorgueil Date: Thu, 18 Jun 2026 15:32:47 +0200 Subject: [PATCH 1/2] feat(billing): surface capacity equivalences on the nav compute gauge Wire the (previously orphaned) BillingEquivalencesChipsComponent into the nav compute-gauge tooltip. When serverConfig.billing.equivalences is set, derive a human-readable remaining-capacity estimate per entry: count = floor(totalRemaining / unitCost), totalRemaining = max(0, (meterQuota + extrasRemaining) - meterUsed). Only consumption-scaled kinds (easy/hard) with a finite positive unitCost render; the unit-cost framing handles per-period and one-shot grants with no special branch. Config-driven, no-op when absent. Also adds a mount fade/scale-in and a low-remaining (>= 80%) pulse to the ring to make the gauge more noticeable; both respect prefers-reduced-motion. Tests (15 new) cover the derivation, filtering, one-shot grant, exhaustion and ring motion. Documents the equivalences contract in the module README. Closes #4349 --- src/modules/billing/README.md | 36 +++ .../billing.navComputeGauge.component.vue | 127 +++++++++- ...ng.navComputeGauge.component.unit.tests.js | 219 ++++++++++++++++++ 3 files changed, 372 insertions(+), 10 deletions(-) diff --git a/src/modules/billing/README.md b/src/modules/billing/README.md index 984f560e7..6cb3cccf7 100644 --- a/src/modules/billing/README.md +++ b/src/modules/billing/README.md @@ -49,3 +49,39 @@ Auto-colored progress bar (green/orange/red). Shows "Unlimited" for uncapped pla ```vue ``` + +### `` + +Sidenav compute-usage indicator (meter mode). A color-coded ring + `X% used` +label; the hover tooltip shows `used / total compute · resets `. The ring +fades/scales in on mount and pulses near exhaustion (≥ 80%) — both motions +respect `prefers-reduced-motion`. + +When the server config defines `billing.equivalences`, the tooltip also surfaces +a human-readable remaining-capacity estimate via ``. + +### `` + the `equivalences` contract + +Renders capacity chips (`{ kind, count, label }`) color-coded by kind +(`easy` → green, `hard` → amber, `feature` → brand). + +The nav gauge derives those chips from `serverConfig.billing.equivalences` — an +**opaque passthrough** from the backend auth config that **downstream projects +define** (the devkit ships only a neutral demo default): + +```js +// serverConfig.billing.equivalences (null / absent / [] → gauge shows raw units only) +[ + { kind: 'easy', unitCost: 200, label: 'easy operations' }, + { kind: 'hard', unitCost: 2000, label: 'heavy operations' }, +] +``` + +- `unitCost` = compute units consumed per ONE operation of that kind (finite, `> 0`). +- The gauge renders `count = floor(totalRemaining / unitCost)` per entry, where + `totalRemaining = max(0, (meterQuota + extrasRemaining) − meterUsed)`. +- Only the consumption-scaled kinds `easy` / `hard` are surfaced; entries with a + non-positive/non-finite `unitCost`, a non-string `label`, or any other kind are + dropped. The unit-cost framing means **per-period and one-shot grants need no + special handling** — for a one-shot grant, `totalRemaining` is simply the + remaining grant. diff --git a/src/modules/billing/components/billing.navComputeGauge.component.vue b/src/modules/billing/components/billing.navComputeGauge.component.vue index edbf2b295..04af9e179 100644 --- a/src/modules/billing/components/billing.navComputeGauge.component.vue +++ b/src/modules/billing/components/billing.navComputeGauge.component.vue @@ -5,11 +5,15 @@ nav items (icon in #prepend, title), only the icon is a colored circle reflecting consumption level. - Hover tooltip exposes the precise figures: "X / Y compute · resets ". + Hover tooltip exposes the precise figures: "X / Y compute · resets ", + plus optional capacity chips ("~N easy / ~M heavy ops") when the server config + defines billing.equivalences (rendered via BillingEquivalencesChipsComponent). Admin users see a permanent ∞ rainbow state (no quota applies). Click → navigates to /users/billing. Auto-fetches on mount + on window.focus. + The ring fades/scales in on mount and pulses when usage nears exhaustion + (>= 80%); both motions respect prefers-reduced-motion. Gate: hidden when not logged in or meterMode false. --> @@ -27,15 +31,16 @@ @@ -48,6 +53,11 @@ @@ -55,10 +65,13 @@