Matt Mabrey a6c3fbfe50 T11.5: admin console Activity & usage card (CR-019)
New card in admin.html/admin.js, above the old per-browser Usage logs
card (which T11.6 retires next). Filters (date range, project, user,
tool) drive GET /api/usage/summary; two export buttons call
GET /api/usage/export (raw / sanitized) and save the CSV via a Blob,
same download pattern wp-usage.js already uses.

Access: the card lives inside admin.html, already gated admin-only
client-side by gateByRole() (unchanged) - a non-admin never sees the
card. The underlying API is gated server-side by require_user_manager
regardless (admin or project_super_user on >=1 project), independent
of and stricter than the client-side gate, so a non-admin request is
refused even if someone reached the endpoint directly.

Accessibility (C1): every control is a real <input>/<select>/<button>,
keyboard-operable. Status/export banners use the existing '.banner' /
'*-banner' id convention, which console-util.js's MutationObserver
already turns into aria-live (role=status, or role=alert on a '.bad'
banner) - no new announcement plumbing needed. No new CSS: reuses
.toolbar/.banner/.card/.row/.kv/.users, so nothing here adds a second
token source (the token rule).

Verified so far:
  - node --check html/admin.js: no syntax errors
  - every id admin.js's new code references exists in admin.html
    (scripted diff against the full getElementById/id= sets)
  - live-server check: GET /api/usage/summary with the exact
    (possibly-empty) query string _activityFilters() builds returns
    the {active_users, per_user_last_active, by_tool, event_count}
    shape renderActivity() expects; project_id filter narrows
    correctly; GET /api/usage/export?sanitize=true returns
    text/csv with the expected header row
  - full smoke test + seed_demo.py still pass

NOT yet verified: rendering at 390px/1440px with before/after
screenshots (CLAUDE.md verification step). This sandbox has no
headless-capable browser (no chromium/msedge on PATH) and the
playwright/chromium download is blocked by this environment's
network allowlist, so tests/cdp.py's harness can't run here. Deferred
to T11.7, same as wave 10's browser checks — flagging rather than
skipping silently.
2026-09-23 12:31:55 -07:00
Description
No description provided
11 MiB
Languages
Python 49.3%
JavaScript 32.6%
CSS 8.9%
HTML 8.7%
Shell 0.4%