perf: make admin navigation responsive

This commit is contained in:
ImBenji
2026-08-04 21:36:39 +01:00
parent 5680ee8a44
commit bd162606b6
15 changed files with 204 additions and 80 deletions
+49 -2
View File
@@ -148,7 +148,7 @@ async function loadGlobalStats() {
if (!bar) return;
try {
const data = await api("/admin/api/stats");
const data = await api("/admin/api/stats/summary");
const t = document.getElementById("s-total");
if (t) t.textContent = data.total.toLocaleString();
const c = document.getElementById("s-content");
@@ -161,6 +161,49 @@ async function loadGlobalStats() {
}
function installLoadingStates() {
document.querySelectorAll("tbody:empty").forEach(tbody => {
const columns = Math.max(1, tbody.closest("table")?.querySelectorAll("thead th").length || 5);
tbody.setAttribute("aria-busy", "true");
tbody.innerHTML = `<tr class="skeleton-row"><td colspan="${columns}"><span></span><span></span><span></span><span></span></td></tr>`;
const observer = new MutationObserver(() => {
if (!tbody.querySelector(".skeleton-row")) {
tbody.removeAttribute("aria-busy");
observer.disconnect();
}
});
observer.observe(tbody, { childList: true });
});
["intel-signals-grid", "sourceTable", "statusTable"].forEach(id => {
const node = document.getElementById(id);
if (node && !node.children.length) node.classList.add("content-loading");
});
}
function installNavigationPrefetch() {
const seen = new Set();
const prefetch = link => {
const href = link?.href;
if (!href || seen.has(href) || link.origin !== location.origin) return;
seen.add(href);
const hint = document.createElement("link");
hint.rel = "prefetch";
hint.href = href;
hint.as = "document";
document.head.appendChild(hint);
};
document.querySelectorAll(".tabs a, .subnav a").forEach(link => {
link.addEventListener("pointerenter", () => prefetch(link), { once: true });
link.addEventListener("focus", () => prefetch(link), { once: true });
link.addEventListener("click", () => document.documentElement.classList.add("navigating"));
});
const warm = () => document.querySelectorAll(".tabs a, .subnav a").forEach(prefetch);
if ("requestIdleCallback" in window) requestIdleCallback(warm, { timeout: 2500 });
else setTimeout(warm, 1200);
}
// common overlay close-on-backdrop wiring
function wireOverlays() {
document.querySelectorAll(".overlay").forEach(ov => {
@@ -173,6 +216,10 @@ function wireOverlays() {
document.addEventListener("DOMContentLoaded", () => {
installChrome();
installLoadingStates();
installNavigationPrefetch();
wireOverlays();
loadGlobalStats();
const loadStats = () => loadGlobalStats();
if ("requestIdleCallback" in window) requestIdleCallback(loadStats, { timeout: 1800 });
else setTimeout(loadStats, 500);
});
+1
View File
@@ -13,6 +13,7 @@ const signalsById = new Map();
function renderSignals(data) {
const grid = document.getElementById("intel-signals-grid");
const empty = document.getElementById("intel-signals-empty");
grid.classList.remove("content-loading");
if (!data || data.length === 0) {
grid.innerHTML = "";
+3
View File
@@ -4,6 +4,9 @@
async function loadStatsPage() {
const data = await api("/admin/api/stats");
document.getElementById("sourceTable").classList.remove("content-loading");
document.getElementById("statusTable").classList.remove("content-loading");
document.getElementById("sourceTable").innerHTML = data.bySource
.map(r => `<tr><td>${escapeHtml(r.source)}</td><td style="text-align:right; padding-left:24px">${r.n.toLocaleString()}</td></tr>`).join("");