import { useState } from "react"; import { CheckCircle, RotateCcw, AlertTriangle, Loader2, Shield, ShieldCheck, FileIcon, X, } from "lucide-react"; import type { Test } from "../../types/models"; // ── Props ────────────────────────────────────────────────────────── type RedDecision = "approve" | "reopen"; type BlueDecision = "approve" | "reopen" | "gap"; interface ReviewModalProps { side: "red" | "blue"; test: Test; isSubmitting: boolean; onSubmitRed?: (decision: RedDecision, notes: string) => void; onSubmitBlue?: (decision: BlueDecision, notes: string, systemGaps: string) => void; onClose: () => void; } // ── Component ────────────────────────────────────────────────────── export default function ReviewModal({ side, test, isSubmitting, onSubmitRed, onSubmitBlue, onClose, }: ReviewModalProps) { const isRed = side === "red"; const [decision, setDecision] = useState(null); const [notes, setNotes] = useState(""); const [systemGaps, setSystemGaps] = useState(""); const title = isRed ? "Review Red Team Submission" : "Review Blue Team Submission"; const accent = isRed ? "orange" : "indigo"; const evidences = isRed ? test.red_evidences || [] : test.blue_evidences || []; const requiresNotes = decision === "reopen" && notes.trim().length === 0; const requiresGaps = decision === "gap" && systemGaps.trim().length === 0; const canSubmit = decision !== null && !isSubmitting && !requiresNotes && !requiresGaps; const handleSubmit = () => { if (!decision) return; if (isRed) { onSubmitRed?.(decision as RedDecision, notes); } else { onSubmitBlue?.(decision as BlueDecision, notes, systemGaps); } }; return (
{/* Header */}
{isRed ? ( ) : ( )}

{title}

{/* Body */}
{/* Evidence summary */}

{isRed ? "Red" : "Blue"} Team Evidence ({evidences.length})

{evidences.length > 0 ? (
{evidences.map((ev) => (
{ev.file_name}
))}
) : (

No evidence files uploaded.

)}
{/* Decision */}

Decision

{!isRed && ( )}
{decision === "gap" && (

Use this when the operator did everything right but Blue Team is missing a capability (tooling, visibility) needed to detect/block this technique. The test still proceeds to cross-validation.

)}
{/* Notes (reopen) */} {decision === "reopen" && (