spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1/**2 * TypeScript mirror of the compare / rankings / regions / indicators response models in3 * `src/countryatlas/api/schemas.py` (CompareResponse, CompareSnapshotResponse, RankingsListResponse,4 * RankHistoryResponse, IndicatorsResponse, RegionsResponse). Kept in its own file so the compare / rankings5 * pages and the indicators / regions pages can be built concurrently without touching `types.ts`.6 */7import type { CountryCard, IndicatorCard, IndicatorSummary, Meta, MetricValue, Series, TopicId } from './types';89// ---------------------------------------------------------------------------------------------- compare1011/** `mode` query of `/compare` (docs/API.md § Compare). */12export type CompareMode = 'absolute' | 'per-capita' | 'index100' | 'pct';13export const COMPARE_MODES: readonly CompareMode[] = ['absolute', 'per-capita', 'index100', 'pct'];14/** UI-only modes computed client-side: `percentile` (world percentile from rank/n) and `change` (value − value at the range start). */15export type CompareUiMode = CompareMode | 'percentile' | 'change';16export const COMPARE_UI_MODES: readonly CompareUiMode[] = ['absolute', 'per-capita', 'index100', 'pct', 'percentile', 'change'];17/** Map a UI mode to the API mode (client-side modes fetch the absolute series). */18export function apiModeOf(mode: CompareUiMode): CompareMode {19 return mode === 'percentile' || mode === 'change' ? 'absolute' : mode;20}2122/** Transformation echo added by the router on every series (`extra="allow"`). */23export interface CompareTransform {24 mode: CompareMode | string;25 base_year?: number | null;26 applied: boolean;27 unit?: string | null;28}2930export interface CompareSeries extends Series {31 transform?: CompareTransform;32}3334export interface CompareResponse {35 meta: Meta;36 mode: CompareMode | string;37 base_year: number | null;38 countries: CountryCard[];39 indicators: IndicatorCard[];40 series: CompareSeries[];41}4243/** One row of `/compare/snapshot`: the indicator, its latest value per country (ISO3 → MetricValue), the best ISO3. */44export interface CompareSnapshotRow {45 indicator: IndicatorCard;46 values: Record<string, MetricValue>;47 /** ISO3 of the best value when `higher_is_better` is known, else null. */48 best: string | null;49}5051export interface CompareSnapshotResponse {52 meta: Meta;53 topic: { id: TopicId | string; name: string; short: string | null; blurb: string | null } | null;54 countries: CountryCard[];55 rows: CompareSnapshotRow[];56}5758// ---------------------------------------------------------------------------------------------- rankings5960/** `/rankings` item — an IndicatorSummary enriched with the latest ranking year and its size. */61export interface RankableIndicator extends IndicatorSummary {62 ranking_year: number | null;63 ranking_n: number | null;64}6566export interface RankingsListResponse {67 meta: Meta;68 n: number;69 items: RankableIndicator[];70}7172export interface RankHistoryPoint {73 year: number;74 rank: number | null;75 n: number | null;76 value: number | null;77 pct_rank: number | null;78}7980export interface RankHistoryResponse {81 meta: Meta;82 indicator: IndicatorCard;83 countries: CountryCard[];84 years: number[];85 /** ISO3 → points (one per year with a rank). */86 series: Record<string, RankHistoryPoint[]>;87}8889// ---------------------------------------------------------------------------------------------- indicators list / regions list9091export interface IndicatorsResponse {92 meta: Meta;93 n: number;94 filters: { topic: string | null; q: string | null; featured: boolean | null; source: string | null };95 items: IndicatorSummary[];96}9798/** `/regions` item (schemas.RegionsResponse.items is `dict[str, Any]`; these are the keys the router emits). */99export interface RegionItem {100 id: string;101 slug: string;102 name: string;103 kind: 'world' | 'region' | 'continent' | 'income' | 'org' | string;104 wb_code: string | null;105 n_members: number | null;106 description: string | null;107 population_latest: number | null;108 gdp_latest: number | null;109}110111export interface RegionsResponse {112 meta: Meta;113 n: number;114 items: RegionItem[];115}116117// ---------------------------------------------------------------------------------------------- UI-side helpers118119/** Minimal country reference passed to client components (kept small: the full CountrySummary list is ~60 kB). */120export interface CountryLite {121 id: string;122 slug: string;123 name: string;124 flag: string | null;125 region: string | null;126 income: string | null;127 /** Optional: latest population (rankings min-population filter). */128 population?: number | null;129}130131export function toCountryLite(c: CountryCard & { population_latest?: number | null }): CountryLite {132 return { id: c.id, slug: c.slug ?? c.id.toLowerCase(), name: c.name ?? c.id, flag: c.flag, region: c.region, income: c.income, population: c.population_latest ?? null };133}134