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%
2.6 KB · 52 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { t } from '@/i18n';4import { routes } from '@/lib/site';5import { STORIES, storyChartCount, storyIndicators } from '@/lib/stories';6import { topicById } from '@/lib/topics';7import { PageHeader } from '@/components/explore/page-header';8import { JsonLd } from '@/components/platform/json-ld';9import { jsonLd } from '@/lib/seo';1011export const revalidate = 3600;1213export const metadata: Metadata = {14  title: t('stories.title'),15  description: t('stories.description'),16  alternates: { canonical: routes.stories() },17  openGraph: { title: `${t('stories.title')} — ${t('site.name')}`, description: t('stories.description'), url: routes.stories(), type: 'website' },18};1920/** /stories — editorial index: one rule per story, standfirst, topics, chart count. */21export default function StoriesPage() {22  return (23    <>24      <JsonLd data={jsonLd.breadcrumbs([{ name: t('site.name'), path: '/' }, { name: t('stories.title'), path: routes.stories() }])} />25      <PageHeader title={t('stories.title')} lede={t('stories.sub')} />26      <ol className="divide-y divide-rule border-y border-rule">27        {STORIES.map((s, i) => {28          const topics = s.topics.map((tp) => topicById(tp)?.short ?? tp);29          return (30            <li key={s.slug}>31              <Link href={routes.story(s.slug)} className="group grid gap-x-8 gap-y-2 py-6 md:grid-cols-[4rem_minmax(0,1fr)_14rem] md:py-8">32                <span className="tnum display text-2xl text-ink-3 md:text-3xl">{String(i + 1).padStart(2, '0')}</span>33                <span className="min-w-0">34                  <span className="display block text-2xl leading-tight text-ink group-hover:text-accent md:text-3xl">{s.title}</span>35                  <span className="mt-2 block max-w-prose text-base text-ink-2">{s.standfirst}</span>36                </span>37                <span className="tnum flex flex-wrap items-start gap-x-3 gap-y-1 text-xs text-ink-3 md:flex-col md:items-end md:text-right">38                  <span>{topics.join(' · ')}</span>39                  <span>{t('stories.charts', { n: storyChartCount(s) })}</span>40                  <span>{t('stories.minutes', { n: Math.max(2, Math.round(storyChartCount(s) * 0.8)) })}</span>41                  <span className="hidden md:block">{storyIndicators(s).length === 1 ? t('stories.indicatorsOne') : t('stories.indicatorsN', { n: storyIndicators(s).length })}</span>42                </span>43              </Link>44            </li>45          );46        })}47      </ol>48      <p className="mt-6 max-w-prose text-sm leading-relaxed text-ink-3">{t('stories.methodText')}</p>49    </>50  );51}52