Files
Project-SDE-WP-Suite/html/admin.html
Matt Mabrey b2a083ac7c T11.6: retire the per-browser Usage report (CR-019)
Removes the old "Usage logs" card from admin.html/admin.js (the
usage-admin panel + loadUsage(), D5/T7.10) now that T11.5's real,
server-side Activity & usage card exists and reads real per-user data
instead of per-browser localStorage.

wp-usage.js is deleted outright, along with its three <script> includes
(admin.html, wp-creation-index.html, work-package-suite.html) - it had
no reader left once the panel above it was removed (the "download the
full event log" button lived only in that panel), and per the original
CR-019 decision record it was never reliably tied to a real identity,
so it was never a candidate data source for the new report either.

Its two call sites (wp-creation-app.js, work-package-suite-app.js) keep
a local track() function as a documented no-op rather than having each
of their ~45 individual track('event', ...) call sites deleted one at a
time - that would be a much larger, riskier diff for the same outcome
(no data is recorded either way), and each call site still marks what
was worth recording if usage analytics are ever rebuilt server-side.
work-package-suite-app.js's dwell-timer plumbing (_stepEnter /
trackStepDwell), which only ever fed track(), was left in place for the
same reason: inert, not broken.

Also removes tests/usage_check.py, which tested exactly the retired
feature, and updates its line in docs/reference/file-map.md to point at
the 2026-09-17 decision record instead.

Verified:
  - grep across the whole repo for WPUsage / wp-usage.js / usage-admin /
    usage_check: no live references remain, only explanatory comments
    and planning docs (decisions-2026-09-17.md, wave-11.md) that
    describe the removal itself
  - node --check on all three touched .js files: no syntax errors
  - full backend smoke test (27/27) and seed_demo.py still pass
  - tests/baseline_shots.py --pages admin,creator,sop at 390px/1440px,
    run locally: all three pages render with no new JS errors (the one
    "beforeunload" log line on sop/creator at 1440px is pre-existing
    harness noise from wp-autosave.js's unsaved-work guard, unrelated
    to this change) and no horizontal overflow; refreshed baseline
    screenshots committed alongside this change
2026-09-23 14:46:32 -07:00

