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 / MetricsRow.jsx 2624 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
import { Rss, Timer, Database } from 'lucide-react'
import { formatUtc } from '../utils/format'

// Three global key-metric cards across the top of the main pane.
export default function MetricsRow({ activeCount, totalSources, lastPull, totalIocs, loading }) {
  return (
    <div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
      <MetricCard
        icon={Rss}
        accent="emerald"
        label="Active Intelligence Feeds"
        value={`${activeCount} / ${totalSources}`}
        sub={`${activeCount === 0 ? 'no' : activeCount} ingestion pipeline${activeCount === 1 ? '' : 's'} live`}
        loading={false}
      />
      <MetricCard
        icon={Timer}
        accent="sky"
        label="Latest Feed Pull"
        value={loading ? 'Ingesting…' : formatUtc(lastPull)}
        sub={loading ? 'lifecycle in progress' : 'last lifecycle completed'}
        loading={loading}
        mono={!loading}
      />
      <MetricCard
        icon={Database}
        accent="amber"
        label="Total Aggregated IOCs"
        value={loading ? '—' : totalIocs.toLocaleString()}
        sub="normalized rows held in state"
        loading={loading}
      />
    </div>
  )
}

const ACCENTS = {
  emerald: { icon: 'text-emerald-400 bg-emerald-500/10 ring-emerald-500/25', bar: 'from-emerald-500/40' },
  sky: { icon: 'text-sky-400 bg-sky-500/10 ring-sky-500/25', bar: 'from-sky-500/40' },
  amber: { icon: 'text-amber-400 bg-amber-500/10 ring-amber-500/25', bar: 'from-amber-500/40' },
}

function MetricCard({ icon: Icon, accent, label, value, sub, loading, mono }) {
  const a = ACCENTS[accent]
  return (
    <div className="relative overflow-hidden rounded-xl border border-slate-800/80 bg-slate-900/50 p-4">
      <div className={`absolute inset-x-0 top-0 h-px bg-gradient-to-r ${a.bar} to-transparent`} />
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0">
          <p className="text-[11px] font-medium uppercase tracking-[0.12em] text-slate-500">
            {label}
          </p>
          {loading ? (
            <div className="mt-2 h-7 w-32 animate-pulse rounded bg-slate-800" />
          ) : (
            <p className={`mt-1.5 truncate text-2xl font-semibold text-slate-100 ${mono ? 'font-mono text-lg' : ''}`}>
              {value}
            </p>
          )}
          <p className="mt-1 truncate text-[11px] text-slate-500">{sub}</p>
        </div>
        <div className={`grid h-9 w-9 shrink-0 place-items-center rounded-lg ring-1 ring-inset ${a.icon}`}>
          <Icon className="h-4.5 w-4.5" />
        </div>
      </div>
    </div>
  )
}