"use client"; import { useState } from "react"; import { Protest } from "@gridpilot/racing/domain/entities/Protest"; import { PenaltyType } from "@gridpilot/racing/domain/entities/Penalty"; import Modal from "../ui/Modal"; import Button from "../ui/Button"; import Card from "../ui/Card"; import { AlertCircle, Video, Clock, Grid3x3, TrendingDown, CheckCircle, XCircle, FileText, AlertTriangle, ShieldAlert, Ban, DollarSign, FileWarning, } from "lucide-react"; interface ReviewProtestModalProps { protest: Protest | null; onClose: () => void; onAccept: ( protestId: string, penaltyType: PenaltyType, penaltyValue: number, stewardNotes: string ) => Promise; onReject: (protestId: string, stewardNotes: string) => Promise; } export function ReviewProtestModal({ protest, onClose, onAccept, onReject, }: ReviewProtestModalProps) { const [decision, setDecision] = useState<"accept" | "reject" | null>(null); const [penaltyType, setPenaltyType] = useState("time_penalty"); const [penaltyValue, setPenaltyValue] = useState(5); const [stewardNotes, setStewardNotes] = useState(""); const [submitting, setSubmitting] = useState(false); const [showConfirmation, setShowConfirmation] = useState(false); if (!protest) return null; const handleSubmit = async () => { if (!decision || !stewardNotes.trim()) return; setSubmitting(true); try { if (decision === "accept") { await onAccept(protest.id, penaltyType, penaltyValue, stewardNotes); } else { await onReject(protest.id, stewardNotes); } onClose(); } catch (err) { alert(err instanceof Error ? err.message : "Failed to submit decision"); } finally { setSubmitting(false); setShowConfirmation(false); } }; const getPenaltyIcon = (type: PenaltyType) => { switch (type) { case "time_penalty": return Clock; case "grid_penalty": return Grid3x3; case "points_deduction": return TrendingDown; case "disqualification": return XCircle; case "warning": return AlertTriangle; case "license_points": return ShieldAlert; case "probation": return FileWarning; case "fine": return DollarSign; case "race_ban": return Ban; default: return AlertCircle; } }; const getPenaltyLabel = (type: PenaltyType) => { switch (type) { case "time_penalty": return "seconds"; case "grid_penalty": return "grid positions"; case "points_deduction": return "points"; case "license_points": return "points"; case "fine": return "points"; case "race_ban": return "races"; default: return ""; } }; const getPenaltyColor = (type: PenaltyType) => { switch (type) { case "time_penalty": return "text-blue-400 bg-blue-500/10 border-blue-500/30"; case "grid_penalty": return "text-purple-400 bg-purple-500/10 border-purple-500/30"; case "points_deduction": return "text-red-400 bg-red-500/10 border-red-500/30"; case "disqualification": return "text-red-500 bg-red-500/10 border-red-500/30"; case "warning": return "text-yellow-400 bg-yellow-500/10 border-yellow-500/30"; case "license_points": return "text-orange-400 bg-orange-500/10 border-orange-500/30"; case "probation": return "text-amber-400 bg-amber-500/10 border-amber-500/30"; case "fine": return "text-green-400 bg-green-500/10 border-green-500/30"; case "race_ban": return "text-red-600 bg-red-600/10 border-red-600/30"; default: return "text-warning-amber bg-warning-amber/10 border-warning-amber/30"; } }; if (showConfirmation) { return ( setShowConfirmation(false)}>
{decision === "accept" ? (
) : (
)}

Confirm Decision

{decision === "accept" ? `Issue ${penaltyValue} ${getPenaltyLabel(penaltyType)} penalty?` : "Reject this protest?"}

{stewardNotes}

); } return (

Review Protest

Protest #{protest.id.substring(0, 8)}

Filed Date {new Date(protest.filedAt).toLocaleString()}
Incident Lap Lap {protest.incident.lap}
Status {protest.status}

{protest.incident.description}

{protest.comment && (

{protest.comment}

)} {protest.proofVideoUrl && ( )}

Stewarding Decision

{decision === "accept" && (
{[ { type: "time_penalty" as PenaltyType, label: "Time Penalty" }, { type: "grid_penalty" as PenaltyType, label: "Grid Penalty" }, { type: "points_deduction" as PenaltyType, label: "Points Deduction" }, { type: "disqualification" as PenaltyType, label: "Disqualification" }, { type: "warning" as PenaltyType, label: "Warning" }, { type: "license_points" as PenaltyType, label: "License Points" }, { type: "probation" as PenaltyType, label: "Probation" }, { type: "fine" as PenaltyType, label: "Fine" }, { type: "race_ban" as PenaltyType, label: "Race Ban" }, ].map(({ type, label }) => { const Icon = getPenaltyIcon(type); const colorClass = getPenaltyColor(type); const isSelected = penaltyType === type; return ( ); })}
{['time_penalty', 'grid_penalty', 'points_deduction', 'license_points', 'fine', 'race_ban'].includes(penaltyType) && (
setPenaltyValue(Number(e.target.value))} min="1" className="w-full px-4 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white focus:outline-none focus:border-orange-500" />
)}
)}