'use client'; import { ChevronRight } from 'lucide-react'; import Link from 'next/link'; import { t } from '@/i18n'; import { cn } from '@/lib/cn'; import { COMPARE_TOPIC_TABS, compareQuery, type CompareState, type CompareTab } from '@/lib/compare-state'; import { displayValue, formatChange, isNum, ordinal } from '@/lib/format'; import { routes } from '@/lib/site'; import type { CompareSnapshotRow, CountryLite } from '@/lib/types-compare'; import type { MetricValue } from '@/lib/types'; import { seriesVar } from '@/components/charts/palette'; import { payloadFor } from '@/components/data/metric'; import { useProvenance } from '@/components/data/provenance-context'; /** Cell text for the current value mode: absolute value, world percentile (from rank / n) or 10-year change. */ export function cellText(m: MetricValue, mode: CompareState['mode']): { text: string; sub: string | null } { if (mode === 'percentile') { if (isNum(m.rank_world) && isNum(m.n_world) && m.n_world > 1) return { text: t('compare.cell.percentile', { p: ordinal(Math.round((1 - (m.rank_world - 1) / (m.n_world - 1)) * 100)) }), sub: `${m.rank_world}/${m.n_world}` }; return { text: t('common.na'), sub: null }; } if (mode === 'change') { const c = m.change_10y ? formatChange(m.change_10y.abs, m.change_10y.pct, m) : null; return c ? { text: m.change_10y?.formatted ?? c.text, sub: t('compare.cell.change10y') } : { text: t('common.na'), sub: null }; } return { text: displayValue(m.value, m, m.formatted), sub: null }; } /** Where a snapshot indicator's chart lives: its topic tab when it is one of the compare tabs, else the custom tab. */ export function chartHrefFor(slugs: string[], state: CompareState, indicator: { slug: string; topic: string | null }): string { const tab = (COMPARE_TOPIC_TABS as readonly string[]).includes(indicator.topic ?? '') ? (indicator.topic as CompareTab) : 'custom'; const indicators = tab === 'custom' ? Array.from(new Set([indicator.slug, ...state.indicators])).slice(0, 6) : state.indicators; return `${routes.compare(...slugs)}${compareQuery({ ...state, tab, indicator: indicator.slug, indicators })}`; } /** * Compact latest-values table used inside a collapsed subtopic block (no charts): one row per indicator, * one cell per country (value · year, best highlighted). Indicator name → its chart (hero). Any cell → * provenance. Phones: value cells wrap under the indicator name. */ export function SnapshotCompact({ rows, countries, slugs, state }: { rows: CompareSnapshotRow[]; countries: CountryLite[]; slugs: string[]; state: CompareState }) { const { open } = useProvenance(); const openCell = (m: MetricValue, c: CountryLite, name: string) => open(payloadFor(m, { id: c.id, slug: c.slug, name: c.name, flag: c.flag }, { name })); if (rows.length === 0) return null; const latestYear = Math.max(0, ...rows.flatMap((r) => Object.values(r.values).map((v) => v.year ?? 0))); return (
{t('compare.snapshot.empty')}
; const openCell = (m: MetricValue, c: CountryLite, name: string) => open(payloadFor(m, { id: c.id, slug: c.slug, name: c.name, flag: c.flag }, { name })); return (| {t('compare.snapshot.indicatorCol')} | {countries.map((c, i) => ({c.flag} {c.name} | ))}|
|---|---|---|
|
{r.indicator.name ?? name}
{r.indicator.unit}
{hib != null ? · {hib ? t('compare.snapshot.hib.higher') : t('compare.snapshot.hib.lower')} : null}
|
{countries.map((c) => {
const m = r.values[c.id];
const best = r.best === c.id;
if (!m || !m.has_data)
return (
{t('common.noData')} | ); return (); })} |