SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
2.4 KB · 42 lines typescript
Raw Blame History
1'use client';2import { ClientApiError } from './client-api';3import type { MapResponse, MultiSeriesResponse, RankingResponse, SearchResponse, SimilarResponse } from './types';4import type { ChangesFeedResponse, TrendResponse } from './types-explore';56/**7 * Browser-side fetches used by the interactive widgets of the indicator / region / explore / changes pages.8 * Same-origin `/api/v1/*` (rewritten to the FastAPI service). Small and typed; server components do the9 * first render, these only react to user input.10 */11async function get<T>(path: string, signal?: AbortSignal): Promise<T> {12  const res = await fetch(`/api/v1${path}`, { headers: { accept: 'application/json' }, signal });13  if (!res.ok) throw new ClientApiError(res.status, `API ${res.status}`);14  return (await res.json()) as T;15}1617function qs(query: Record<string, string | number | boolean | null | undefined>): string {18  const p = new URLSearchParams();19  for (const [k, v] of Object.entries(query)) if (v !== undefined && v !== null && v !== '') p.set(k, String(v));20  const s = p.toString();21  return s ? `?${s}` : '';22}2324export const clientExplore = {25  indicatorMap: (slug: string, year: number | null, signal?: AbortSignal) => get<MapResponse>(`/indicators/${encodeURIComponent(slug)}/map${qs({ year })}`, signal),2627  indicatorTrend: (slug: string, group: string, signal?: AbortSignal) => get<TrendResponse>(`/indicators/${encodeURIComponent(slug)}/trend${qs({ group })}`, signal),2829  seriesBundle: (countries: string[], indicator: string, signal?: AbortSignal) =>30    get<MultiSeriesResponse>(`/series${qs({ country: countries.join(','), indicator })}`, signal),3132  ranking: (indicator: string, group: string, limit = 60, signal?: AbortSignal) =>33    get<RankingResponse>(`/rankings/${encodeURIComponent(indicator)}${qs({ group, limit, sparkline: false })}`, signal),3435  changes: (q: { limit?: number; kind?: string | null; indicator?: string | null; topic?: string | null; min_severity?: number | null }, signal?: AbortSignal) =>36    get<ChangesFeedResponse>(`/changes${qs({ limit: 100, ...q })}`, signal),3738  search: (q: string, type?: string, limit = 8, signal?: AbortSignal) => get<SearchResponse>(`/search${qs({ q, type, limit })}`, signal),3940  similar: (id: string, mode: string, limit = 10, signal?: AbortSignal) => get<SimilarResponse>(`/countries/${encodeURIComponent(id)}/similar${qs({ mode, limit })}`, signal),41};42