import { Badge } from "@/components/ui/badge"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import type { BackupJob, BackupRun } from "../types/backups"; interface Props { jobs: BackupJob[]; latestRuns: Map; } function formatInterval(seconds: number | null): string { if (!seconds) return "N/A"; if (seconds < 60) return `${seconds}s`; if (seconds < 3600) return `${Math.floor(seconds / 60)}m`; if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`; return `${Math.floor(seconds / 86400)}d`; } function formatTimestamp(ts: number | null): string { if (!ts) return "Never"; return new Date(ts * 1000).toLocaleString(); } type StatusVariant = "success" | "destructive" | "warning" | "secondary"; /** * Map a job/run status onto a Badge variant per design §2.3: * `success` → success (chart-2); `failure` → destructive (chart-4); * `in_progress` → warning (chart-3); unknown → secondary (neutral accent). */ function statusVariant(status: string): StatusVariant { if (status === "success") return "success"; if (status === "failure") return "destructive"; if (status === "in_progress") return "warning"; return "secondary"; } export default function BackupJobsTable({ jobs, latestRuns }: Props) { return (
Name Source Target Schedule Last Status Last Run Next Expected {jobs.map((job) => { const run = latestRuns.get(job.id); const status = run?.status ?? "unknown"; const nextExpected = run && job.schedule_interval_seconds ? run.started_at + job.schedule_interval_seconds : null; return ( {job.name} {job.source ?? "—"} {job.target ?? "—"} {formatInterval(job.schedule_interval_seconds)} {status} {formatTimestamp(run?.started_at ?? null)} {formatTimestamp(nextExpected)} ); })}
); }