HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { RollbackButton } from '@/components/admin/rollback-button';4import { AdminFilters, AdminTitle, Mono, Notice, StatusChip, Trunc } from '@/components/admin/ui';5import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';6import { LiveAgo } from '@/components/ui/live';7import { Unavailable } from '@/components/ui/unavailable';8import { adminApi, load, requireAdmin } from '@/lib/admin/admin-api';9import { fmtDateTime, fmtDuration, fmtInt, num } from '@/lib/format';1011export const metadata: Metadata = { title: 'Runs', robots: { index: false, follow: false } };12export const dynamic = 'force-dynamic';1314export default async function AdminRunsPage({ searchParams }: { searchParams: Promise<Record<string, string | undefined>> }) {15 await requireAdmin();16 const sp = await searchParams;17 const limit = Math.min(500, Math.max(10, Number(sp.limit) || 100));18 const [runs, connectors] = await Promise.all([load(adminApi.runs({ connector: sp.connector, limit })), load(adminApi.connectors())]);19 const names = connectors.ok ? connectors.data.items.map((c) => c.name).sort() : [];20 const ret = `/admin/runs${sp.connector ? `?connector=${encodeURIComponent(sp.connector)}` : ''}`;21 return (22 <>23 <AdminTitle title="Runs" count={runs.ok ? fmtInt(runs.data.total) : undefined} lede="Rollback retracts a run's claims, closes its relations / prices / results and flags its events as backfill — only rows carrying that run_id; nothing is deleted." />24 <Notice notice={sp.notice} level={sp.level} />25 <AdminFilters26 action="/admin/runs"27 className="mb-4"28 fields={[29 { kind: 'select', name: 'connector', label: 'Connector', value: sp.connector, options: names.map((n) => ({ value: n, label: n })) },30 { kind: 'select', name: 'limit', label: 'Rows', value: String(limit), any: '100', options: ['50', '100', '250', '500'].map((v) => ({ value: v, label: v })) },31 ]}32 />33 {!runs.ok ? (34 <Unavailable what="Runs" reason={runs.error} />35 ) : (36 <DataTable compact scroll caption="Connector runs">37 <thead>38 <tr>39 <Th>Connector</Th>40 <Th>Started</Th>41 <Th>Status</Th>42 <Th num>Duration</Th>43 <Th num>Disc.</Th>44 <Th num>Fetched</Th>45 <Th num>Changed</Th>46 <Th num>Unch.</Th>47 <Th num>Failed</Th>48 <Th num>Ent. +/~</Th>49 <Th num>Claims</Th>50 <Th num>Rel.</Th>51 <Th num>Events</Th>52 <Th>Error</Th>53 <Th>Run id</Th>54 <Th>Rollback</Th>55 </tr>56 </thead>57 <tbody>58 {runs.data.items.length === 0 && <EmptyRow cols={16}>No runs match.</EmptyRow>}59 {runs.data.items.map((r) => (60 <tr key={r.id}>61 <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>62 <Td className="text-xs" title={fmtDateTime(r.started_at)}><LiveAgo at={r.started_at} /></Td>63 <Td><StatusChip value={r.status} /></Td>64 <Td num className="tnum text-xs">{num(r.duration_ms) === null ? '—' : fmtDuration(Math.round((num(r.duration_ms) ?? 0) / 1000))}</Td>65 <Td num className="tnum text-xs">{fmtInt(r.docs_discovered)}</Td>66 <Td num className="tnum text-xs">{fmtInt(r.docs_fetched)}</Td>67 <Td num className="tnum text-xs">{fmtInt(r.docs_changed)}</Td>68 <Td num className="tnum text-xs text-ink-3">{fmtInt(r.docs_unchanged)}</Td>69 <Td num className={`tnum text-xs ${num(r.docs_failed) ? 'text-danger' : ''}`}>{fmtInt(r.docs_failed)}</Td>70 <Td num className="tnum text-xs">{fmtInt(r.entities_created)} / {fmtInt(r.entities_updated)}</Td>71 <Td num className="tnum text-xs">{fmtInt(r.claims_written)}</Td>72 <Td num className="tnum text-xs">{fmtInt(r.relations_written)}</Td>73 <Td num className="tnum text-xs">{fmtInt(r.events_emitted)}</Td>74 <Td className="text-xs text-danger"><Trunc text={r.error} max={50} /></Td>75 <Td><Mono>{r.id}</Mono></Td>76 <Td>77 <RollbackButton runId={r.id} connector={r.connector_name} returnTo={ret} disabled={r.status === 'running' || r.status === 'queued' || Boolean((r.meta as { rolled_back?: unknown } | null)?.rolled_back)} />78 </Td>79 </tr>80 ))}81 </tbody>82 </DataTable>83 )}84 </>85 );86}87