spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1import type { Metadata } from 'next';2import { t } from '@/i18n';3import { isNotBuilt } from '@/lib/api';4import { apiAnalytics } from '@/lib/api-analytics';5import { grouped } from '@/lib/format';6import { routes } from '@/lib/site';7import { isTopicId } from '@/lib/topics';8import type { ExtremesResponse } from '@/lib/types-analytics';9import { NotBuiltState } from '@/components/data/empty-state';10import { ExtremeFacetBlock } from '@/components/explorer/extreme-facet';11import { ExtremesControls } from '@/components/explorer/extremes-controls';12import { firstParam, type SP } from '@/components/explorer/options';1314export const revalidate = 900;15const WINDOWS = new Set(['1', '5', '10', '25', 'since1990']);1617function parseState(sp: SP) {18 const w = firstParam(sp, 'window') ?? '10';19 const topic = (firstParam(sp, 'topic') ?? '').toLowerCase();20 return { window: WINDOWS.has(w) ? w : '10', topic: isTopicId(topic) ? topic : null, all: firstParam(sp, 'all') === '1' };21}2223export async function generateMetadata({ searchParams }: { searchParams: Promise<SP> }): Promise<Metadata> {24 const sp = await searchParams;25 return {26 title: t('extremes.metaTitle'),27 description: t('extremes.description'),28 alternates: { canonical: routes.extremes() },29 robots: Object.keys(sp).length ? { index: false, follow: true } : undefined,30 openGraph: { title: `${t('extremes.metaTitle')} — ${t('site.name')}`, description: t('extremes.description'), url: routes.extremes(), type: 'website' },31 };32}3334export default async function ExtremesPage({ searchParams }: { searchParams: Promise<SP> }) {35 const state = parseState(await searchParams);36 let data: ExtremesResponse;37 try {38 data = await apiAnalytics.extremes({ window: state.window, topic: state.topic, min_population: state.all ? 0 : null });39 } catch (e) {40 if (isNotBuilt(e)) return <NotBuiltState />;41 throw e;42 }43 return (44 <>45 <header className="pb-3 pt-6 md:pt-10">46 <h1 className="display text-3xl leading-tight text-ink md:text-4xl">{t('extremes.title')}</h1>47 <p className="mt-2 max-w-prose text-sm text-ink-2 md:text-base">{t('extremes.lede')}</p>48 <p className="tnum mt-1 text-xs text-ink-3">49 {t('extremes.facets', { n: grouped(data.facets.length), y0: data.from_year ?? '', y1: data.to_year ?? '' })}50 {data.filter_note ? ` · ${data.filter_note}` : ` · ${t('extremes.allCountries')}`}51 </p>52 </header>53 <ExtremesControls window={state.window} topic={state.topic} all={state.all} />54 <p className="max-w-prose py-3 text-xs text-ink-3">{t('extremes.semantics')}</p>55 {data.facets.length ? (56 <>57 <nav aria-label={t('extremes.title')} className="-mx-4 px-4 sm:mx-0 sm:px-0">58 <ul className="scrollbar-none flex gap-1.5 overflow-x-auto py-1 sm:flex-wrap">59 {data.facets.map((f) => (60 <li key={f.id} className="shrink-0">61 <a href={`#${f.id}`} className="inline-flex h-11 items-center whitespace-nowrap rounded-sm border border-rule px-3 text-sm text-ink-2 hover:border-accent hover:text-accent md:h-9 md:px-2.5">62 {f.title}63 </a>64 </li>65 ))}66 </ul>67 </nav>68 {data.facets.map((f) => (69 <ExtremeFacetBlock key={f.id} facet={f} />70 ))}71 </>72 ) : (73 <p className="py-10 text-center text-sm text-ink-3">{t('extremes.none')}</p>74 )}75 </>76 );77}78