| import type { MetricValue } from '../../types/api'; |
| import { ConfidenceBadge } from './ConfidenceBadge'; |
|
|
| interface Props { |
| label: string; |
| value: MetricValue; |
| } |
|
|
| function formatLabel(key: string): string { |
| return key |
| .replace(/_/g, ' ') |
| .replace(/\b\w/g, (c) => c.toUpperCase()); |
| } |
|
|
| function formatValue(v: number | string | null): string { |
| if (v === null || v === undefined) return '—'; |
| if (typeof v === 'number') { |
| if (Math.abs(v) >= 1_000_000_000) |
| return `$${(v / 1_000_000_000).toFixed(2)}B`; |
| if (Math.abs(v) >= 1_000_000) |
| return `$${(v / 1_000_000).toFixed(2)}M`; |
| if (Math.abs(v) >= 1_000) |
| return v.toLocaleString(); |
| |
| return v.toFixed(2); |
| } |
| return String(v); |
| } |
|
|
| export function MetricCard({ label, value }: Props) { |
| const isObject = value !== null && typeof value === 'object'; |
|
|
| if (!isObject) { |
| |
| return ( |
| <div className="metric-card"> |
| <span className="metric-card__label">{formatLabel(label)}</span> |
| <span className="metric-card__value">{formatValue(value as number | null)}</span> |
| </div> |
| ); |
| } |
|
|
| const { value: v, confidence, needs_clarification } = value; |
|
|
| return ( |
| <div className={`metric-card ${needs_clarification ? 'metric-card--flagged' : ''}`}> |
| <span className="metric-card__label">{formatLabel(label)}</span> |
| <span className="metric-card__value">{formatValue(v)}</span> |
| <div className="metric-card__meta"> |
| <ConfidenceBadge confidence={confidence} /> |
| {needs_clarification && ( |
| <span className="metric-card__clarify" title="Needs clarification">⚠</span> |
| )} |
| </div> |
| </div> |
| ); |
| } |
|
|