From 59c3c4b72435d09d0210d31fbf2ef5cccd42d830 Mon Sep 17 00:00:00 2001 From: nate Date: Tue, 24 Mar 2026 17:02:09 +0400 Subject: [PATCH] add ping detail modal --- apps/web/src/views/detail.ejs | 105 ++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/apps/web/src/views/detail.ejs b/apps/web/src/views/detail.ejs index ced518b..9f352ef 100644 --- a/apps/web/src/views/detail.ejs +++ b/apps/web/src/views/detail.ejs @@ -116,8 +116,6 @@
- <% - %> @@ -130,8 +128,10 @@ - <% pings.slice(0, 30).forEach(function(c) { %> - + <% pings.slice(0, 30).forEach(function(c) { + const pingJson = JSON.stringify(c).split('&').join('&').split('"').join('"').split('<').join('<').split(String.fromCharCode(62)).join('>'); + %> + @@ -146,6 +146,18 @@ + + +

Edit Monitor

@@ -161,6 +173,88 @@ const _initialQuery = <%~ JSON.stringify(m.query || null) %>; <%~ include('./partials/monitor-form-js') %> + // ── Ping detail modal ────────────────────────────────────────── + function openPingModal(ping) { + const body = document.getElementById('ping-modal-body'); + const meta = ping.meta || {}; + const headers = meta.headers || {}; + const bodyPreview = meta.body_preview || null; + const time = new Date(ping.checked_at); + const scheduled = ping.scheduled_at ? new Date(ping.scheduled_at) : null; + + let html = '
'; + + // Status row + html += '
'; + html += ping.up + ? 'Up' + : 'Down'; + if (ping.status_code != null) html += `${ping.status_code}`; + if (ping.latency_ms != null) html += `${ping.latency_ms}ms`; + html += '
'; + + // Info grid + html += '
'; + html += `
Checked at
${time.toLocaleString()}
`; + if (scheduled) html += `
Scheduled at
${scheduled.toLocaleString()}
`; + if (ping.jitter_ms != null) html += `
Jitter
${ping.jitter_ms}ms
`; + if (ping.region) html += `
Region
${escapeHtml(ping.region)}
`; + if (ping.run_id) html += `
Run ID
${escapeHtml(ping.run_id)}
`; + if (meta.cert_expiry_days != null) html += `
Cert expiry
${meta.cert_expiry_days} days
`; + html += '
'; + + // Error + if (ping.error) { + html += '
'; + html += '
Error
'; + html += `
${escapeHtml(ping.error)}
`; + html += '
'; + } + + // Response headers + const headerKeys = Object.keys(headers); + if (headerKeys.length > 0) { + html += '
'; + html += '
Response Headers
'; + html += '
'; + for (const [k, v] of Object.entries(headers)) { + html += `
${escapeHtml(k)}:${escapeHtml(String(v))}
`; + } + html += '
'; + } + + // Body preview + if (bodyPreview) { + html += '
'; + html += '
Response Body (first 500 chars)
'; + html += `
${escapeHtml(bodyPreview)}
`; + html += '
'; + } + + html += '
'; + body.innerHTML = html; + document.getElementById('ping-modal').classList.remove('hidden'); + } + + function closePingModal() { + document.getElementById('ping-modal').classList.add('hidden'); + } + + // Close on Escape + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') closePingModal(); + }); + + // Click handler for ping rows (event delegation) + document.getElementById('pings-table').addEventListener('click', (e) => { + const row = e.target.closest('tr[data-ping]'); + if (!row) return; + try { + const ping = JSON.parse(row.dataset.ping); + openPingModal(ping); + } catch {} + }); + // Toggle button document.getElementById('toggle-btn').onclick = async (e) => { e.preventDefault(); @@ -610,7 +704,8 @@ const tbody = document.getElementById('pings-table'); if (tbody) { const tr = document.createElement('tr'); - tr.className = 'table-row-alt'; + tr.className = 'table-row-alt cursor-pointer hover:bg-white/[0.02]'; + tr.dataset.ping = JSON.stringify(ping); const regionDisplay = ping.region || '—'; tr.innerHTML = `
Status
<%~ c.up ? 'Up' : 'Down' %> <%= c.status_code != null ? c.status_code : '—' %> <%= c.latency_ms != null ? c.latency_ms + 'ms' : '—' %> ${ping.up ? 'Up' : 'Down'}