fix
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
// Click-to-expand for compact display mode on the public status page.
|
||||
// Loaded by status pages that have at least one monitor in compact mode.
|
||||
// Reads its config from window.PINGQL_PAGE.
|
||||
(function () {
|
||||
var cfg = window.PINGQL_PAGE || {};
|
||||
var slug = cfg.slug;
|
||||
var defaultWindow = cfg.default_window || "24h";
|
||||
var showResponseTime = !!cfg.show_response_time;
|
||||
if (!slug) return;
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
function bucketColor(b) {
|
||||
if (!b || b.total === 0) return "var(--bar-empty)";
|
||||
if (b.up === b.total) return "var(--bar-up)";
|
||||
if (b.up === 0) return "var(--bar-down)";
|
||||
return "var(--bar-partial)";
|
||||
}
|
||||
function uptimeBand(p) {
|
||||
if (p == null) return "empty";
|
||||
if (p >= 99.9) return "good";
|
||||
if (p >= 99.0) return "warn";
|
||||
return "bad";
|
||||
}
|
||||
function fmtUptime(p) {
|
||||
if (p == null) return "—";
|
||||
if (p === 100) return "100" + "%";
|
||||
return p.toFixed(2) + "%";
|
||||
}
|
||||
function windowLabel(w) {
|
||||
if (w === "24h") return "Last 24 hours";
|
||||
if (w === "7d") return "Last 7 days";
|
||||
if (w === "30d") return "Last 30 days";
|
||||
return "Last 90 days";
|
||||
}
|
||||
function fmtTs(iso) {
|
||||
var d = new Date(iso);
|
||||
return d.toLocaleString(undefined, {
|
||||
month: "short", day: "numeric", hour: "2-digit", minute: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
function renderBars(buckets) {
|
||||
var html = "";
|
||||
for (var i = 0; i < buckets.length; i++) {
|
||||
var b = buckets[i];
|
||||
var pct = b.total > 0 ? Math.round(100 * b.up / b.total) + "% up" : "no data";
|
||||
html += '<div class="bar" style="background: ' + bucketColor(b) + ';" title="' + pct + '"></div>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderIncident(i) {
|
||||
var klass = "incident " + i.severity + (i.resolved_at ? " resolved" : "");
|
||||
var html = '<div class="' + klass + '" style="margin-top:1rem">';
|
||||
html += '<div class="incident-title">' + escapeHtml(i.title) + "</div>";
|
||||
html += '<div class="incident-meta"><span class="pill ' + i.status + '">' + i.status + "</span>";
|
||||
html += "Started " + fmtTs(i.started_at);
|
||||
if (i.resolved_at) html += " · Resolved " + fmtTs(i.resolved_at);
|
||||
html += "</div>";
|
||||
if (i.updates && i.updates.length > 0) {
|
||||
html += '<div class="incident-timeline">';
|
||||
for (var k = 0; k < i.updates.length; k++) {
|
||||
var u = i.updates[k];
|
||||
html += '<div class="incident-update">';
|
||||
html += '<div class="head"><span class="status ' + u.status + '">' + u.status + "</span><span>" + fmtTs(u.created_at) + "</span></div>";
|
||||
html += '<div class="body">' + u.body_html + "</div>";
|
||||
html += "</div>";
|
||||
}
|
||||
html += "</div>";
|
||||
}
|
||||
html += "</div>";
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderDetail(payload) {
|
||||
var m = payload.monitor;
|
||||
var u = m.uptime || { d24: null, d7: null, d30: null, d90: null };
|
||||
var buckets = m.buckets || [];
|
||||
var hasData = false;
|
||||
for (var i = 0; i < buckets.length; i++) if (buckets[i].total > 0) { hasData = true; break; }
|
||||
|
||||
var barsHtml = renderBars(buckets);
|
||||
|
||||
var regionsHtml = "";
|
||||
if (m.region_states && m.region_states.length > 1) {
|
||||
regionsHtml = '<div class="regions">';
|
||||
for (var r = 0; r < m.region_states.length; r++) {
|
||||
var rs = m.region_states[r];
|
||||
regionsHtml += '<span class="region ' + rs.state + '">' + escapeHtml(rs.region) + "</span>";
|
||||
}
|
||||
regionsHtml += "</div>";
|
||||
}
|
||||
|
||||
var latencyHtml = "";
|
||||
if (showResponseTime && m.avg_latency != null) {
|
||||
latencyHtml = "<span>" + m.avg_latency + "ms · </span>";
|
||||
}
|
||||
|
||||
var incidentsHtml = "";
|
||||
if (payload.incidents && payload.incidents.length > 0) {
|
||||
for (var x = 0; x < payload.incidents.length; x++) {
|
||||
incidentsHtml += renderIncident(payload.incidents[x]);
|
||||
}
|
||||
}
|
||||
|
||||
var html = "";
|
||||
html += '<div class="bars" title="' + m.current_state + '">' + barsHtml + "</div>";
|
||||
html += '<div class="bars-meta"><span>' + windowLabel(defaultWindow) + "</span><span>" + latencyHtml + (hasData ? "uptime over window" : "awaiting data") + "</span></div>";
|
||||
html += '<div class="uptime-row">';
|
||||
html += '<div class="uptime-cell ' + uptimeBand(u.d24) + '"><div class="label">24h</div><div class="value">' + fmtUptime(u.d24) + "</div></div>";
|
||||
html += '<div class="uptime-cell ' + uptimeBand(u.d7) + '"><div class="label">7d</div><div class="value">' + fmtUptime(u.d7) + "</div></div>";
|
||||
html += '<div class="uptime-cell ' + uptimeBand(u.d30) + '"><div class="label">30d</div><div class="value">' + fmtUptime(u.d30) + "</div></div>";
|
||||
html += '<div class="uptime-cell ' + uptimeBand(u.d90) + '"><div class="label">90d</div><div class="value">' + fmtUptime(u.d90) + "</div></div>";
|
||||
html += "</div>";
|
||||
html += regionsHtml;
|
||||
html += incidentsHtml;
|
||||
return html;
|
||||
}
|
||||
|
||||
var cards = document.querySelectorAll(".monitor.compact");
|
||||
for (var c = 0; c < cards.length; c++) {
|
||||
(function (card) {
|
||||
var id = card.dataset.monitorId;
|
||||
var button = card.querySelector(".monitor-row");
|
||||
var detail = card.querySelector(".monitor-detail");
|
||||
button.addEventListener("click", function () {
|
||||
var isOpen = card.classList.toggle("expanded-state");
|
||||
button.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
||||
if (isOpen && detail.dataset.loaded === "false") {
|
||||
fetch("/" + slug + "/monitor/" + encodeURIComponent(id) + ".json", { cache: "no-store" })
|
||||
.then(function (r) {
|
||||
if (!r.ok) throw new Error("status " + r.status);
|
||||
return r.json();
|
||||
})
|
||||
.then(function (payload) {
|
||||
detail.innerHTML = renderDetail(payload);
|
||||
detail.dataset.loaded = "true";
|
||||
})
|
||||
.catch(function () {
|
||||
detail.innerHTML =
|
||||
'<div style="color:var(--bar-down);font-size:0.85rem;padding:0.5rem 0">Failed to load detail.</div>';
|
||||
});
|
||||
}
|
||||
});
|
||||
})(cards[c]);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user