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%
1.9 KB · 49 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import { t } from '@/i18n';3import { isNotBuilt, safe } from '@/lib/api';4import { apiCompare } from '@/lib/api-compare';5import { routes } from '@/lib/site';6import type { RankingRow } from '@/lib/types';7import { NotBuiltState } from '@/components/data/empty-state';8import { RankingsDirectory } from '@/components/rankings/rankings-directory';910export const revalidate = 900;1112export const metadata: Metadata = {13  title: t('rankings.metaTitle'),14  description: t('rankings.description', { n: 230 }),15  alternates: { canonical: routes.rankings() },16  openGraph: { title: `${t('rankings.metaTitle')} — ${t('site.name')}`, description: t('rankings.description', { n: 230 }), url: routes.rankings(), type: 'website' },17};1819/**20 * /rankings — every rankable indicator grouped by topic (featured first). Top-3 previews are fetched21 * server-side for the featured ones (≈ 27 requests, under the API's 120 req/min budget); the others load22 * client-side on scroll.23 */24export default async function RankingsPage() {25  let list;26  try {27    list = await apiCompare.rankings();28  } catch (e) {29    if (isNotBuilt(e)) return <NotBuiltState />;30    throw e;31  }32  const featured = list.items.filter((i) => i.featured).slice(0, 30);33  const tops = await Promise.all(featured.map((i) => safe(apiCompare.ranking(i.slug, { limit: 3, sparkline: false }))));34  const previews: Record<string, RankingRow[] | null> = {};35  featured.forEach((i, k) => {36    previews[i.slug] = tops[k]?.rows ?? null;37  });3839  return (40    <>41      <header className="pb-2 pt-6 md:pt-10">42        <h1 className="display text-3xl leading-tight text-ink md:text-5xl">{t('rankings.title')}</h1>43        <p className="mt-2 max-w-prose text-sm text-ink-2 md:text-base">{t('rankings.sub', { n: list.n })}</p>44      </header>45      <RankingsDirectory items={list.items} previews={previews} />46    </>47  );48}49