"use client"; import { useEffect, useState } from "react"; import type { JobSummary } from "@transportationer/shared"; const STATE_STYLES: Record = { active: "bg-yellow-100 text-yellow-800", waiting: "bg-blue-100 text-blue-800", "waiting-children": "bg-purple-100 text-purple-800", completed: "bg-green-100 text-green-800", failed: "bg-red-100 text-red-800", delayed: "bg-gray-100 text-gray-600", }; function formatDuration(ms: number | null): string { if (ms === null) return "—"; if (ms < 1000) return `${ms}ms`; if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`; return `${(ms / 60_000).toFixed(1)}m`; } export default function JobsPage() { const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const refresh = () => { fetch("/api/admin/jobs") .then((r) => r.json()) .then(setJobs) .finally(() => setLoading(false)); }; useEffect(() => { refresh(); const id = setInterval(refresh, 5000); return () => clearInterval(id); }, []); const handleDelete = async (jobId: string) => { await fetch(`/api/admin/jobs/${jobId}`, { method: "DELETE" }); refresh(); }; return (

Job Queue

{loading ? (
Loading…
) : jobs.length === 0 ? (
No jobs in the queue.
) : (
{["ID", "Type", "City", "State", "Progress", "Duration", "Created", "Actions"].map( (h) => ( ), )} {jobs.map((job) => ( ))}
{h}
{job.id.slice(0, 8)}… {job.type} {job.citySlug} {job.state} {job.progress ? (
{job.progress.pct}%
) : ( "—" )}
{formatDuration(job.duration)} {new Date(job.createdAt).toLocaleTimeString()} {job.state !== "active" && ( )} {job.failedReason && (

{job.failedReason}

)}
)}
); }