login.html/login.js: the two reset views are gone along with the reset-token handling, and the sign-in form now says which password to type - "your Windows password, the same one you use to sign in to your computer" - using the .hint class the page already had, so no new CSS and no new literal. "Forgot password?" is KEPT and points at https://primecontrols.okta.com/. An earlier draft of this task deleted the link and I proposed a plain "contact IT" sentence instead; Okta is the better answer, and with no app password and no break-glass it is the only recovery path that exists. Three details that would each have broken it: - The old click handler on #forgot-link called preventDefault() to swap views. Left in place it would have silently swallowed the navigation, so the link would look right and do nothing. There is now deliberately no handler, and login.js says why so nobody adds one back. - target="_blank" without rel="noopener noreferrer" hands the opened page a window.opener handle back to the login page. - Worth recording since it was checked rather than assumed: the CSP allows this. form-action 'self' governs form submission, not link navigation, and no navigate-to directive is set - so a plain <a href> off-origin is fine and the nginx config needs no change. login.js also handles 503 distinctly now. T10.2 made that mean "the directory is unreachable or misconfigured", which is our fault - showing "invalid password" would send people hunting for a password they no longer have while a deploy is broken. Also removed, because T10.3 deleted the endpoints behind them and leaving them would have produced visible 404s rather than dead-but-harmless markup: auth-guard.js the whole change-password dialog (POST /api/auth/password) wp-sidenav.js the "Password / Change your password" menu entry that opened it users.js the per-row "Reset password" action users.js the password field in the create-account form - NewUserIn no users.html longer accepts one, so the form was posting a rejected field The self-row placeholder button pointed at a top-bar Password link that no longer exists; it is now a plain "you" marker. Verified: node --check passes on all four touched JS files; the only password references left in html/ are the sign-in form and the SMTP config in admin.js, which is unrelated and stays. Logged BL-027 rather than acted on: the Okta URL is the first sign of an Okta tenant on this estate, which means an OIDC flow is available in principle and would remove the domain-lockout hazard that forced AUTH_MAX_ATTEMPTS to 2. D13 was decided and reaffirmed and T10.1-T10.4 are built, so swapping the mechanism mid-wave is the reordering CLAUDE.md forbids. Recording is not reopening. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
113 lines
4.8 KiB
HTML
113 lines
4.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>User Directory — 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 — everything structural is in console.css.
|
|
|
|
The directory is one wide table, so the column exceptions live here: email is
|
|
the one cell long enough to stretch a row, and the two role dropdowns need
|
|
room for "Assistant Project Manager" without pushing Actions off screen. */
|
|
#users-table table td:nth-child(3){ max-width:230px; overflow:hidden; text-overflow:ellipsis; }
|
|
#users-banner:not(:empty), #scope-banner:not(:empty){ margin-bottom:var(--s3); }
|
|
/* The create form is a lot of fields; let them wrap and
|
|
let the project picker take a full row of its own. */
|
|
#nu-projects{ margin-top:var(--s2); }
|
|
#nu-projects .pickrow{ padding:var(--s1) var(--s1); }
|
|
/* A manager with one project doesn't need a scrolling picker; a manager with
|
|
thirty does, and it must not push the Create button below the fold. */
|
|
#nu-project-list{ max-height:200px; overflow:auto; border:1px solid var(--border); }
|
|
.whoami-chip{ font-size:12px; color:var(--muted); }
|
|
.whoami-chip strong{ color:var(--text); }
|
|
</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">| User Directory</span></span>
|
|
</a>
|
|
</header>
|
|
|
|
<div class="wrap" id="users-main" style="display:none">
|
|
<div class="row" style="justify-content:space-between; margin-bottom:var(--s5)">
|
|
<div>
|
|
<h1>User Directory</h1>
|
|
<div class="sub" style="margin:0" id="dir-sub">The people on your projects — who they are, and how to reach them.</div>
|
|
</div>
|
|
<div class="row"><a class="home" href="index.html">← Site</a></div>
|
|
</div>
|
|
|
|
<!-- WHAT YOU MAY DO HERE (rendered from GET /api/auth/user-scope) -->
|
|
<div id="scope-banner"></div>
|
|
|
|
<!-- THE DIRECTORY -->
|
|
<div class="card">
|
|
<h2>People</h2>
|
|
<div class="sub" id="people-sub"></div>
|
|
<div class="toolbar">
|
|
<button onclick="loadUsers()">Refresh</button>
|
|
<input id="user-search" placeholder="Search name / username / email / job function…" oninput="renderUsers()">
|
|
<select id="user-filter" onchange="renderUsers()">
|
|
<option value="">Everyone</option>
|
|
<option value="active">Active only</option>
|
|
<option value="disabled">Disabled only</option>
|
|
<option value="mine">Accounts I manage</option>
|
|
</select>
|
|
</div>
|
|
<div id="users-banner"></div>
|
|
<div id="users-table"><div class="note">Loading…</div></div>
|
|
</div>
|
|
|
|
<!-- ADD A USER (managers only; hidden otherwise) -->
|
|
<div class="card" id="create-card" style="display:none">
|
|
<h2>Add a user</h2>
|
|
<div class="sub" id="create-sub"></div>
|
|
<div class="urow">
|
|
<input id="nu-username" placeholder="Username *" autocomplete="off">
|
|
<input id="nu-fullname" placeholder="Full name" autocomplete="off">
|
|
<input id="nu-email" placeholder="Email" autocomplete="off">
|
|
<select id="nu-role" title="Permissions — what this account may do"></select>
|
|
<select id="nu-project-role" title="Job function on the project"></select>
|
|
</div>
|
|
<div id="nu-projects">
|
|
<div class="note" id="nu-projects-label" style="margin-bottom:var(--s1)"></div>
|
|
<div id="nu-project-list"></div>
|
|
</div>
|
|
<div class="row" style="margin-top:var(--s3)">
|
|
<button class="primary" onclick="createUser()">Create user</button>
|
|
<span id="users-create-msg" class="note" style="margin:0"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="console-util.js"></script>
|
|
<script src="wp-dialog.js"></script>
|
|
<script src="users.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>
|