'use client'; import { useState, FormEvent, type ChangeEvent } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import Link from 'next/link'; import { motion } from 'framer-motion'; import { Mail, Lock, Eye, EyeOff, LogIn, AlertCircle, Flag, Gamepad2, Shield, ChevronRight, } from 'lucide-react'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import Input from '@/components/ui/Input'; import Heading from '@/components/ui/Heading'; import { useAuth } from '@/lib/auth/AuthContext'; import AuthWorkflowMockup from '@/components/auth/AuthWorkflowMockup'; import UserRolesPreview from '@/components/auth/UserRolesPreview'; interface FormErrors { email?: string; password?: string; submit?: string; } export default function LoginPage() { const router = useRouter(); const searchParams = useSearchParams(); const { refreshSession } = useAuth(); const returnTo = searchParams.get('returnTo') ?? '/dashboard'; const [loading, setLoading] = useState(false); const [showPassword, setShowPassword] = useState(false); const [errors, setErrors] = useState({}); const [formData, setFormData] = useState({ email: '', password: '', }); const validateForm = (): boolean => { const newErrors: FormErrors = {}; if (!formData.email.trim()) { newErrors.email = 'Email is required'; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { newErrors.email = 'Invalid email format'; } if (!formData.password) { newErrors.password = 'Password is required'; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e: FormEvent) => { e.preventDefault(); if (loading) return; if (!validateForm()) return; setLoading(true); setErrors({}); try { const { ServiceFactory } = await import('@/lib/services/ServiceFactory'); const serviceFactory = new ServiceFactory(process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001'); const authService = serviceFactory.createAuthService(); await authService.login({ email: formData.email, password: formData.password, }); // Refresh session in context so header updates immediately await refreshSession(); router.push(returnTo); } catch (error) { setErrors({ submit: error instanceof Error ? error.message : 'Login failed. Please try again.', }); setLoading(false); } }; const handleDemoLogin = async () => { setLoading(true); try { const { ServiceFactory } = await import('@/lib/services/ServiceFactory'); const serviceFactory = new ServiceFactory(process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001'); const authService = serviceFactory.createAuthService(); await authService.demoLogin({ role: 'driver' }); await new Promise(resolve => setTimeout(resolve, 500)); router.push(returnTo); } catch (error) { setErrors({ submit: 'Demo login failed. Please try again.', }); setLoading(false); } }; return (
{/* Background Pattern */}
{/* Left Side - Info Panel (Hidden on mobile) */}
{/* Logo */}
GridPilot
Your Sim Racing Infrastructure

Manage leagues, track performance, join teams, and compete with drivers worldwide. One account, multiple roles.

{/* Role Cards */} {/* Workflow Mockup */} {/* Trust Indicators */}
Secure login
iRacing verified
{/* Right Side - Login Form */}
{/* Mobile Logo/Header */}
Welcome Back

Sign in to continue to GridPilot

{/* Desktop Header */}
Welcome Back

Sign in to access your racing dashboard

{/* Background accent */}
{/* Email */}
) => setFormData({ ...formData, email: e.target.value })} error={!!errors.email} errorMessage={errors.email} placeholder="you@example.com" disabled={loading} className="pl-10" autoComplete="email" />
{/* Password */}
Forgot password?
) => setFormData({ ...formData, password: e.target.value })} error={!!errors.password} errorMessage={errors.password} placeholder="••••••••" disabled={loading} className="pl-10 pr-10" autoComplete="current-password" />
{/* Error Message */} {errors.submit && (

{errors.submit}

)} {/* Submit Button */}
{/* Divider */}
or continue with
{/* Demo Login */} Demo Login {/* Sign Up Link */}

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

{/* Name Immutability Notice */}
Note: Your display name cannot be changed after signup. Please ensure it's correct when creating your account.
{/* Footer */}

By signing in, you agree to our{' '} Terms of Service {' '}and{' '} Privacy Policy

{/* Mobile Role Info */}
); }