'use client'; import { ChevronRight } from 'lucide-react'; import Link from 'next/link'; import { t } from '@/i18n'; import { cn } from '@/lib/cn'; import { displayValue, formatPeriod, ordinal } from '@/lib/format'; import type { MetricValue } from '@/lib/types'; import { pointsFromSpark } from '@/components/charts/scales'; import { Sparkline } from '@/components/charts/sparkline'; import { ChangeChip } from './change-chip'; import { RankBadge } from './rank-badge'; import { useProvenance, type ProvenancePayload } from './provenance-context'; export interface MetricCountry { id: string; slug: string | null; name: string; flag?: string | null; } /** Build the provenance-sheet payload for a MetricValue (also used by topic rows and chart source lines). */ export function payloadFor(m: MetricValue, country?: MetricCountry | null, extra?: { name?: string | null; methodology?: string | null; description?: string | null }): ProvenancePayload { return { indicator: { slug: m.indicator, name: extra?.name ?? m.indicator_name ?? m.indicator, format: m.format, unit: m.unit, unit_short: m.unit_short, frequency: m.frequency, higher_is_better: m.higher_is_better, methodology: extra?.methodology ?? null, description: extra?.description ?? null }, value: m.has_data ? { value: m.value, formatted: m.formatted, period: m.period, year: m.year, unit: m.unit, is_estimate: m.is_estimate, is_forecast: m.is_forecast, status: m.status, provenance: m.provenance } : null, country: country ?? null, }; } /** * Percentile of the latest value within the country's own history (share of earlier points strictly below it). * Null with fewer than 5 earlier points. 100 = highest ever, 0 = lowest ever. */ export function ownHistoryPercentile(m: MetricValue): number | null { const pts = m.sparkline.filter((p) => typeof p[1] === 'number') as Array<[number, number]>; if (pts.length < 6 || m.value == null) return null; const earlier = pts.slice(0, -1).map((p) => p[1]); const below = earlier.filter((v) => v < m.value!).length; return Math.round((below / earlier.length) * 100); } /** * Headline metric module: label (link) + sparkline, big value, period, change chip, ranks (world · region), * percentile of own history. Click on the value → provenance sheet. 1 px separators, no card. Heights are * reserved so the grid does not shift while data loads. */ export function Metric({ metric, country, regionName, href, className, size = 'md', showOwnPercentile = true }: { metric: MetricValue; country?: MetricCountry | null; regionName?: string | null; href?: string | null; className?: string; size?: 'sm' | 'md' | 'lg'; showOwnPercentile?: boolean }) { const { open } = useProvenance(); const m = metric; const hasValue = m.has_data && m.value != null; const dir = m.change?.abs == null ? null : m.change.abs > 0 ? 'up' : m.change.abs < 0 ? 'down' : 'flat'; const points = pointsFromSpark(m.sparkline); const pct = showOwnPercentile && hasValue ? ownHistoryPercentile(m) : null; const firstYear = points[0]?.year; return (