Skip to content

Commit 4fcc956

Browse files
committed
chore: ignore tmp/ debug artifacts
1 parent bf8e7a6 commit 4fcc956

File tree

4 files changed

+86
-8
lines changed

4 files changed

+86
-8
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ coverage/
1414
.tmp/
1515
.temp/
1616

17+
# Local temporary artifacts created during debugging/log capture
18+
tmp/
19+
1720
# Environment files
1821
.env
1922
.env.*.local

src/components/WebhookInspector.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@
1616
.preview-time{margin-bottom:8px;color:#666}
1717
.preview-json{background:#fafafa;padding:12px;border-radius:6px;overflow:auto}
1818
.preview-empty{color:#666}
19+
20+
/* Remove ugly default focus ring on the left list, but keep a subtle ring for keyboard users */
21+
.webhook-inspector-list:focus{outline: none; box-shadow: none}
22+
.webhook-inspector-list:focus-visible{outline: none; box-shadow: 0 0 0 3px rgba(0,120,212,0.15);}

src/components/WebhookInspector.tsx

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ interface Props {
1515
userPath: string;
1616
selectedBayDbId?: number | null;
1717
selectedBayId?: string | null;
18+
clearSignal?: number;
1819
}
1920

2021
const getBayIdFromEvent = (e: EventItem) => {
@@ -35,11 +36,12 @@ const getBayIdFromEvent = (e: EventItem) => {
3536
}
3637
};
3738

38-
const WebhookInspector: React.FC<Props> = ({ userPath, selectedBayDbId = null, selectedBayId = null }) => {
39+
const WebhookInspector: React.FC<Props> = ({ userPath, selectedBayDbId = null, selectedBayId = null, clearSignal }) => {
3940
const [allEvents, setAllEvents] = React.useState<EventItem[]>([]);
4041
const [connected, setConnected] = React.useState(false);
4142
const [selectedIndex, setSelectedIndex] = React.useState<number | null>(null);
4243
const listRef = React.useRef<HTMLUListElement | null>(null);
44+
const listContainerRef = React.useRef<HTMLDivElement | null>(null);
4345

4446
// Fetch initial events
4547
React.useEffect(() => {
@@ -77,7 +79,20 @@ const WebhookInspector: React.FC<Props> = ({ userPath, selectedBayDbId = null, s
7779
es.onmessage = (ev) => {
7880
try {
7981
const data = JSON.parse(ev.data);
80-
setAllEvents(prev => [{ id: data.id, eventType: data.eventType, timestamp: data.timestamp, data: data.data, raw: data.raw, expanded: false }, ...prev]);
82+
const newItem: EventItem = { id: data.id, eventType: data.eventType, timestamp: data.timestamp, data: data.data, raw: data.raw, expanded: false };
83+
setAllEvents(prev => [newItem, ...prev]);
84+
// If the new item matches the current bay filter (or there is no filter) select it and focus the list
85+
try {
86+
const bayId = getBayIdFromEvent(newItem);
87+
const matches = (!selectedBayDbId && !selectedBayId) || (bayId && (String(bayId) === String(selectedBayId) || String(bayId) === String(selectedBayDbId)));
88+
if (matches) {
89+
setSelectedIndex(0);
90+
// focus the list container so keyboard navigation continues from the newly added item
91+
setTimeout(() => listContainerRef.current?.focus(), 0);
92+
}
93+
} catch (e) {
94+
// ignore
95+
}
8196
} catch (err) {
8297
console.warn('Invalid SSE payload', err);
8398
}
@@ -113,6 +128,30 @@ const WebhookInspector: React.FC<Props> = ({ userPath, selectedBayDbId = null, s
113128
}
114129
}, [selectedIndex, filtered]);
115130

131+
// clear local events when requested
132+
React.useEffect(() => {
133+
if (typeof clearSignal === 'undefined') return;
134+
setAllEvents([]);
135+
setSelectedIndex(null);
136+
}, [clearSignal]);
137+
138+
// global fallback: listen for 'webhook:clear' events
139+
React.useEffect(() => {
140+
const handler = (ev: any) => {
141+
try {
142+
if (!ev || !ev.detail) return;
143+
const detailPath = ev.detail.userPath;
144+
if (!detailPath) return;
145+
if (String(detailPath) === String(userPath)) {
146+
setAllEvents([]);
147+
setSelectedIndex(null);
148+
}
149+
} catch (err) { /* ignore */ }
150+
};
151+
window.addEventListener('webhook:clear', handler as EventListener);
152+
return () => window.removeEventListener('webhook:clear', handler as EventListener);
153+
}, [userPath]);
154+
116155
const select = (idx: number) => {
117156
setSelectedIndex(idx);
118157
};
@@ -132,9 +171,29 @@ const WebhookInspector: React.FC<Props> = ({ userPath, selectedBayDbId = null, s
132171

133172
const selectedEvent = selectedIndex === null ? null : filtered[selectedIndex];
134173

174+
const getEventModelPayload = (e: EventItem) => {
175+
try {
176+
// Common places where the EventModel might appear
177+
const maybe = (e.data ?? e.raw) as any;
178+
if (!maybe) return e.data ?? e.raw ?? {};
179+
// If envelope where data contains EventModel
180+
if (maybe.EventModel) return maybe.EventModel;
181+
// Some payloads might have data: { EventModel: {...} }
182+
if (maybe.data && maybe.data.EventModel) return maybe.data.EventModel;
183+
// Some normalized records put typed payload under 'data' already
184+
if (e.data && (e.data.EventModel || e.data.eventModel)) return e.data.EventModel ?? e.data.eventModel;
185+
// Fallback to raw.data.EventModel
186+
if (e.raw && e.raw.data && (e.raw.data.EventModel || e.raw.data.eventModel)) return e.raw.data.EventModel ?? e.raw.data.eventModel;
187+
// Last resort: return the whole data/raw object
188+
return maybe;
189+
} catch (err) {
190+
return e.data ?? e.raw ?? {};
191+
}
192+
};
193+
135194
return (
136195
<div className="webhook-inspector">
137-
<div className="webhook-inspector-list" tabIndex={0} onKeyDown={onListKeyDown}>
196+
<div ref={listContainerRef} className="webhook-inspector-list" tabIndex={0} onKeyDown={onListKeyDown}>
138197
<div className="webhook-events-header">
139198
<strong>Events</strong>
140199
<span className={`webhook-events-status ${connected ? 'live' : ''}`}>{connected ? 'live' : 'disconnected'}</span>
@@ -159,7 +218,7 @@ const WebhookInspector: React.FC<Props> = ({ userPath, selectedBayDbId = null, s
159218
<h4 className="preview-title">{selectedEvent.eventType}</h4>
160219
<div className="preview-time">{new Date(selectedEvent.timestamp).toLocaleString()}</div>
161220
{/* Version 1: render JSON fallback of event.data or raw */}
162-
<pre className="preview-json">{JSON.stringify(selectedEvent.data || selectedEvent.raw || {}, null, 2)}</pre>
221+
<pre className="preview-json">{JSON.stringify(getEventModelPayload(selectedEvent), null, 2)}</pre>
163222
</div>
164223
) : (
165224
<div className="preview-empty">Select an event to preview</div>

src/components/WebhookView.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const WebhookView: React.FC<WebhookViewProps> = ({ selectedBayDbId = null
1919

2020
const webhookKey = 'WEBHOOK_PATH';
2121
const [localWebhook, setLocalWebhook] = React.useState<string | null>(null);
22+
const [clearSignal, setClearSignal] = React.useState(0);
2223

2324
// Development override: allow forcing a webhook path via ?webhook=<id>
2425
// This is intentionally non-destructive and only used when no saved property exists.
@@ -92,9 +93,20 @@ export const WebhookView: React.FC<WebhookViewProps> = ({ selectedBayDbId = null
9293
<button onClick={async () => {
9394
if (!url) return;
9495
try {
95-
await fetch(`${url}/events`, { method: 'DELETE' });
96-
setLocalWebhook((v) => v ? null : '');
97-
setTimeout(() => setLocalWebhook((v) => v === '' ? (new URLSearchParams(window.location.search).get('webhook') || null) : v), 50);
96+
const r = await fetch(`${url}/events`, { method: 'DELETE' });
97+
if (!r.ok) {
98+
console.warn('Failed to clear events', await r.text());
99+
return;
100+
}
101+
// notify inspector to clear its local state
102+
setClearSignal(s => s + 1);
103+
try {
104+
// global fallback for listeners that don't receive prop change
105+
const ev = new CustomEvent('webhook:clear', { detail: { userPath: (localWebhook || webhookPath) } });
106+
window.dispatchEvent(ev);
107+
} catch (err) {
108+
// ignore - older browsers may not support CustomEvent constructor
109+
}
98110
} catch (err) {
99111
console.warn('Failed to clear events', err);
100112
}
@@ -103,7 +115,7 @@ export const WebhookView: React.FC<WebhookViewProps> = ({ selectedBayDbId = null
103115

104116
<div className="webhook-inspector-wrap">
105117
{url && (
106-
<WebhookInspector userPath={(localWebhook || webhookPath) as string} selectedBayDbId={selectedBayDbId} selectedBayId={selectedBayIdProp} />
118+
<WebhookInspector userPath={(localWebhook || webhookPath) as string} selectedBayDbId={selectedBayDbId} selectedBayId={selectedBayIdProp} clearSignal={clearSignal} />
107119
)}
108120
</div>
109121
</div>

0 commit comments

Comments
 (0)