import React from 'react'; import { InvestigationCase, ThreatCategory, ThreatLevel } from '../types/forensics'; import { ShieldAlert, Flame, Search, Filter, Clock, AlertOctagon, ChevronRight, CheckCircle2, DollarSign, Activity } from 'lucide-react'; interface CaseListProps { cases: InvestigationCase[]; selectedCaseId: string; onSelectCase: (id: string) => void; searchQuery: string; setSearchQuery: (query: string) => void; categoryFilter: string; setCategoryFilter: (cat: string) => void; threatFilter: string; setThreatFilter: (lvl: string) => void; } export const CaseList: React.FC = ({ cases, selectedCaseId, onSelectCase, searchQuery, setSearchQuery, categoryFilter, setCategoryFilter, threatFilter, setThreatFilter, }) => { const filteredCases = cases.filter(c => { const matchesSearch = c.title.toLowerCase().includes(searchQuery.toLowerCase()) || c.id.toLowerCase().includes(searchQuery.toLowerCase()) || c.entityTarget.primaryAlias.toLowerCase().includes(searchQuery.toLowerCase()) || c.networkTelemetry.claimedGeo.ip.includes(searchQuery) || (c.entityTarget.walletAddress && c.entityTarget.walletAddress.toLowerCase().includes(searchQuery.toLowerCase())); const matchesCategory = categoryFilter === 'ALL' || c.category === categoryFilter; const matchesThreat = threatFilter === 'ALL' || c.threatLevel === threatFilter; return matchesSearch && matchesCategory && matchesThreat; }); const getThreatBadge = (level: ThreatLevel) => { switch (level) { case 'CRITICAL': return CRITICAL; case 'HIGH': return HIGH; case 'ELEVATED': return ELEVATED; default: return GUARDED; } }; const getTrustScoreColor = (score: number) => { if (score < 20) return 'text-rose-400 bg-rose-950/60 border-rose-800/80'; if (score < 50) return 'text-amber-400 bg-amber-950/60 border-amber-800/80'; return 'text-emerald-400 bg-emerald-950/60 border-emerald-800/80'; }; return (
{/* Search Bar */}
setSearchQuery(e.target.value)} placeholder="Filter by IP, hash, wallet, alias..." className="w-full bg-slate-950 border border-slate-800 pl-9 pr-3 py-1.5 rounded text-xs font-mono text-slate-200 placeholder-slate-500 focus:outline-none focus:border-cyan-500 transition-colors" /> {searchQuery && ( )}
{/* Filter Pills */}
{/* Case List Scroll Area */}
{filteredCases.length === 0 ? (
No dockets matching criteria.
) : ( filteredCases.map((c) => { const isSelected = c.id === selectedCaseId; return (
onSelectCase(c.id)} className={`p-3 rounded-lg cursor-pointer transition-all border ${ isSelected ? 'bg-slate-800/95 border-cyan-500/80 shadow-md shadow-cyan-950/50' : 'bg-slate-900/40 border-transparent hover:bg-slate-800/50 hover:border-slate-800' }`} >
{c.id} {getThreatBadge(c.threatLevel)}
Trust: {c.compositeTrustScore}/100
{c.title}
{c.entityTarget.primaryAlias} {c.networkTelemetry.claimedGeo.ip}
{/* Quick tags */}
{c.statutoryMatrix.euTcoReg2021_784 && ( TCO 1-HR )} {c.financialLedger.detectedStructuring && ( BSA SMURFING )} {c.networkTelemetry.bgpAnycast.speedOfLightViolation && ( BGP VIOLATION )} {c.statutoryMatrix.gifctTier === 'TIER_1' && ( GIFCT-T1 )}
); }) )}
{/* Docket Count Footer */}
Showing {filteredCases.length} of {cases.length} Dockets SENTINEL-ONLINE
); };