From 59df3c37c804c6a1c9650c179e457ba8755ee5f2 Mon Sep 17 00:00:00 2001 From: Josue Zamudio Date: Tue, 25 Aug 2026 21:10:22 +0000 Subject: [PATCH] Home: watch-time heatmap in the stats area - extract the 365d fetch+render into heatSection() so ?stats and the home stats widget share it (no duplicated async loader) - home order: stats cards + top 3 -> watch-time heatmap (past year, month labels, scrollable) -> history widget --- static/app.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/static/app.js b/static/app.js index 506f7c8..83fc4a6 100644 --- a/static/app.js +++ b/static/app.js @@ -185,6 +185,8 @@ function showHome() { const ta = topAnime(d, 3); if (ta) sw.append(ta); }).catch(() => load.remove()); + // 5b. watch-time heatmap (past year, shared with ?stats) + main.append(heatSection()); // 6. history widget (data already loaded, capped at 6) const hh = el("div", "sec-head"); hh.append(el("h2", null, "History"), link(`all ${history.length} \u2192`, "?history")); @@ -531,24 +533,29 @@ function heatMap(byDay, days) { return [lbl, grid, lg]; } +function heatSection() { + const sec = el("section", null); + const h = el("div", "sec-head"); + h.append(el("h2", null, "Watch time by day"), el("span", "muted", "past year")); + const load = el("p", "muted", "loading\u2026"); + sec.append(h, load); + api("/api/stats?days=365").then(d2 => { + load.remove(); + const [l, g, leg] = heatMap(d2.by_day, 365); + const w = el("div", "heat-wrap"); + w.append(l, g); + sec.append(w, leg); + }).catch(() => load.remove()); + return sec; +} + async function showStats() { skel(5); const d = await api("/api/stats?days=30"); main.innerHTML = ""; main.append(el("h2", null, "Stats \u2014 last 30 days")); main.append(statsCards(d)); - const hm = el("section", null); - const hmh = el("div", "sec-head"); - hmh.append(el("h2", null, "Watch time by day"), el("span", "muted", "past year")); - hm.append(hmh, el("p", "muted", "loading\u2026")); - main.append(hm); - api("/api/stats?days=365").then(d2 => { - hm.querySelector("p.muted")?.remove(); - const [l, g, leg] = heatMap(d2.by_day, 365); - const w = el("div", "heat-wrap"); - w.append(l, g); - hm.append(w, leg); - }).catch(() => hm.querySelector("p.muted")?.remove()); + main.append(heatSection()); const ta = topAnime(d); if (ta) main.append(ta); if (d.top_searches.length) {