fix: weekly meal-plan nutrition compared a week's total against a daily goal

userNutritionGoals are daily targets (the nutrition diary already compares
a single day's totals against them directly) — but the weekly meal-plan
route summed an entire week's entries and compared that raw total straight
against the same daily number, so coverage read roughly 7x too high.

Now computes a daily average (across days that actually have planned
meals) and compares that against the goal instead, adds a per-day
breakdown (byDay) for a future day-view, and tracks unknownCount for
entries whose recipe has no nutrition data yet, matching the diary route.

Also fixed a real bug this surfaced: meal-plan/page.tsx and print/meal-plan
parsed the ?week= date via new Date(dateStr) (UTC midnight) then read
.getDay() (local time) — in positive-UTC-offset timezones this resolves to
the wrong Monday, and the reverse (Date -> string via toISOString()) has
the same mismatch in the other direction. Both now do local y/m/d math
throughout.

Verified locally: correct daily-average math end to end (recipe entry +
batch-dish entry, which already attributed nutrition correctly via its
required parent recipeId — no separate fix needed there), and confirmed
the meal-plan page now resolves the right week and renders real coverage
bars against actual planned entries.
This commit is contained in:
Arnaud
2026-07-12 18:35:58 +02:00
parent a9dc1b63c1
commit 9566e19cd0
4 changed files with 118 additions and 53 deletions
+19 -4
View File
@@ -8,14 +8,29 @@ const DAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] as const;
const MEAL_ORDER = ["breakfast", "lunch", "dinner", "snack"] as const;
function getMonday(dateStr?: string): Date {
const d = dateStr ? new Date(dateStr) : new Date();
const day = d.getDay();
const diff = day === 0 ? -6 : 1 - day;
// Parse y/m/d directly into local time — new Date("YYYY-MM-DD") parses as
// UTC midnight, which getDay() (local time) can read as the wrong weekday
// depending on the server's timezone offset.
const d = dateStr
? (() => {
const [y, m, day] = dateStr.split("-").map(Number);
return new Date(y!, m! - 1, day!);
})()
: new Date();
const dow = d.getDay();
const diff = dow === 0 ? -6 : 1 - dow;
d.setDate(d.getDate() + diff);
d.setHours(0, 0, 0, 0);
return d;
}
function toDateStr(d: Date): string {
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day}`;
}
export default async function MealPlanPrintPage({
searchParams,
}: {
@@ -28,7 +43,7 @@ export default async function MealPlanPrintPage({
const m = getMessages((session.user as { locale?: string }).locale);
const monday = getMonday(week);
const weekStart = monday.toISOString().slice(0, 10);
const weekStart = toDateStr(monday);
const sunday = new Date(monday);
sunday.setDate(sunday.getDate() + 6);