/** * Server-safe helpers for the explorer pages (no 'use client' — the shared `toIndicatorOption` in * components/controls/indicator-select.tsx and `parseYear` in lib/url-state.ts live in client modules and cannot * be invoked from server components). */ import type { IndicatorOption } from '@/components/controls/indicator-select'; import type { IndicatorSummary } from '@/lib/types'; export function indicatorOption(i: IndicatorSummary): IndicatorOption { return { slug: i.slug, name: i.name ?? i.slug, short_name: i.short_name, topic: i.topic, unit: i.unit, featured: i.featured, first_year: i.first_year, last_year: i.last_year, n_countries: i.n_countries }; } export function parseYearParam(v: string | null | undefined): number | null { if (!v) return null; const n = Number(v); return Number.isInteger(n) && n >= 1750 && n <= 2100 ? n : null; } const SLUG = /^[a-z0-9][a-z0-9-]*$/; export function slugParam(v: string | null | undefined, fallback: string | null = null): string | null { const s = (v ?? '').toLowerCase(); return SLUG.test(s) ? s : fallback; } export function listParam(v: string | null | undefined, max = 8): string[] { if (!v) return []; const out: string[] = []; for (const part of v.split(',')) { const s = part.trim(); if (s && /^[A-Za-z0-9-]+$/.test(s) && !out.includes(s)) out.push(s); if (out.length >= max) break; } return out; } export type SP = Record; export function firstParam(sp: SP, k: string): string | null { const v = sp[k]; return (Array.isArray(v) ? v[0] : v) ?? null; } /** Annual indicators with enough countries for cross-country views. */ export function explorerIndicators(items: IndicatorSummary[], minCountries = 20): IndicatorOption[] { return items.filter((i) => (i.frequency ?? 'A') === 'A' && (i.n_countries ?? 0) >= minCountries).map(indicatorOption); } export type ExplorerView = 'map' | 'rank' | 'trend' | 'distribution'; export const EXPLORER_VIEWS: ExplorerView[] = ['map', 'rank', 'trend', 'distribution']; export const DEFAULT_EXPLORER_INDICATOR = 'gdp-per-capita-ppp'; export const DEFAULT_TRAJ = { x: 'gdp-per-capita-ppp', y: 'life-expectancy', size: 'population' } as const;