diff --git a/public/i18n/de.json b/public/i18n/de.json index 9d2402e..e3f42ef 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -11,7 +11,10 @@ "SUBMIT": "Spiel betreten", "NO_ACCOUNT": "Noch kein Konto?", "ERROR_EMAIL": "Gib eine gültige Email-Adresse ein mit '@' und mit '.'.", - "ERROR_PASSWORD": "Das Passwort darf nicht leer sein." + "ERROR_PASSWORD": "Das Passwort darf nicht leer sein.", + "ERROR_INVALID_CREDENTIALS": "E-Mail oder Passwort ist falsch.", + "ERROR_TOO_MANY_ATTEMPTS": "Zu viele fehlgeschlagene Versuche. Bitte in {{minutes}} Minuten erneut versuchen.", + "ERROR_GENERIC": "Anmeldung fehlgeschlagen. Bitte später erneut versuchen." }, "REGISTRATION": { "TITLE": "Registrieren", diff --git a/public/i18n/en.json b/public/i18n/en.json index d5d2790..3bab297 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -11,7 +11,10 @@ "SUBMIT": "Enter the game", "NO_ACCOUNT": "No account yet?", "ERROR_EMAIL": "Enter a valid email address with '@' and with '.'.", - "ERROR_PASSWORD": "The password must have at least one character." + "ERROR_PASSWORD": "The password must have at least one character.", + "ERROR_INVALID_CREDENTIALS": "Email or password is wrong.", + "ERROR_TOO_MANY_ATTEMPTS": "Too many failed attempts. Please try again in {{minutes}} minutes.", + "ERROR_GENERIC": "Login failed. Please try again later." }, "REGISTRATION": { "TITLE": "Register", diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index 89b8773..14b03e4 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -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 {