1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
import { useMemo } from "react";
import type { DirectiveWithProgress } from "../../lib/api";
import { useDirectiveEventSubscription } from "../../hooks/useDirectives";
export function EventsTab({ directive }: { directive: DirectiveWithProgress }) {
// Subscribe to real-time events via SSE
const { events: streamEvents, isConnected, error: sseError } = useDirectiveEventSubscription(directive.id);
// Combine initial events with streamed events (avoiding duplicates)
const allEvents = useMemo(() => {
const eventMap = new Map();
// Add initial events first
directive.recentEvents.forEach((e) => eventMap.set(e.id, e));
// Add streamed events (will override any duplicates)
streamEvents.forEach((e) => eventMap.set(e.id, e));
// Sort by created_at descending (most recent first)
return Array.from(eventMap.values()).sort(
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
);
}, [directive.recentEvents, streamEvents]);
return (
<div className="space-y-4">
{/* Connection status */}
<div className="flex items-center justify-between text-[10px] font-mono">
<div className="flex items-center gap-2">
<span className={isConnected ? "text-green-400" : "text-[#556677]"}>
{isConnected ? "\u25CF Live" : "\u25CB Connecting..."}
</span>
{sseError && <span className="text-red-400">{sseError}</span>}
</div>
<span className="text-[#556677]">{allEvents.length} events</span>
</div>
{/* Event list */}
{allEvents.length === 0 ? (
<div className="text-center py-8">
<p className="font-mono text-sm text-[#556677]">No events yet</p>
</div>
) : (
<div className="space-y-2">
{allEvents.map((event) => {
const severityColors: Record<string, string> = {
info: "text-[#75aafc]",
warning: "text-yellow-400",
error: "text-red-400",
critical: "text-red-600",
};
const severityColor = severityColors[event.severity] || "text-[#556677]";
return (
<div
key={event.id}
className="p-3 bg-[rgba(117,170,252,0.02)] border border-[rgba(117,170,252,0.1)]"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className={`font-mono text-xs ${severityColor}`}>{event.eventType}</span>
<span className="font-mono text-[10px] text-[#556677]">{event.actorType}</span>
</div>
<span className="font-mono text-[10px] text-[#556677]">
{new Date(event.createdAt).toLocaleString()}
</span>
</div>
{event.eventData != null && (
<pre className="font-mono text-[10px] text-[#556677] mt-1 overflow-x-auto">
{JSON.stringify(event.eventData, null, 2)}
</pre>
)}
</div>
);
})}
</div>
)}
</div>
);
}
|