admin / SAynapse-Horizon
publicSelf Hosted Cyber Threat Intelligence Hub
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> ) } |