Files
homelab-optimized/dashboard/ui/components/activity-feed.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

81 lines
2.4 KiB
TypeScript

"use client";
import { useSSE } from "@/lib/use-sse";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { ScrollArea } from "@/components/ui/scroll-area";
import type { ActivityEvent } from "@/lib/types";
const typeColors: Record<string, string> = {
stack_healthy: "bg-green-500",
backup_result: "bg-green-500",
email_classified: "bg-blue-500",
receipt_extracted: "bg-amber-500",
container_unhealthy: "bg-red-500",
};
function formatTime(ts: string) {
try {
return new Date(ts).toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
});
} catch {
return ts;
}
}
function eventMessage(event: ActivityEvent): string {
if (event.raw) return event.raw;
return `${event.type} from ${event.source}`;
}
export function ActivityFeed() {
const events = useSSE("/api/events/stream");
return (
<Card className="col-span-2">
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium">Activity Feed</CardTitle>
<Badge
variant="outline"
className="text-[10px] border-green-500/50 text-green-400"
>
LIVE
</Badge>
</CardHeader>
<CardContent>
<ScrollArea className="h-[220px]">
{events.length === 0 && (
<p className="text-xs text-muted-foreground py-4 text-center">
Waiting for events...
</p>
)}
<div className="space-y-2">
{events.map((event, i) => (
<div
key={`${event.timestamp}-${i}`}
className="flex items-start gap-2 text-xs"
>
<span
className={`w-2 h-2 rounded-full mt-1 shrink-0 ${
typeColors[event.type] ?? "bg-gray-500"
}`}
/>
<div className="flex-1 min-w-0">
<p className="text-foreground truncate">
{eventMessage(event)}
</p>
<p className="text-muted-foreground">
{formatTime(event.timestamp)} &middot; {event.source}
</p>
</div>
</div>
))}
</div>
</ScrollArea>
</CardContent>
</Card>
);
}