'use client'; import Link from 'next/link'; import { Suspense, type FormEvent, useCallback, useMemo, useState } from 'react'; import { useSearchParams } from 'next/navigation'; import { AuthShell } from '@/components/auth/auth-shell'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { useAuthHandoff } from '@/hooks/use-auth-handoff'; import { authClient } from '@/lib/auth-client'; function sanitizeNextPath(value: string | null) { if (!value || !value.startsWith('/')) { return '/'; } return value; } export default function SignInPage() { return ( Loading sign in...}> ); } function SignInPageContent() { const searchParams = useSearchParams(); const nextPath = useMemo(() => sanitizeNextPath(searchParams.get('next')), [searchParams]); const { data: rawSession, isPending } = authClient.useSession(); const session = (rawSession ?? null) as { user?: { id?: string } } | null; const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(null); const [handoffError, setHandoffError] = useState(null); const [message, setMessage] = useState(null); const [busyAction, setBusyAction] = useState<'password' | 'magic' | null>(null); const [awaitingSession, setAwaitingSession] = useState(false); const handleHandoffTimeout = useCallback(() => { setAwaitingSession(false); setHandoffError('Authentication completed, but the session was not established on this device. Please sign in again.'); }, []); const { isHandingOff, statusText } = useAuthHandoff({ nextPath, session, isPending, awaitingSession, onTimeout: handleHandoffTimeout }); const signInWithPassword = async (event: FormEvent) => { event.preventDefault(); setError(null); setHandoffError(null); setMessage(null); setBusyAction('password'); const { error: signInError } = await authClient.signIn.email({ email: email.trim(), password, callbackURL: nextPath }); setBusyAction(null); if (signInError) { setError(signInError.message || 'Sign in failed.'); return; } setAwaitingSession(true); }; const signInWithMagicLink = async () => { const targetEmail = email.trim(); if (!targetEmail) { setError('Email is required for magic link sign in.'); return; } setError(null); setHandoffError(null); setMessage(null); setBusyAction('magic'); const { error: magicError } = await authClient.signIn.magicLink({ email: targetEmail, callbackURL: nextPath }); setBusyAction(null); if (magicError) { setError(magicError.message || 'Unable to send magic link.'); return; } setMessage('Magic link sent. Check your inbox and open the link on this device.'); }; return ( Need an account?{' '} Create one > )} > Email setEmail(event.target.value)} required disabled={busyAction !== null || isHandingOff} /> Password setPassword(event.target.value)} required disabled={busyAction !== null || isHandingOff} /> {error ? {error} : null} {handoffError ? {handoffError} : null} {message ? {message} : null} {statusText ? {statusText} : null} {busyAction === 'password' ? 'Signing in...' : isHandingOff ? 'Finishing sign-in...' : 'Sign in with password'} void signInWithMagicLink()} > {busyAction === 'magic' ? 'Sending link...' : isHandingOff ? 'Finishing sign-in...' : 'Send magic link'} ); }
{error}
{handoffError}
{message}
{statusText}