From 1afe9c605b4a2a8938762adbb6fd6a5f8f82f1b6 Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Wed, 4 Feb 2026 13:03:51 +1100 Subject: [PATCH] more graphs --- cmd/ingestd/web/app.js | 126 ++++++++++++++++++++++++++++--------- cmd/ingestd/web/index.html | 8 ++- 2 files changed, 101 insertions(+), 33 deletions(-) diff --git a/cmd/ingestd/web/app.js b/cmd/ingestd/web/app.js index 2e3b55b..7ef4313 100644 --- a/cmd/ingestd/web/app.js +++ b/cmd/ingestd/web/app.js @@ -1,5 +1,6 @@ const state = { range: "24h", + mode: "current", bucket: "5m", tz: "local", rangeStart: null, @@ -134,59 +135,96 @@ function startOfWeekSunday(date, tz) { return new Date(base.getTime() - diff * 24 * 60 * 60 * 1000); } +function startOfMonth(date, tz) { + if (tz === "utc") { + return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1)); + } + return new Date(date.getFullYear(), date.getMonth(), 1); +} + +function addMonths(date, months, tz) { + if (tz === "utc") { + return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + months, 1)); + } + return new Date(date.getFullYear(), date.getMonth() + months, 1); +} + function computeRange(range, tz) { const now = new Date(); let start = null; let end = null; let axisStart = null; let axisEnd = null; + let mode = state.mode; + + if (range.endsWith("-last")) { + mode = "last"; + range = range.replace(/-last$/, ""); + } switch (range) { case "6h": { - end = now; - start = new Date(now.getTime() - 6 * 60 * 60 * 1000); + if (mode === "current") { + const hour = tz === "utc" ? now.getUTCHours() : now.getHours(); + const block = Math.floor(hour / 6) * 6; + if (tz === "utc") { + start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), block)); + } else { + start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), block); + } + end = new Date(start.getTime() + 6 * 60 * 60 * 1000); + } else { + end = now; + start = new Date(now.getTime() - 6 * 60 * 60 * 1000); + } axisStart = start; axisEnd = end; break; } case "24h": { - start = startOfDay(now, tz); - end = new Date(start.getTime() + 24 * 60 * 60 * 1000); - axisStart = start; - axisEnd = end; - break; - } - case "24h-last": { - end = now; - start = new Date(now.getTime() - 24 * 60 * 60 * 1000); - axisStart = start; - axisEnd = end; - break; - } - case "72h-last": { - end = now; - start = new Date(now.getTime() - 72 * 60 * 60 * 1000); + if (mode === "current") { + start = startOfDay(now, tz); + end = new Date(start.getTime() + 24 * 60 * 60 * 1000); + } else { + end = now; + start = new Date(now.getTime() - 24 * 60 * 60 * 1000); + } axisStart = start; axisEnd = end; break; } case "72h": { - end = startOfDay(now, tz); - start = new Date(end.getTime() - 72 * 60 * 60 * 1000); - axisStart = start; - axisEnd = end; - break; - } - case "7d-last": { - end = now; - start = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); + if (mode === "current") { + end = startOfDay(now, tz); + start = new Date(end.getTime() - 72 * 60 * 60 * 1000); + } else { + end = now; + start = new Date(now.getTime() - 72 * 60 * 60 * 1000); + } axisStart = start; axisEnd = end; break; } case "7d": { - start = startOfWeekSunday(now, tz); - end = new Date(start.getTime() + 7 * 24 * 60 * 60 * 1000); + if (mode === "current") { + start = startOfWeekSunday(now, tz); + end = new Date(start.getTime() + 7 * 24 * 60 * 60 * 1000); + } else { + end = now; + start = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); + } + axisStart = start; + axisEnd = end; + break; + } + case "1m": { + if (mode === "current") { + start = startOfMonth(now, tz); + end = addMonths(start, 1, tz); + } else { + end = now; + start = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); + } axisStart = start; axisEnd = end; break; @@ -206,8 +244,19 @@ function readStateFromURL() { const url = new URL(window.location.href); const range = url.searchParams.get("range"); const tz = url.searchParams.get("tz"); + const mode = url.searchParams.get("mode"); if (range) { - state.range = range; + if (range.endsWith("-last")) { + state.range = range.replace(/-last$/, ""); + state.mode = "last"; + } else { + state.range = range; + } + } + if (mode === "current" || mode === "last") { + state.mode = mode; + } else if (range === "6h" && !mode) { + state.mode = "last"; } if (tz === "utc" || tz === "local") { state.tz = tz; @@ -228,6 +277,10 @@ function syncControls() { rangeButtons.forEach((btn) => { btn.classList.toggle("active", btn.dataset.range === state.range); }); + const modeButtons = document.querySelectorAll(".btn[data-mode]"); + modeButtons.forEach((btn) => { + btn.classList.toggle("active", btn.dataset.mode === state.mode); + }); const tzButtons = document.querySelectorAll(".btn[data-tz]"); tzButtons.forEach((btn) => { btn.classList.toggle("active", btn.dataset.tz === state.tz); @@ -252,6 +305,7 @@ function updateSingleChartMode() { function buildChartURL(chartId) { const url = new URL(window.location.origin + "/chart/" + chartId); url.searchParams.set("range", state.range); + url.searchParams.set("mode", state.mode); url.searchParams.set("tz", state.tz); return url.toString(); } @@ -822,6 +876,17 @@ function setupControls() { }); }); + const modeButtons = document.querySelectorAll(".btn[data-mode]"); + modeButtons.forEach((btn) => { + btn.addEventListener("click", () => { + modeButtons.forEach((b) => b.classList.remove("active")); + btn.classList.add("active"); + state.mode = btn.dataset.mode; + updateURLParams(); + loadAndRender(); + }); + }); + const tzButtons = document.querySelectorAll(".btn[data-tz]"); tzButtons.forEach((btn) => { btn.addEventListener("click", () => { @@ -837,6 +902,7 @@ function setupControls() { function updateURLParams() { const url = new URL(window.location.href); url.searchParams.set("range", state.range); + url.searchParams.set("mode", state.mode); url.searchParams.set("tz", state.tz); history.replaceState({}, "", url); } diff --git a/cmd/ingestd/web/index.html b/cmd/ingestd/web/index.html index e44386b..56c5513 100644 --- a/cmd/ingestd/web/index.html +++ b/cmd/ingestd/web/index.html @@ -27,12 +27,14 @@
- - - + +
+
+ +