add ping detail modal
This commit is contained in:
parent
9a9f872fa6
commit
59c3c4b724
|
|
@ -116,8 +116,6 @@
|
|||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<%
|
||||
%>
|
||||
<thead>
|
||||
<tr class="text-gray-500 text-xs">
|
||||
<th class="text-left px-4 py-2 font-medium">Status</th>
|
||||
|
|
@ -130,8 +128,10 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody id="pings-table" class="divide-y divide-border-subtle">
|
||||
<% pings.slice(0, 30).forEach(function(c) { %>
|
||||
<tr class="table-row-alt">
|
||||
<% pings.slice(0, 30).forEach(function(c) {
|
||||
const pingJson = JSON.stringify(c).split('&').join('&').split('"').join('"').split('<').join('<').split(String.fromCharCode(62)).join('>');
|
||||
%>
|
||||
<tr class="table-row-alt cursor-pointer hover:bg-white/[0.02]" data-ping="<%~ pingJson %>">
|
||||
<td class="px-4 py-2"><%~ c.up ? '<span class="text-green-400">Up</span>' : '<span class="text-red-400">Down</span>' %></td>
|
||||
<td class="px-4 py-2 text-gray-300"><%= c.status_code != null ? c.status_code : '—' %></td>
|
||||
<td class="px-4 py-2 text-gray-300"><%= c.latency_ms != null ? c.latency_ms + 'ms' : '—' %></td>
|
||||
|
|
@ -146,6 +146,18 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ping detail modal -->
|
||||
<div id="ping-modal" class="fixed inset-0 z-50 hidden">
|
||||
<div class="absolute inset-0 bg-black/60 backdrop-blur-sm" onclick="closePingModal()"></div>
|
||||
<div class="absolute inset-4 sm:inset-auto sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2 sm:-translate-y-1/2 sm:w-full sm:max-w-lg sm:max-h-[80vh] card-static overflow-y-auto">
|
||||
<div class="sticky top-0 bg-surface-solid flex items-center justify-between px-5 py-3 border-b divider">
|
||||
<h3 class="text-sm font-medium text-gray-200">Ping Details</h3>
|
||||
<button onclick="closePingModal()" class="text-gray-500 hover:text-gray-300 text-lg leading-none">×</button>
|
||||
</div>
|
||||
<div id="ping-modal-body" class="p-5 text-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit form -->
|
||||
<div class="card-static p-6">
|
||||
<h3 class="text-sm text-gray-400 mb-4">Edit Monitor</h3>
|
||||
|
|
@ -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 = '<div class="space-y-4">';
|
||||
|
||||
// Status row
|
||||
html += '<div class="flex items-center gap-3">';
|
||||
html += ping.up
|
||||
? '<span class="inline-block w-2.5 h-2.5 rounded-full dot-up"></span><span class="text-green-400 font-medium">Up</span>'
|
||||
: '<span class="inline-block w-2.5 h-2.5 rounded-full dot-down"></span><span class="text-red-400 font-medium">Down</span>';
|
||||
if (ping.status_code != null) html += `<span class="text-gray-400 font-mono">${ping.status_code}</span>`;
|
||||
if (ping.latency_ms != null) html += `<span class="text-gray-500">${ping.latency_ms}ms</span>`;
|
||||
html += '</div>';
|
||||
|
||||
// Info grid
|
||||
html += '<div class="grid grid-cols-2 gap-x-4 gap-y-2 text-xs">';
|
||||
html += `<div class="text-gray-500">Checked at</div><div class="text-gray-300">${time.toLocaleString()}</div>`;
|
||||
if (scheduled) html += `<div class="text-gray-500">Scheduled at</div><div class="text-gray-300">${scheduled.toLocaleString()}</div>`;
|
||||
if (ping.jitter_ms != null) html += `<div class="text-gray-500">Jitter</div><div class="text-gray-300">${ping.jitter_ms}ms</div>`;
|
||||
if (ping.region) html += `<div class="text-gray-500">Region</div><div class="text-gray-300">${escapeHtml(ping.region)}</div>`;
|
||||
if (ping.run_id) html += `<div class="text-gray-500">Run ID</div><div class="text-gray-300 font-mono break-all">${escapeHtml(ping.run_id)}</div>`;
|
||||
if (meta.cert_expiry_days != null) html += `<div class="text-gray-500">Cert expiry</div><div class="text-gray-300">${meta.cert_expiry_days} days</div>`;
|
||||
html += '</div>';
|
||||
|
||||
// Error
|
||||
if (ping.error) {
|
||||
html += '<div>';
|
||||
html += '<div class="text-xs text-gray-500 mb-1">Error</div>';
|
||||
html += `<div class="text-red-400 text-xs bg-red-500/5 border border-red-500/10 rounded-lg px-3 py-2 font-mono break-all">${escapeHtml(ping.error)}</div>`;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// Response headers
|
||||
const headerKeys = Object.keys(headers);
|
||||
if (headerKeys.length > 0) {
|
||||
html += '<div>';
|
||||
html += '<div class="text-xs text-gray-500 mb-1">Response Headers</div>';
|
||||
html += '<div class="bg-gray-800/50 border border-border-subtle rounded-lg px-3 py-2 text-xs font-mono max-h-48 overflow-y-auto">';
|
||||
for (const [k, v] of Object.entries(headers)) {
|
||||
html += `<div class="flex gap-2"><span class="text-blue-400 shrink-0">${escapeHtml(k)}:</span><span class="text-gray-300 break-all">${escapeHtml(String(v))}</span></div>`;
|
||||
}
|
||||
html += '</div></div>';
|
||||
}
|
||||
|
||||
// Body preview
|
||||
if (bodyPreview) {
|
||||
html += '<div>';
|
||||
html += '<div class="text-xs text-gray-500 mb-1">Response Body <span class="text-gray-600">(first 500 chars)</span></div>';
|
||||
html += `<pre class="bg-gray-800/50 border border-border-subtle rounded-lg px-3 py-2 text-xs font-mono text-gray-300 whitespace-pre-wrap break-all max-h-64 overflow-y-auto">${escapeHtml(bodyPreview)}</pre>`;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
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 = `
|
||||
<td class="px-4 py-2">${ping.up ? '<span class="text-green-400">Up</span>' : '<span class="text-red-400">Down</span>'}</td>
|
||||
|
|
|
|||
Loading…
Reference in New Issue