Sanitized mirror from private repository - 2026-04-18 11:19:59 UTC
This commit is contained in:
53
dashboard/ui/components/toast-provider.tsx
Normal file
53
dashboard/ui/components/toast-provider.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
"use client";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useSSE } from "@/lib/use-sse";
|
||||
|
||||
interface Toast {
|
||||
id: number;
|
||||
message: string;
|
||||
type: "info" | "warning" | "error";
|
||||
}
|
||||
|
||||
const ALERT_TYPES = ["container_unhealthy", "container_restarted", "drift_found"];
|
||||
|
||||
export function ToastProvider() {
|
||||
const events = useSSE("/api/activity", 5);
|
||||
const [toasts, setToasts] = useState<Toast[]>([]);
|
||||
const [seen, setSeen] = useState(new Set<string>());
|
||||
|
||||
const addToast = useCallback((message: string, type: Toast["type"]) => {
|
||||
const id = Date.now();
|
||||
setToasts(prev => [...prev, { id, message, type }]);
|
||||
setTimeout(() => setToasts(prev => prev.filter(t => t.id !== id)), 5000);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
for (const event of events) {
|
||||
const key = `${event.type}-${event.timestamp}`;
|
||||
if (seen.has(key)) continue;
|
||||
if (ALERT_TYPES.includes(event.type)) {
|
||||
setSeen(prev => new Set(prev).add(key));
|
||||
addToast(event.raw || `${event.type}: ${event.source}`,
|
||||
event.type.includes("unhealthy") ? "error" : "warning");
|
||||
}
|
||||
}
|
||||
}, [events, seen, addToast]);
|
||||
|
||||
if (toasts.length === 0) return null;
|
||||
|
||||
const colors = {
|
||||
info: "border-blue-500/20 bg-blue-500/5 backdrop-blur-xl",
|
||||
warning: "border-amber-500/20 bg-amber-500/5 backdrop-blur-xl",
|
||||
error: "border-red-500/20 bg-red-500/5 backdrop-blur-xl",
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 z-50 space-y-2 max-w-sm">
|
||||
{toasts.map(t => (
|
||||
<div key={t.id} className={`rounded-lg border px-4 py-3 text-xs shadow-lg backdrop-blur-md animate-slide-in ${colors[t.type]}`}>
|
||||
<p className="text-foreground">{t.message}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user