fix(stats): unambiguous chart x-labels + unit-carrying tooltips

The daily charts' x-axis labels were bare MM-DD ('10-15', '10-16') — on
the Listening per Day chart (y in hours) those read as hour values, which
is exactly the '10-16 hours a day' misread that prompted this. Labels now
spell the month ('Oct 15') and every chart's tooltip carries its unit
(min / pages / hrs). main.js?v=5
This commit is contained in:
John O'Keefe committed 2026-10-16 11:53:56 -04:00
1 parent ce366b1604
commit b4ae42326a
3 files changed
+26 -10

No files matched your search

+1 -1
View File
@@ -213,7 +213,7 @@ templ Header(user User, currentPath string) {
</header> </header>
<!-- module bundle; ?v=<commit-ish stamp> busts stale intermediate <!-- module bundle; ?v=<commit-ish stamp> busts stale intermediate
builds across app restarts (browser caches are keyed by URL) --> builds across app restarts (browser caches are keyed by URL) -->
<script type="module" src="/static/main.js?v=4"></script> <script type="module" src="/static/main.js?v=5"></script>
</div> </div>
} }
+1 -1
View File
@@ -631,7 +631,7 @@ func Header(user User, currentPath string) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</span> <input type=\"text\" id=\"header-search\" placeholder=\"Search your library…\" class=\"input pl-10\" autocomplete=\"off\"></div></div></header><!-- module bundle; ?v=<commit-ish stamp> busts stale intermediate\n\t\t builds across app restarts (browser caches are keyed by URL) --><script type=\"module\" src=\"/static/main.js?v=4\"></script></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</span> <input type=\"text\" id=\"header-search\" placeholder=\"Search your library…\" class=\"input pl-10\" autocomplete=\"off\"></div></div></header><!-- module bundle; ?v=<commit-ish stamp> busts stale intermediate\n\t\t builds across app restarts (browser caches are keyed by URL) --><script type=\"module\" src=\"/static/main.js?v=5\"></script></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
+24 -8
View File
@@ -244,8 +244,12 @@ async function loadSummary(): Promise<void> {
// Rendering ---------------------------------------------------------------- // Rendering ----------------------------------------------------------------
function fmtDay(iso: string): string { function fmtDay(iso: string): string {
const parts = iso.split("-"); // "2026-10-15" → "Oct 15" — a bare "10-15" x-label reads like a NUMBER
return parts.length >= 3 ? `${parts[1]}-${parts[2]}` : iso; // RANGE next to hour values (it was mistaken for "10-15 hours" on the
// listening chart), so spell the month out.
const d = new Date(iso + "T00:00:00");
if (isNaN(d.getTime())) return iso;
return d.toLocaleDateString(undefined, { month: "short", day: "numeric" });
} }
function themeVar(name: string, fallback: string): string { function themeVar(name: string, fallback: string): string {
@@ -335,15 +339,27 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
const accent = themeVar("--accent", "#7aa2f7"); const accent = themeVar("--accent", "#7aa2f7");
const secondary = themeVar("--text-secondary", "#9aa0b5"); const secondary = themeVar("--text-secondary", "#9aa0b5");
const labels = daily.map((d) => fmtDay(d.date)); const labels = daily.map((d) => fmtDay(d.date));
const baseOptions: ChartConfiguration["options"] = { // Unit-carrying tooltips: a bare number next to "hrs"/"min"/"pages"
// invites misreading (the x-axis date confusion happened exactly once).
const chartOptions = (unit: string): ChartConfiguration["options"] => ({
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
plugins: { legend: { display: false } }, plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: (ctx) => {
const v = typeof ctx.parsed === "number" ? ctx.parsed : ctx.parsed.y;
return `${Math.round(v * 10) / 10} ${unit}`;
},
},
},
},
scales: { scales: {
x: { grid: { display: false }, ticks: { color: secondary, maxTicksLimit: 10 } }, x: { grid: { display: false }, ticks: { color: secondary, maxTicksLimit: 10 } },
y: { grid: { color: "rgba(128,128,128,.15)" }, ticks: { color: secondary }, beginAtZero: true }, y: { grid: { color: "rgba(128,128,128,.15)" }, ticks: { color: secondary }, beginAtZero: true },
}, },
}; });
minutesChart?.destroy(); minutesChart?.destroy();
pagesChart?.destroy(); pagesChart?.destroy();
@@ -360,7 +376,7 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
labels, labels,
datasets: [{ data: daily.map((d) => d.minutes), backgroundColor: accent, borderRadius: 4 }], datasets: [{ data: daily.map((d) => d.minutes), backgroundColor: accent, borderRadius: 4 }],
}, },
options: baseOptions, options: chartOptions("min"),
}); });
} }
@@ -381,7 +397,7 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
}, },
], ],
}, },
options: baseOptions, options: chartOptions("pages"),
}); });
} }
@@ -402,7 +418,7 @@ async function renderDailyCharts(daily: DailyEntry[]): Promise<void> {
}, },
], ],
}, },
options: baseOptions, options: chartOptions("hrs"),
}); });
} }
} }