import { EventRow } from "@/components/EventRow";
import { type Stats, fetchStats, listEvents } from "@/lib/queries";
import { useRealtimeWorkerEvents } from "@/lib/realtime";
import type { WorkerEvent } from "@/types";
import { useEffect, useState } from "react";
export default function Overview() {
const [stats, setStats] = useState<Stats | null>(null);
const [recent, setRecent] = useState<WorkerEvent[]>([]);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let alive = true;
Promise.all([fetchStats(), listEvents({ limit: 25 })])
.then(([s, r]) => {
if (!alive) return;
setStats(s);
setRecent(r);
})
.catch((e) => alive && setError(String(e?.message ?? e)));
return () => {
alive = false;
};
}, []);
useRealtimeWorkerEvents({
onInsert: (ev) => {
setRecent((prev) => [ev, ...prev].slice(0, 25));
setStats((prev) => {
if (!prev) return prev;
const isToday =
new Date(ev.created_at).toDateString() === new Date().toDateString();
return {
events_today: prev.events_today + (isToday ? 1 : 0),
advice_this_week:
prev.advice_this_week + (ev.event_type === "advice" ? 1 : 0),
warn_block_this_week:
prev.warn_block_this_week +
(ev.severity === "warn" || ev.severity === "block" ? 1 : 0),
};
});
},
onUpdate: (ev) => {
setRecent((prev) => prev.map((e) => (e.id === ev.id ? { ...e, ...ev } : e)));
},
});
return (
<div className="p-6 space-y-6">
<header>
<h1 className="text-xl font-semibold">Overview</h1>
<p className="text-sm text-muted-foreground">
Activity from your devist workers across all projects.
</p>
</header>
{error && (
<div className="rounded-md border border-red-300 bg-red-50 p-3 text-sm text-red-700">
{error}
</div>
)}
<section className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<Stat label="Events today" value={stats?.events_today} />
<Stat label="Advice this week" value={stats?.advice_this_week} />
<Stat label="Warn / block (7d)" value={stats?.warn_block_this_week} />
</section>
<section>
<h2 className="text-sm font-semibold mb-2">Recent events</h2>
<div className="rounded-md border bg-card">
{recent.length === 0 && (
<div className="p-4 text-sm text-muted-foreground">No events yet.</div>
)}
{recent.map((ev) => (
<EventRow key={ev.id} event={ev} />
))}
</div>
</section>
</div>
);
}
function Stat({ label, value }: { label: string; value: number | undefined }) {
return (
<div className="rounded-md border bg-card p-4">
<div className="text-xs uppercase tracking-wide text-muted-foreground">{label}</div>
<div className="mt-1 text-2xl font-semibold tabular-nums">{value ?? "—"}</div>
</div>
);
}