spb/company-atlas
Public
Python 66.3%
TypeScript 22.7%
JavaScript 8.6%
HTML 1.4%
CSS 0.7%
1import Link from 'next/link';2import { Sparkline } from '@/components/charts/sparkline';3import { cn } from '@/lib/cn';4import { fmtPctSigned, fmtScore, num } from '@/lib/format';5import { METRIC_LABELS } from '@/lib/site';6import type { CompanyDetail, Metric, MetricDetail } from '@/lib/types';78const TILES: { metric: Metric; spark?: 'activity_30d' | 'hiring_90d'; pct?: boolean }[] = [9 { metric: 'activity_score', spark: 'activity_30d' },10 { metric: 'hiring_momentum_30d', spark: 'hiring_90d', pct: true },11 { metric: 'product_velocity' },12 { metric: 'ai_adoption' },13 { metric: 'corporate_change_index' },14];1516/**17 * Metric tiles with sparklines and confidence. A metric with no value is shown as "not enough evidence" rather than 018 * (API omits metrics without inputs — spec §165). Each tile links to the methodology.19 */20export function MetricTiles({ c, className }: { c: CompanyDetail; className?: string }) {21 const detail = new Map<string, MetricDetail>((c.metrics_detail ?? []).map((m) => [m.metric, m]));22 return (23 <div className={cn('grid grid-cols-2 gap-x-6 border-y border-rule md:grid-cols-5 [&>*]:border-b [&>*]:border-rule md:[&>*]:border-b-0', className)} data-metric-tiles>24 {TILES.map((t) => {25 const v = num(c.metrics[t.metric]);26 const d = detail.get(t.metric);27 const spark = t.spark ? c.sparklines?.[t.spark] : undefined;28 return (29 <Link key={t.metric} href={`/methodology#${t.metric}`} className="group block min-w-0 py-3 hover:text-accent md:py-4">30 <p className="eyebrow">{METRIC_LABELS[t.metric]}</p>31 <div className="mt-1 flex items-end justify-between gap-2">32 <p className={cn('tnum whitespace-nowrap text-[22px] font-semibold leading-none tracking-tight md:text-[30px]', v === null && 'text-ink-3', t.pct && v !== null && (v > 0 ? 'text-positive' : v < 0 ? 'text-danger' : ''))}>{v === null ? '—' : t.pct ? fmtPctSigned(v) : fmtScore(v)}</p>33 {spark && spark.length > 1 && <Sparkline values={spark} width={64} height={22} tone={t.pct ? 'auto' : 'accent'} className="max-[400px]:hidden" />}34 </div>35 <p className="mt-1.5 text-[11px] text-ink-3">{v === null ? 'not enough monitored evidence' : d ? `confidence ${Math.round(d.confidence * 100)} %${d.formula_version ? ` · ${d.formula_version}` : ''}` : t.pct ? 'vs 30 days ago' : '0–100'}</p>36 </Link>37 );38 })}39 </div>40 );41}42