more graphs

This commit is contained in:
2026-02-04 13:03:51 +11:00
parent ffeeb1a835
commit 1afe9c605b
2 changed files with 101 additions and 33 deletions

View File

@@ -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);
}

View File

@@ -27,12 +27,14 @@
<div class="controls">
<div class="segmented" role="group" aria-label="time range">
<button class="btn" data-range="6h">6h</button>
<button class="btn" data-range="24h-last">Last 24h</button>
<button class="btn active" data-range="24h">24h</button>
<button class="btn" data-range="72h-last">Last 72h</button>
<button class="btn" data-range="72h">72h</button>
<button class="btn" data-range="7d-last">Last 7d</button>
<button class="btn" data-range="7d">7d</button>
<button class="btn" data-range="1m">1mo</button>
</div>
<div class="segmented" role="group" aria-label="window">
<button class="btn active" data-mode="current">Current</button>
<button class="btn" data-mode="last">Last</button>
</div>
<div class="segmented" role="group" aria-label="timezone">
<button class="btn active" data-tz="local">Local</button>