Sanitized mirror from private repository - 2026-04-04 12:42:10 UTC
This commit is contained in:
201
dashboard/ui/app/automations/page.tsx
Normal file
201
dashboard/ui/app/automations/page.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user