SPB Git

spb/ai-atlas

Public
4.0 KB · 80 lines tsx
1import type { Metadata } from 'next';2import Link from 'next/link';3import { AdminFilters, AdminTitle, Mono, StatusChip, Trunc } from '@/components/admin/ui';4import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';5import { LiveAgo } from '@/components/ui/live';6import { Unavailable } from '@/components/ui/unavailable';7import { adminApi, load, requireAdmin } from '@/lib/admin/admin-api';8import { fmtDateTime, fmtDuration, fmtInt, num } from '@/lib/format';910export const metadata: Metadata = { title: 'Runs', robots: { index: false, follow: false } };11export const dynamic = 'force-dynamic';1213export default async function AdminRunsPage({ searchParams }: { searchParams: Promise<Record<string, string | undefined>> }) {14  await requireAdmin();15  const sp = await searchParams;16  const limit = Math.min(500, Math.max(10, Number(sp.limit) || 100));17  const [runs, connectors] = await Promise.all([load(adminApi.runs({ connector: sp.connector, limit })), load(adminApi.connectors())]);18  const names = connectors.ok ? connectors.data.items.map((c) => c.name).sort() : [];19  return (20    <>21      <AdminTitle title="Runs" count={runs.ok ? fmtInt(runs.data.total) : undefined} />22      <AdminFilters23        action="/admin/runs"24        className="mb-4"25        fields={[26          { kind: 'select', name: 'connector', label: 'Connector', value: sp.connector, options: names.map((n) => ({ value: n, label: n })) },27          { kind: 'select', name: 'limit', label: 'Rows', value: String(limit), any: '100', options: ['50', '100', '250', '500'].map((v) => ({ value: v, label: v })) },28        ]}29      />30      {!runs.ok ? (31        <Unavailable what="Runs" reason={runs.error} />32      ) : (33        <DataTable compact scroll caption="Connector runs">34          <thead>35            <tr>36              <Th>Connector</Th>37              <Th>Started</Th>38              <Th>Status</Th>39              <Th num>Duration</Th>40              <Th num>Disc.</Th>41              <Th num>Fetched</Th>42              <Th num>Changed</Th>43              <Th num>Unch.</Th>44              <Th num>Failed</Th>45              <Th num>Ent. +/~</Th>46              <Th num>Claims</Th>47              <Th num>Rel.</Th>48              <Th num>Events</Th>49              <Th>Error</Th>50              <Th>Run id</Th>51            </tr>52          </thead>53          <tbody>54            {runs.data.items.length === 0 && <EmptyRow cols={15}>No runs match.</EmptyRow>}55            {runs.data.items.map((r) => (56              <tr key={r.id}>57                <Td><Link href={`/admin/runs?connector=${encodeURIComponent(r.connector_name)}`} className="mono text-xs font-medium text-ink hover:text-accent">{r.connector_name}</Link></Td>58                <Td className="text-xs" title={fmtDateTime(r.started_at)}><LiveAgo at={r.started_at} /></Td>59                <Td><StatusChip value={r.status} /></Td>60                <Td num className="tnum text-xs">{num(r.duration_ms) === null ? '—' : fmtDuration(Math.round((num(r.duration_ms) ?? 0) / 1000))}</Td>61                <Td num className="tnum text-xs">{fmtInt(r.docs_discovered)}</Td>62                <Td num className="tnum text-xs">{fmtInt(r.docs_fetched)}</Td>63                <Td num className="tnum text-xs">{fmtInt(r.docs_changed)}</Td>64                <Td num className="tnum text-xs text-ink-3">{fmtInt(r.docs_unchanged)}</Td>65                <Td num className={`tnum text-xs ${num(r.docs_failed) ? 'text-danger' : ''}`}>{fmtInt(r.docs_failed)}</Td>66                <Td num className="tnum text-xs">{fmtInt(r.entities_created)} / {fmtInt(r.entities_updated)}</Td>67                <Td num className="tnum text-xs">{fmtInt(r.claims_written)}</Td>68                <Td num className="tnum text-xs">{fmtInt(r.relations_written)}</Td>69                <Td num className="tnum text-xs">{fmtInt(r.events_emitted)}</Td>70                <Td className="text-xs text-danger"><Trunc text={r.error} max={50} /></Td>71                <Td><Mono>{r.id}</Mono></Td>72              </tr>73            ))}74          </tbody>75        </DataTable>76      )}77    </>78  );79}80
Files · 9f1db171c786cf4a3b99436329f29b145c02aeec
Menu

Theme