import type { Metadata } from 'next'; import { ActionButton, AdminFilters, AdminTitle, Mono, Notice, StatusChip, Trunc } from '@/components/admin/ui'; import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table'; import { LiveAgo } from '@/components/ui/live'; import { Unavailable } from '@/components/ui/unavailable'; import { requeueDeadAction, retryJobAction } from '@/lib/admin/actions'; import { adminApi, load, requireAdmin } from '@/lib/admin/admin-api'; import { fmtDateTime, fmtInt } from '@/lib/format'; export const metadata: Metadata = { title: 'Jobs', robots: { index: false, follow: false } }; export const dynamic = 'force-dynamic'; const STATUSES = ['queued', 'running', 'done', 'failed', 'dead']; function payloadSummary(p: Record): string { return Object.entries(p ?? {}) .filter(([, v]) => v !== null && v !== undefined && v !== '') .map(([k, v]) => `${k}=${typeof v === 'object' ? JSON.stringify(v) : String(v)}`) .join(' ยท '); } export default async function AdminJobsPage({ searchParams }: { searchParams: Promise> }) { await requireAdmin(); const sp = await searchParams; const ret = `/admin/jobs${sp.status || sp.kind ? `?${new URLSearchParams(Object.fromEntries(Object.entries({ status: sp.status, kind: sp.kind }).filter(([, v]) => v)) as Record).toString()}` : ''}`; const res = await load(adminApi.jobs({ status: sp.status, kind: sp.kind, limit: 200 })); const kinds = res.ok ? Object.keys(res.data.depth ?? {}) : []; return ( <>
Requeue dead
{res.ok && res.data.depth && Object.keys(res.data.depth).length > 0 && (
    {Object.entries(res.data.depth).map(([kind, byStatus]) => (
  • {kind} {Object.entries(byStatus).map(([st, n]) => ( {fmtInt(n)} ))}
  • ))}
)} ({ value: k, label: k })) }, { kind: 'select', name: 'status', label: 'Status', value: sp.status, options: STATUSES.map((s) => ({ value: s, label: s })) }, ]} /> {!res.ok ? ( ) : ( Job Kind Status Prio Attempts Run after Locked by Started Finished Error Action {res.data.items.length === 0 && No jobs match.} {res.data.items.map((j) => ( {j.id} {j.kind} {fmtInt(j.priority)} {fmtInt(j.attempts)} / {fmtInt(j.max_attempts)} {j.run_after ? : 'โ€”'} {j.started_at ? : โ€”} {j.finished_at ? : โ€”} {(j.status === 'failed' || j.status === 'dead') && (
Retry
)} ))}
)} ); }