Skip to content

Commit e6b5aa1

Browse files
committed
Merge feat/HB-022-request-feed: Request feed with pagination
2 parents 8693708 + 0fba30a commit e6b5aa1

1 file changed

Lines changed: 92 additions & 1 deletion

File tree

ui/assets/app.js

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,100 @@ async function renderHookList() {
224224
});
225225
}
226226

227+
// --- Request Feed View ---
227228
async function renderRequestFeed(hookId) {
228229
show('view-requests');
229-
$('#view-requests').innerHTML = '<p class="empty-state">Loading requests...</p>';
230+
const el = $('#view-requests');
231+
el.innerHTML = '<p class="empty-state">Loading requests...</p>';
232+
233+
const hook = await api.get('/api/hooks/' + hookId);
234+
const origin = location.origin;
235+
const ingestUrl = origin + hook.url;
236+
237+
let offset = 0;
238+
const limit = 50;
239+
240+
async function loadRequests(append) {
241+
const data = await api.get('/api/hooks/' + hookId + '/requests?limit=' + limit + '&offset=' + offset);
242+
const requests = data.requests || [];
243+
const total = data.total || 0;
244+
245+
if (!append) {
246+
let html = '<div class="breadcrumb"><a href="#/hooks">Hooks</a> / ' + escapeHtml(hook.name) + '</div>';
247+
html += '<div class="url-box" style="margin-bottom:16px;">';
248+
html += '<span style="flex:1;">' + escapeHtml(ingestUrl) + '</span>';
249+
html += '<button class="btn btn-small btn-copy-feed-url" data-url="' + escapeHtml(ingestUrl) + '">copy</button>';
250+
html += '</div>';
251+
html += '<div class="toolbar">';
252+
html += '<span class="status-text" id="feed-status"></span>';
253+
html += '</div>';
254+
html += '<div id="request-list"></div>';
255+
html += '<div id="feed-footer" style="text-align:center;margin:16px 0;"></div>';
256+
el.innerHTML = html;
257+
258+
el.querySelector('.btn-copy-feed-url').addEventListener('click', (e) => {
259+
e.stopPropagation();
260+
copyText(e.target.dataset.url);
261+
});
262+
}
263+
264+
const listEl = document.getElementById('request-list');
265+
const statusEl = document.getElementById('feed-status');
266+
const footerEl = document.getElementById('feed-footer');
267+
268+
const currentCount = listEl.querySelectorAll('.card').length + requests.length;
269+
statusEl.textContent = 'Showing ' + currentCount + ' of ' + total;
270+
271+
if (total === 0 && !append) {
272+
listEl.innerHTML = '<p class="empty-state">No requests captured yet. Send a webhook to ' + escapeHtml(ingestUrl) + '</p>';
273+
footerEl.innerHTML = '';
274+
return;
275+
}
276+
277+
let cards = '';
278+
for (const req of requests) {
279+
cards += '<div class="card card-clickable" data-hook-id="' + escapeHtml(hookId) + '" data-request-id="' + escapeHtml(req.request_id) + '">';
280+
cards += '<div class="card-header">';
281+
cards += '<span>' + methodBadge(req.method) + ' <span style="color:var(--text-muted);">' + escapeHtml(req.path) + '</span></span>';
282+
cards += '<span class="card-meta" style="margin:0;">' + timeAgo(req.received_at) + '</span>';
283+
cards += '</div>';
284+
cards += '<div class="card-meta">';
285+
cards += '<span>' + formatBytes(req.content_length) + '</span>';
286+
cards += '<span>' + escapeHtml(req.source_ip) + '</span>';
287+
cards += '</div>';
288+
cards += '</div>';
289+
}
290+
291+
if (append) {
292+
listEl.insertAdjacentHTML('beforeend', cards);
293+
} else {
294+
listEl.innerHTML = cards;
295+
}
296+
297+
// Click to view detail
298+
listEl.querySelectorAll('.card-clickable').forEach(card => {
299+
card.onclick = () => {
300+
navigate('/hooks/' + card.dataset.hookId + '/requests/' + card.dataset.requestId);
301+
};
302+
});
303+
304+
// Load more
305+
if (currentCount < total) {
306+
footerEl.innerHTML = '<button class="btn" id="btn-load-more">Load more</button>';
307+
document.getElementById('btn-load-more').addEventListener('click', async () => {
308+
offset += limit;
309+
try {
310+
await loadRequests(true);
311+
} catch (err) {
312+
toast(err.error || 'Failed to load more', 'error');
313+
}
314+
});
315+
} else {
316+
footerEl.innerHTML = '';
317+
}
318+
}
319+
320+
await loadRequests(false);
230321
}
231322

232323
async function renderRequestDetail(hookId, requestId) {

0 commit comments

Comments
 (0)