admin / SAynapse-Horizon
publicSelf Hosted Cyber Threat Intelligence Hub
SAynapse-Horizon / Synapse-Horizon / src / components / CriticalTriagePanel.jsx
4255 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import { Siren, ChevronRight, ShieldAlert } from 'lucide-react' import { SOURCE_MAP } from '../data/sources' import { severityMeta } from '../utils/theme' import { formatUtcTime, relativeAge } from '../utils/format' // Executive critical radar: at most 5 highest-priority live events. export default function CriticalTriagePanel({ rows, loading, onSelect, displayTick }) { return ( <section className="rounded-xl border border-rose-500/20 bg-gradient-to-b from-rose-950/20 to-slate-900/40"> <header className="flex items-center justify-between gap-3 border-b border-slate-800/70 px-4 py-2.5"> <div className="flex items-center gap-2"> <Siren className="h-4 w-4 text-rose-400" /> <h2 className="text-sm font-semibold text-slate-100">Critical Priority Triage</h2> <span className="rounded bg-rose-500/15 px-1.5 py-px text-[10px] font-semibold uppercase tracking-wide text-rose-300 ring-1 ring-inset ring-rose-500/30"> Top 5 </span> </div> <span className="text-[10px] font-medium uppercase tracking-widest text-slate-500"> Auto-resorting radar </span> </header> <div className="p-2"> {loading ? ( <SkeletonRows /> ) : rows.length === 0 ? ( <Empty /> ) : ( <ul className="space-y-1"> {rows.map((r) => { const sev = severityMeta(r.severity) const src = SOURCE_MAP[r.source] return ( <li key={r.id}> <button type="button" onClick={() => onSelect(r)} className={`group flex w-full animate-slide-in items-center gap-3 rounded-lg border border-transparent px-2.5 py-2 text-left transition-colors hover:border-slate-700/70 ${sev.row}`} > <span className={`h-8 w-1 shrink-0 rounded-full ${sev.bar}`} /> <span className={`inline-flex w-[74px] shrink-0 justify-center rounded px-1.5 py-1 text-[10px] font-bold tracking-wide ${sev.badge}`} > {sev.label} </span> <div className="min-w-0 flex-1"> <div className="truncate text-sm font-medium text-slate-100"> {r.target} </div> <div className="truncate text-[11px] text-slate-500">{r.category}</div> </div> <div className="hidden shrink-0 text-right sm:block"> <div className="text-xs font-medium text-slate-300">{src?.name}</div> <div className="font-mono text-[10px] text-slate-500"> {formatUtcTime(r.detectedAt)} · {relativeAge(r.detectedAt, displayTick)} </div> </div> <ChevronRight className="h-4 w-4 shrink-0 text-slate-600 transition-transform group-hover:translate-x-0.5 group-hover:text-slate-400" /> </button> </li> ) })} </ul> )} </div> </section> ) } function SkeletonRows() { return ( <ul className="space-y-1"> {Array.from({ length: 4 }).map((_, i) => ( <li key={i} className="flex items-center gap-3 px-2.5 py-2"> <div className="h-8 w-1 rounded-full bg-slate-800" /> <div className="h-6 w-[74px] animate-pulse rounded bg-slate-800" /> <div className="flex-1 space-y-1.5"> <div className="h-3 w-1/2 animate-pulse rounded bg-slate-800" /> <div className="h-2.5 w-1/4 animate-pulse rounded bg-slate-800/70" /> </div> </li> ))} </ul> ) } function Empty() { return ( <div className="flex flex-col items-center justify-center gap-2 px-4 py-8 text-center"> <ShieldAlert className="h-6 w-6 text-slate-600" /> <p className="text-sm font-medium text-slate-400">No critical events in active channels</p> <p className="text-[11px] text-slate-500"> Enable more feed sources or run a force sync to populate the radar. </p> </div> ) } |