import { useForm } from "@tanstack/react-form"; import { Link, createFileRoute } from "@tanstack/react-router"; import { useState } from "react"; import { toast } from "sonner"; import { AuthPageCard, 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 { z } from "zod"; const signInSchema = z.object({ email: z.string().trim().email("Enter a valid email address."), password: z.string().min(1, "Enter your password."), }); export const Route = createFileRoute("/_auth/sign-in")({ validateSearch: authRedirectSearchSchema, component: SignInPage, }); function SignInPage() { const search = Route.useSearch(); const { redirectTo, oauthQuery, isHostedMode } = useAuthPageState( search.redirect, ); const [verificationEmail, setVerificationEmail] = useState( null, ); const [isSendingVerification, setIsSendingVerification] = useState(false); const form = useForm({ defaultValues: { email: "", password: "", }, validators: { onSubmit: signInSchema, }, onSubmit: async ({ formApi, value }) => { try { const email = value.email.trim(); captureClientEvent("auth:sign_in_submit", { redirect_to: redirectTo, }); setVerificationEmail(null); const result = await authClient.signIn.email({ email, password: value.password, callbackURL: redirectTo, ...(oauthQuery ? { oauth_query: oauthQuery } : {}), }); if (!result.error) { captureClientEvent("auth:sign_in_success", { redirect_to: redirectTo, }); return; } if (result.error.status === 403) { captureClientEvent("auth:sign_in_block_unverified", { redirect_to: redirectTo, }); setVerificationEmail(email); formApi.setErrorMap({ onSubmit: { form: "Please confirm your email before signing in.", fields: {}, }, }); return; } formApi.setErrorMap({ onSubmit: { form: result.error.message || "We couldn't sign you in.", fields: {}, }, }); } catch { formApi.setErrorMap({ onSubmit: { form: "Unable to sign in right now. Please try again.", fields: {}, }, }); } }, }); async function handleResendVerification() { if (!verificationEmail) { return; } setIsSendingVerification(true); try { const callbackURL = new URL("/verify-email", window.location.origin); if (redirectTo !== "/") callbackURL.searchParams.set("redirect", redirectTo); const result = await authClient.sendVerificationEmail({ email: verificationEmail, callbackURL: callbackURL.toString(), }); if (result.error) { toast.error(result.error.message || "We couldn't send another email."); return; } captureClientEvent("auth:verification_resend"); toast.success("A new email is on the way."); } catch { toast.error( "We couldn't send another email right now. Please try again.", ); } finally { setIsSendingVerification(false); } } return ( Forgot password? Create account ) : null } >
{ event.preventDefault(); void form.handleSubmit(); }} > {(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="current-password" disabled={!isHostedMode} required /> {error ? (

{error}

) : null}
); }}
{verificationEmail ? (

Please check {verificationEmail} for a link to confirm your email.

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

{errorMessage}

) : null} ); }}
); }