'use client' import { useState, useEffect, Suspense } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import Link from 'next/link' import Image from 'next/image' import { useStore } from '@/lib/store' import { api } from '@/lib/api' import { Loader2, ArrowRight, Eye, EyeOff, CheckCircle } from 'lucide-react' // Logo Component function Logo() { return ( pounce ) } // OAuth Icons function GoogleIcon({ className }: { className?: string }) { return ( ) } function GitHubIcon({ className }: { className?: string }) { return ( ) } function LoginForm() { const router = useRouter() const searchParams = useSearchParams() const { login } = useStore() const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [showPassword, setShowPassword] = useState(false) const [error, setError] = useState(null) const [loading, setLoading] = useState(false) const [oauthProviders, setOauthProviders] = useState({ google_enabled: false, github_enabled: false }) const [verified, setVerified] = useState(false) const sanitizeRedirect = (value: string | null | undefined): string => { const fallback = '/terminal/radar' if (!value) return fallback const v = value.trim() if (!v.startsWith('/')) return fallback if (v.startsWith('//')) return fallback if (v.includes('://')) return fallback if (v.includes('\\')) return fallback if (v.length > 2048) return fallback return v } // Get redirect URL from query params or localStorage (set during registration) const paramRedirect = searchParams.get('redirect') const [redirectTo, setRedirectTo] = useState(sanitizeRedirect(paramRedirect)) // Check localStorage for redirect (set during registration before email verification) useEffect(() => { const storedRedirect = localStorage.getItem('pounce_redirect_after_login') if (storedRedirect && !paramRedirect) { setRedirectTo(sanitizeRedirect(storedRedirect)) } }, [paramRedirect]) // Check for verified status useEffect(() => { if (searchParams.get('verified') === 'true') { setVerified(true) } if (searchParams.get('error')) { setError(searchParams.get('error') === 'oauth_failed' ? 'OAuth authentication failed. Please try again.' : 'Authentication failed') } }, [searchParams]) // Load OAuth providers useEffect(() => { api.getOAuthProviders().then(setOauthProviders).catch(() => {}) }, []) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError(null) setLoading(true) try { await login(email, password) // Check if email is verified const user = await api.getMe() if (!user.is_verified) { // Redirect to verify-email page if not verified router.push(`/verify-email?email=${encodeURIComponent(email)}`) return } // Clear stored redirect (was set during registration) localStorage.removeItem('pounce_redirect_after_login') // Redirect to intended destination or dashboard router.push(sanitizeRedirect(redirectTo)) } catch (err: unknown) { console.error('Login error:', err) if (err instanceof Error) { setError(err.message || 'Authentication failed') } else if (typeof err === 'object' && err !== null) { if ('detail' in err) { setError(String((err as { detail: unknown }).detail)) } else if ('message' in err) { setError(String((err as { message: unknown }).message)) } else { setError('Authentication failed. Please try again.') } } else if (typeof err === 'string') { setError(err) } else { setError('Authentication failed. Please try again.') } } finally { setLoading(false) } } // Generate register link with redirect preserved const registerLink = redirectTo !== '/terminal/radar' ? `/register?redirect=${encodeURIComponent(redirectTo)}` : '/register' return (
{/* Logo */} {/* Header */}

Back to the hunt.

Sign in to your account

{/* Verified Message */} {verified && (

Email verified successfully! You can now sign in.

)} {/* Form */}
{error && (

{error}

)}
setEmail(e.target.value)} placeholder="Email address" required autoComplete="email" className="input-elegant text-body-sm sm:text-body" />
setPassword(e.target.value)} placeholder="Password" required minLength={8} autoComplete="current-password" className="input-elegant text-body-sm sm:text-body pr-12" />
Forgot password?
{/* OAuth Buttons */} {(oauthProviders.google_enabled || oauthProviders.github_enabled) && (
{/* Divider */}
or continue with
{oauthProviders.google_enabled && ( Continue with Google )} {oauthProviders.github_enabled && ( Continue with GitHub )}
)} {/* Register Link */}

Don't have an account?{' '} Create one

) } export default function LoginPage() { return (
{/* Ambient glow */}
}>
) }