Files
manage/frontend/src/components/BackupJobsTable.tsx
T

85 lines
3.3 KiB
TypeScript

import {
Chip,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from "@mui/material";
import type { BackupJob, BackupRun } from "../types/backups";
interface Props {
jobs: BackupJob[];
latestRuns: Map<string, BackupRun>;
}
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();
}
export default function BackupJobsTable({ jobs, latestRuns }: Props) {
return (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Source</TableCell>
<TableCell>Target</TableCell>
<TableCell>Schedule</TableCell>
<TableCell>Last Status</TableCell>
<TableCell>Last Run</TableCell>
<TableCell>Next Expected</TableCell>
</TableRow>
</TableHead>
<TableBody>
{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 (
<TableRow key={job.id} hover>
<TableCell>{job.name}</TableCell>
<TableCell>{job.source ?? "—"}</TableCell>
<TableCell>{job.target ?? "—"}</TableCell>
<TableCell>{formatInterval(job.schedule_interval_seconds)}</TableCell>
<TableCell>
<Chip
label={status}
color={
status === "success"
? "success"
: status === "failure"
? "error"
: status === "in_progress"
? "warning"
: "default"
}
size="small"
/>
</TableCell>
<TableCell>{formatTimestamp(run?.started_at ?? null)}</TableCell>
<TableCell>{formatTimestamp(nextExpected)}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
);
}