feat: improve account lockout UX with severity-aware error styling
Login errors now distinguish between wrong-password (red), progressive lockout warnings (amber, Lock icon), and temporary lockout (amber, Lock icon) based on the backend detail string. Removes the dead 423 branch from handleCredentialSubmit — account lockout is now returned as 401 with a descriptive detail message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1b868ba503
commit
863e9e2c45
@ -155,11 +155,10 @@ export default function LockScreen() {
|
|||||||
// mfaSetupRequired / mfaRequired handled by hook state → activeMode switches automatically
|
// mfaSetupRequired / mfaRequired handled by hook state → activeMode switches automatically
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const status = error?.response?.status;
|
const status = error?.response?.status;
|
||||||
if (status === 423) {
|
if (status === 403) {
|
||||||
setLoginError(error.response.data?.detail || 'Account locked. Try again later.');
|
|
||||||
} else if (status === 403) {
|
|
||||||
setLoginError(error.response.data?.detail || 'Account is disabled. Contact an administrator.');
|
setLoginError(error.response.data?.detail || 'Account is disabled. Contact an administrator.');
|
||||||
} else {
|
} else {
|
||||||
|
// 401 covers both wrong password and account lockout (backend embeds detail string)
|
||||||
setLoginError(getErrorMessage(error, 'Invalid username or password'));
|
setLoginError(getErrorMessage(error, 'Invalid username or password'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -519,18 +518,28 @@ export default function LockScreen() {
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{loginError && (
|
{loginError && (() => {
|
||||||
<div
|
const isLockWarning =
|
||||||
role="alert"
|
loginError.includes('remaining') || loginError.includes('temporarily locked');
|
||||||
className={cn(
|
return (
|
||||||
'flex items-center gap-2 rounded-md border border-red-500/30',
|
<div
|
||||||
'bg-red-500/10 px-3 py-2 mb-4'
|
role="alert"
|
||||||
)}
|
className={cn(
|
||||||
>
|
'flex items-center gap-2 rounded-md border px-3 py-2 mb-4',
|
||||||
<AlertTriangle className="h-4 w-4 text-red-400 shrink-0" aria-hidden="true" />
|
isLockWarning
|
||||||
<p className="text-xs text-red-400">{loginError}</p>
|
? 'bg-amber-500/10 border-amber-500/30'
|
||||||
</div>
|
: 'bg-red-500/10 border-red-500/30'
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
|
{isLockWarning
|
||||||
|
? <Lock className="h-4 w-4 text-amber-400 shrink-0" aria-hidden="true" />
|
||||||
|
: <AlertTriangle className="h-4 w-4 text-red-400 shrink-0" aria-hidden="true" />}
|
||||||
|
<p className={cn('text-xs', isLockWarning ? 'text-amber-400' : 'text-red-400')}>
|
||||||
|
{loginError}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
<form onSubmit={handleCredentialSubmit} className="space-y-4">
|
<form onSubmit={handleCredentialSubmit} className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="username" required>Username</Label>
|
<Label htmlFor="username" required>Username</Label>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user