import { TemplateProps } from '@/lib/contracts/components/ComponentContracts'; import { ViewData } from '@/lib/contracts/view-data/ViewData'; import { routes } from '@/lib/routing/RouteConfig'; import { Box } from '@/ui/Box'; import { Button } from '@/ui/Button'; import { Card } from '@/ui/Card'; import { Container } from '@/ui/Container'; import { Grid } from '@/ui/Grid'; import { GridItem } from '@/ui/GridItem'; import { Heading } from '@/ui/Heading'; import { Icon } from '@/ui/Icon'; import { Link as UILink } from '@/ui/Link'; import { Stack } from '@/ui/Stack'; import { Text } from '@/ui/Text'; import { AlertCircle, AlertTriangle, ArrowLeft, CheckCircle, ChevronDown, ExternalLink, Flag, Gavel, MapPin, Calendar, MessageCircle, Send, User, Video, XCircle } from 'lucide-react'; interface ProtestDetailTemplateProps extends TemplateProps { protestDetail: any; leagueId: string; showDecisionPanel: boolean; setShowDecisionPanel: (show: boolean) => void; decision: 'uphold' | 'dismiss' | null; setDecision: (decision: 'uphold' | 'dismiss' | null) => void; penaltyType: string; setPenaltyType: (type: string) => void; penaltyValue: number; setPenaltyValue: (value: number) => void; stewardNotes: string; setStewardNotes: (notes: string) => void; submitting: boolean; newComment: string; setNewComment: (comment: string) => void; penaltyTypes: any[]; selectedPenalty: any; onSubmitDecision: () => void; onRequestDefense: () => void; getStatusConfig: (status: string) => any; } export function ProtestDetailTemplate({ protestDetail, leagueId, showDecisionPanel, setShowDecisionPanel, decision, setDecision, penaltyType, setPenaltyType, penaltyValue, setPenaltyValue, stewardNotes, setStewardNotes, submitting, newComment, setNewComment, penaltyTypes, selectedPenalty, onSubmitDecision, onRequestDefense, getStatusConfig, }: ProtestDetailTemplateProps) { if (!protestDetail) return null; const protest = protestDetail.protest || protestDetail; const race = protestDetail.race; const protestingDriver = protestDetail.protestingDriver; const accusedDriver = protestDetail.accusedDriver; const statusConfig = getStatusConfig(protest.status); const StatusIcon = statusConfig.icon; const isPending = protest.status === 'pending'; const submittedAt = protest.submittedAt || protestDetail.submittedAt; const daysSinceFiled = Math.floor((Date.now() - new Date(submittedAt).getTime()) / (1000 * 60 * 60 * 24)); return ( {/* Compact Header */} Protest Review {statusConfig.label} {daysSinceFiled > 2 && isPending && ( {daysSinceFiled}d old )} {/* Main Layout: Feed + Sidebar */} {/* Left Sidebar - Incident Info */} {/* Drivers Involved */} Parties Involved {/* Protesting Driver */} Protesting {protestingDriver?.name || 'Unknown'} {/* Accused Driver */} Accused {accusedDriver?.name || 'Unknown'} {/* Race Info */} Race Details {race?.name || 'Unknown Race'} {race?.name || 'Unknown Track'} {race?.formattedDate || (race?.scheduledAt ? new Date(race.scheduledAt).toLocaleDateString() : 'Unknown Date')} {protest.incident?.lap && ( Lap {protest.incident.lap} )} {protest.proofVideoUrl && ( Evidence Watch Video )} {/* Quick Stats */} Timeline Filed {new Date(submittedAt).toLocaleDateString()} Age 2 ? 'text-red-400' : 'text-gray-300'}>{daysSinceFiled} days {protest.reviewedAt && ( Resolved {new Date(protest.reviewedAt).toLocaleDateString()} )} {/* Center - Discussion Feed */} {/* Timeline / Feed */} Discussion {/* Initial Protest Filing */} {protestingDriver?.name || 'Unknown'} filed protest {new Date(submittedAt).toLocaleString()} {protest.description || protestDetail.incident?.description} {(protest.comment || protestDetail.comment) && ( Additional details: {protest.comment || protestDetail.comment} )} {/* Defense placeholder */} {protest.status === 'awaiting_defense' && ( Defense Requested Waiting for {accusedDriver?.name || 'the accused driver'} to submit their defense... )} {/* Decision (if resolved) */} {(protest.status === 'upheld' || protest.status === 'dismissed') && protest.decisionNotes && ( Steward Decision {protest.status === 'upheld' ? 'Protest Upheld' : 'Protest Dismissed'} {protest.reviewedAt && ( <> {new Date(protest.reviewedAt).toLocaleString()} )} {protest.decisionNotes} )} {/* Add Comment */} {isPending && ( ) => setNewComment(e.target.value)} placeholder="Add a comment or request more information..." style={{ height: '4rem' }} w="full" px={4} py={3} bg="bg-deep-graphite" border borderColor="border-charcoal-outline" rounded="lg" color="text-white" fontSize="sm" /> )} {/* Right Sidebar - Actions */} {isPending && ( <> {/* Quick Actions */} Actions {/* Decision Panel */} {showDecisionPanel && ( Stewarding Decision {/* Decision Selection */} {/* Penalty Selection (if upholding) */} {decision === 'uphold' && ( Penalty Type {penaltyTypes.length === 0 ? ( Loading penalty types... ) : ( <> {penaltyTypes.map((penalty: any) => { const Icon = penalty.icon; const isSelected = penaltyType === penalty.type; return ( ); })} {selectedPenalty?.requiresValue && ( Value ({selectedPenalty.valueLabel}) ) => setPenaltyValue(Number(e.target.value))} min="1" w="full" px={3} py={2} bg="bg-deep-graphite" border borderColor="border-charcoal-outline" rounded="lg" color="text-white" fontSize="sm" /> )} )} )} {/* Steward Notes */} Decision Reasoning * ) => setStewardNotes(e.target.value)} placeholder="Explain your decision..." style={{ height: '8rem' }} w="full" px={3} py={2} bg="bg-deep-graphite" border borderColor="border-charcoal-outline" rounded="lg" color="text-white" fontSize="sm" /> {/* Submit */} )} )} {/* Already Resolved Info */} {!isPending && ( Case Closed {protest.status === 'upheld' ? 'Protest was upheld' : 'Protest was dismissed'} )} ); }