fix(web): password requirement indicators on registration and admin create-user

The /register checklist was dead markup: initPasswordValidation() was
only ever called on the setup page, so the requirement rows never
updated and the Register button stayed disabled forever — web
self-registration was effectively broken. The register Alpine component
now initializes it.

The validator is parameterized (field/submit ids, confirm + username/
email checks optional) so the admin Create User form reuses it: confirm
field, live requirement rows, and the Create button gated on all checks
passing. Requirements mirror the server's passwordcomplex rules
(8+ chars, upper/lower/number/special — the hardcoded defaults; server-
tuned rule values are a future nicety, the server remains the enforcer
either way).
This commit is contained in:
John O'Keefe committed 2026-10-13 12:38:26 -04:00
1 parent f3c9d4b4e8
commit bc6ce41762
7 files changed
+153 -124

No files matched your search

+32 -5
View File
@@ -5,7 +5,7 @@ package templates
// default — so creation stays available with self-service registration
// disabled without dominating the page.
templ adminCreateUserForm() {
<div id="create-user-card" class="card p-4 md:p-6 mb-6" x-cloak x-show="createOpen">
<div id="create-user-card" class="card p-4 md:p-6 mb-6" x-cloak x-show="createOpen" x-data="adminCreateUser">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center gap-2">
@Icon("user", "h-4 w-4")
@@ -18,15 +18,19 @@ templ adminCreateUserForm() {
<form hx-post="/api/auth/users" hx-target="#create-user-result" hx-swap="innerHTML" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div>
<label class="text-xs block mb-1" style="color: var(--text-secondary)">Username *</label>
<input type="text" name="username" required minlength="3" maxlength="50" class="input w-full"/>
<input type="text" id="username" name="username" required minlength="3" maxlength="50" class="input w-full"/>
</div>
<div>
<label class="text-xs block mb-1" style="color: var(--text-secondary)">Email *</label>
<input type="email" name="email" required class="input w-full"/>
<input type="email" id="email" name="email" required class="input w-full"/>
</div>
<div>
<label class="text-xs block mb-1" style="color: var(--text-secondary)">Password *</label>
<input type="password" name="password" required class="input w-full"/>
<input type="password" id="password" name="password" required class="input w-full"/>
</div>
<div>
<label class="text-xs block mb-1" style="color: var(--text-secondary)">Confirm password *</label>
<input type="password" id="cu-confirm-password" name="confirm_password" required class="input w-full"/>
</div>
<div>
<label class="text-xs block mb-1" style="color: var(--text-secondary)">First name</label>
@@ -43,8 +47,31 @@ templ adminCreateUserForm() {
<option value="admin">Admin</option>
</select>
</div>
<div class="sm:col-span-2 lg:col-span-3" style="color: var(--text-secondary)">
<p class="font-semibold mb-2 text-xs uppercase tracking-wide">Password Requirements</p>
<ul class="grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-1 text-xs">
<li id="req-length" class="flex items-center gap-2">
<span class="requirement-icon">○</span> At least 8 characters
</li>
<li id="req-upper" class="flex items-center gap-2">
<span class="requirement-icon">○</span> One uppercase letter
</li>
<li id="req-lower" class="flex items-center gap-2">
<span class="requirement-icon">○</span> One lowercase letter
</li>
<li id="req-number" class="flex items-center gap-2">
<span class="requirement-icon">○</span> One number
</li>
<li id="req-special" class="flex items-center gap-2">
<span class="requirement-icon">○</span> One special character
</li>
<li id="req-match" class="flex items-center gap-2">
<span class="requirement-icon">○</span> Passwords match
</li>
</ul>
</div>
<div class="sm:col-span-2 lg:col-span-3">
<button type="submit" class="btn btn-primary text-sm px-4 py-2">Create User</button>
<button type="submit" id="create-user-submit" class="btn btn-primary text-sm px-4 py-2 opacity-50 cursor-not-allowed" disabled>Create User</button>
</div>
</form>
<div id="create-user-result" class="text-sm mt-3"></div>