'use client'; import { useState, useEffect, useMemo } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import Heading from '@/components/ui/Heading'; import { Race, RaceStatus } from '@gridpilot/racing/domain/entities/Race'; import { League } from '@gridpilot/racing/domain/entities/League'; import { getRaceRepository, getLeagueRepository } from '@/lib/di-container'; import { Calendar, Clock, Flag, ChevronRight, Filter, MapPin, Car, Trophy, Users, Zap, PlayCircle, CheckCircle2, XCircle, CalendarDays, ArrowRight, } from 'lucide-react'; type TimeFilter = 'all' | 'upcoming' | 'live' | 'past'; export default function RacesPage() { const router = useRouter(); const [races, setRaces] = useState([]); const [leagues, setLeagues] = useState>(new Map()); const [loading, setLoading] = useState(true); // Filters const [statusFilter, setStatusFilter] = useState('all'); const [leagueFilter, setLeagueFilter] = useState('all'); const [timeFilter, setTimeFilter] = useState('upcoming'); const loadRaces = async () => { try { const raceRepo = getRaceRepository(); const leagueRepo = getLeagueRepository(); const [allRaces, allLeagues] = await Promise.all([ raceRepo.findAll(), leagueRepo.findAll() ]); setRaces(allRaces.sort((a, b) => a.scheduledAt.getTime() - b.scheduledAt.getTime())); const leagueMap = new Map(); allLeagues.forEach(league => leagueMap.set(league.id, league)); setLeagues(leagueMap); } catch (err) { console.error('Failed to load races:', err); } finally { setLoading(false); } }; useEffect(() => { loadRaces(); }, []); // Filter races const filteredRaces = useMemo(() => { return races.filter(race => { // Status filter if (statusFilter !== 'all' && race.status !== statusFilter) { return false; } // League filter if (leagueFilter !== 'all' && race.leagueId !== leagueFilter) { return false; } // Time filter if (timeFilter === 'upcoming' && !race.isUpcoming()) { return false; } if (timeFilter === 'live' && !race.isLive()) { return false; } if (timeFilter === 'past' && !race.isPast()) { return false; } return true; }); }, [races, statusFilter, leagueFilter, timeFilter]); // Group races by date for calendar view const racesByDate = useMemo(() => { const grouped = new Map(); filteredRaces.forEach(race => { const dateKey = race.scheduledAt.toISOString().split('T')[0]; if (!grouped.has(dateKey)) { grouped.set(dateKey, []); } grouped.get(dateKey)!.push(race); }); return grouped; }, [filteredRaces]); // Get upcoming races (next 7 days) const upcomingRaces = useMemo(() => { const now = new Date(); const nextWeek = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); return races.filter(race => race.isUpcoming() && race.scheduledAt >= now && race.scheduledAt <= nextWeek ).slice(0, 5); }, [races]); // Get live races const liveRaces = useMemo(() => { return races.filter(race => race.isLive()); }, [races]); // Get recent results const recentResults = useMemo(() => { return races .filter(race => race.status === 'completed') .sort((a, b) => b.scheduledAt.getTime() - a.scheduledAt.getTime()) .slice(0, 3); }, [races]); // Stats const stats = useMemo(() => { const total = races.length; const scheduled = races.filter(r => r.status === 'scheduled').length; const running = races.filter(r => r.status === 'running').length; const completed = races.filter(r => r.status === 'completed').length; return { total, scheduled, running, completed }; }, [races]); const formatDate = (date: Date) => { return new Date(date).toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', }); }; const formatTime = (date: Date) => { return new Date(date).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', }); }; const formatFullDate = (date: Date) => { return new Date(date).toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric', }); }; const getRelativeTime = (date: Date) => { const now = new Date(); const targetDate = new Date(date); const diffMs = targetDate.getTime() - now.getTime(); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); if (diffMs < 0) return 'Past'; if (diffHours < 1) return 'Starting soon'; if (diffHours < 24) return `In ${diffHours}h`; if (diffDays === 1) return 'Tomorrow'; if (diffDays < 7) return `In ${diffDays} days`; return formatDate(date); }; const statusConfig = { scheduled: { icon: Clock, color: 'text-primary-blue', bg: 'bg-primary-blue/10', border: 'border-primary-blue/30', label: 'Scheduled', }, running: { icon: PlayCircle, color: 'text-performance-green', bg: 'bg-performance-green/10', border: 'border-performance-green/30', label: 'LIVE', }, completed: { icon: CheckCircle2, color: 'text-gray-400', bg: 'bg-gray-500/10', border: 'border-gray-500/30', label: 'Completed', }, cancelled: { icon: XCircle, color: 'text-warning-amber', bg: 'bg-warning-amber/10', border: 'border-warning-amber/30', label: 'Cancelled', }, }; if (loading) { return (
{[1, 2, 3, 4].map(i => (
))}
); } return (
{/* Hero Header */}
Race Calendar

Track upcoming races, view live events, and explore results across all your leagues.

{/* Quick Stats */}
Total

{stats.total}

Scheduled

{stats.scheduled}

Live Now

{stats.running}

Completed

{stats.completed}

{/* Live Races Banner */} {liveRaces.length > 0 && (
LIVE NOW
{liveRaces.map(race => (
router.push(`/races/${race.id}`)} className="flex items-center justify-between p-4 bg-deep-graphite/80 rounded-lg border border-performance-green/20 cursor-pointer hover:border-performance-green/40 transition-all" >

{race.track}

{leagues.get(race.leagueId)?.name}

))}
)}
{/* Main Content - Race List */}
{/* Filters */}
{/* Time Filter Tabs */}
{(['upcoming', 'live', 'past', 'all'] as TimeFilter[]).map(filter => ( ))}
{/* League Filter */}
{/* Race List by Date */} {filteredRaces.length === 0 ? (

No races found

{races.length === 0 ? 'No races have been scheduled yet' : 'Try adjusting your filters'}

) : (
{Array.from(racesByDate.entries()).map(([dateKey, dayRaces]) => (
{/* Date Header */}
{formatFullDate(new Date(dateKey))} {dayRaces.length} race{dayRaces.length !== 1 ? 's' : ''}
{/* Races for this date */}
{dayRaces.map(race => { const config = statusConfig[race.status]; const StatusIcon = config.icon; const league = leagues.get(race.leagueId); return (
router.push(`/races/${race.id}`)} className={`group relative overflow-hidden rounded-xl bg-iron-gray border ${config.border} p-4 cursor-pointer transition-all duration-200 hover:scale-[1.01] hover:border-primary-blue`} > {/* Live indicator */} {race.status === 'running' && (
)}
{/* Time Column */}

{formatTime(race.scheduledAt)}

{race.status === 'running' ? 'LIVE' : getRelativeTime(race.scheduledAt)}

{/* Divider */}
{/* Main Content */}

{race.track}

{race.car} {race.strengthOfField && ( SOF {race.strengthOfField} )}
{/* Status Badge */}
{config.label}
{/* League Link */} {league && (
e.stopPropagation()} className="inline-flex items-center gap-2 text-sm text-primary-blue hover:underline" > {league.name}
)}
{/* Arrow */}
); })}
))}
)} {/* View All Link */} {filteredRaces.length > 0 && (
View All Races
)}
{/* Sidebar */}
{/* Upcoming This Week */}

Next Up

This week
{upcomingRaces.length === 0 ? (

No races scheduled this week

) : (
{upcomingRaces.map((race, index) => (
router.push(`/races/${race.id}`)} className="flex items-center gap-3 p-2 rounded-lg hover:bg-deep-graphite cursor-pointer transition-colors" >
{new Date(race.scheduledAt).getDate()}

{race.track}

{formatTime(race.scheduledAt)}

))}
)}
{/* Recent Results */}

Recent Results

{recentResults.length === 0 ? (

No completed races yet

) : (
{recentResults.map(race => (
router.push(`/races/${race.id}/results`)} className="flex items-center gap-3 p-2 rounded-lg hover:bg-deep-graphite cursor-pointer transition-colors" >

{race.track}

{formatDate(race.scheduledAt)}

))}
)}
{/* Quick Actions */}

Quick Actions

Browse Leagues
View Leaderboards
); }