import { Alert, Box, Button, Chip, IconButton, Paper, Stack, TextField, Tooltip, Typography } from "@exotel-npm-dev/signal-design-system"; import { useMemo, useState, type MouseEvent } from "react"; import { safeJson } from "../utils/safeJson"; export type EventEntry = { id: string; ts: number; name: string; data: unknown; }; function formatTs(ts: number): string { const d = new Date(ts); const hh = String(d.getHours()).padStart(2, "0"); const mm = String(d.getMinutes()).padStart(2, "0"); const ss = String(d.getSeconds()).padStart(2, "0"); const ms = String(d.getMilliseconds()).padStart(3, "0"); return `${hh}:${mm}:${ss}.${ms}`; } function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); return ( ) => { e.stopPropagation(); void navigator.clipboard .writeText(text) .then(() => { setCopied(true); window.setTimeout(() => setCopied(false), 1200); }) .catch(() => undefined); }} aria-label="Copy" > {copied ? "✓" : "⧉"} ); } export function EventsPanel({ events, onClear, registered }: { events: EventEntry[]; onClear: () => void; registered: string[]; }) { const [filter, setFilter] = useState(""); const [expanded, setExpanded] = useState>({}); const visible = useMemo(() => { const q = filter.trim().toLowerCase(); const matched = q ? events.filter((e) => { if (e.name.toLowerCase().includes(q)) return true; try { return safeJson(e.data).toLowerCase().includes(q); } catch { return false; } }) : events; // Force newest-first regardless of how the parent stored them — defensive // against future state changes / restored sessionStorage order. return [...matched].sort((a, b) => b.ts - a.ts); }, [events, filter]); return ( setFilter(e.target.value)} sx={{ minWidth: 240, flex: 1 }} /> Showing {visible.length} of {events.length} {events.length === 0 && ( No events yet. Trigger something in the agent UI (start a call, change state, accept chat). Listeners include:{" "} {registered.slice(0, 8).join(", ")} {registered.length > 8 ? `, +${registered.length - 8} more` : ""} )} {visible.map((ev) => { const isOpen = !!expanded[ev.id]; const payloadStr = safeJson(ev.data); return ( setExpanded((m) => ({ ...m, [ev.id]: !isOpen }))} sx={{ p: 1.25, cursor: "pointer", userSelect: "none", borderLeft: 3, borderLeftColor: "primary.main", "&:hover": { bgcolor: "action.hover" } }} > {formatTs(ev.ts)} {isOpen && ( ) => e.stopPropagation()} sx={{ mt: 1, mb: 0, p: 1, bgcolor: "action.hover", borderRadius: 1, fontSize: 12, overflow: "auto", maxHeight: 320, cursor: "text" }} > {payloadStr} )} ); })} ); }