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.7 KB · 40 lines tsx
Raw Blame History
1import Link from 'next/link';2import { t, tOpt } from '@/i18n';3import { cn } from '@/lib/cn';4import { compact, formatDate, grouped } from '@/lib/format';5import { routes } from '@/lib/site';6import type { UpdatesResponse } from '@/lib/types-analytics';78/** Latest data updates: one row per source (status, last import, vintage, latest year, values changed). */9export function LatestUpdates({ data, limit = 9 }: { data: UpdatesResponse; limit?: number }) {10  const rows = [...data.sources].sort((a, b) => (b.last_success_at ?? '').localeCompare(a.last_success_at ?? '')).slice(0, limit);11  return (12    <div className="min-w-0">13      <ul className="divide-y divide-rule border-y border-rule text-sm">14        {rows.map((s) => (15          <li key={s.source.id} className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-4 py-2 md:grid-cols-[minmax(0,12rem)_6rem_7rem_5rem_minmax(0,1fr)]">16            <Link href={routes.source(s.source.id)} className="link-quiet flex min-h-[32px] min-w-0 items-center gap-2 font-medium text-ink">17              <span className={cn('inline-block h-2 w-2 shrink-0 rounded-full', s.status === 'ok' ? 'bg-up' : s.status === 'partial' ? 'bg-warn' : s.status === 'failed' ? 'bg-down' : 'bg-rule-strong')} aria-hidden />18              <span className="truncate">{s.source.name ?? s.source.id}</span>19              <span className="sr-only">{tOpt(`home.updates.status.${s.status}`, s.status)}</span>20            </Link>21            <span className="tnum text-right text-xs text-ink-2 md:text-left">{formatDate(s.last_success_at ?? s.last_retrieved_at)}</span>22            <span className="tnum hidden text-xs text-ink-3 md:block">{s.source_updated_at ? t('home.updates.vintage', { date: formatDate(s.source_updated_at) }) : ''}</span>23            <span className="tnum hidden text-xs text-ink-3 md:block">{s.latest_year ? `→ ${s.latest_year}` : ''}</span>24            <span className="tnum hidden truncate text-xs text-ink-3 md:block">25              {t('home.updates.counts', { obs: compact(s.n_observations), ind: grouped(s.n_indicators) })}26              {s.values_changed ? ` · ${t('home.updates.changed', { n: grouped(s.values_changed), c: grouped(s.countries_affected) })}` : ''}27            </span>28          </li>29        ))}30      </ul>31      <div className="mt-2 flex flex-wrap items-center justify-between gap-2 text-xs text-ink-3">32        <span className="tnum">{t('home.updates.snapshot', { run: data.snapshot.run_id ?? '', date: formatDate(data.snapshot.built_at) })}</span>33        <Link href={routes.updates()} className="inline-flex min-h-[36px] items-center text-sm text-accent hover:underline">34          {t('home.updates.all')} →35        </Link>36      </div>37    </div>38  );39}40