From 39687bc8a95ffdedda72a64e7652da4ad132c06e Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:46:30 +0200 Subject: [PATCH] fix: fix unicode in roles, add password confirmation field - RolesTab: wrap \u00b7 in JS expression {'\u00b7'} so JSX renders the middle dot correctly instead of literal backslash-u sequence - UsersTab: add confirm password field with mismatch validation, hint text for password policy, and reset on cancel/success - UserManagement.module.css: add .hintText style for password policy hint Co-Authored-By: Claude Sonnet 4.6 --- ui/src/pages/Admin/RolesTab.tsx | 2 +- ui/src/pages/Admin/UserManagement.module.css | 5 +++ ui/src/pages/Admin/UsersTab.tsx | 33 +++++++++++++++----- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/ui/src/pages/Admin/RolesTab.tsx b/ui/src/pages/Admin/RolesTab.tsx index 514551fb..c616cc31 100644 --- a/ui/src/pages/Admin/RolesTab.tsx +++ b/ui/src/pages/Admin/RolesTab.tsx @@ -179,7 +179,7 @@ export default function RolesTab({ highlightId, onHighlightConsumed }: { highlig )}
- {role.description || '\u2014'} \u00b7{' '} + {role.description || '\u2014'} {'\u00b7'}{' '} {getAssignmentCount(role)} assignments
diff --git a/ui/src/pages/Admin/UserManagement.module.css b/ui/src/pages/Admin/UserManagement.module.css index b4864871..d2b0b423 100644 --- a/ui/src/pages/Admin/UserManagement.module.css +++ b/ui/src/pages/Admin/UserManagement.module.css @@ -156,3 +156,8 @@ color: var(--error); font-size: 12px; } + +.hintText { + font-size: 12px; + color: var(--text-muted); +} diff --git a/ui/src/pages/Admin/UsersTab.tsx b/ui/src/pages/Admin/UsersTab.tsx index 34dbc278..c30808cb 100644 --- a/ui/src/pages/Admin/UsersTab.tsx +++ b/ui/src/pages/Admin/UsersTab.tsx @@ -67,8 +67,11 @@ export default function UsersTab({ highlightId, onHighlightConsumed }: { highlig const [newDisplay, setNewDisplay] = useState(''); const [newEmail, setNewEmail] = useState(''); const [newPassword, setNewPassword] = useState(''); + const [newPasswordConfirm, setNewPasswordConfirm] = useState(''); const [newProvider, setNewProvider] = useState<'local' | 'oidc'>('local'); + const passwordMismatch = newPassword.length > 0 && newPasswordConfirm.length > 0 && newPassword !== newPasswordConfirm; + // Password reset state const [resettingPassword, setResettingPassword] = useState(false); const [newPw, setNewPw] = useState(''); @@ -145,6 +148,7 @@ export default function UsersTab({ highlightId, onHighlightConsumed }: { highlig setNewDisplay(''); setNewEmail(''); setNewPassword(''); + setNewPasswordConfirm(''); setNewProvider('local'); }, onError: (err: unknown) => { @@ -241,12 +245,24 @@ export default function UsersTab({ highlightId, onHighlightConsumed }: { highlig onChange={(e) => setNewEmail(e.target.value)} /> {newProvider === 'local' && ( - setNewPassword(e.target.value)} - /> + <> + setNewPassword(e.target.value)} + /> + setNewPasswordConfirm(e.target.value)} + /> + {passwordMismatch && ( + Passwords do not match + )} + Min 12 chars, 3 of 4: uppercase, lowercase, number, special + )} {newProvider === 'oidc' && ( @@ -258,7 +274,7 @@ export default function UsersTab({ highlightId, onHighlightConsumed }: { highlig @@ -270,7 +286,8 @@ export default function UsersTab({ highlightId, onHighlightConsumed }: { highlig disabled={ !newUsername.trim() || (newProvider === 'local' && !newPassword.trim()) || - duplicateUsername + duplicateUsername || + passwordMismatch } > Create