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.
This commit is contained in:
@@ -34,12 +34,12 @@
|
||||
/* Every container admin.js paints a table into is a scrollport of its own, so a
|
||||
sticky header always has something to stick to rather than sliding up behind
|
||||
the app bar. Same rule as console.css's .tscroll. */
|
||||
#comments-admin, #audit-admin, #notif-box, #usage-admin, #projects-table, #defmem-table{
|
||||
#comments-admin, #audit-admin, #notif-box, #usage-admin, #activity-admin, #projects-table, #defmem-table{
|
||||
overflow:auto; max-height:min(70vh,640px); overscroll-behavior:contain; }
|
||||
/* If admin.js wraps its table in its own .tscroll, the outer box steps aside so
|
||||
one table never ends up with two scrollbars. */
|
||||
#comments-admin:has(.tscroll), #audit-admin:has(.tscroll), #notif-box:has(.tscroll),
|
||||
#usage-admin:has(.tscroll), #projects-table:has(.tscroll), #defmem-table:has(.tscroll){
|
||||
#usage-admin:has(.tscroll), #activity-admin:has(.tscroll), #projects-table:has(.tscroll), #defmem-table:has(.tscroll){
|
||||
overflow:visible; max-height:none; }
|
||||
|
||||
/* Comment text and audit detail are the two columns you are actually here to
|
||||
@@ -53,7 +53,7 @@
|
||||
border:1px solid var(--border-strong); border-radius:0; margin-bottom:var(--s3); }
|
||||
|
||||
@media (max-width:900px){
|
||||
#comments-admin, #audit-admin, #notif-box, #usage-admin, #projects-table, #defmem-table{
|
||||
#comments-admin, #audit-admin, #notif-box, #usage-admin, #activity-admin, #projects-table, #defmem-table{
|
||||
max-height:none; }
|
||||
}
|
||||
</style>
|
||||
@@ -175,7 +175,41 @@
|
||||
<div id="audit-admin" class="note">Click refresh to load.</div>
|
||||
</div>
|
||||
|
||||
<!-- USAGE LOGS -->
|
||||
<!-- ACTIVITY & USAGE (CR-019) -->
|
||||
<div class="card">
|
||||
<h2>Activity & usage</h2>
|
||||
<div class="sub">Who is using the suite, which tools, and how often — recorded server-side on every
|
||||
sign-in and page open, kept indefinitely. Filter below, or export a CSV: raw (real usernames) for
|
||||
internal use, or sanitized (each user replaced with a stable, non-reversible id) for feeding into
|
||||
Power BI or another external reporting tool without carrying real identities.</div>
|
||||
<div class="toolbar">
|
||||
<label for="act-from">From</label>
|
||||
<input type="date" id="act-from" onchange="loadActivity()">
|
||||
<label for="act-to">To</label>
|
||||
<input type="date" id="act-to" onchange="loadActivity()">
|
||||
<select id="act-project" onchange="loadActivity()"><option value="">All projects</option></select>
|
||||
<input id="act-user" placeholder="Username…" oninput="loadActivity()">
|
||||
<select id="act-tool" onchange="loadActivity()">
|
||||
<option value="">All tools</option>
|
||||
<option value="launcher">Launcher</option>
|
||||
<option value="wizard">SOP wizard</option>
|
||||
<option value="creator">Work package creator</option>
|
||||
<option value="field_view">Field view</option>
|
||||
<option value="admin">Admin console</option>
|
||||
<option value="directory">User directory</option>
|
||||
</select>
|
||||
<button onclick="loadActivity()">Refresh</button>
|
||||
</div>
|
||||
<div id="activity-banner" class="banner" style="display:none"></div>
|
||||
<div id="activity-admin" class="note">Loading…</div>
|
||||
<div class="toolbar" style="margin-top:var(--s3)">
|
||||
<button onclick="exportActivity(false)">Download CSV (raw)</button>
|
||||
<button onclick="exportActivity(true)">Download CSV (sanitized)</button>
|
||||
</div>
|
||||
<div id="activity-export-banner" class="banner" style="display:none"></div>
|
||||
</div>
|
||||
|
||||
<!-- USAGE LOGS (per-browser — retired once the report above is confirmed, T11.6) -->
|
||||
<div class="card">
|
||||
<h2>Usage logs</h2>
|
||||
<div class="sub">Engagement recorded by both tools — the work package creator and the SOP wizard —
|
||||
|
||||
Reference in New Issue
Block a user