94 lines
4.2 KiB
JavaScript
94 lines
4.2 KiB
JavaScript
// Public status page client JS:
|
|
// 1. Click any monitor row to collapse/expand its detail panel.
|
|
// 2. Hover any heartbeat bar to see the bucket time range and uptime breakdown.
|
|
// All monitor detail HTML is server-rendered; this script only toggles a CSS
|
|
// class on click. Reads its config from window.PINGQL_PAGE.
|
|
(function () {
|
|
var cfg = window.PINGQL_PAGE || {};
|
|
var barFrequency = cfg.bar_frequency || "daily";
|
|
|
|
// ── Click to expand/collapse ──────────────────────────────────────────
|
|
var rows = document.querySelectorAll(".monitor .monitor-row");
|
|
for (var r = 0; r < rows.length; r++) {
|
|
(function (row) {
|
|
var card = row.parentElement;
|
|
row.addEventListener("click", function () {
|
|
var isOpen = card.classList.toggle("expanded-state");
|
|
row.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
|
});
|
|
})(rows[r]);
|
|
}
|
|
|
|
// ── Bar tooltips ──────────────────────────────────────────────────────
|
|
var tooltip = document.getElementById("bar-tooltip");
|
|
if (!tooltip) return;
|
|
|
|
var bucketSpanMs = (barFrequency === "hourly") ? 3600 * 1000 : 86400 * 1000;
|
|
function fmtBucketRange(startIso) {
|
|
var start = new Date(startIso);
|
|
var end = new Date(start.getTime() + bucketSpanMs);
|
|
var dateOpts = { month: "short", day: "numeric" };
|
|
var timeOpts = { hour: "2-digit", minute: "2-digit" };
|
|
if (barFrequency === "hourly") {
|
|
return start.toLocaleDateString(undefined, dateOpts) + ", " +
|
|
start.toLocaleTimeString(undefined, timeOpts) + " - " +
|
|
end.toLocaleTimeString(undefined, timeOpts);
|
|
}
|
|
return start.toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
|
|
}
|
|
function uptimeBand(p) {
|
|
if (p == null) return "";
|
|
if (p >= 99.9) return "good";
|
|
if (p >= 99.0) return "warn";
|
|
return "bad";
|
|
}
|
|
|
|
function showTooltipForBar(bar) {
|
|
var total = parseInt(bar.getAttribute("data-total") || "0", 10);
|
|
var up = parseInt(bar.getAttribute("data-up") || "0", 10);
|
|
var start = bar.getAttribute("data-start");
|
|
var latRaw = bar.getAttribute("data-latency");
|
|
var lat = latRaw == null ? null : parseInt(latRaw, 10);
|
|
if (!start) return;
|
|
// Full precision pct so the formatter can decide. Anything below 100% gets
|
|
// 2 truncated (not rounded) decimals - same rule as the page-level uptime
|
|
// numbers, so a bucket with one failed check never displays as "100%".
|
|
var pct = total > 0 ? (100 * up / total) : null;
|
|
var pctText;
|
|
if (pct == null) pctText = "-";
|
|
else if (pct >= 100) pctText = "100%";
|
|
else pctText = (Math.floor(pct * 100) / 100).toFixed(2) + "%";
|
|
var html = '<div class="head">' + fmtBucketRange(start) + "</div>";
|
|
if (total > 0) {
|
|
html += '<div class="row"><span>Checks</span><span>' + total + "</span></div>";
|
|
html += '<div class="row"><span>Successful</span><span>' + up + "</span></div>";
|
|
html += '<div class="row"><span>Uptime</span><span class="pct ' + uptimeBand(pct) + '">' + pctText + "</span></div>";
|
|
if (lat != null) {
|
|
html += '<div class="row"><span>Avg ping</span><span>' + lat + "ms</span></div>";
|
|
}
|
|
} else {
|
|
html += '<div class="row"><span>No data</span><span>-</span></div>';
|
|
}
|
|
tooltip.innerHTML = html;
|
|
tooltip.style.display = "block";
|
|
var rect = bar.getBoundingClientRect();
|
|
tooltip.style.left = (rect.left + rect.width / 2) + "px";
|
|
tooltip.style.top = rect.top + "px";
|
|
}
|
|
function hideTooltip() {
|
|
tooltip.style.display = "none";
|
|
}
|
|
|
|
document.addEventListener("mouseover", function (e) {
|
|
var bar = e.target && e.target.closest && e.target.closest(".bar");
|
|
if (bar && bar.parentElement && bar.parentElement.classList.contains("bars")) {
|
|
showTooltipForBar(bar);
|
|
}
|
|
});
|
|
document.addEventListener("mouseout", function (e) {
|
|
var bar = e.target && e.target.closest && e.target.closest(".bar");
|
|
if (bar) hideTooltip();
|
|
});
|
|
window.addEventListener("scroll", hideTooltip, { passive: true });
|
|
})();
|