checkout: buttons verbessert, paddle zahlungsschritt schicker gemacht, schritt 4 optimiert+schick gemacht. Dashboard: translations ergänzt. Startseite vom Event Admin optimiert.

This commit is contained in:
Codex Agent
2025-11-17 11:06:46 +01:00
parent 5290072ffe
commit 167734f87a
25 changed files with 1981 additions and 1002 deletions

View File

@@ -50,6 +50,7 @@ export default function LoginForm({ onSuccess, canResetPassword = true, locale }
const [errors, setErrors] = useState<FieldErrors>({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [hasTriedSubmit, setHasTriedSubmit] = useState(false);
const [shouldFocusError, setShouldFocusError] = useState(false);
useEffect(() => {
if (!hasTriedSubmit) {
@@ -63,7 +64,7 @@ export default function LoginForm({ onSuccess, canResetPassword = true, locale }
}, [errors, hasTriedSubmit]);
useEffect(() => {
if (!hasTriedSubmit) {
if (!hasTriedSubmit || !shouldFocusError) {
return;
}
@@ -75,11 +76,13 @@ export default function LoginForm({ onSuccess, canResetPassword = true, locale }
const field = document.querySelector<HTMLInputElement>(`[name="${firstKey}"]`);
field?.scrollIntoView({ behavior: "smooth", block: "center" });
field?.focus();
}, [errors, hasTriedSubmit]);
setShouldFocusError(false);
}, [errors, hasTriedSubmit, shouldFocusError]);
const updateValue = (key: keyof typeof values, value: string | boolean) => {
setValues((current) => ({ ...current, [key]: value }));
setErrors((current) => ({ ...current, [key as string]: "" }));
setShouldFocusError(false);
};
const submit = async (event: React.FormEvent<HTMLFormElement>) => {
@@ -129,11 +132,13 @@ export default function LoginForm({ onSuccess, canResetPassword = true, locale }
});
setErrors(fieldErrors);
setShouldFocusError(true);
toast.error(t("login.failed_generic", "Ungueltige Anmeldedaten"));
return;
}
toast.error(t("login.unexpected_error", "Beim Login ist ein Fehler aufgetreten."));
setShouldFocusError(false);
} catch (error) {
console.error("Login request failed", error);
toast.error(t("login.unexpected_error", "Beim Login ist ein Fehler aufgetreten."));
@@ -205,4 +210,3 @@ export default function LoginForm({ onSuccess, canResetPassword = true, locale }
</form>
);
}

View File

@@ -390,7 +390,7 @@ export default function RegisterForm({ packageId, onSuccess, privacyHtml, locale
<div className="md:col-span-1">
<label htmlFor="password_confirmation" className="block text-sm font-medium text-gray-700 mb-1">
{t('register.confirm_password')} {t('common:required')}
{t('register.password_confirmation')} {t('common:required')}
</label>
<div className="relative">
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
@@ -410,7 +410,7 @@ export default function RegisterForm({ packageId, onSuccess, privacyHtml, locale
}
}}
className={`block w-full pl-10 pr-3 py-3 border rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-[#FFB6C1] focus:border-[#FFB6C1] sm:text-sm ${errors.password_confirmation ? 'border-red-500' : 'border-gray-300'}`}
placeholder={t('register.confirm_password_placeholder')}
placeholder={t('register.password_confirmation_placeholder')}
/>
</div>
{errors.password_confirmation && <p className="text-sm text-red-600 mt-1">{errors.password_confirmation}</p>}
@@ -442,7 +442,7 @@ export default function RegisterForm({ packageId, onSuccess, privacyHtml, locale
onClick={() => setPrivacyOpen(true)}
className="text-[#FFB6C1] hover:underline inline bg-transparent border-none cursor-pointer p-0 font-medium"
>
{t('register.privacy_policy')}
{t('register.privacy_policy_link')}
</button>.
</label>
{errors.privacy_consent && <p className="mt-2 text-sm text-red-600">{errors.privacy_consent}</p>}
@@ -485,5 +485,3 @@ export default function RegisterForm({ packageId, onSuccess, privacyHtml, locale
</div>
);
}

View File

@@ -265,7 +265,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
<div className="md:col-span-1">
<label htmlFor="password_confirmation" className="block text-sm font-medium text-gray-700 mb-1">
{t('register.confirm_password')} {t('common:required')}
{t('register.password_confirmation')} {t('common:required')}
</label>
<div className="relative">
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
@@ -285,7 +285,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
}
}}
className={`block w-full pl-10 pr-3 py-3 border rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-[#FFB6C1] focus:border-[#FFB6C1] sm:text-sm ${errors.password_confirmation ? 'border-red-500' : 'border-gray-300'}`}
placeholder={t('register.confirm_password_placeholder')}
placeholder={t('register.password_confirmation_placeholder')}
/>
</div>
{errors.password_confirmation && <p key={`error-password_confirmation`} className="text-sm text-red-600 mt-1">{errors.password_confirmation}</p>}
@@ -313,7 +313,7 @@ export default function Register({ package: initialPackage, privacyHtml }: Regis
onClick={() => setPrivacyOpen(true)}
className="text-[#FFB6C1] hover:underline inline bg-transparent border-none cursor-pointer p-0 font-medium"
>
{t('register.privacy_policy')}
{t('register.privacy_policy_link')}
</button>.
</label>
{errors.privacy_consent && <p className="mt-2 text-sm text-red-600">{errors.privacy_consent}</p>}