'use client' import { useState } from 'react' import { useSearchParams, useRouter } from 'next/navigation' import { signIn } from 'next-auth/react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Mail, Loader2, CheckCircle2, AlertCircle, Lock, KeyRound } from 'lucide-react' import { AnimatedCard } from '@/components/shared/animated-container' type LoginMode = 'password' | 'magic-link' export default function LoginPage() { const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [mode, setMode] = useState('password') const [isLoading, setIsLoading] = useState(false) const [isSent, setIsSent] = useState(false) const [error, setError] = useState(null) const searchParams = useSearchParams() const router = useRouter() const callbackUrl = searchParams.get('callbackUrl') || '/' const errorParam = searchParams.get('error') const handlePasswordLogin = async (e: React.FormEvent) => { e.preventDefault() setIsLoading(true) setError(null) try { const result = await signIn('credentials', { email, password, redirect: false, callbackUrl, }) if (result?.error) { setError('Invalid email or password. Please try again.') } else if (result?.ok) { // Use window.location for external redirects or callback URLs window.location.href = callbackUrl } } catch (err: unknown) { if (err instanceof Error && err.message.includes('429')) { setError('Too many attempts. Please wait a few minutes before trying again.') } else { setError('An unexpected error occurred. Please try again.') } } finally { setIsLoading(false) } } const handleMagicLink = async (e: React.FormEvent) => { e.preventDefault() setIsLoading(true) setError(null) try { // Get CSRF token first const csrfRes = await fetch('/api/auth/csrf') const { csrfToken } = await csrfRes.json() // POST directly to the signin endpoint const res = await fetch('/api/auth/signin/email', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ csrfToken, email, callbackUrl, }), redirect: 'manual', }) // 302 redirect means success if (res.type === 'opaqueredirect' || res.status === 302 || res.ok) { setIsSent(true) } else { setError('Failed to send magic link. Please try again.') } } catch (err: unknown) { if (err instanceof Error && err.message.includes('429')) { setError('Too many attempts. Please wait a few minutes before trying again.') } else { setError('An unexpected error occurred. Please try again.') } } finally { setIsLoading(false) } } // Success state after sending magic link if (isSent) { return (
Check your email We've sent a magic link to {email}

Click the link in the email to sign in. The link will expire in 15 minutes.

If you don't see it, check your spam folder.

Having trouble?{' '} Contact support

) } return (
Welcome back {mode === 'password' ? 'Sign in with your email and password' : 'Sign in with a magic link'} {mode === 'password' ? ( // Password login form
{(error || errorParam) && (

{error || (errorParam === 'Verification' ? 'The magic link has expired or is invalid.' : errorParam === 'CredentialsSignin' ? 'Invalid email or password.' : 'An error occurred during sign in.')}

)}
setEmail(e.target.value)} required disabled={isLoading} autoComplete="email" autoFocus />
setPassword(e.target.value)} required disabled={isLoading} autoComplete="current-password" />
) : ( // Magic link form
{(error || errorParam) && (

{error || (errorParam === 'Verification' ? 'The magic link has expired or is invalid.' : 'An error occurred during sign in.')}

)}
setEmail(e.target.value)} required disabled={isLoading} autoComplete="email" autoFocus />

We'll send you a secure link to sign in or reset your password.

)} {/* Toggle between modes */}
) }