77c6b62ee2
Web UI rework. - Migrate BackupAlertsTable, BackupJobsTable, BackupRunsTable, BackupsPage, BackupDashboardWidget off @mui (shadcn Table + Badge severity variants: success=chart-2, warning=chart-3, destructive) - App.tsx IA: Backups now top-level nav (DatabaseBackup icon); Media surface primary at /media; /applications -> /media redirect in both route trees (mirrors /monitoring -> /observability) Gate: build + lint + test green.
50 lines
1.4 KiB
TypeScript
50 lines
1.4 KiB
TypeScript
import { Badge } from "@/components/ui/badge";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { useBackupDashboard } from "../hooks/useBackups";
|
|
|
|
export default function BackupDashboardWidget() {
|
|
const { data, isLoading } = useBackupDashboard();
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Backups</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{isLoading || !data ? (
|
|
<p className="text-sm text-muted-foreground">Loading…</p>
|
|
) : (
|
|
<div className="flex flex-row flex-wrap gap-6">
|
|
<div>
|
|
<div className="text-2xl font-semibold">{data.total_jobs}</div>
|
|
<div className="text-xs text-muted-foreground">Jobs</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-2xl font-semibold">
|
|
{data.success_rate_24h}%
|
|
</div>
|
|
<div className="text-xs text-muted-foreground">24h Success</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-2xl font-semibold">
|
|
{data.active_alerts > 0 ? (
|
|
<Badge variant="destructive">{data.active_alerts}</Badge>
|
|
) : (
|
|
0
|
|
)}
|
|
</div>
|
|
<div className="text-xs text-muted-foreground">Alerts</div>
|
|
</div>
|
|
{data.last_failed_at && (
|
|
<div className="self-center text-xs text-destructive">
|
|
Last failed:{" "}
|
|
{new Date(data.last_failed_at * 1000).toLocaleString()}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|