spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1import Link from 'next/link';2import { Section } from '@/components/ui/section';3import { Badge, StatusBadge } from '@/components/ui/badge';4import { EmptyState } from '@/components/ui/empty-state';5import { connectorHealth } from '@/lib/queries/admin';6import { fmtDateTime, fmtInt, relativeTime } from '@/lib/format';78export default async function AdminConnectorsPage() {9 const rows = await connectorHealth();10 return (11 <div className="space-y-6">12 <header className="pt-4">13 <h1 className="text-3xl">Connectors</h1>14 <p className="mt-1 text-[13.5px] text-ink-2">Health, last success and attempt, record counts of the last run, drift signals and license status per connector.</p>15 </header>16 <Section id="connectors" title="All connectors" kicker={`${rows.length} registered`}>17 {rows.length ? (18 <div className="ci-table-wrap">19 <table className="ci-table">20 <thead>21 <tr>22 <th>Connector</th>23 <th>Status</th>24 <th>License</th>25 <th>Health</th>26 <th>Last success</th>27 <th>Last attempt</th>28 <th>Last run</th>29 <th className="num">Fetched</th>30 <th className="num">Created</th>31 <th className="num">Updated</th>32 <th className="num">Rejected</th>33 <th>Drift</th>34 <th className="num">Runs</th>35 </tr>36 </thead>37 <tbody>38 {rows.map((r) => (39 <tr key={r.slug}>40 <td>41 <Link className="ci-link" href={`/source/${r.slug}`}>42 {r.name}43 </Link>44 <span className="ci-mono block text-[10.5px] text-ink-4">{r.slug}</span>45 </td>46 <td>47 <StatusBadge status={r.status} />48 </td>49 <td>50 <Badge tone={r.license_status === 'approved' ? 'ok' : 'warn'}>{r.license_status}</Badge>51 </td>52 <td>53 <StatusBadge status={r.health ?? 'unknown'} />54 {r.paused ? <Badge tone="warn" className="ml-1">paused</Badge> : null}55 {r.health_detail ? <span className="block max-w-[220px] text-[11.5px] text-ink-3">{r.health_detail}</span> : null}56 </td>57 <td className="whitespace-nowrap text-[12.5px]" title={r.last_success_at ? fmtDateTime(r.last_success_at) : ''}>58 {r.last_success_at ? relativeTime(r.last_success_at) : '—'}59 </td>60 <td className="whitespace-nowrap text-[12.5px]" title={r.last_attempt_at ? fmtDateTime(r.last_attempt_at) : ''}>61 {r.last_attempt_at ? relativeTime(r.last_attempt_at) : '—'}62 </td>63 <td>64 {r.last_run_id ? (65 <>66 <Link className="ci-mono ci-link text-[11.5px]" href={`/admin/runs/${r.last_run_id}`}>67 {r.last_run_id}68 </Link>{' '}69 <StatusBadge status={r.last_run_status} />70 </>71 ) : (72 <span className="text-ink-4">—</span>73 )}74 </td>75 <td className="num">{r.records_fetched == null ? '—' : fmtInt(r.records_fetched)}</td>76 <td className="num">{r.records_created == null ? '—' : fmtInt(r.records_created)}</td>77 <td className="num">{r.records_updated == null ? '—' : fmtInt(r.records_updated)}</td>78 <td className="num">{r.records_rejected == null ? '—' : fmtInt(r.records_rejected)}</td>79 <td>80 {r.schema_drift?.length ? <Badge tone="warn">{r.schema_drift.length} drift</Badge> : <span className="text-ink-4">none</span>}81 {r.drift_fields ? <span className="block text-[11px] text-ink-3">{fmtInt(r.drift_fields)} fields tracked</span> : null}82 </td>83 <td className="num">{fmtInt(r.runs_total)}</td>84 </tr>85 ))}86 </tbody>87 </table>88 </div>89 ) : (90 <EmptyState compact>No connector registered.</EmptyState>91 )}92 </Section>93 </div>94 );95}96