SPB Git forge

spb/market-atlas

Public
12commits 1branches 0releases
1.1 MBsize
maindefault branch
10 days agolast push
TypeScript 96.7% SQL 1.6% CSS 0.8% JavaScript 0.5%
3.0 KB · 63 lines tsx
Raw Blame History
1import Link from "next/link";2import { Sparkline } from "@/components/ui/sparkline";3import { cx, formatDuration } from "@/lib/format";4import type { DataHealth } from "@/lib/types";56/** Compact source-health panel for the homepage (server component; data from /v1/data-health). */7export function HealthPanel({ health, className }: { health: DataHealth; className?: string }) {8  const c = health.connectors;9  const ratio = health.healthy_ratio == null ? null : Math.round(health.healthy_ratio * 100);10  const segs: Array<[number, string, string]> = [11    [c.healthy, "bg-positive", "healthy"],12    [c.degraded, "bg-warning", "degraded"],13    [c.recovering, "bg-accent", "recovering"],14    [c.failed, "bg-negative", "failed"],15    [c.paused, "bg-stale", "paused"],16  ];17  return (18    <div className={cx("rounded-md border border-rule bg-surface p-4", className)}>19      <div className="flex items-baseline justify-between">20        <h3 className="text-[11px] font-medium uppercase tracking-wide text-ink-3">Source health</h3>21        <Link href="/data-health" className="text-xs text-accent hover:underline">22          Data health →23        </Link>24      </div>25      <div className="mono mt-1 text-2xl font-semibold tnum">26        {ratio == null ? "—" : `${ratio}%`} <span className="text-sm font-normal text-ink-3">connectors healthy</span>27      </div>28      <div className="mt-2 flex h-2 overflow-hidden rounded-full bg-surface-3">29        {segs.map(([n, cls, label]) => (n > 0 ? <span key={label} className={cls} style={{ width: `${(n / Math.max(1, c.total)) * 100}%` }} title={`${n} ${label}`} /> : null))}30      </div>31      <div className="mt-2 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-ink-3">32        {segs.map(([n, cls, label]) => (33          <span key={label} className="inline-flex items-center gap-1">34            <span className={cx("inline-block h-1.5 w-1.5 rounded-full", cls)} />35            {n} {label}36          </span>37        ))}38      </div>39      <dl className="mt-3 grid grid-cols-2 gap-x-4 text-sm">40        <Row k="Median freshness" v={formatDuration(health.median_freshness_ms)} />41        <Row k="Multi-source quotes" v={`${health.multi_source_quotes} / ${health.quotes_public}`} />42        <Row k="Mean confidence" v={health.mean_confidence == null ? "—" : `${Math.round(health.mean_confidence * 100)}%`} />43        <Row k="Observations / s" v={health.observations_per_sec.toFixed(1)} />44      </dl>45      {health.history.length > 2 && (46        <div className="mt-3 flex items-center justify-between text-[11px] text-ink-3">47          <span>48 h · healthy ratio</span>48          <Sparkline values={health.history.map((h) => h.healthy_ratio)} width={140} height={24} stroke="var(--accent)" />49        </div>50      )}51    </div>52  );53}5455function Row({ k, v }: { k: string; v: string }) {56  return (57    <div className="flex items-baseline justify-between border-b border-rule py-1">58      <dt className="text-xs text-ink-3">{k}</dt>59      <dd className="mono text-xs tnum">{v}</dd>60    </div>61  );62}63