Explore: refresh with its own filters; the Statistics refresh stays on its page

The Statistics page's once-a-minute refresh also ran on #/stats/explore and drew the Statistics page over it, losing the filters. It now runs on the Statistics page only, and the explore page refreshes itself every minute with the same filters (not while the filter bar is being edited).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-13 19:16:10 +01:00
1 parent e94d3634b6
commit 7d93c373d1
2 files changed
+26 -2

No files matched your search

+23 -1
View File
@@ -49,7 +49,11 @@ function copy(f) { const g = {}; for (const k of kinds) g[k] = [...(f[k] || [])]
const includes = (f, k) => (f[k] || []).filter((v) => !isNot(v));
const anyFilter = (f) => kinds.some((k) => (f[k] || []).length);
let generation = 0;
export async function render(el, params, query) {
const gen = ++generation;
const hash = location.hash;
const f = parse(query);
const since = query.get("since") || "24h";
const go = (g) => exploreHref(g, since);
@@ -105,7 +109,25 @@ export async function render(el, params, query) {
if (lists.length) parts.push(h("div", { class: "chart-grid" }, lists));
parts.push(recentPanel(ex, (k, v) => go(addTo(f, k, v))));
if (gen !== generation) return; // the user moved on
el.classList.remove("refreshing");
mount(el, parts);
scheduleRefresh(el, params, query, gen, hash);
}
// scheduleRefresh redraws the page a minute later with the same filters,
// so the graphs move along; not while a filter is being typed in.
function scheduleRefresh(el, params, query, gen, hash) {
setTimeout(() => {
if (gen !== generation || location.hash !== hash || !el.isConnected) return;
const form = el.querySelector("form.explore-filters");
if (form && (form.contains(document.activeElement) || form.dataset.dirty)) {
scheduleRefresh(el, params, query, gen, hash); // try again later
return;
}
el.classList.add("refreshing");
render(el, params, query).catch(() => el.classList.remove("refreshing"));
}, 60_000);
}
// filterBar types filters in. Each field takes several values, comma
@@ -126,7 +148,7 @@ function filterBar(f, since, ex, views) {
type: split(type.value).map((v) => v.toUpperCase()), rcode: split(rcode.value).map((v) => v.toUpperCase()), blocked: split(blocked.value) };
location.hash = exploreHref(g, since);
};
const form = h("form", { class: "explore-filters", onsubmit: (e) => { e.preventDefault(); apply(); } },
const form = h("form", { class: "explore-filters", onsubmit: (e) => { e.preventDefault(); apply(); }, oninput: () => { form.dataset.dirty = "1"; } },
h("label", null, "Client ", client), h("label", null, "Name ", name), h("label", null, "View ", view),
h("label", null, "Type ", type), h("label", null, "Result ", rcode), h("label", null, "Blocked ", blocked),
h("button", { type: "submit", class: "primary small" }, "Show"),
+3 -1
View File
@@ -183,7 +183,9 @@ async function loadAll(since, heatSince, queryLog, vf, filtered) {
// keeping the current render (dimmed) until the new one is ready.
function scheduleRefresh(el, params, query, gen) {
setTimeout(() => {
if (gen !== generation || !location.hash.startsWith("#/stats") || !el.isConnected) return;
// Only on the Statistics page itself: not #/stats/explore.
const h = location.hash;
if (gen !== generation || !(h === "#/stats" || h.startsWith("#/stats?")) || !el.isConnected) return;
el.classList.add("refreshing");
render(el, params, query).catch(() => el.classList.remove("refreshing"));
}, 60_000);