"use client"; import { useJobProgress } from "@/hooks/use-job-progress"; import type { StageStatus, RoutingDetail as RoutingDetailType } from "@/hooks/use-job-progress"; function StageIcon({ status }: { status: StageStatus["status"] }) { if (status === "completed") return ( ); if (status === "failed") return ( ); if (status === "active") return ( ); return ; } function StageRow({ stage, error }: { stage: StageStatus; error?: string }) { return (

{stage.label}

{stage.status === "active" && ( <>

{stage.message}

)} {stage.status === "failed" && error && (

{error}

)}
); } function RoutingGrid({ routingDetail }: { routingDetail: RoutingDetailType }) { const MODE_LABELS: Record = { walking: "Walking", cycling: "Cycling", driving: "Driving", transit: "Transit", }; const entries = Object.entries(routingDetail); if (entries.length === 0) return null; return (
{entries.map(([mode, { done, total }]) => (
{MODE_LABELS[mode] ?? mode}
0 ? `${(done / total) * 100}%` : done > 0 ? "100%" : "0%" }} />
{total > 1 ? `${done}/${total}` : done >= 1 ? "done" : "…"}
))}
); } export function CityIngestProgress({ jobId, className = "card max-w-lg", }: { jobId: string | null; className?: string; }) { const { stages, overall, error, routingDetail } = useJobProgress(jobId); if (!jobId) return null; type StageGroup = | { kind: "single"; stage: StageStatus } | { kind: "parallel"; stages: StageStatus[] }; const groups: StageGroup[] = []; for (const stage of stages) { if (stage.parallelGroup) { const last = groups[groups.length - 1]; if (last?.kind === "parallel" && last.stages[0].parallelGroup === stage.parallelGroup) { last.stages.push(stage); } else { groups.push({ kind: "parallel", stages: [stage] }); } } else { groups.push({ kind: "single", stage }); } } return (

Processing City Data

{groups.map((group, gi) => group.kind === "single" ? (
{group.stage.key === "Computing scores" && group.stage.status === "active" && routingDetail && ( )}
) : (

Running in parallel

{group.stages.map((s) => ( ))}
), )}
{overall === "completed" && (
✓ City ingestion complete!{" "} Return to dashboard {" "} or{" "} view on map .
)} {overall === "failed" && (
✗ Ingestion failed: {error}{" "} Return to dashboard .
)}
); }