Files
homelab-optimized/dashboard/ui/app/automations/page.tsx
Gitea Mirror Bot 27d742a147
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m2s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-04-04 12:42:10 UTC
2026-04-04 12:42:10 +00:00

202 lines
6.3 KiB
TypeScript

"use client";
import { usePoll } from "@/lib/use-poll";
import type { EmailStats } from "@/lib/types";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { StatusBadge } from "@/components/status-badge";
interface BackupResult {
host: string;
status: string;
last_run: string;
size?: string;
}
interface DriftResult {
stack: string;
drifted: boolean;
details?: string;
}
interface StackRestart {
stack: string;
status: string;
timestamp: string;
}
export default function AutomationsPage() {
const { data: emails } = usePoll<EmailStats>(
"/api/automations/emails",
60000
);
const { data: backups } = usePoll<BackupResult[]>(
"/api/automations/backups",
120000
);
const { data: drift } = usePoll<DriftResult[]>(
"/api/automations/drift",
120000
);
const { data: restarts } = usePoll<StackRestart[]>(
"/api/automations/restarts",
60000
);
return (
<div className="space-y-6">
<h1 className="text-lg font-semibold">Automations</h1>
{/* Email Organizers */}
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium">
Email Organizers
</CardTitle>
</CardHeader>
<CardContent>
{!emails ? (
<p className="text-xs text-muted-foreground">Loading...</p>
) : (
<div className="space-y-4">
{emails.accounts.map((acct) => (
<div key={acct.account} className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-foreground">
{acct.account}
</span>
<span className="text-xs text-muted-foreground">
{acct.today} today
</span>
</div>
<div className="flex flex-wrap gap-1.5">
{Object.entries(acct.categories).map(([cat, count]) => (
<Badge
key={cat}
variant="secondary"
className="text-[10px]"
>
{cat}: {count}
</Badge>
))}
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
{/* Row 2: Backups, Drift, Restarts */}
<div className="grid grid-cols-3 gap-4">
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium">
Backup Status
</CardTitle>
</CardHeader>
<CardContent>
{!backups ? (
<p className="text-xs text-muted-foreground">Loading...</p>
) : backups.length === 0 ? (
<p className="text-xs text-muted-foreground">No backups found</p>
) : (
<div className="space-y-2">
{backups.map((b) => (
<div
key={b.host}
className="flex items-center justify-between text-xs"
>
<span className="text-foreground">{b.host}</span>
<div className="flex items-center gap-2">
<StatusBadge
color={b.status === "success" ? "green" : "red"}
label={b.status}
/>
<span className="text-muted-foreground">
{b.size ?? ""}
</span>
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium">
Config Drift
</CardTitle>
</CardHeader>
<CardContent>
{!drift ? (
<p className="text-xs text-muted-foreground">Loading...</p>
) : drift.length === 0 ? (
<p className="text-xs text-muted-foreground">
No drift detected
</p>
) : (
<div className="space-y-2">
{drift.map((d) => (
<div
key={d.stack}
className="flex items-center justify-between text-xs"
>
<span className="text-foreground">{d.stack}</span>
<StatusBadge
color={d.drifted ? "amber" : "green"}
label={d.drifted ? "drifted" : "clean"}
/>
</div>
))}
</div>
)}
</CardContent>
</Card>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium">
Stack Restarts
</CardTitle>
</CardHeader>
<CardContent>
{!restarts ? (
<p className="text-xs text-muted-foreground">Loading...</p>
) : restarts.length === 0 ? (
<p className="text-xs text-muted-foreground">
No recent restarts
</p>
) : (
<div className="space-y-2">
{restarts.map((r, i) => (
<div
key={i}
className="flex items-center justify-between text-xs"
>
<span className="text-foreground">{r.stack}</span>
<div className="flex items-center gap-2">
<StatusBadge
color={r.status === "success" ? "green" : "red"}
label={r.status}
/>
<span className="text-muted-foreground">
{new Date(r.timestamp).toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
})}
</span>
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
</div>
</div>
);
}