import { useMemo, useState } from "react"; import { Form, Link, useNavigation, useSearchParams } from "react-router"; import { useTranslation } from "react-i18next"; import { FeatherMail } from "@subframe/core"; import { useRootData } from "~/utils/data/useRootData"; import { AuthShell, AuthStatus } from "~/components/auth/AuthShell"; import { Button } from "~/ui/components/Button"; import { Checkbox } from "~/ui/components/Checkbox"; import { LinkButton } from "~/ui/components/LinkButton"; import { Progress } from "~/ui/components/Progress"; import { TextInput } from "~/ui/components/TextInput"; import Captcha from "~/components/captcha/Captcha"; import { getPasswordChecks, type PasswordPolicy } from "~/modules/users/components/RegisterForm"; // Signup-Screen nach Davids Subframe-Design ("Signup"): Logo + Card (Konto erstellen) + E-Mail, Passwort // (+ Staerke-Progress), Firmenname, Vor-/Nachname, Telefon, USt-IdNr, 3 Consent-Checkboxen + "Konto erstellen". // Voll an die bestehende Register-Action verdrahtet (gleiche Feld-Namen, Password-Policy, Captcha, Lead-Felder). // verificationEmailSent -> Davids Success-Status. Footer "Schon ein Konto? Anmelden". // // 💡 Backend-Luecken (gerendert + gepostet, aber von registerFromRequest noch nicht persistiert): // - phone: User-Modell hat die Spalte (accounts.prisma) -> Action muss das Feld nur noch lesen. // - vatNumber (USt-IdNr): keine Spalte im LOCKED-Modell -> braucht Data-Model-Entscheidung. // - 3 Consents: Backend nutzt impliziten Terms-Consent; hier als Client-Gate (kein Server-Tracking). type RegisterData = { company?: string; firstName?: string; lastName?: string; email?: string; slug?: string }; export default function RegisterScreen({ requireCaptcha = false, error, verificationEmailSent, data = {}, leadConversion, passwordPolicy, }: { requireCaptcha?: boolean; error?: string; verificationEmailSent?: boolean; data?: RegisterData; leadConversion?: { leadId: string; token: string; planId?: string | null }; passwordPolicy?: PasswordPolicy; }) { const { t } = useTranslation(); const { appConfiguration, csrf } = useRootData(); const navigation = useNavigation(); const [searchParams] = useSearchParams(); const isSubmitting = navigation.state !== "idle"; const redirect = searchParams.get("redirect") ?? ""; const [password, setPassword] = useState(""); const [acceptTerms, setAcceptTerms] = useState(false); const [acceptPrivacy, setAcceptPrivacy] = useState(false); const [acceptDpa, setAcceptDpa] = useState(false); const allConsents = acceptTerms && acceptPrivacy && acceptDpa; const showPasswordInput = !appConfiguration.auth.requireEmailVerification; const checks = useMemo(() => getPasswordChecks(password, passwordPolicy), [password, passwordPolicy]); const strength = checks.length === 0 ? 0 : Math.round((checks.filter((c) => c.valid).length / checks.length) * 100); if (verificationEmailSent) { return ( } tone="positive" title={t("account.verify.title")} message={t("account.verify.emailSent")} dataUiId="ui.auth.register.verificationSent" > {t("account.login.backToLogin")} ); } return ( {t("account.register.haveAccount")} {t("account.register.signIn")} } > {appConfiguration.auth.authMethods.emailPassword.enabled ? (
{leadConversion ? ( <> {leadConversion.planId ? : null} ) : null} {/* Davids Felder-Gruppe: alle Inputs in einem gap-4-Wrapper; Form-gap-5 trennt Gruppe/Consent/Button. */}
{showPasswordInput ? ( <>
setPassword(event.target.value)} disabled={isSubmitting} data-ui-id="ui.auth.register.password" />
{passwordPolicy ? ( <> {t("account.register.passwordStrength")} ) : null}
{passwordPolicy ? ( {t("account.register.passwordHint")} ) : null} ) : null}
{error && navigation.state === "idle" ? (

{error}

) : null} ) : null}
); }