diff --git a/app/(dashboard)/OverviewClient.tsx b/app/(dashboard)/OverviewClient.tsx index f0dc99c5..8b21755a 100644 --- a/app/(dashboard)/OverviewClient.tsx +++ b/app/(dashboard)/OverviewClient.tsx @@ -1,10 +1,9 @@ "use client"; import Link from "next/link"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { BarChart2 } from "lucide-react"; +import { Card, CardContent } from "@/components/ui/card"; +import { BarChart2, Activity } from "lucide-react"; import { ReactNode } from "react"; -import { Separator } from "@/components/ui/separator"; type StatCard = { label: string; @@ -23,6 +22,36 @@ type TrafficSummary = { blockedPercent: number; } | null; +// Per-position accent colors for stat cards (proxy hosts, certs, access lists, traffic) +const CARD_ACCENTS = [ + { border: "border-l-violet-500", icon: "border-violet-500/30 bg-violet-500/10 text-violet-500", count: "text-violet-600 dark:text-violet-400" }, + { border: "border-l-emerald-500", icon: "border-emerald-500/30 bg-emerald-500/10 text-emerald-500", count: "text-emerald-600 dark:text-emerald-400" }, + { border: "border-l-amber-500", icon: "border-amber-500/30 bg-amber-500/10 text-amber-500", count: "text-amber-600 dark:text-amber-400" }, +]; + +const TRAFFIC_ACCENT = { + border: "border-l-cyan-500", + icon: "border-cyan-500/30 bg-cyan-500/10 text-cyan-500", + count: "text-cyan-600 dark:text-cyan-400", +}; + +function getEventDotColor(summary: string): string { + const lower = summary.toLowerCase(); + if (lower.startsWith("delete") || lower.startsWith("remove")) return "bg-rose-500 shadow-[0_0_6px_rgba(244,63,94,0.5)]"; + if (lower.startsWith("create") || lower.startsWith("add")) return "bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.5)]"; + return "bg-primary shadow-[0_0_6px_var(--primary)]"; +} + +function formatRelativeTime(iso: string): string { + const diff = Date.now() - new Date(iso).getTime(); + const mins = Math.floor(diff / 60000); + if (mins < 1) return "just now"; + if (mins < 60) return `${mins}m ago`; + const hrs = Math.floor(mins / 60); + if (hrs < 24) return `${hrs}h ago`; + return new Date(iso).toLocaleDateString(); +} + export default function OverviewClient({ userName, stats, @@ -36,89 +65,108 @@ export default function OverviewClient({ }) { return (
Everything you need to orchestrate Caddy proxies, certificates, and secure edge services.
{stat.label}
+Traffic (24h)
+ {trafficSummary && trafficSummary.totalRequests > 0 && ( +0 ? "text-destructive" : "text-muted-foreground"}`}> - {trafficSummary.blockedPercent}% blocked -
- )} - > - ) : ( -No activity recorded yet.
- ) : ( -No activity recorded yet.
+ ) : ( +Accounts
- {list.entries.length === 0 ? ( -No credentials configured.
- ) : ( -{entry.username}
-- Created {new Date(entry.created_at).toLocaleDateString()} -
-One per line, username:password
Traffic Intelligence
+Traffic Intelligence
{r.domains.join(", ")}
+{r.name}
++ {r.domains[0]} + {r.domains.length > 1 && ( + +{r.domains.length - 1} + )} +
+{r.certIssuer ?? "—"}
+ {r.certIssuer ?? "—"} ), }, { @@ -41,25 +55,24 @@ const columns = [ { id: "status", label: "Status", + width: 110, render: (r: AcmeHost) => ( -{r.domains.join(", ")}
-{r.name}
++ {r.domains[0]}{r.domains.length > 1 ? ` +${r.domains.length - 1}` : ""} +
+