🐛 fix(auth): require name field in registration forms

Add required name field (min 2 chars) to all registration forms to fix
Better Auth validation error. Updates backend DTO, shared-auth service,
shared-auth-ui RegisterPage component, i18n translations, and all app
auth stores and register pages.
This commit is contained in:
Wuesteon 2025-12-16 20:28:28 +01:00
parent 11324b5e68
commit d3e11b320a
28 changed files with 151 additions and 56 deletions

View file

@ -8,6 +8,9 @@
/** Translation strings for the register page */
export interface RegisterTranslations {
title: string;
namePlaceholder: string;
nameRequired: string;
nameTooShort: string;
emailPlaceholder: string;
passwordPlaceholder: string;
confirmPasswordPlaceholder: string;
@ -46,6 +49,9 @@
/** Default English translations */
const defaultTranslations: RegisterTranslations = {
title: 'Create Account',
namePlaceholder: 'Name',
nameRequired: 'Name is required',
nameTooShort: 'Name must be at least 2 characters',
emailPlaceholder: 'Email',
passwordPlaceholder: 'Password',
confirmPasswordPlaceholder: 'Confirm Password',
@ -81,8 +87,13 @@
logo: Component<{ size?: number; color?: string }>;
/** Primary color (hex) */
primaryColor: string;
/** Sign up function (with optional referral code) */
onSignUp: (email: string, password: string, referralCode?: string) => Promise<AuthResult>;
/** Sign up function (with name and optional referral code) */
onSignUp: (
email: string,
password: string,
name: string,
referralCode?: string
) => Promise<AuthResult>;
/** Navigation function */
goto: (path: string) => void;
/** Success redirect path */
@ -132,6 +143,7 @@
let error = $state<string | null>(null);
let success = $state(false);
let needsVerification = $state(false);
let name = $state('');
let email = $state('');
let password = $state('');
let confirmPassword = $state('');
@ -249,6 +261,18 @@
success = false;
// Validation
if (!name) {
error = t.nameRequired;
loading = false;
return;
}
if (name.length < 2) {
error = t.nameTooShort;
loading = false;
return;
}
if (!email) {
error = t.emailRequired;
loading = false;
@ -293,7 +317,7 @@
// Pass referral code if valid
const validReferralCode = referralValidation?.valid ? referralCode : undefined;
const result = await onSignUp(email, password, validReferralCode);
const result = await onSignUp(email, password, name, validReferralCode);
loading = false;
@ -301,6 +325,7 @@
if (result.needsVerification) {
needsVerification = true;
success = true;
name = '';
password = '';
confirmPassword = '';
} else {
@ -407,6 +432,24 @@
}}
class="pb-4"
>
<div class="mb-2">
<input
type="text"
bind:value={name}
placeholder={t.namePlaceholder}
required
minlength={2}
class="h-14 w-full rounded-xl border px-4 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark
? 'rgba(0, 0, 0, 0.2)'
: 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark
? '#ffffff'
: '#000000'}; --tw-ring-color: {primaryColor};"
/>
</div>
<div class="mb-2">
<input
type="email"
@ -444,7 +487,8 @@
<button
type="button"
onclick={() => (showPassword = !showPassword)}
class="absolute inset-y-0 right-0 flex items-center justify-center w-14 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors"
class="absolute top-1/2 -translate-y-1/2 right-4 flex items-center justify-center transition-colors"
style="color: {isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)'};"
aria-label={showPassword ? t.hidePassword : t.showPassword}
>
{#if showPassword}
@ -476,7 +520,8 @@
<button
type="button"
onclick={() => (showConfirmPassword = !showConfirmPassword)}
class="absolute inset-y-0 right-0 flex items-center justify-center w-14 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors"
class="absolute top-1/2 -translate-y-1/2 right-4 flex items-center justify-center transition-colors"
style="color: {isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)'};"
aria-label={showConfirmPassword ? t.hidePassword : t.showPassword}
>
{#if showConfirmPassword}

View file

@ -103,14 +103,20 @@ export function createAuthService(config: AuthServiceConfig) {
},
/**
* Sign up with email and password
* Sign up with email, password, and name
* @param email User email
* @param password User password
* @param name User display name (min 2 characters)
* @param referralCode Optional referral code for bonus credits
*/
async signUp(email: string, password: string, referralCode?: string): Promise<AuthResult> {
async signUp(
email: string,
password: string,
name: string,
referralCode?: string
): Promise<AuthResult> {
try {
const body: Record<string, string> = { email, password };
const body: Record<string, string> = { email, password, name };
if (referralCode) {
body.referralCode = referralCode;
}

View file

@ -25,6 +25,9 @@
},
"register": {
"title": "Konto erstellen",
"namePlaceholder": "Name",
"nameRequired": "Name ist erforderlich",
"nameTooShort": "Name muss mindestens 2 Zeichen lang sein",
"emailPlaceholder": "E-Mail",
"passwordPlaceholder": "Passwort",
"confirmPasswordPlaceholder": "Passwort bestätigen",

View file

@ -25,6 +25,9 @@
},
"register": {
"title": "Create Account",
"namePlaceholder": "Name",
"nameRequired": "Name is required",
"nameTooShort": "Name must be at least 2 characters",
"emailPlaceholder": "Email",
"passwordPlaceholder": "Password",
"confirmPasswordPlaceholder": "Confirm Password",

View file

@ -25,6 +25,9 @@
},
"register": {
"title": "Crear Cuenta",
"namePlaceholder": "Nombre",
"nameRequired": "El nombre es obligatorio",
"nameTooShort": "El nombre debe tener al menos 2 caracteres",
"emailPlaceholder": "Correo electrónico",
"passwordPlaceholder": "Contraseña",
"confirmPasswordPlaceholder": "Confirmar Contraseña",

View file

@ -25,6 +25,9 @@
},
"register": {
"title": "Créer un compte",
"namePlaceholder": "Nom",
"nameRequired": "Le nom est requis",
"nameTooShort": "Le nom doit contenir au moins 2 caractères",
"emailPlaceholder": "Email",
"passwordPlaceholder": "Mot de passe",
"confirmPasswordPlaceholder": "Confirmer le mot de passe",

View file

@ -40,6 +40,9 @@ export interface AuthTranslations {
};
register: {
title: string;
namePlaceholder: string;
nameRequired: string;
nameTooShort: string;
emailPlaceholder: string;
passwordPlaceholder: string;
confirmPasswordPlaceholder: string;

View file

@ -25,6 +25,9 @@
},
"register": {
"title": "Crea Account",
"namePlaceholder": "Nome",
"nameRequired": "Il nome è obbligatorio",
"nameTooShort": "Il nome deve contenere almeno 2 caratteri",
"emailPlaceholder": "Email",
"passwordPlaceholder": "Password",
"confirmPasswordPlaceholder": "Conferma Password",