import { Alert, Box, Button, Chip, Paper, Stack, StructuredDialog, Tab, Tabs, Tooltip, Typography } from "@exotel-npm-dev/signal-design-system"; import { useEffect, useRef, useState, type SyntheticEvent } from "react"; import { GWT_ALL_GLOBAL_EVENT_NAMES, initAafClient, registerGlobalEvent, type AafClient } from "../aaf/gwtAafSdk"; import { ensureAafSdkLoaded, getAafSdkScriptUrl } from "../aaf/loadAafSdkIfNeeded"; import { extractContextUpdates, type AafEventContext } from "../utils/eventContext"; import { eventStore, makePersistable } from "../utils/eventStore"; import { EventsPanel, type EventEntry } from "./EventsPanel"; import { InvokeMethodPanel } from "./InvokeMethodPanel"; type ErrorEntry = { id: string; ts: number; name: string; message: string; }; type TabId = "events" | "methods"; function uid(): string { return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; } 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"); return `${hh}:${mm}:${ss}`; } export function EventLoggerView() { const [tab, setTab] = useState("events"); const [events, setEvents] = useState(() => eventStore.loadEvents()); const [errors, setErrors] = useState(() => eventStore.loadErrors()); const [registered, setRegistered] = useState([]); const [bootMsg, setBootMsg] = useState("Bootstrapping SDK…"); const [bootError, setBootError] = useState(null); const [lifetimeCount, setLifetimeCount] = useState(() => eventStore.loadCounter()); const [errorsOpen, setErrorsOpen] = useState(false); const [ctx, setCtx] = useState({}); const clientRef = useRef(null); useEffect(() => { eventStore.saveEvents(events); }, [events]); useEffect(() => { eventStore.saveErrors(errors); }, [errors]); useEffect(() => { eventStore.saveCounter(lifetimeCount); }, [lifetimeCount]); useEffect(() => { let cancelled = false; void (async () => { try { await ensureAafSdkLoaded(); if (cancelled) return; const client = initAafClient(); if (cancelled) return; clientRef.current = client; setBootMsg( `AAF client ready — registering ${GWT_ALL_GLOBAL_EVENT_NAMES.length} globalEvent listener(s)…` ); const okList: string[] = []; await Promise.all( GWT_ALL_GLOBAL_EVENT_NAMES.map((name) => { const handler = (data: unknown) => { const printable = makePersistable(data); try { console.info(`[AAF globalEvent] ${String(name)}`, data); } catch { // ignore console failures } setLifetimeCount((n) => n + 1); const updated = extractContextUpdates(ctxRef.current, data); if (updated) { ctxRef.current = updated; setCtx(updated); } setEvents((prev) => [{ id: uid(), ts: Date.now(), name: String(name), data: printable }, ...prev].slice( 0, 500 ) ); }; return registerGlobalEvent(client, name, handler).then( () => { okList.push(String(name)); }, (err: unknown) => { setErrors((prev) => [ { id: uid(), ts: Date.now(), name: String(name), message: err instanceof Error ? err.message : String(err) }, ...prev ].slice(0, 500) ); } ); }) ); if (cancelled) return; setRegistered(okList.sort()); setBootMsg(`Registered ${okList.length}/${GWT_ALL_GLOBAL_EVENT_NAMES.length} listener(s).`); } catch (err: unknown) { if (cancelled) return; setBootError(err instanceof Error ? err.message : String(err)); setBootMsg("AAF bootstrap failed — see Registration errors."); } })(); return () => { cancelled = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Mirror ctx into a ref so the long-lived event handlers see the latest value // without re-registering when ctx changes. const ctxRef = useRef(ctx); useEffect(() => { ctxRef.current = ctx; }, [ctx]); return ( GWT AAF — SDK Debugger Mirrors the Omni SDK debugger workflow: chronological event timeline (click to expand payload), method playground with placeholders that auto-fill from recent events, and a manual-invoke dialog for any aaf_sdk.js namespace. SDK URL:{" "} {getAafSdkScriptUrl()} 0 ? "success" : "default"} /> 0 ? "warning" : "default"} variant={errors.length > 0 ? "filled" : "outlined"} onClick={errors.length > 0 ? () => setErrorsOpen(true) : undefined} clickable={errors.length > 0} /> {bootError ? bootError : bootMsg} setTab(v as TabId)} textColor="primary" indicatorColor="primary" variant="scrollable" scrollButtons="auto" allowScrollButtonsMobile aria-label="SDK Debugger sections" > {tab === "events" && ( setEvents([])} registered={registered} /> )} {tab === "methods" && } setErrorsOpen(false)} onClear={() => { setErrors([]); setErrorsOpen(false); }} /> ); } function countContextKeys(ctx: AafEventContext): number { return Object.values(ctx).filter((v) => v != null && v !== "").length; } function contextSummary(ctx: AafEventContext): string { return Object.entries(ctx) .filter(([, v]) => v != null && v !== "") .map(([k, v]) => `${k}=${String(v)}`) .join(" · "); } function RegistrationErrorsDialog({ open, errors, bootError, onClose, onClear }: { open: boolean; errors: ErrorEntry[]; bootError: string | null; onClose: () => void; onClear: () => void; }) { return ( } > {bootError && ( {bootError} )} Per-event registration failures. The host typically returns 121 / 122 codes when an event name is unknown to this SDK build. {errors.length === 0 ? ( No registration errors. ) : ( {errors.map((er) => ( {formatTs(er.ts)} {er.message} ))} )} ); }