Catalyst / admin/SAynapse-Horizon 14.8 GB / 57.8 GB 40.0 GB free
Help Sign in

admin / SAynapse-Horizon

public

Self Hosted Cyber Threat Intelligence Hub

Code Issues Pull requests Pipelines Packages Security Insights Wiki Settings
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>
  )
}