'use client'; import { useState, useEffect, useMemo } from 'react'; import { useRouter, useSearchParams } 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 Breadcrumbs from '@/components/layout/Breadcrumbs'; import { getGetAllRacesPageDataUseCase } from '@/lib/di-container'; import type { AllRacesPageViewModel, AllRacesListItemViewModel, } from '@gridpilot/racing/application/presenters/IAllRacesPagePresenter'; import { Calendar, Clock, Flag, ChevronRight, ChevronLeft, Filter, Car, Trophy, Zap, PlayCircle, CheckCircle2, XCircle, ArrowRight, Search, SlidersHorizontal, } from 'lucide-react'; const ITEMS_PER_PAGE = 10; type StatusFilter = 'scheduled' | 'running' | 'completed' | 'cancelled' | 'all'; export default function AllRacesPage() { const router = useRouter(); const searchParams = useSearchParams(); const [pageData, setPageData] = useState(null); const [loading, setLoading] = useState(true); // Pagination const [currentPage, setCurrentPage] = useState(1); // Filters const [statusFilter, setStatusFilter] = useState('all'); const [leagueFilter, setLeagueFilter] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); const [showFilters, setShowFilters] = useState(false); const loadRaces = async () => { try { const useCase = getGetAllRacesPageDataUseCase(); await useCase.execute(); const viewModel = useCase.presenter.getViewModel(); setPageData(viewModel); } catch (err) { console.error('Failed to load races:', err); } finally { setLoading(false); } }; useEffect(() => { void loadRaces(); }, []); const races: AllRacesListItemViewModel[] = pageData?.races ?? []; const filteredRaces = useMemo(() => { return races.filter(race => { if (statusFilter !== 'all' && race.status !== statusFilter) { return false; } if (leagueFilter !== 'all' && race.leagueId !== leagueFilter) { return false; } if (searchQuery) { const query = searchQuery.toLowerCase(); const matchesTrack = race.track.toLowerCase().includes(query); const matchesCar = race.car.toLowerCase().includes(query); const matchesLeague = race.leagueName.toLowerCase().includes(query); if (!matchesTrack && !matchesCar && !matchesLeague) { return false; } } return true; }); }, [races, statusFilter, leagueFilter, searchQuery]); // Paginate const totalPages = Math.ceil(filteredRaces.length / ITEMS_PER_PAGE); const paginatedRaces = useMemo(() => { const start = (currentPage - 1) * ITEMS_PER_PAGE; return filteredRaces.slice(start, start + ITEMS_PER_PAGE); }, [filteredRaces, currentPage]); // Reset page when filters change useEffect(() => { setCurrentPage(1); }, [statusFilter, leagueFilter, searchQuery]); const formatDate = (date: Date | string) => { const d = typeof date === 'string' ? new Date(date) : date; return d.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', }); }; const formatTime = (date: Date | string) => { const d = typeof date === 'string' ? new Date(date) : date; return d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', }); }; 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', }, }; const breadcrumbItems = [ { label: 'Races', href: '/races' }, { label: 'All Races' }, ]; if (loading) { return (
{[1, 2, 3, 4, 5].map(i => (
))}
); } return (
{/* Breadcrumbs */} {/* Header */}
All Races

{filteredRaces.length} race{filteredRaces.length !== 1 ? 's' : ''} found

{/* Search & Filters */}
{/* Search */}
setSearchQuery(e.target.value)} placeholder="Search by track, car, or league..." className="w-full pl-10 pr-4 py-2.5 bg-deep-graphite border border-charcoal-outline rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-blue" />
{/* Filter Row */}
{/* Status Filter */} {/* League Filter */} {/* Clear Filters */} {(statusFilter !== 'all' || leagueFilter !== 'all' || searchQuery) && ( )}
{/* Race List */} {paginatedRaces.length === 0 ? (

No races found

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

) : (
{paginatedRaces.map(race => { const config = statusConfig[race.status]; const StatusIcon = config.icon; 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' && (
)}
{/* Date Column */}

{new Date(race.scheduledAt).toLocaleDateString('en-US', { month: 'short' })}

{new Date(race.scheduledAt).getDate()}

{formatTime(race.scheduledAt)}

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

{race.track}

{race.car} {race.strengthOfField && ( SOF {race.strengthOfField} )} {formatDate(race.scheduledAt)}
e.stopPropagation()} className="inline-flex items-center gap-1.5 mt-2 text-sm text-primary-blue hover:underline" > {race.leagueName}
{/* Status Badge */}
{config.label}
{/* Arrow */}
); })}
)} {/* Pagination */} {totalPages > 1 && (

Showing {((currentPage - 1) * ITEMS_PER_PAGE) + 1}–{Math.min(currentPage * ITEMS_PER_PAGE, filteredRaces.length)} of {filteredRaces.length}

{Array.from({ length: Math.min(5, totalPages) }, (_, i) => { let pageNum: number; if (totalPages <= 5) { pageNum = i + 1; } else if (currentPage <= 3) { pageNum = i + 1; } else if (currentPage >= totalPages - 2) { pageNum = totalPages - 4 + i; } else { pageNum = currentPage - 2 + i; } return ( ); })}
)}
); }