fix(login): translated error messages for failed and rate-limited logins

Map INVALID_CREDENTIALS and TOO_MANY_ATTEMPTS to i18n keys. A non-JSON error body (Traefik 502/504) no longer shows as a network error.
This commit is contained in:
lobo committed 2026-09-24 10:29:57 +02:00
1 parent e480369a98
commit 6773040d59
3 files changed
+18 -8

No files matched your search

+10 -6
View File
@@ -10,6 +10,11 @@ import AuthLayout from '../components/AuthLayout.jsx';
import FeedbackAlert from '../components/FeedbackAlert.jsx';
import {getValidToken} from '../utils/auth.js';
const LOGIN_ERROR_KEY = {
INVALID_CREDENTIALS: 'LOGIN.ERROR_INVALID_CREDENTIALS',
TOO_MANY_ATTEMPTS: 'LOGIN.ERROR_TOO_MANY_ATTEMPTS',
};
export default function LoginPage() {
const {t} = useTranslation();
const navigate = useNavigate();
@@ -40,17 +45,16 @@ export default function LoginPage() {
body: JSON.stringify({email, password}),
});
// Parse the JSON response (matches our Spring Boot LoginResponse record)
const data = await response.json();
// A proxy error (502/504) has an HTML body, so a parse failure is not a network error.
const data = await response.json().catch(() => ({}));
if (response.ok) {
// HTTP 200 OK
localStorage.setItem('tbp_jwt', data.token);
navigate('/dashboard', {replace: true});
} else {
// HTTP 401 Unauthorized or other errors
const minutes = Math.ceil(Number(response.headers.get('Retry-After') ?? 900) / 60);
setFeedback({
message: `Error: ${data.message || 'Login failed'}`,
message: t(LOGIN_ERROR_KEY[data.code] ?? 'LOGIN.ERROR_GENERIC', {minutes}),
isError: true
});
}
@@ -58,7 +62,7 @@ export default function LoginPage() {
// Network errors (e.g., backend is not running or CORS issue)
console.error('Login API Error:', error);
setFeedback({
message: 'Network error: Could not connect to the authentication service.',
message: t('INFO.ERROR.NETWORK_FAILURE'),
isError: true
});
} finally {