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%
4.0 KB · 74 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 } from '@/components/admin/admin-ui';78export const dynamic = 'force-dynamic';9const SEVERITIES = ['error', 'warning', 'info'];1011export default async function AdminIssuesPage({ searchParams }: { searchParams: Promise<{ severity?: string; connector?: string; indicator?: string; code?: string; run_id?: string }> }) {12  const sp = await searchParams;13  try {14    const r = await adminApi.issues({ severity: sp.severity, connector: sp.connector, indicator: sp.indicator, code: sp.code, run_id: sp.run_id, limit: 300 });15    const link = (patch: Record<string, string | undefined>) => {16      const p = new URLSearchParams();17      for (const [k, v] of Object.entries({ ...sp, ...patch })) if (v) p.set(k, v);18      const s = p.toString();19      return `${routes.admin('issues')}${s ? `?${s}` : ''}`;20    };21    return (22      <>23        <AdminSection24          title={t('admin.issues.title')}25          sub={t('admin.issues.sub', { n: grouped(r.n) })}26          actions={27            <div className="flex flex-wrap gap-1 text-xs">28              <Link href={link({ severity: undefined })} className={`inline-flex h-8 items-center rounded-sm px-2 ${!sp.severity ? 'bg-ink text-paper' : 'text-ink-2 hover:bg-surface-2'}`}>29                {t('admin.issues.all')}30              </Link>31              {SEVERITIES.map((s) => (32                <Link key={s} href={link({ severity: s })} className={`inline-flex h-8 items-center rounded-sm px-2 ${sp.severity === s ? 'bg-ink text-paper' : 'text-ink-2 hover:bg-surface-2'}`}>33                  {s}34                </Link>35              ))}36            </div>37          }38        >39          {r.summary.length ? (40            <ul className="mb-3 flex flex-wrap gap-1.5 text-xs">41              {r.summary.map((s) => (42                <li key={`${s.severity}-${s.code}`}>43                  <Link href={link({ severity: s.severity, code: s.code })} className="inline-flex h-8 items-center gap-1.5 rounded-sm border border-rule px-2 hover:bg-surface-2">44                    <Pill tone={statusTone(s.severity)}>{s.severity}</Pill>45                    <span className="font-mono">{s.code}</span>46                    <span className="tnum text-ink-3">{grouped(s.n)}</span>47                  </Link>48                </li>49              ))}50            </ul>51          ) : null}52          <AdminTable53            rows={r.items}54            rowKey={(x, i) => `${x.run_id}-${x.indicator_id}-${x.country_id}-${i}`}55            empty={t('admin.issues.none')}56            columns={[57              { key: 'sev', header: t('admin.issues.severity'), cell: (x) => <Pill tone={statusTone(x.severity)}>{x.severity ?? t('admin.na')}</Pill> },58              { key: 'code', header: t('admin.issues.code'), cell: (x) => <span className="font-mono">{x.code}</span> },59              { key: 'c', header: t('admin.issues.connector'), cell: (x) => x.connector },60              { key: 'i', header: t('admin.issues.indicator'), cell: (x) => (x.indicator_id ? <Link href={routes.indicator(x.indicator_id)} className="font-mono text-accent hover:underline">{x.indicator_id}</Link> : t('admin.na')) },61              { key: 'cty', header: t('admin.issues.country'), cell: (x) => x.country_id ?? t('admin.na') },62              { key: 'p', header: t('admin.issues.period'), cell: (x) => x.period ?? t('admin.na') },63              { key: 'v', header: t('admin.issues.value'), numeric: true, cell: (x) => (x.value != null ? String(x.value) : t('admin.na')) },64              { key: 'm', header: t('admin.issues.message'), cell: (x) => <span className="text-ink-2">{x.message ?? ''}</span>, className: 'max-w-[28rem]' },65            ]}66          />67        </AdminSection>68      </>69    );70  } catch (e) {71    return <p className="mt-3 rounded-sm border border-down/40 px-3 py-2 text-xs text-down">{adminErrorMessage(e)}</p>;72  }73}74