@@ -224,9 +224,100 @@ async function renderHookList() {
224224 } ) ;
225225}
226226
227+ // --- Request Feed View ---
227228async 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
232323async function renderRequestDetail ( hookId , requestId ) {
0 commit comments