spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
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