SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
13 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
3.1 KB · 53 lines tsx
Raw Blame History
1import Link from 'next/link';2import { t } from '@/i18n';3import { adminApi, adminErrorMessage } from '@/lib/admin-api';4import { grouped } from '@/lib/format';5import { routes } from '@/lib/site';6import { AdminSection, AdminTable, Pill, statusTone, ts } from '@/components/admin/admin-ui';78export const dynamic = 'force-dynamic';910export default async function AdminRunsPage({ searchParams }: { searchParams: Promise<{ connector?: string; status?: string }> }) {11  const sp = await searchParams;12  try {13    const r = await adminApi.runs({ connector: sp.connector, status: sp.status, limit: 300 });14    const connectors = Array.from(new Set(r.items.map((x) => x.connector).filter((x): x is string => !!x))).sort();15    return (16      <AdminSection17        title={t('admin.runs.title')}18        sub={t('admin.runs.sub', { n: grouped(r.n) })}19        actions={20          <div className="flex flex-wrap gap-1 text-xs">21            <Link href={routes.admin('runs')} className={`inline-flex h-8 items-center rounded-sm px-2 ${!sp.connector ? 'bg-ink text-paper' : 'text-ink-2 hover:bg-surface-2'}`}>22              {t('common.all')}23            </Link>24            {connectors.map((c) => (25              <Link key={c} href={`${routes.admin('runs')}?connector=${c}`} className={`inline-flex h-8 items-center rounded-sm px-2 ${sp.connector === c ? 'bg-ink text-paper' : 'text-ink-2 hover:bg-surface-2'}`}>26                {c}27              </Link>28            ))}29          </div>30        }31      >32        <AdminTable33          rows={r.items}34          rowKey={(x, i) => `${x.run_id}-${x.dataset}-${i}`}35          columns={[36            { key: 'run', header: t('admin.runs.run'), cell: (x) => (x.run_id ? <Link href={`${routes.admin('raw')}?run_id=${encodeURIComponent(x.run_id)}`} className="font-mono text-accent hover:underline">{x.run_id}</Link> : t('admin.na')) },37            { key: 'c', header: t('admin.connectors.connector'), cell: (x) => x.connector },38            { key: 'd', header: t('source.runs.dataset'), cell: (x) => <span className="break-all font-mono">{x.dataset}</span>, className: 'max-w-[22rem]' },39            { key: 's', header: t('source.runs.status'), cell: (x) => <Pill tone={statusTone(x.status)}>{x.status ?? t('admin.na')}</Pill> },40            { key: 'st', header: t('source.runs.started'), cell: (x) => ts(x.started_at) },41            { key: 'rows', header: t('source.runs.rows'), numeric: true, cell: (x) => (x.rows_valid != null ? grouped(x.rows_valid) : t('admin.na')) },42            { key: 'w', header: t('source.runs.warnings'), numeric: true, cell: (x) => (x.warnings != null ? grouped(x.warnings) : t('admin.na')) },43            { key: 'e', header: t('source.runs.errors'), numeric: true, cell: (x) => (x.errors ? <span className="text-down">{grouped(x.errors)}</span> : '0') },44            { key: 'm', header: t('admin.issues.message'), cell: (x) => <span className="text-ink-2">{x.message}</span>, className: 'max-w-[24rem]' },45          ]}46        />47      </AdminSection>48    );49  } catch (e) {50    return <p className="mt-3 rounded-sm border border-down/40 px-3 py-2 text-xs text-down">{adminErrorMessage(e)}</p>;51  }52}53