import { useForm } from "@tanstack/react-form"; import { Link, createFileRoute, useNavigate } from "@tanstack/react-router"; import { useState } from "react"; import { AuthPageCard, AuthMethodChooser, authRedirectSearchSchema, getFieldError, getFormError, useAuthPageState, } from "@/client/features/auth/AuthPage"; import { captureClientEvent } from "@/client/lib/posthog"; import { authClient } from "@/lib/auth-client"; import { getSignInSearch } from "@/lib/auth-redirect"; import { HOSTED_PASSWORD_MAX_LENGTH, HOSTED_PASSWORD_MIN_LENGTH, } from "@/lib/auth-options"; import { z } from "zod"; const signUpSchema = z .object({ name: z.string().trim(), email: z.string().trim().email("Enter a valid email address."), password: z .string() .min( HOSTED_PASSWORD_MIN_LENGTH, `Password must be at least ${HOSTED_PASSWORD_MIN_LENGTH} characters.`, ) .max( HOSTED_PASSWORD_MAX_LENGTH, `Password must be at most ${HOSTED_PASSWORD_MAX_LENGTH} characters.`, ), confirmPassword: z.string(), }) .refine((value) => value.password === value.confirmPassword, { message: "Passwords do not match.", path: ["confirmPassword"], }); export const Route = createFileRoute("/_auth/sign-up")({ validateSearch: authRedirectSearchSchema, component: SignUpPage, }); function SignUpPage() { const search = Route.useSearch(); const navigate = useNavigate(); const { redirectTo, isHostedMode } = useAuthPageState(search.redirect); const [showEmailForm, setShowEmailForm] = useState(false); const [isStartingGoogle, setIsStartingGoogle] = useState(false); const [socialError, setSocialError] = useState(null); const form = useForm({ defaultValues: { name: "", email: "", password: "", confirmPassword: "", }, validators: { onSubmit: signUpSchema, }, onSubmit: async ({ formApi, value }) => { try { const email = value.email.trim(); captureClientEvent("auth:sign_up_submit", { redirect_to: redirectTo, }); const resolvedName = value.name.trim() || email.split("@")[0] || "OpenSEO User"; const result = await authClient.signUp.email({ name: resolvedName, email, password: value.password, callbackURL: (() => { const url = new URL("/verify-email", window.location.origin); url.searchParams.set("redirect", "/subscribe"); return url.toString(); })(), }); if (result.error) { formApi.setErrorMap({ onSubmit: { form: result.error.message || "Unable to create account.", fields: {}, }, }); return; } captureClientEvent("auth:sign_up_success", { redirect_to: redirectTo, }); void navigate({ to: "/verify-email", search: { email, ...getSignInSearch(redirectTo) }, }); } catch { formApi.setErrorMap({ onSubmit: { form: "Unable to create account right now. Please try again.", fields: {}, }, }); } }, }); async function handleContinueWithGoogle() { const callbackURL = redirectTo === "/" ? "/subscribe" : redirectTo; setSocialError(null); setIsStartingGoogle(true); try { captureClientEvent("auth:sign_up_google_start", { redirect_to: callbackURL, }); const result = await authClient.signIn.social({ provider: "google", callbackURL, newUserCallbackURL: callbackURL, requestSignUp: true, }); if (result.error) { setSocialError( result.error.message || "Google sign up is not available right now.", ); setIsStartingGoogle(false); } } catch { setSocialError("Google sign up is not available right now."); setIsStartingGoogle(false); } } return ( { setShowEmailForm(false); setSocialError(null); }} > Back to signup ) : (

By signing up, you agree to our{" "} Terms {" "} and{" "} Privacy Policy .

Already have an account?{" "} Sign in

) ) : null } > {!showEmailForm ? ( <> { void handleContinueWithGoogle(); }} onContinueWithEmail={() => { setShowEmailForm(true); setSocialError(null); }} /> {socialError ? (

{socialError}

) : null} ) : (
{ event.preventDefault(); void form.handleSubmit(); }} > {(field) => { const error = getFieldError(field.state.meta.errors); return (
field.handleChange(event.target.value)} autoComplete="name" disabled={!isHostedMode} /> {error ? (

{error}

) : null}
); }}
{(field) => { const error = getFieldError(field.state.meta.errors); return (
field.handleChange(event.target.value)} autoComplete="email" disabled={!isHostedMode} required /> {error ? (

{error}

) : null}
); }}
{(field) => { const error = getFieldError(field.state.meta.errors); return (
field.handleChange(event.target.value)} autoComplete="new-password" disabled={!isHostedMode} required minLength={HOSTED_PASSWORD_MIN_LENGTH} maxLength={HOSTED_PASSWORD_MAX_LENGTH} /> {error ? (

{error}

) : null}
); }}
{(field) => { const error = getFieldError(field.state.meta.errors); return (
field.handleChange(event.target.value)} autoComplete="new-password" disabled={!isHostedMode} required minLength={HOSTED_PASSWORD_MIN_LENGTH} maxLength={HOSTED_PASSWORD_MAX_LENGTH} /> {error ? (

{error}

) : null}
); }}
({ submitError: state.errorMap.onSubmit, isSubmitting: state.isSubmitting, })} > {({ submitError, isSubmitting }) => { const errorMessage = getFormError(submitError); return ( <> {errorMessage ? (

{errorMessage}

) : null} ); }}
)}
); }