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%
3.1 KB · 78 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import { t } from '@/i18n';3import { api, isNotBuilt, safe } from '@/lib/api';4import { apiAnalytics } from '@/lib/api-analytics';5import { apiExplore } from '@/lib/api-explore';6import { routes } from '@/lib/site';7import { NotBuiltState } from '@/components/data/empty-state';8import { WorldExplorer } from '@/components/explorer/world-explorer';9import { baseFeatures } from '@/components/indicators/map-geometry';10import { DEFAULT_EXPLORER_INDICATOR, EXPLORER_VIEWS, explorerIndicators, parseYearParam, type ExplorerView } from '@/components/explorer/options';1112export const revalidate = 900;1314type SP = Record<string, string | string[] | undefined>;15const SLUG = /^[a-z0-9][a-z0-9-]*$/;1617function first(sp: SP, k: string): string | null {18  const v = sp[k];19  return (Array.isArray(v) ? v[0] : v) ?? null;20}2122function parseState(sp: SP) {23  const ind = (first(sp, 'indicator') ?? '').toLowerCase();24  const view = first(sp, 'view') as ExplorerView | null;25  const country = (first(sp, 'country') ?? '').toLowerCase();26  const group = (first(sp, 'group') ?? 'world').toLowerCase();27  return {28    indicator: SLUG.test(ind) ? ind : DEFAULT_EXPLORER_INDICATOR,29    year: parseYearParam(first(sp, 'year')),30    view: view && EXPLORER_VIEWS.includes(view) ? view : ('map' as ExplorerView),31    country: /^[a-z]{3}$/.test(country) ? country : null,32    group: SLUG.test(group) ? group : 'world',33  };34}3536export async function generateMetadata({ searchParams }: { searchParams: Promise<SP> }): Promise<Metadata> {37  const sp = await searchParams;38  const parameterized = Object.keys(sp).length > 0;39  return {40    title: t('explorer.metaTitle'),41    description: t('explorer.description'),42    alternates: { canonical: routes.explore() },43    robots: parameterized ? { index: false, follow: true } : undefined,44    openGraph: { title: `${t('explorer.metaTitle')} — ${t('site.name')}`, description: t('explorer.description'), url: routes.explore(), type: 'website' },45  };46}4748export default async function ExplorePage({ searchParams }: { searchParams: Promise<SP> }) {49  const sp = await searchParams;50  const state = parseState(sp);51  let countriesRes;52  try {53    countriesRes = await api.countries();54  } catch (e) {55    if (isNotBuilt(e)) return <NotBuiltState />;56    throw e;57  }58  const [indicatorsRes, groupsRes, frames] = await Promise.all([59    safe(apiExplore.indicators({ with_data: true })),60    safe(apiExplore.regions()),61    safe(apiAnalytics.indicatorFrames(state.indicator, { group: state.group !== 'world' ? state.group : null })),62  ]);63  const countries = countriesRes.items.filter((c) => (c.kind ?? 'country') === 'country');64  const { features, sphere } = baseFeatures(countriesRes.items);65  const indicators = explorerIndicators(indicatorsRes?.items ?? []);66  return (67    <WorldExplorer68      features={features}69      sphere={sphere}70      countries={countries.map((c) => ({ id: c.id, slug: c.slug, name: c.name, flag: c.flag, region: c.region, income: c.income }))}71      indicators={indicators}72      groups={groupsRes?.items ?? []}73      initial={frames}74      initialState={state}75    />76  );77}78