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
This commit is contained in:
@@ -1523,6 +1523,10 @@
|
|||||||
"successMessage": "Ihr Konto wurde erfolgreich erstellt. Sie können sich jetzt mit Ihren Zugangsdaten anmelden.",
|
"successMessage": "Ihr Konto wurde erfolgreich erstellt. Sie können sich jetzt mit Ihren Zugangsdaten anmelden.",
|
||||||
"redirecting": "Weiterleitung zur Anmeldung in {{seconds}}...",
|
"redirecting": "Weiterleitung zur Anmeldung in {{seconds}}...",
|
||||||
"goToLogin": "Zur Anmeldung",
|
"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": {
|
"errors": {
|
||||||
"usernameRequired": "Benutzername ist erforderlich",
|
"usernameRequired": "Benutzername ist erforderlich",
|
||||||
"usernameTooShort": "Benutzername muss mindestens 3 Zeichen lang sein",
|
"usernameTooShort": "Benutzername muss mindestens 3 Zeichen lang sein",
|
||||||
|
|||||||
@@ -1284,6 +1284,10 @@
|
|||||||
"successMessage": "Your account has been created successfully. You can now log in with your credentials.",
|
"successMessage": "Your account has been created successfully. You can now log in with your credentials.",
|
||||||
"redirecting": "Redirecting to login in {{seconds}}...",
|
"redirecting": "Redirecting to login in {{seconds}}...",
|
||||||
"goToLogin": "Go to Login",
|
"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": {
|
"errors": {
|
||||||
"usernameRequired": "Username is required",
|
"usernameRequired": "Username is required",
|
||||||
"usernameTooShort": "Username must be at least 3 characters",
|
"usernameTooShort": "Username must be at least 3 characters",
|
||||||
|
|||||||
@@ -94,16 +94,16 @@ export const AcceptInvitePage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
setErrors(validationErrors);
|
setErrors(validationErrors);
|
||||||
} else {
|
} else {
|
||||||
toast.error(errorMessage || t('acceptInvitation.validationError'));
|
toast.error(errorMessage || t('acceptInvitation.errors.genericError'));
|
||||||
}
|
}
|
||||||
} else if (error.response?.status === 422) {
|
} 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) {
|
} else if (error.response?.status === 404) {
|
||||||
toast.error(t('acceptInvitation.invalidOrExpired'));
|
toast.error(t('acceptInvitation.invalidTokenMessage'));
|
||||||
} else if (error.response?.status === 409) {
|
} else if (error.response?.status === 409) {
|
||||||
toast.error(errorMessage || t('acceptInvitation.alreadyUsed'));
|
toast.error(errorMessage || t('acceptInvitation.alreadyUsedMessage'));
|
||||||
} else {
|
} 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'),
|
label: t('acceptInvitation.requirements.minLength'),
|
||||||
met: false,
|
met: false,
|
||||||
test: (pwd: string) => pwd.length >= 8,
|
test: (pwd: string) => pwd.length >= 12,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('acceptInvitation.requirements.uppercase'),
|
label: t('acceptInvitation.requirements.uppercase'),
|
||||||
@@ -166,16 +166,16 @@ export const AcceptInvitePage: React.FC = () => {
|
|||||||
// Validate username
|
// Validate username
|
||||||
const validateUsername = (username: string): string | null => {
|
const validateUsername = (username: string): string | null => {
|
||||||
if (!username) {
|
if (!username) {
|
||||||
return t('acceptInvitation.usernameRequired');
|
return t('acceptInvitation.errors.usernameRequired');
|
||||||
}
|
}
|
||||||
if (username.length < 3) {
|
if (username.length < 3) {
|
||||||
return t('acceptInvitation.usernameTooShort');
|
return t('acceptInvitation.errors.usernameTooShort');
|
||||||
}
|
}
|
||||||
if (username.length > 50) {
|
if (username.length > 50) {
|
||||||
return t('acceptInvitation.usernameTooLong');
|
return t('acceptInvitation.errors.usernameTooLong');
|
||||||
}
|
}
|
||||||
if (!/^[a-zA-Z0-9_-]+$/.test(username)) {
|
if (!/^[a-zA-Z0-9_-]+$/.test(username)) {
|
||||||
return t('acceptInvitation.usernameInvalid');
|
return t('acceptInvitation.errors.usernameInvalid');
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@@ -190,18 +190,18 @@ export const AcceptInvitePage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!formData.password) {
|
if (!formData.password) {
|
||||||
newErrors.password = t('acceptInvitation.passwordRequired');
|
newErrors.password = t('acceptInvitation.errors.passwordRequired');
|
||||||
} else {
|
} else {
|
||||||
const allRequirementsMet = passwordRequirements.every(req => req.test(formData.password));
|
const allRequirementsMet = passwordRequirements.every(req => req.test(formData.password));
|
||||||
if (!allRequirementsMet) {
|
if (!allRequirementsMet) {
|
||||||
newErrors.password = t('acceptInvitation.passwordRequirements');
|
newErrors.password = t('acceptInvitation.errors.passwordTooShort');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!formData.confirmPassword) {
|
if (!formData.confirmPassword) {
|
||||||
newErrors.confirmPassword = t('acceptInvitation.confirmPasswordRequired');
|
newErrors.confirmPassword = t('acceptInvitation.errors.confirmPasswordRequired');
|
||||||
} else if (formData.password !== formData.confirmPassword) {
|
} else if (formData.password !== formData.confirmPassword) {
|
||||||
newErrors.confirmPassword = t('acceptInvitation.passwordsDoNotMatch');
|
newErrors.confirmPassword = t('acceptInvitation.errors.passwordsDoNotMatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors);
|
||||||
@@ -269,7 +269,7 @@ export const AcceptInvitePage: React.FC = () => {
|
|||||||
|
|
||||||
// Error state - invalid or expired token
|
// Error state - invalid or expired token
|
||||||
if (isError || !invitation?.valid) {
|
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 (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center p-4" style={{ backgroundColor: 'var(--color-background, #fafafa)' }}>
|
<div className="min-h-screen flex items-center justify-center p-4" style={{ backgroundColor: 'var(--color-background, #fafafa)' }}>
|
||||||
@@ -280,13 +280,13 @@ export const AcceptInvitePage: React.FC = () => {
|
|||||||
<XCircle className="w-8 h-8 text-red-600" />
|
<XCircle className="w-8 h-8 text-red-600" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-2xl font-bold text-neutral-900 mb-2">
|
<h1 className="text-2xl font-bold text-neutral-900 mb-2">
|
||||||
{t('acceptInvitation.invalidTitle')}
|
{t('acceptInvitation.invalidToken')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-neutral-600 mb-6">
|
<p className="text-neutral-600 mb-6">
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-neutral-500 mb-6">
|
<p className="text-sm text-neutral-500 mb-6">
|
||||||
{t('acceptInvitation.contactAdmin')}
|
{t('acceptInvitation.contactAdminMessage')}
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@@ -312,7 +312,7 @@ export const AcceptInvitePage: React.FC = () => {
|
|||||||
<CheckCircle className="w-8 h-8 text-green-600" />
|
<CheckCircle className="w-8 h-8 text-green-600" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-2xl font-bold text-neutral-900 mb-2">
|
<h1 className="text-2xl font-bold text-neutral-900 mb-2">
|
||||||
{t('acceptInvitation.successTitle')}
|
{t('acceptInvitation.success')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-neutral-600 mb-6">
|
<p className="text-neutral-600 mb-6">
|
||||||
{t('acceptInvitation.successMessage')}
|
{t('acceptInvitation.successMessage')}
|
||||||
@@ -324,7 +324,7 @@ export const AcceptInvitePage: React.FC = () => {
|
|||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={() => navigate('/admin/login')}
|
onClick={() => navigate('/admin/login')}
|
||||||
>
|
>
|
||||||
{t('acceptInvitation.goToLoginNow')}
|
{t('acceptInvitation.goToLogin')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -466,7 +466,7 @@ export const AcceptInvitePage: React.FC = () => {
|
|||||||
|
|
||||||
{/* Password Requirements */}
|
{/* Password Requirements */}
|
||||||
<div className="mt-3 space-y-1.5">
|
<div className="mt-3 space-y-1.5">
|
||||||
<p className="text-xs font-medium text-neutral-600">{t('acceptInvitation.requirementsTitle')}</p>
|
<p className="text-xs font-medium text-neutral-600">{t('acceptInvitation.requirements.title')}</p>
|
||||||
{passwordRequirements.map((req, index) => {
|
{passwordRequirements.map((req, index) => {
|
||||||
const isMet = req.test(formData.password);
|
const isMet = req.test(formData.password);
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user