Remove the walking goal

It was mine, not the user's: added unprompted alongside the Walks panel because
sleepTargetFor existed and it seemed to fit. The number came from the
"five-minute rule" — five minutes per month of age, twice a day — written from
general knowledge with no source consulted and none cited in the code.

That rule is widely repeated but is folk guidance, not a veterinary standard.
Its origin is unclear, it is contested by what evidence exists on early
exercise and joint development, and it ignores breed, terrain, lead or off-lead
and the individual dog. Drawing it as a line labelled "Goal", with a ✓ for
clearing it, stated it far more confidently than a rule of thumb earns —
particularly in an app whose entire job is otherwise to record what happened
rather than to advise.

So the target line goes, along with its legend chip, the ✓, and walkTargetFor
itself, which had no other caller. The trend keeps today against yesterday and
the average: a record, with the judgement left to whoever knows the dog.
This commit is contained in:
Alexander Heldt
2026-09-01 10:07:20 +00:00
parent 31e04fb9a1
commit 122897c732
4 changed files with 6 additions and 57 deletions
+4 -48
View File
@@ -664,19 +664,6 @@
.map(w => ({ start: w.start, end: w.end, ongoing: w.ongoing && today }));
}
// Rough age-based walking guideline (the widely used "five-minute rule"):
// about 5 minutes per month of age per walk, twice a day, until the puppy is
// grown. Returns null without a birthday or once it's a year old, the same
// way sleepTargetFor bows out.
function walkTargetFor(birthday) {
const a = ageParts(birthday);
if (!a || a.months >= 12) return null;
// Under a month of counted age the rule has nothing to say yet; treat it
// as one "month" so the advice stays a short outing rather than zero.
const months = Math.max(1, a.months);
return { perWalk: months * 5, walks: 2, total: months * 10 };
}
// ---------- rendering ----------
const dayPicker = document.getElementById("day-picker");
const eventList = document.getElementById("event-list");
@@ -1762,7 +1749,7 @@
};
}
function drawWalkTrendChart(curves, target) {
function drawWalkTrendChart(curves) {
const svg = document.getElementById("chart-walk-trend");
if (!svg) return;
const W = 320, H = 180;
@@ -1777,12 +1764,7 @@
{ pts: curves.today, cls: "wtrend-today", label: curves.dayLabel },
].filter(s => s.pts && s.pts.length > 1);
// The axis has to reach the goal even on a day that fell well short of it,
// or the line it is measured against would sit off the top of the chart.
const rawMax = Math.max(
target ? target.total : 0,
...series.flatMap(s => s.pts.map(p => p.y)),
);
const rawMax = Math.max(...series.flatMap(s => s.pts.map(p => p.y)));
const { yMax, steps } = niceAxisLinear(rawMax);
// Minutes are linear all the way down, so no split axis: the sleep trend
// stretches its top because the interesting hours cluster near a 16h goal,
@@ -1804,16 +1786,6 @@
parts.push(`<text x="${x.toFixed(1)}" y="${H - MB + 14}" text-anchor="${anchor}">${pad2(hr)}</text>`);
}
// The five-minute rule as one line rather than a band: it is a target to
// reach, with no upper bound a band would imply.
if (target) {
const y = yOf(Math.min(target.total, yMax));
parts.push(
`<line class="wtrend-goal" x1="${ML}" y1="${y.toFixed(1)}" x2="${W - MR}" y2="${y.toFixed(1)}">` +
`<title>${escapeText(`Goal ~${target.total} min (${target.perWalk} min × ${target.walks})`)}</title></line>`
);
}
for (const s of series) {
const d = s.pts
.map((p, i) => `${i === 0 ? "M" : "L"}${xOf(p.x).toFixed(1)} ${yOf(p.y).toFixed(1)}`)
@@ -1830,22 +1802,11 @@
function renderWalkTrend(events) {
const curves = walkTrendCurves(events);
const target = walkTargetFor(loadConfig().birthday);
drawWalkTrendChart(curves, target);
drawWalkTrendChart(curves);
const chip = (id) => document.getElementById(id);
const mins = (pts) => `${Math.round(pts[pts.length - 1].y)} min`;
// With no projection to carry it, the ✓ goes on today's own chip: the goal
// is a total for the day, so today's line is what meets it or doesn't.
let mark = "";
if (target) {
const done = curves.today[curves.today.length - 1].y >= target.total;
mark = done ? " ✓" : "";
chip("legend-wtrend-today-text").parentElement.title = done
? "Past the walking goal for this age"
: `Goal is about ${target.total} min a day at this age`;
}
chip("legend-wtrend-today-text").textContent = `${curves.dayLabel} ${mins(curves.today)}${mark}`;
chip("legend-wtrend-today-text").textContent = `${curves.dayLabel} ${mins(curves.today)}`;
const yLegend = chip("legend-wtrend-yesterday");
yLegend.hidden = !curves.yesterday;
@@ -1857,11 +1818,6 @@
if (curves.avg) {
chip("legend-wtrend-avg-text").textContent = `${curves.avgDays}-day avg ${mins(curves.avg)}`;
}
const gLegend = chip("legend-wtrend-goal");
gLegend.hidden = !target;
if (target) {
chip("legend-wtrend-goal-text").textContent = `Goal ~${target.total} min`;
}
}
// Which heatmap block is focused, remembered across re-renders so a background