import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { ChevronDown, ChevronRight, Search, Filter, ExternalLink, Info, ShieldAlert } from "lucide-react"; import type { ComplianceControlStatus } from "../../api/compliance"; interface ControlsTableProps { controls: ComplianceControlStatus[]; } const STATUS_COLORS: Record = { covered: { bg: "bg-green-500/10", text: "text-green-400", dot: "bg-green-500", border: "border-green-500/20" }, partially_covered:{ bg: "bg-yellow-500/10", text: "text-yellow-400", dot: "bg-yellow-500", border: "border-yellow-500/20" }, not_covered: { bg: "bg-red-500/10", text: "text-red-400", dot: "bg-red-500", border: "border-red-500/20" }, not_evaluated: { bg: "bg-gray-500/10", text: "text-gray-400", dot: "bg-gray-500", border: "border-gray-600/20" }, }; const STATUS_LABELS: Record = { covered: "Covered", partially_covered: "Partial", not_covered: "Not Covered", not_evaluated: "Not Evaluated", }; function ScoreBar({ score }: { score: number }) { const color = score >= 70 ? "bg-green-500" : score >= 30 ? "bg-yellow-500" : score > 0 ? "bg-red-500" : "bg-gray-700"; return (
{score.toFixed(0)}
); } export default function ControlsTable({ controls }: ControlsTableProps) { const navigate = useNavigate(); const [expandedId, setExpandedId] = useState(null); const [statusFilter, setStatusFilter] = useState("all"); const [categoryFilter, setCategoryFilter] = useState("all"); const [search, setSearch] = useState(""); const categories = [...new Set(controls.map((c) => c.category).filter(Boolean))] as string[]; const filteredControls = controls.filter((c) => { if (statusFilter !== "all" && c.status !== statusFilter) return false; if (categoryFilter !== "all" && c.category !== categoryFilter) return false; if (search) { const q = search.toLowerCase(); return ( c.control_id.toLowerCase().includes(q) || c.title.toLowerCase().includes(q) || (c.category || "").toLowerCase().includes(q) ); } return true; }); const toggleExpand = (id: string) => setExpandedId(expandedId === id ? null : id); return (
{/* Filters */}
{categories.length > 0 && ( )}
setSearch(e.target.value)} placeholder="Search ID or title…" className="w-full rounded-lg border border-gray-700 bg-gray-800 py-1.5 pl-8 pr-3 text-xs text-gray-200 placeholder-gray-500 focus:border-cyan-500 focus:outline-none" />
{filteredControls.length} / {controls.length}
{/* Table */}
{filteredControls.length === 0 && ( )} {filteredControls.map((control) => { const isExpanded = expandedId === control.control_id; const statusStyle = STATUS_COLORS[control.status] || STATUS_COLORS.not_evaluated; return ( <> {/* Main row */} toggleExpand(control.control_id)} > {/* Expanded detail row */} {isExpanded && ( )} ); })}
Control ID Title Category Status Score Techniques
No controls match the current filters.
{isExpanded ? : } {control.control_id} {control.title} {control.category || "—"} {STATUS_LABELS[control.status] ?? control.status} 0 ? "text-gray-200" : "text-gray-600"}> {control.techniques_covered} / {control.techniques_count}
{/* Executive description */} {control.description && (

What this control requires — and why it matters

{control.description}

)} {/* Techniques grid */} {control.techniques.length === 0 ? (
No ATT&CK techniques mapped to this control yet.
) : (

ATT&CK techniques covered ({control.techniques.length}) — sorted by coverage score

{control.techniques.map((tech) => { const techStyle = tech.score >= 70 ? "border-green-500/20 bg-green-500/5 text-green-400" : tech.score >= 30 ? "border-yellow-500/20 bg-yellow-500/5 text-yellow-400" : tech.score > 0 ? "border-red-500/20 bg-red-500/5 text-red-400" : "border-gray-700 bg-gray-800/30 text-gray-500"; return (
{ e.stopPropagation(); navigate(`/techniques/${tech.mitre_id}`); }} >
{tech.mitre_id} {tech.name}
{tech.score.toFixed(0)}
); })}
)}
); }