251 buttons across the 7 pages, counted in the browser with every wizard step,
creator section and tool panel forced visible. Two thirds of them are
display:none at load, so a static grep sees about eighty and misses the rest.
FOUR ROLES, defined once in theme-light.css as --wp-btn-*, and no fifth:
primary the one action the screen exists for. Filled accent.
secondary every other real action. White, --border-strong, accent on hover.
tertiary navigating or undoing. No fill, no border, accent text.
danger destructive. Outlined red; filled red only where the control is too
small for an outline to read - the 28px x on a sequence row.
Every button class is mapped to a role in docs/reference/tokens.md section 12.
No value is new: these are the fills the sheets already rendered, given one
definition so that "primary" means one thing.
GREEN IS A STATUS COLOUR AND NO LONGER FILLS A BUTTON. A5 names two green action
buttons; there are four. .use-btn and the launcher's completed-SOP card button
never render green in the default fixture, so the review could not have seen
them - the SOP has to be finished and a suggested value has to be offered first.
.nav-btn.primary "SOP complete" wizard
.btn.btn-generate "Save & view" creator
.use-btn creator
.card.complete .card-button launcher
The green did not go anywhere. .cstatus button.on-cleared, .toggle-btn.enabled,
.wp-nav-dot.ok, .rb-ready, .badge-R and the launcher card's own left border and
status line all still carry it, and every one of those is a state rather than an
action. The launcher card in particular still says "complete" twice after this
change; it just no longer says it on the button.
SENTENCE CASE, applied to buttons and field labels only, which is the scope A5
sets. First word capitalised, the rest lowercased, acronyms and external proper
nouns left alone (SOP, QC, WP, UPN, PM/APM/CM/QM, PDF, JSON, CSV, BIM, MIMO,
Excel, Acumatica).
~30 button labels across launcher, wizard, creator, admin and two scripts
46 field labels
text-transform:uppercase removed from 4 rules - .btn and .add-btn (creator
buttons), label and .cmt-namebar label (creator field labels)
Labels carrying markup - a .req asterisk, a .help-tip chip - had only their text
nodes transformed, so the markup survives and "first word" means the first word
of the label rather than of each fragment. The creator's mono face, 10px size and
tracking are its idiom and are untouched; only the forced uppercase goes.
help.js was updated too. It names "Load Sample" and "SOP Complete" in prose, so
renaming the buttons without it would have left the help centre describing
controls that no longer exist. That coupling is the only place in the app where
button text is referenced by name.
Verified by re-running the inventory: 0 green action buttons, 0 uppercase button
labels, 251 buttons still present - nothing was lost in the rename.
console.css card headers are unchanged, confirmed by diff: the only six lines
this task touches in that file are token substitutions on button/button.primary/
button.danger, none of them within twenty lines of .card h2.
f_items 5 FIXED / F6 REPRODUCES. browser_check 71/71.
Left alone and logged: .step-tab is still uppercase (BL-015) - it is a stepper
tab, neither a button nor a field label, and A4/S9 rebuild the stepper. Table
headers, section eyebrows and headings keep their case throughout. BL-008 and
BL-009 were re-targeted from T3.5 to wave 9: both are colour merges on a field
fill and a status pill, and this task is scoped to buttons.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
224 lines
11 KiB
HTML
224 lines
11 KiB
HTML
<!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>
|
||
<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, #usage-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){
|
||
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, #usage-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 doesn’t 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 & 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 & 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 & comments</h2>
|
||
<div class="sub">Every comment submitted across the suite — who wrote it, what they said, and where they were (page & 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>
|
||
|
||
<!-- USAGE LOGS -->
|
||
<div class="card">
|
||
<h2>Usage logs</h2>
|
||
<div class="sub">Engagement recorded by the suite — sessions, step views, and actions. Note: stored locally per browser, so this reflects activity on <strong>this</strong> machine.</div>
|
||
<div class="toolbar">
|
||
<button onclick="loadUsage()">Refresh</button>
|
||
<button onclick="downloadUsage()">Download JSON</button>
|
||
</div>
|
||
<div id="usage-admin" class="note">Click refresh to load.</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="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>
|