'use client'; import { useState, useEffect } from 'react'; import { useRouter, useParams } from 'next/navigation'; import { ArrowLeft, Zap, Trophy, Users, Clock, Calendar } from 'lucide-react'; import Button from '@/components/ui/Button'; import Card from '@/components/ui/Card'; import Breadcrumbs from '@/components/layout/Breadcrumbs'; import ResultsTable from '@/components/races/ResultsTable'; import ImportResultsForm from '@/components/races/ImportResultsForm'; import { getGetRaceWithSOFUseCase, getGetRaceResultsDetailUseCase, getImportRaceResultsUseCase, } from '@/lib/di-container'; import type { RaceResultsHeaderViewModel, RaceResultsLeagueViewModel, } from '@gridpilot/racing/application/presenters/IRaceResultsDetailPresenter'; type PenaltyTypeDTO = | 'time_penalty' | 'grid_penalty' | 'points_deduction' | 'disqualification' | 'warning' | 'license_points' | string; interface PenaltyData { driverId: string; type: PenaltyTypeDTO; value?: number; } interface RaceResultRowDTO { id: string; raceId: string; driverId: string; position: number; fastestLap: number; incidents: number; startPosition: number; getPositionChange(): number; } interface DriverRowDTO { id: string; name: string; } interface ImportResultRowDTO { id: string; raceId: string; driverId: string; position: number; fastestLap: number; incidents: number; startPosition: number; } export default function RaceResultsPage() { const router = useRouter(); const params = useParams(); const raceId = params.id as string; const [race, setRace] = useState(null); const [league, setLeague] = useState(null); const [results, setResults] = useState([]); const [drivers, setDrivers] = useState([]); const [currentDriverId, setCurrentDriverId] = useState(undefined); const [raceSOF, setRaceSOF] = useState(null); const [penalties, setPenalties] = useState([]); const [pointsSystem, setPointsSystem] = useState>({}); const [fastestLapTime, setFastestLapTime] = useState(undefined); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [importing, setImporting] = useState(false); const [importSuccess, setImportSuccess] = useState(false); const loadData = async () => { try { const raceResultsUseCase = getGetRaceResultsDetailUseCase(); await raceResultsUseCase.execute({ raceId }); const viewModel = raceResultsUseCase.presenter.getViewModel(); if (!viewModel) { setError('Failed to load race data'); setLoading(false); return; } if (viewModel.error && !viewModel.race) { setError(viewModel.error); setRace(null); setLeague(null); setResults([]); setDrivers([]); setPenalties([]); setPointsSystem({}); setFastestLapTime(undefined); setCurrentDriverId(undefined); } else { setError(null); setRace(viewModel.race); setLeague(viewModel.league); setResults(viewModel.results as unknown as RaceResultRowDTO[]); setDrivers( viewModel.drivers.map((d) => ({ id: d.id, name: d.name, })), ); setPointsSystem(viewModel.pointsSystem); setFastestLapTime(viewModel.fastestLapTime); setCurrentDriverId(viewModel.currentDriverId); const mappedPenalties: PenaltyData[] = viewModel.penalties.map((p) => { const base: PenaltyData = { driverId: p.driverId, type: p.type as PenaltyTypeDTO, }; if (typeof p.value === 'number') { return { ...base, value: p.value }; } return base; }); setPenalties(mappedPenalties); } try { const raceWithSOFUseCase = getGetRaceWithSOFUseCase(); await raceWithSOFUseCase.execute({ raceId }); const raceViewModel = raceWithSOFUseCase.presenter.getViewModel(); if (raceViewModel) { setRaceSOF(raceViewModel.strengthOfField); } } catch (sofErr) { console.error('Failed to load SOF:', sofErr); } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load race data'); } finally { setLoading(false); } }; useEffect(() => { loadData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [raceId]); const handleImportSuccess = async (importedResults: ImportResultRowDTO[]) => { setImporting(true); setError(null); try { const importUseCase = getImportRaceResultsUseCase(); await importUseCase.execute({ raceId, results: importedResults, }); setImportSuccess(true); await loadData(); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to import results'); } finally { setImporting(false); } }; const handleImportError = (errorMessage: string) => { setError(errorMessage); }; if (loading) { return (
Loading results...
); } if (error && !race) { return (
{error || 'Race not found'}
); } const hasResults = results.length > 0; const breadcrumbItems = [ { label: 'Races', href: '/races' }, ...(league ? [{ label: league.name, href: `/leagues/${league.id}` }] : []), ...(race ? [{ label: race.track, href: `/races/${race.id}` }] : []), { label: 'Results' }, ]; return (
Final Results
{raceSOF && ( SOF {raceSOF} )}

{race?.track ?? 'Race'} Results

{race && ( <> {new Date(race.scheduledAt).toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', })} {results.length} drivers classified )} {league && {league.name}}
{importSuccess && (
Success! Results imported and standings updated.
)} {error && (
Error: {error}
)} {hasResults ? ( ) : ( <>

Import Results

No results imported. Upload CSV to test the standings system.

{importing ? (
Importing results and updating standings...
) : ( )} )}
); }