From 991aa98f98cffd1d7785c272726615325e2c0208 Mon Sep 17 00:00:00 2001 From: Paul Nothaft Date: Sun, 18 Jan 2026 15:00:38 +0100 Subject: [PATCH] fix: correct invitation activation validation and add missing translations - Fix password minimum length validation: frontend now correctly requires 12 characters to match backend validation (was incorrectly checking for 8) - Fix translation key references in AcceptInvitePage to use correct paths (e.g., acceptInvitation.errors.* instead of acceptInvitation.*) - Add missing translations for both EN and DE: - contactAdminMessage - passwordsMatch - alreadyHaveAccount - signIn Fixes #129 --- frontend/src/i18n/locales/de.json | 4 ++ frontend/src/i18n/locales/en.json | 4 ++ .../src/pages/public/AcceptInvitePage.tsx | 40 +++++++++---------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index ab8161cd..93d56e89 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -1523,6 +1523,10 @@ "successMessage": "Ihr Konto wurde erfolgreich erstellt. Sie können sich jetzt mit Ihren Zugangsdaten anmelden.", "redirecting": "Weiterleitung zur Anmeldung in {{seconds}}...", "goToLogin": "Zur Anmeldung", + "contactAdminMessage": "Bitte kontaktieren Sie Ihren Administrator, um eine neue Einladung anzufordern.", + "passwordsMatch": "Passwörter stimmen überein", + "alreadyHaveAccount": "Bereits ein Konto?", + "signIn": "Anmelden", "errors": { "usernameRequired": "Benutzername ist erforderlich", "usernameTooShort": "Benutzername muss mindestens 3 Zeichen lang sein", diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index f11e78bf..78523323 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -1284,6 +1284,10 @@ "successMessage": "Your account has been created successfully. You can now log in with your credentials.", "redirecting": "Redirecting to login in {{seconds}}...", "goToLogin": "Go to Login", + "contactAdminMessage": "Please contact your administrator to request a new invitation.", + "passwordsMatch": "Passwords match", + "alreadyHaveAccount": "Already have an account?", + "signIn": "Sign in", "errors": { "usernameRequired": "Username is required", "usernameTooShort": "Username must be at least 3 characters", diff --git a/frontend/src/pages/public/AcceptInvitePage.tsx b/frontend/src/pages/public/AcceptInvitePage.tsx index 08722d49..150c7750 100644 --- a/frontend/src/pages/public/AcceptInvitePage.tsx +++ b/frontend/src/pages/public/AcceptInvitePage.tsx @@ -94,16 +94,16 @@ export const AcceptInvitePage: React.FC = () => { }); setErrors(validationErrors); } else { - toast.error(errorMessage || t('acceptInvitation.validationError')); + toast.error(errorMessage || t('acceptInvitation.errors.genericError')); } } else if (error.response?.status === 422) { - toast.error(errorMessage || t('acceptInvitation.validationError')); + toast.error(errorMessage || t('acceptInvitation.errors.genericError')); } else if (error.response?.status === 404) { - toast.error(t('acceptInvitation.invalidOrExpired')); + toast.error(t('acceptInvitation.invalidTokenMessage')); } else if (error.response?.status === 409) { - toast.error(errorMessage || t('acceptInvitation.alreadyUsed')); + toast.error(errorMessage || t('acceptInvitation.alreadyUsedMessage')); } else { - toast.error(t('acceptInvitation.generalError')); + toast.error(t('acceptInvitation.errors.genericError')); } }, }); @@ -113,7 +113,7 @@ export const AcceptInvitePage: React.FC = () => { { label: t('acceptInvitation.requirements.minLength'), met: false, - test: (pwd: string) => pwd.length >= 8, + test: (pwd: string) => pwd.length >= 12, }, { label: t('acceptInvitation.requirements.uppercase'), @@ -166,16 +166,16 @@ export const AcceptInvitePage: React.FC = () => { // Validate username const validateUsername = (username: string): string | null => { if (!username) { - return t('acceptInvitation.usernameRequired'); + return t('acceptInvitation.errors.usernameRequired'); } if (username.length < 3) { - return t('acceptInvitation.usernameTooShort'); + return t('acceptInvitation.errors.usernameTooShort'); } if (username.length > 50) { - return t('acceptInvitation.usernameTooLong'); + return t('acceptInvitation.errors.usernameTooLong'); } if (!/^[a-zA-Z0-9_-]+$/.test(username)) { - return t('acceptInvitation.usernameInvalid'); + return t('acceptInvitation.errors.usernameInvalid'); } return null; }; @@ -190,18 +190,18 @@ export const AcceptInvitePage: React.FC = () => { } if (!formData.password) { - newErrors.password = t('acceptInvitation.passwordRequired'); + newErrors.password = t('acceptInvitation.errors.passwordRequired'); } else { const allRequirementsMet = passwordRequirements.every(req => req.test(formData.password)); if (!allRequirementsMet) { - newErrors.password = t('acceptInvitation.passwordRequirements'); + newErrors.password = t('acceptInvitation.errors.passwordTooShort'); } } if (!formData.confirmPassword) { - newErrors.confirmPassword = t('acceptInvitation.confirmPasswordRequired'); + newErrors.confirmPassword = t('acceptInvitation.errors.confirmPasswordRequired'); } else if (formData.password !== formData.confirmPassword) { - newErrors.confirmPassword = t('acceptInvitation.passwordsDoNotMatch'); + newErrors.confirmPassword = t('acceptInvitation.errors.passwordsDoNotMatch'); } setErrors(newErrors); @@ -269,7 +269,7 @@ export const AcceptInvitePage: React.FC = () => { // Error state - invalid or expired token if (isError || !invitation?.valid) { - const errorMessage = (validationError as any)?.response?.data?.error || t('acceptInvitation.invalidOrExpired'); + const errorMessage = (validationError as any)?.response?.data?.error || t('acceptInvitation.invalidTokenMessage'); return (
@@ -280,13 +280,13 @@ export const AcceptInvitePage: React.FC = () => {

- {t('acceptInvitation.invalidTitle')} + {t('acceptInvitation.invalidToken')}

{errorMessage}

- {t('acceptInvitation.contactAdmin')} + {t('acceptInvitation.contactAdminMessage')}

@@ -466,7 +466,7 @@ export const AcceptInvitePage: React.FC = () => { {/* Password Requirements */}
-

{t('acceptInvitation.requirementsTitle')}

+

{t('acceptInvitation.requirements.title')}

{passwordRequirements.map((req, index) => { const isMet = req.test(formData.password); return (