251 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Console — Work Package Suite</title>
<script src="auth-guard.js"></script>
<!-- Date/number formatting. Must parse BEFORE the app scripts: they format
timestamps during their own boot. -->
<script src="wp-format.js"></script>
<!-- Addressable state (S3). Parses before the app scripts, which read the URL
during their own boot. -->
<script src="wp-url.js"></script>
<link rel="icon" href="favicon.ico" sizes="any">
<link rel="manifest" href="manifest.webmanifest">
<meta name="theme-color" content="#161616">
<link rel="stylesheet" href="theme-light.css">
<link rel="stylesheet" href="wp-chrome.css">
<link rel="stylesheet" href="console.css">
<link rel="stylesheet" href="wp-sidenav.css">
<style>
/* Page-specific only — the tokens, cards, controls, tables, banners and modal
live in console.css, shared with the User Directory. What stays here is what
only this page has: the per-card scroll boxes admin.js paints tables into, the
column exceptions for those tables, and the admins-only notice.
These are addressed by ID because admin.js emits the tables without per-cell
classes. */
/* These start life as empty divs that admin.js fills on demand, so they only earn
their gap once they are actually saying something. */
#projects-banner:not(:empty), #defmem-banner:not(:empty){ margin-bottom:var(--s3); }
/* 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, #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),
#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
read, so they wrap inside a sane width instead of truncating. */
#comments-admin table td:nth-child(5){ white-space:normal; min-width:260px; max-width:640px; }
#audit-admin table td:nth-child(6){ white-space:normal; max-width:420px; }
/* The denial notice is a sentence, not a table — don't stretch it to 1240px. */
#admin-denied .card{ max-width:560px; }
.gate-box input{ width:100%; height:var(--ctl); padding:0 var(--s3); font:inherit; font-size:14px;
border:1px solid var(--border-strong); border-radius:0; margin-bottom:var(--s3); }
@media (max-width:900px){
#comments-admin, #audit-admin, #notif-box, #activity-admin, #projects-table, #defmem-table{
max-height:none; }
}
</style>
</head>
<body>
<!-- SHARED DARK APP BAR -->
<header class="wp-appbar">
<a href="index.html" class="wp-appbar-brand" title="Back to site">
<span class="wp-logo-chip"><img src="prime-controls-logo.jpg" alt="Prime Controls"></span>
<span class="wp-appbar-title">Work Package Suite <span class="wp-appbar-sub">| Admin Console</span></span>
</a>
</header>
<!-- ADMINS ONLY (shown if the signed-in account isn't an admin) -->
<div class="wrap" id="admin-denied" style="display:none">
<div class="card">
<h2>Admins only</h2>
<p class="sub">Your account doesnt have admin access. Sign in with an admin account, or ask an administrator to grant you the admin role.</p>
<div class="row"><a class="home" href="index.html">← Back to site</a> <button onclick="wpLogout()">Sign out</button></div>
</div>
</div>
<!-- CONSOLE -->
<div class="wrap" id="admin-main" style="display:none">
<div class="row" style="justify-content:space-between; margin-bottom:var(--s5)">
<div><h1>Admin Console</h1><div class="sub" style="margin:0">Stack diagnostics &amp; tests · talks to <code>/api</code> on this host</div></div>
<div class="row"><a class="home" href="index.html">← Site</a></div>
</div>
<!-- CONNECTIVITY -->
<div class="card">
<h2>API connectivity</h2>
<div class="toolbar"><button class="primary" onclick="checkHealth()">Check /api/health</button></div>
<div class="banner" id="health-banner"></div>
</div>
<!-- USER ADMINISTRATION — moved out to its own page -->
<div class="card">
<h2>User accounts</h2>
<div class="sub">Login accounts, permissions and project access now live on the
<strong>User Directory</strong> page. They moved because user administration is no longer
admin-only: a <strong>Project Super User</strong> creates and manages the accounts on the
projects they administer, and they must never be sent through this console to do it.</div>
<div class="toolbar"><a class="home" href="users.html"><button class="primary">Open the user directory →</button></a></div>
</div>
<!-- PROJECTS (ARCHIVE / UNARCHIVE) -->
<div class="card">
<h2>Projects</h2>
<div class="sub">Archiving a project hides it from every picker, switcher and search, and freezes it
read-only — nothing is deleted and every work package, SOP and comment is kept exactly as it is.
Unarchive here to bring it back; the project returns unchanged.</div>
<div class="toolbar">
<button onclick="loadProjects()">Refresh projects</button>
<label class="chk"><input type="checkbox" id="proj-show-archived" onchange="renderProjects()"> Show archived</label>
<input id="proj-search" placeholder="Search name / number / client…" oninput="renderProjects()">
</div>
<div id="projects-banner"></div>
<div id="projects-table"><div class="note">Click “Refresh projects” to load.</div></div>
</div>
<!-- DEFAULT MEMBERS ON NEW PROJECTS -->
<div class="card">
<h2>Default members on new projects</h2>
<div class="sub">Everyone flagged here is added automatically to every project created from now on,
with the role chosen here. It does not touch projects that already exist — for those, use
<strong>Project access</strong> on the <a class="home" href="users.html">User Directory</a>.
Administrators are listed with nothing to set: they already reach every project. This card stays
in the console because it is a rule about <em>every</em> future project, including the ones a
Project Super User has no part in — so only an admin sets it.</div>
<div class="toolbar"><button onclick="loadDefaultMembers()">Refresh</button></div>
<div id="defmem-banner"></div>
<div id="defmem-table"><div class="note">Click “Refresh” to load.</div></div>
</div>
<!-- FEATURE FLAGS -->
<div class="card">
<h2>Features</h2>
<div class="sub">Switches that change what the suite offers on every project.</div>
<div id="features-box" class="note">Loading…</div>
</div>
<!-- NOTIFICATIONS / EMAIL -->
<div class="card">
<h2>Notifications &amp; email</h2>
<div class="sub">Email notifications for work-package assignments, and self-service password resets. <strong>Off by default</strong> — turn this on only once SMTP is configured. The SMTP <strong>password</strong> is read from the <code>SMTP_PASSWORD</code> environment variable and is never stored here.</div>
<div id="settings-box" class="note">Loading…</div>
<div id="notif-box" class="note" style="margin-top:14px"></div>
</div>
<!-- ALL FEEDBACK / COMMENTS -->
<div class="card">
<h2>All feedback &amp; comments</h2>
<div class="sub">Every comment submitted across the suite — who wrote it, what they said, and where they were (page &amp; step) when they commented.</div>
<div class="toolbar">
<button onclick="loadComments()">Refresh comments</button>
<select id="cmt-filter" onchange="renderComments()"><option value="">All sources</option></select>
<input id="cmt-search" placeholder="Search text / author…" oninput="renderComments()">
</div>
<div id="comments-admin" class="note">Click refresh to load.</div>
</div>
<!-- ACTIVITY LOG (AUDIT TRAIL) -->
<div class="card">
<h2>Activity log</h2>
<div class="sub">Who changed what, and when — across projects, SOPs, work packages, and user accounts. Stored server-side in the shared database.</div>
<div class="toolbar">
<button onclick="loadAudit()">Refresh</button>
<select id="audit-type" onchange="renderAudit()">
<option value="">All types</option>
<option value="wp">Work packages</option>
<option value="sop">SOPs</option>
<option value="project">Projects</option>
<option value="user">User accounts</option>
</select>
<input id="audit-search" placeholder="Search actor / action / item…" oninput="renderAudit()">
</div>
<div id="audit-admin" class="note">Click refresh to load.</div>
</div>
<!-- ACTIVITY &amp; USAGE (CR-019) -->
<div class="card">
<h2>Activity &amp; 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>
<!-- DB SNAPSHOT -->
<div class="card">
<h2>Database snapshot</h2>
<div class="toolbar"><button onclick="snapshot()">Refresh counts</button></div>
<div id="snapshot-out" class="note">Click refresh to read row counts from SQL via the API.</div>
</div>
<!-- SMOKE TEST -->
<div class="card">
<h2>End-to-end smoke test</h2>
<div class="sub">Creates a throwaway project, exercises the issue gate / status / metrics / comments, then deletes it (cascade). Mirrors <code>server/smoketest.py</code>.</div>
<div class="toolbar"><button class="primary" onclick="runSmokeTest()">Run smoke test</button></div>
<pre class="out" id="smoke-out">Ready.</pre>
</div>
<!-- DEMO DATA -->
<div class="card">
<h2>Demo data</h2>
<div class="sub">Seed a realistic <code>DEMO</code> project (SOP + a spread of Work Packages) into SQL, or remove all <code>DEMO-</code>/<code>SMOKE-</code> projects.</div>
<div class="toolbar">
<button class="primary" onclick="seedDemo()">Seed demo project</button>
<button class="danger" onclick="cleanDemo()">Clean DEMO / SMOKE projects</button>
</div>
<pre class="out" id="demo-out">Ready.</pre>
<div class="note">Note: the seeded <strong>project</strong> appears in the home picker; its SOP/WPs live in SQL but won't render in the Creator/Dashboard until the front end is wired to the API (Phase 2).</div>
</div>
</div>
<script src="console-util.js"></script>
<script src="wp-dialog.js"></script>
<script src="admin.js"></script>
<!-- The app bar's project switcher reads ProjectData; without this the bar on this
page could never show a project and always read "Select a project" (F1). Must
parse before wp-chrome.js, which reads it as it mounts. -->
<script src="project-data.js"></script>
<script src="wp-chrome.js"></script>
<script src="wp-sidenav.js"></script>
</body>
</html>