Break today's food down by kind in the overview
The Food chart splits by kind but today's overview did not, so the one place you look first still lumped dry and fresh into a single total. It is a line under the stat tiles rather than part of the Meals tile. That tile is about 90px wide with a 0.7rem sub-line, and two kinds will not sit in it without wrapping into a mess — so the tile keeps the day's total, which is the headline figure, and the breakdown gets the room it needs. It follows the chart's conventions so the two read as one breakdown rather than two arbitrary lists: the same layer order, "No kind" last, a kind deleted since still named through its tombstone, and a meal logged without an amount adding nothing. Hidden unless a meal that day carries a kind, which keeps the overview untouched for anyone not using them — the case the first assertion in the new suite pins down. This was a gap rather than a reversal: when the split was scoped to "the grams chart only", the options named the counts chart, the by-hour heatmap and Timing as staying put. The overview's food total was in neither list, so it was never decided either way.
This commit is contained in:
+37
@@ -447,6 +447,42 @@
|
||||
return new Map(loadExercises().map(x => [x.id, x.name]));
|
||||
}
|
||||
|
||||
// The day's food, split the way the chart splits the window. The Meals tile
|
||||
// keeps the total — that is the headline figure, and the breakdown will not
|
||||
// fit in a 90px tile — so this sits under the grid instead.
|
||||
//
|
||||
// Only shown when a meal that day actually carries a kind: an account not
|
||||
// using kinds should see the overview it has always seen, and a day whose
|
||||
// meals all predate kinds is exactly that case.
|
||||
function renderDayFoodKinds(dayEvents) {
|
||||
const el = document.getElementById("stat-food-kinds");
|
||||
if (!el) return;
|
||||
const names = foodKindNames();
|
||||
const order = liveFoodKinds().map(k => k.id);
|
||||
const totals = new Map();
|
||||
let anyKind = false;
|
||||
for (const e of dayEvents) {
|
||||
if (e.type !== "eat" || !(Number.isFinite(e.grams) && e.grams > 0)) continue;
|
||||
const id = e.foodKindId || NO_KIND;
|
||||
if (id !== NO_KIND) anyKind = true;
|
||||
totals.set(id, (totals.get(id) || 0) + e.grams);
|
||||
}
|
||||
if (!anyKind) { el.hidden = true; return; }
|
||||
|
||||
// Same order as the chart's layers, with "No kind" last, so the two read
|
||||
// as the same breakdown rather than two arbitrary lists.
|
||||
const ids = [...totals.keys()].sort((a, b) => {
|
||||
if (a === NO_KIND) return 1;
|
||||
if (b === NO_KIND) return -1;
|
||||
const ia = order.indexOf(a), ib = order.indexOf(b);
|
||||
return (ia === -1 ? Infinity : ia) - (ib === -1 ? Infinity : ib);
|
||||
});
|
||||
el.textContent = ids
|
||||
.map(id => `${id === NO_KIND ? "No kind" : (names.get(id) || "Deleted kind")} ${Math.round(totals.get(id))} g`)
|
||||
.join(" · ");
|
||||
el.hidden = false;
|
||||
}
|
||||
|
||||
// The chips both dialogs use. Built from the live kinds plus "No kind",
|
||||
// which always comes last and is always offered — a meal is allowed to have
|
||||
// no kind, permanently, and the picker should never imply otherwise.
|
||||
@@ -898,6 +934,7 @@
|
||||
const gramsEl = document.getElementById("stat-meals-grams");
|
||||
gramsEl.textContent = gramsTotal > 0 ? `${Math.round(gramsTotal)} g` : "";
|
||||
gramsEl.hidden = !(gramsTotal > 0);
|
||||
renderDayFoodKinds(dayEvents);
|
||||
// Walk time is a duration, not a count, so the tile leads with it and puts
|
||||
// "N walks" underneath — the day's exercise at a glance.
|
||||
document.getElementById("stat-walk").textContent = formatDuration(walkMsInRange(events, from, to));
|
||||
|
||||
Reference in New Issue
Block a user