This commit is contained in:
2025-12-08 23:52:36 +01:00
parent 2d0860d66c
commit 35f988f885
46 changed files with 4624 additions and 1041 deletions

View File

@@ -19,6 +19,7 @@ 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';
interface FormErrors {
email?: string;
@@ -29,6 +30,7 @@ interface FormErrors {
export default function LoginPage() {
const router = useRouter();
const searchParams = useSearchParams();
const { refreshSession } = useAuth();
const returnTo = searchParams.get('returnTo') ?? '/dashboard';
const [loading, setLoading] = useState(false);
@@ -81,8 +83,9 @@ export default function LoginPage() {
throw new Error(data.error || 'Login failed');
}
// Refresh session in context so header updates immediately
await refreshSession();
router.push(returnTo);
router.refresh();
} catch (error) {
setErrors({
submit: error instanceof Error ? error.message : 'Login failed. Please try again.',
@@ -94,9 +97,8 @@ export default function LoginPage() {
const handleDemoLogin = async () => {
setLoading(true);
try {
const authService = getAuthService();
const { redirectUrl } = await authService.startIracingAuthRedirect(returnTo);
router.push(redirectUrl);
// Redirect to iRacing auth start route
router.push(`/auth/iracing/start?returnTo=${encodeURIComponent(returnTo)}`);
} catch (error) {
setErrors({
submit: 'Demo login failed. Please try again.',

View File

@@ -1,6 +1,6 @@
'use client';
import { useState, FormEvent } from 'react';
import { useState, useEffect, FormEvent } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import {
@@ -15,13 +15,14 @@ import {
Check,
X,
Gamepad2,
Loader2,
} 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 { getAuthService } from '@/lib/auth';
import { useAuth } from '@/lib/auth/AuthContext';
interface FormErrors {
displayName?: string;
@@ -54,9 +55,11 @@ function checkPasswordStrength(password: string): PasswordStrength {
export default function SignupPage() {
const router = useRouter();
const searchParams = useSearchParams();
const { refreshSession } = useAuth();
const returnTo = searchParams.get('returnTo') ?? '/onboarding';
const [loading, setLoading] = useState(false);
const [checkingAuth, setCheckingAuth] = useState(true);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [errors, setErrors] = useState<FormErrors>({});
@@ -67,6 +70,25 @@ export default function SignupPage() {
confirmPassword: '',
});
// Check if already authenticated
useEffect(() => {
async function checkAuth() {
try {
const response = await fetch('/api/auth/session');
const data = await response.json();
if (data.authenticated) {
// Already logged in, redirect to dashboard or return URL
router.replace(returnTo === '/onboarding' ? '/dashboard' : returnTo);
}
} catch {
// Not authenticated, continue showing signup page
} finally {
setCheckingAuth(false);
}
}
checkAuth();
}, [router, returnTo]);
const passwordStrength = checkPasswordStrength(formData.password);
const passwordRequirements = [
@@ -117,15 +139,25 @@ export default function SignupPage() {
setErrors({});
try {
const authService = getAuthService();
await authService.signupWithEmail({
email: formData.email,
password: formData.password,
displayName: formData.displayName,
const response = await fetch('/api/auth/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: formData.email,
password: formData.password,
displayName: formData.displayName,
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Signup failed');
}
// Refresh session in context so header updates immediately
await refreshSession();
router.push(returnTo);
router.refresh();
} catch (error) {
setErrors({
submit: error instanceof Error ? error.message : 'Signup failed. Please try again.',
@@ -137,10 +169,9 @@ export default function SignupPage() {
const handleDemoLogin = async () => {
setLoading(true);
try {
const authService = getAuthService();
const { redirectUrl } = await authService.startIracingAuthRedirect(returnTo);
router.push(redirectUrl);
} catch (error) {
// Redirect to iRacing auth start route
router.push(`/auth/iracing/start?returnTo=${encodeURIComponent(returnTo)}`);
} catch {
setErrors({
submit: 'Demo login failed. Please try again.',
});
@@ -148,6 +179,15 @@ export default function SignupPage() {
}
};
// Show loading while checking auth
if (checkingAuth) {
return (
<main className="min-h-screen bg-deep-graphite flex items-center justify-center">
<Loader2 className="w-8 h-8 text-primary-blue animate-spin" />
</main>
);
}
return (
<main className="min-h-screen bg-deep-graphite flex items-center justify-center px-4 py-12">
{/* Background Pattern */}
@@ -189,7 +229,7 @@ export default function SignupPage() {
onChange={(e) => setFormData({ ...formData, displayName: e.target.value })}
error={!!errors.displayName}
errorMessage={errors.displayName}
placeholder="SuperMax33"
placeholder="SpeedyRacer42"
disabled={loading}
className="pl-10"
autoComplete="username"