diff --git a/src/app.js b/src/app.js
index 6a21c5d..71a6896 100644
--- a/src/app.js
+++ b/src/app.js
@@ -222,26 +222,27 @@
localStorage.setItem(CONFIG_KEY, JSON.stringify(cfg));
}
- // Age in whole days / weeks / calendar months from a "YYYY-MM-DD" birthday.
- // Returns null for a missing/invalid/future birthday.
- function ageParts(birthday) {
+ // Age in whole days / weeks / calendar months from a "YYYY-MM-DD" birthday,
+ // measured at `at` (defaults to now — pass a weigh-in's timestamp for its age
+ // at that point). Returns null for a missing/invalid birthday or a date before it.
+ function ageParts(birthday, at) {
if (!birthday) return null;
const [y, mo, d] = birthday.split("-").map(Number);
if (!y || !mo || !d) return null;
const birth = startOfDay(new Date(y, mo - 1, d));
- const now = startOfDay(new Date());
- if (birth > now) return null;
- const days = Math.floor((now - birth) / 86_400_000);
+ const ref = startOfDay(new Date(Number.isFinite(at) ? at : Date.now()));
+ if (birth > ref) return null;
+ const days = Math.floor((ref - birth) / 86_400_000);
const weeks = Math.floor(days / 7);
- let months = (now.getFullYear() - birth.getFullYear()) * 12 +
- (now.getMonth() - birth.getMonth());
- if (now.getDate() < birth.getDate()) months--;
+ let months = (ref.getFullYear() - birth.getFullYear()) * 12 +
+ (ref.getMonth() - birth.getMonth());
+ if (ref.getDate() < birth.getDate()) months--;
if (months < 0) months = 0;
return { days, weeks, months };
}
- function formatAge(birthday) {
- const a = ageParts(birthday);
+ function formatAge(birthday, at) {
+ const a = ageParts(birthday, at);
if (!a) return "";
const wk = `${a.weeks} week${a.weeks === 1 ? "" : "s"}`;
if (a.months < 1) return `${wk} old`;
@@ -939,14 +940,23 @@
return { lo, hi, steps: Math.max(1, Math.round((hi - lo) / step)) };
}
- function drawWeightChart(weights) {
- const svg = document.getElementById("chart-weight");
+ // Human-readable detail for one weigh-in: date, weight, and the puppy's age
+ // at that date (omitted if no birthday is configured).
+ function weightPointInfo(w, birthday) {
+ const dateStr = new Date(w.at).toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
+ const age = formatAge(birthday, w.at);
+ return `${dateStr} — ${formatWeight(w.weight)}${age ? ` · ${age}` : ""}`;
+ }
+
+ function drawWeightChart(weights, birthday) {
+ const svg = document.getElementById("chart-weight");
+ const info = document.getElementById("weight-point-info");
const W = 320, H = 180;
const ML = 30, MR = 8, MT = 10, MB = 24;
const innerW = W - ML - MR;
const innerH = H - MT - MB;
- if (weights.length === 0) { svg.innerHTML = ""; return; }
+ if (weights.length === 0) { svg.innerHTML = ""; info.textContent = ""; return; }
const vals = weights.map(w => w.weight);
const { lo, hi, steps } = niceWeightAxis(Math.min(...vals), Math.max(...vals));
@@ -972,11 +982,16 @@
parts.push(`
No weigh-ins logged yet.
diff --git a/src/style.css b/src/style.css index f7de84e..26d88c3 100644 --- a/src/style.css +++ b/src/style.css @@ -491,6 +491,18 @@ dialog menu { .chart-svg .bar-eat { fill: var(--eat); } .chart-svg .weight-line { stroke: var(--weight); stroke-width: 2; fill: none; } .chart-svg .weight-dot { fill: var(--weight); } +.chart-svg .weight-hit { fill: transparent; cursor: pointer; } +.chart-svg .weight-hit.active { + fill: color-mix(in srgb, var(--weight) 22%, transparent); + stroke: var(--weight); + stroke-width: 1.5; +} + +.weight-point-info { + margin: 6px 4px 0; + min-height: 1.2em; + text-align: center; +} .weight-summary { display: grid; diff --git a/src/sw.js b/src/sw.js index 6fec2b3..d92b650 100644 --- a/src/sw.js +++ b/src/sw.js @@ -1,4 +1,4 @@ -const CACHE = "puppy-tracker-v4"; +const CACHE = "puppy-tracker-v5"; const PHOTO_CACHE = "puppy-tracker-photos-v1"; const ASSETS = [ "./",