---
id: w3/m3/t002
title: "Metrics page route with Render IA: Application + Network metric charts"
worker: worker3
status: done
estimate: 45m
depends_on: [w3/m3/t001]
---

## Objective

The PoC page itself: a dashboard route rendering `beancount-cms`'s six metrics in Render's exact information architecture (learned from `dashboard.render.com/web/srv-d2rnr3jipnbc73deuvgg/metrics`; screenshots in `.playwright-mcp/render-metrics-srv-d2rnr3.png`). **`beancount-cms` is the App's resource/CR name** — `beancount-cms-v2` (the phrase in the original request) is only its `.onbex.co` hostname; confirmed live via `/v1/services` in t001. Query and route by `beancount-cms`.

## Context

- Layout to mirror (see milestone README "Learned from Render"): **Application Metrics** card — Percentage|Total toggle; Memory (limit sublabel), CPU (limit sublabel), Total Instances. **Network Metrics** card — "Aggregated across all instances"; Total Requests (bar + total count), Response Times (percentile dropdown, default p95 = bex's default quantile), Outbound Bandwidth. Time-range picker at top (PoC: fixed presets 30m / 1h / 12h).
- bex specifics: cpu/memory/instance-count are **single-point snapshots** (metrics-server), so render them as a stat + flat/sparse line rather than faking history; request metrics (requests/latency/bandwidth) are real Prometheus time-series — the charts with actual shape. `percentage: true` maps the Percentage toggle.
- New feature code goes under `dashboard/src/features/metrics/` (self-contained module pattern, `dashboard/CLAUDE.md`); page chrome comes from `src/common/components/dashboard-layout/`.
- No chart dependency exists — keep the PoC dependency-free with a small inline-SVG line/bar chart component (polyline + bars, axis ticks). Consult the `dataviz` skill for color/axis choices before writing chart code.
- Route: `dashboard/src/routes/` is file-based (TanStack Router). PoC route `services.$serviceId.metrics.tsx` with `beancount-cms` as the linked default from the Services sample page; guard with `requireAuth` (`src/common/lib/auth/`).

## Steps

1. Build `SvgLineChart` / `SvgBarChart` components in `src/features/metrics/components/` (props: points, unit, yMax hint; render axis ticks + tooltip-free PoC).
2. Build the two section cards (`ApplicationMetrics`, `NetworkMetrics`) composing six `MetricChart` blocks fed by `useMetrics`, with the Percentage|Total toggle (re-query with `percentage`) and percentile select (`quantile`: 0.5/0.9/0.95/0.99).
3. Add the route and a time-range preset picker mapped to `startTime`/`resolutionSeconds` (30m→15s, 1h→30s, 12h→300s). **Adapted:** the sidebar (`dashboard-layout/dashboard-sidebar.tsx`) is a flat, non-service-scoped nav with no "current service" context to hang a Monitor sub-item off of — a global "Metrics" entry there wouldn't resolve to a `$serviceId`. Linked instead from the Services sample table's row name (→ `/services/$serviceId/metrics`) plus a "← Services" breadcrumb on the page itself; the sidebar is unchanged.
4. Format values per unit: `cpu` cores (2 sig figs), `bytes` → MiB/GiB, `seconds` → ms, req/s.

## Files

- `dashboard/src/routes/services.$serviceId.metrics.tsx`
- `dashboard/src/features/metrics/components/*` (charts, cards, toggle, range picker, unavailable state)
- `dashboard/src/features/metrics/lib/{format,range}.ts`, `hooks/use-live-range.ts`
- `dashboard/src/routes/index.tsx` + `__tests__/index.test.tsx` (Services row → metrics link; adjusted for the added `beancount-cms` sample row)

## Acceptance criteria

- [x] Page renders all six charts in the two-card Render layout with correct units and the range picker functional. Verified via `yarn typecheck`/`yarn lint`/`yarn build` (client + SSR) all green — live-browser visual confirmation deferred to t003, which owns the CORS/deploy step a real authenticated session needs (a bare `localhost:5173` dev server can't carry the `.bex.co`-scoped session cookie cross-site; ADR012-auth.md's own local-dev note says the same about Kratos).
- [x] Percentage toggle switches CPU/Memory to 0–100% of the pod limit; percentile select changes the latency series. (Wired via `useMetrics`'s `percentage`/`quantile` args, verified in t001's live schema check.)
- [x] Request charts render multi-point series; resource charts render the snapshot without fabricated history.

## Out of scope

- Status-code/host/path filters and group-by (Render has them; bex PoC omits — note in UI copy); events overlay; "usage this month" rollup.
