Nathan: the disclaimers are not necessary, especially now that reporting is in
place going forward — and then, on the status line under it: remove that too.
Gone from both apps: the explanatory note paragraph, its per-metric config
strings, the status line ("55 days - 2 not measured"), the now-orphaned CSS for
both, and the gaps counter that only fed the status line. The period toggles
still say what range is on screen, so no information is lost.
The harness assertions that described the status line are replaced by stronger
ones: the rendered chart must contain no caveat text at all, and the 30-day view
must still end at the most recent measured day.
Nathan: the members axis should be whole numbers, since it counts people. It
was drawing fractional ticks (19.17 people) because the 1.08 headroom factor
fed straight into the label math.
Ticks now come from a step, with the axis top set to step x 4, so labels land
on round numbers:
- counts (members, requests): whole numbers, rounded up to a half power of ten
(requests read 3,000, not 2,888). Never below 1, so a near-empty series still
gets an integer axis rather than fractions.
- money: the 1/2/5 x 10^k rule, so ticks read $10.00 rather than $4.92.
Rendered now: members 0 | 20 | 40 | 60 | 80; requests 0 | 3,000 | 6,000 |
9,000 | 12,000; spend $0.00 | $10.00 | $20.00 | $30.00 | $40.00.
The node harness gained assertions for this, checked geometrically (tick values
ascend while their screen positions descend, the zero tick sits lowest, ticks
are evenly spaced) rather than assuming a text order, so a fractional
people-axis or a misplaced label fails before a deploy.
Nathan asked to see a chart. The portal now owns a daily metrics series
(metrics_daily, written 03:15 UTC by the portal's own scheduler); this is the
first surface to read it.
- New route /api/metrics proxies the portal's /admin/metrics/series (admin
token stays server-side, same pattern as /api/model-usage).
- New Trends tab, added to switchView in the same edit (every view toggled
there must be shown/hidden there). Lazy: the series loads on first visit, so
the panel's initial load is unchanged.
- Metric toggle (Members / Spend / Requests) and period toggle (30 / 90 / all
time). Fetched once at the widest range and re-rendered client-side, so the
toggles never refetch.
- Inline SVG, hand-rolled: no chart library, no CDN, no external dependency.
Colours come from the panel's existing CSS vars, so it matches the rest.
- Gaps are drawn as gaps. A NULL metric means "not measured" and the line
deliberately breaks rather than drawing through a day we never recorded
(backfilled members_active is NULL every day, by design). The sub-line says
how many days are unmeasured.
- Members shows the cumulative total; spend and requests are per-day bars.
Verified against the live 55-day series with a node harness running the real
page functions: 22 checks, including that a NULL breaks the line into two runs,
that no NaN reaches the markup, that the toggles do not refetch, and that the
SVG carries no hardcoded hex.
No manifest change yet — proxyAuth stays active, so get_identity() still works
via header. OIDC routes only activate when CLOUDRON_OIDC_* env vars are present.
Validates id_token signature (JWKS), iss/aud/nonce, exp/nbf.