'use client'; import { StewardingQueuePanel } from '@/components/leagues/StewardingQueuePanel'; import { PenaltyFAB } from '@/components/races/PenaltyFAB'; import { QuickPenaltyModal } from '@/components/leagues/QuickPenaltyModal'; import { ReviewProtestModal } from '@/components/leagues/ReviewProtestModal'; import { StewardingStats } from '@/components/leagues/StewardingStats'; import { Button } from '@/ui/Button'; import { Card } from '@/ui/Card'; import { useLeagueStewardingMutations } from "@/hooks/league/useLeagueStewardingMutations"; import { useMemo, useState } from 'react'; import { PenaltyHistoryList } from '@/components/leagues/PenaltyHistoryList'; import { Box } from '@/ui/Box'; import { Stack } from '@/ui/Stack'; import { Text } from '@/ui/Text'; import type { StewardingViewData } from '@/lib/view-data/leagues/StewardingViewData'; import { ProtestViewModel } from '@/lib/view-models/ProtestViewModel'; import { RaceViewModel } from '@/lib/view-models/RaceViewModel'; import { DriverViewModel } from '@/lib/view-models/DriverViewModel'; interface StewardingTemplateProps { data: StewardingViewData; leagueId: string; currentDriverId: string; onRefetch: () => void; } export function StewardingPageClient({ data, currentDriverId, onRefetch }: StewardingTemplateProps) { const [activeTab, setActiveTab] = useState<'pending' | 'history'>('pending'); const [selectedProtest, setSelectedProtest] = useState(null); const [showQuickPenaltyModal, setShowQuickPenaltyModal] = useState(false); // Mutations using domain hook const { acceptProtestMutation, rejectProtestMutation } = useLeagueStewardingMutations(onRefetch); // Flatten protests for the specialized list components const allPendingProtests = useMemo(() => { return data.races.flatMap(r => r.pendingProtests.map(p => ({ id: p.id, raceName: r.track || 'Unknown Track', protestingDriver: data.drivers.find(d => d.id === p.protestingDriverId)?.name || 'Unknown', accusedDriver: data.drivers.find(d => d.id === p.accusedDriverId)?.name || 'Unknown', description: p.incident.description, submittedAt: p.filedAt, status: p.status as 'pending' | 'under_review' | 'resolved' | 'rejected', }))); }, [data.races, data.drivers]); const allResolvedProtests = useMemo(() => { return data.races.flatMap(r => r.resolvedProtests.map(p => new ProtestViewModel({ id: p.id, protestingDriverId: p.protestingDriverId, accusedDriverId: p.accusedDriverId, description: p.incident.description, submittedAt: p.filedAt, status: p.status, raceId: r.id, incident: p.incident, proofVideoUrl: p.proofVideoUrl, decisionNotes: p.decisionNotes, } as never))); }, [data.races]); const racesMap = useMemo(() => { const map: Record = {}; data.races.forEach(r => { map[r.id] = new RaceViewModel({ id: r.id, name: '', date: r.scheduledAt, track: r.track, } as never); }); return map; }, [data.races]); const driverMap = useMemo(() => { const map: Record = {}; data.drivers.forEach(d => { map[d.id] = new DriverViewModel({ id: d.id, name: d.name, iracingId: '', country: '', joinedAt: '', avatarUrl: null, }); }); return map; }, [data.drivers]); const handleAcceptProtest = async ( protestId: string, penaltyType: string, penaltyValue: number, stewardNotes: string ) => { // Find the protest to get details for penalty let foundProtest: { raceId: string; accusedDriverId: string; incident: { description: string } } | undefined; data.races.forEach((raceData) => { const p = raceData.pendingProtests.find((pr) => pr.id === protestId) || raceData.resolvedProtests.find((pr) => pr.id === protestId); if (p) foundProtest = { raceId: raceData.id, accusedDriverId: p.accusedDriverId, incident: { description: p.incident.description } }; }); if (foundProtest) { acceptProtestMutation.mutate({ protestId, penaltyType, penaltyValue, stewardNotes, raceId: foundProtest.raceId, accusedDriverId: foundProtest.accusedDriverId, reason: foundProtest.incident.description, }); } }; const handleRejectProtest = async (protestId: string, stewardNotes: string) => { rejectProtestMutation.mutate({ protestId, stewardNotes, }); }; const handleReviewProtest = (id: string) => { // Find the protest in the data let foundProtest: ProtestViewModel | null = null; data.races.forEach(r => { const p = r.pendingProtests.find(p => p.id === id); if (p) { foundProtest = new ProtestViewModel({ id: p.id, protestingDriverId: p.protestingDriverId, accusedDriverId: p.accusedDriverId, description: p.incident.description, submittedAt: p.filedAt, status: p.status, raceId: r.id, incident: p.incident, proofVideoUrl: p.proofVideoUrl, decisionNotes: p.decisionNotes, } as never); } }); if (foundProtest) setSelectedProtest(foundProtest); }; return ( {/* Tab navigation */} {/* Content */} {activeTab === 'pending' ? ( ) : ( )} {activeTab === 'history' && ( setShowQuickPenaltyModal(true)} /> )} {selectedProtest && ( setSelectedProtest(null)} onAccept={handleAcceptProtest} onReject={handleRejectProtest} /> )} {showQuickPenaltyModal && ( new DriverViewModel({ id: d.id, name: d.name, iracingId: '', country: '', joinedAt: '', avatarUrl: null, }))} onClose={() => setShowQuickPenaltyModal(false)} adminId={currentDriverId || ''} races={data.races.map(r => ({ id: r.id, track: r.track, scheduledAt: new Date(r.scheduledAt) }))} /> )} ); }