import type { Metadata } from 'next'; import { t } from '@/i18n'; import { isNotBuilt, safe } from '@/lib/api'; import { apiCompare } from '@/lib/api-compare'; import { routes } from '@/lib/site'; import type { RankingRow } from '@/lib/types'; import { NotBuiltState } from '@/components/data/empty-state'; import { RankingsDirectory } from '@/components/rankings/rankings-directory'; export const revalidate = 900; export const metadata: Metadata = { title: t('rankings.metaTitle'), description: t('rankings.description', { n: 230 }), alternates: { canonical: routes.rankings() }, openGraph: { title: `${t('rankings.metaTitle')} — ${t('site.name')}`, description: t('rankings.description', { n: 230 }), url: routes.rankings(), type: 'website' }, }; /** * /rankings — every rankable indicator grouped by topic (featured first). Top-3 previews are fetched * server-side for the featured ones (≈ 27 requests, under the API's 120 req/min budget); the others load * client-side on scroll. */ export default async function RankingsPage() { let list; try { list = await apiCompare.rankings(); } catch (e) { if (isNotBuilt(e)) return ; throw e; } const featured = list.items.filter((i) => i.featured).slice(0, 30); const tops = await Promise.all(featured.map((i) => safe(apiCompare.ranking(i.slug, { limit: 3, sparkline: false })))); const previews: Record = {}; featured.forEach((i, k) => { previews[i.slug] = tops[k]?.rows ?? null; }); return ( <>

{t('rankings.title')}

{t('rankings.sub', { n: list.n })}

); }