'use client'; import { useState, useEffect } from 'react'; import { useParams, useRouter } from 'next/navigation'; import Link from 'next/link'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import Breadcrumbs from '@/components/layout/Breadcrumbs'; import { getRaceRepository, getLeagueRepository, getProtestRepository, getDriverRepository, getPenaltyRepository, getLeagueMembershipRepository, getReviewProtestUseCase, getApplyPenaltyUseCase, } from '@/lib/di-container'; import { useEffectiveDriverId } from '@/lib/currentDriver'; import { isLeagueAdminOrHigherRole } from '@/lib/leagueRoles'; import type { Race } from '@gridpilot/racing/domain/entities/Race'; import type { League } from '@gridpilot/racing/domain/entities/League'; import type { Protest } from '@gridpilot/racing/domain/entities/Protest'; import type { Penalty, PenaltyType } from '@gridpilot/racing/domain/entities/Penalty'; import type { DriverDTO } from '@gridpilot/racing/application/dto/DriverDTO'; import { EntityMappers } from '@gridpilot/racing/application/mappers/EntityMappers'; import { AlertTriangle, Clock, CheckCircle, Flag, Calendar, MapPin, AlertCircle, Video, Gavel, ArrowLeft, Scale, ChevronRight, Users, Trophy, } from 'lucide-react'; export default function RaceStewardingPage() { const params = useParams(); const router = useRouter(); const raceId = params.id as string; const currentDriverId = useEffectiveDriverId(); const [race, setRace] = useState(null); const [league, setLeague] = useState(null); const [protests, setProtests] = useState([]); const [penalties, setPenalties] = useState([]); const [driversById, setDriversById] = useState>({}); const [loading, setLoading] = useState(true); const [isAdmin, setIsAdmin] = useState(false); const [activeTab, setActiveTab] = useState<'pending' | 'resolved' | 'penalties'>('pending'); useEffect(() => { async function loadData() { setLoading(true); try { const raceRepo = getRaceRepository(); const leagueRepo = getLeagueRepository(); const protestRepo = getProtestRepository(); const penaltyRepo = getPenaltyRepository(); const driverRepo = getDriverRepository(); const membershipRepo = getLeagueMembershipRepository(); // Get race const raceData = await raceRepo.findById(raceId); if (!raceData) { setLoading(false); return; } setRace(raceData); // Get league const leagueData = await leagueRepo.findById(raceData.leagueId); setLeague(leagueData); // Check admin status if (leagueData) { const membership = await membershipRepo.getMembership(leagueData.id, currentDriverId); setIsAdmin(membership ? isLeagueAdminOrHigherRole(membership.role) : false); } // Get protests for this race const raceProtests = await protestRepo.findByRaceId(raceId); setProtests(raceProtests); // Get penalties for this race const racePenalties = await penaltyRepo.findByRaceId(raceId); setPenalties(racePenalties); // Collect driver IDs const driverIds = new Set(); raceProtests.forEach((p) => { driverIds.add(p.protestingDriverId); driverIds.add(p.accusedDriverId); }); racePenalties.forEach((p) => { driverIds.add(p.driverId); }); // Load driver info const driverEntities = await Promise.all( Array.from(driverIds).map((id) => driverRepo.findById(id)) ); const byId: Record = {}; driverEntities.forEach((driver) => { if (driver) { const dto = EntityMappers.toDriverDTO(driver); if (dto) { byId[dto.id] = dto; } } }); setDriversById(byId); } catch (err) { console.error('Failed to load data:', err); } finally { setLoading(false); } } loadData(); }, [raceId, currentDriverId]); const pendingProtests = protests.filter( (p) => p.status === 'pending' || p.status === 'under_review' ); const resolvedProtests = protests.filter( (p) => p.status === 'upheld' || p.status === 'dismissed' || p.status === 'withdrawn' ); const getStatusBadge = (status: string) => { switch (status) { case 'pending': case 'under_review': return ( Pending ); case 'upheld': return ( Upheld ); case 'dismissed': return ( Dismissed ); case 'withdrawn': return ( Withdrawn ); default: return null; } }; const formatDate = (date: Date) => { return new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', }); }; if (loading) { return (
); } if (!race) { return (

Race not found

The race you're looking for doesn't exist.

); } const breadcrumbItems = [ { label: 'Races', href: '/races' }, { label: race.track, href: `/races/${race.id}` }, { label: 'Stewarding' }, ]; return (
{/* Navigation */}
{/* Header */}

Stewarding

{race.track} • {formatDate(race.scheduledAt)}

{/* Stats */}
Pending
{pendingProtests.length}
Resolved
{resolvedProtests.length}
Penalties
{penalties.length}
{/* Tab Navigation */}
{/* Content */} {activeTab === 'pending' && (
{pendingProtests.length === 0 ? (

All Clear!

No pending protests to review

) : ( pendingProtests.map((protest) => { const protester = driversById[protest.protestingDriverId]; const accused = driversById[protest.accusedDriverId]; const daysSinceFiled = Math.floor( (Date.now() - new Date(protest.filedAt).getTime()) / (1000 * 60 * 60 * 24) ); const isUrgent = daysSinceFiled > 2; return (
{protester?.name || 'Unknown'} vs {accused?.name || 'Unknown'} {getStatusBadge(protest.status)} {isUrgent && ( {daysSinceFiled}d old )}
Lap {protest.incident.lap} Filed {formatDate(protest.filedAt)} {protest.proofVideoUrl && ( <> )}

{protest.incident.description}

{isAdmin && league && ( )}
); }) )}
)} {activeTab === 'resolved' && (
{resolvedProtests.length === 0 ? (

No Resolved Protests

Resolved protests will appear here

) : ( resolvedProtests.map((protest) => { const protester = driversById[protest.protestingDriverId]; const accused = driversById[protest.accusedDriverId]; return (
{protester?.name || 'Unknown'} vs {accused?.name || 'Unknown'} {getStatusBadge(protest.status)}
Lap {protest.incident.lap} Filed {formatDate(protest.filedAt)}

{protest.incident.description}

{protest.decisionNotes && (

Steward Decision

{protest.decisionNotes}

)}
); }) )}
)} {activeTab === 'penalties' && (
{penalties.length === 0 ? (

No Penalties

Penalties issued for this race will appear here

) : ( penalties.map((penalty) => { const driver = driversById[penalty.driverId]; return (
{driver?.name || 'Unknown'} {penalty.type.replace('_', ' ')}

{penalty.reason}

{penalty.notes && (

{penalty.notes}

)}
{penalty.type === 'time_penalty' && `+${penalty.value}s`} {penalty.type === 'grid_penalty' && `+${penalty.value} grid`} {penalty.type === 'points_deduction' && `-${penalty.value} pts`} {penalty.type === 'disqualification' && 'DSQ'} {penalty.type === 'warning' && 'Warning'} {penalty.type === 'license_points' && `${penalty.value} LP`}
); }) )}
)}
); }