Public Access
fix(ui): align 'this week' to upcoming Monday (Sprint 7)
User report 2026-06-05: 'webui Meal Planner page is empty' on Friday
morning after the Friday email went out. Root cause: the orchestrator
keyed plans by the most-recent-Friday while the frontend's isoMonday()
returned the most-recent-Monday — a 7-day mismatch on Fridays.
Fixes (one semantic across the stack):
- runner._current_week_start() returns the upcoming Monday (today if
Mon, else the next Mon). The Friday email subject
('Meal plan for week of <date>') automatically picks up the new
value via run.week_start_date.
- frontend isoMonday -> upcomingMonday (same logic; renamed for
intent). isoMonday kept as a deprecated alias.
- New WeekRangeNav component (Dashboard + ShoppingList share it).
Renders [<] Jun 8 - Jun 14 [>] with clickable chevrons and a
clickable range label that jumps to the upcoming week. Replaces
the Sprint 5 inline segmented control on both pages.
- New formatWeekRange(mondayIso) helper (UTC-stable; uses
timeZone: 'UTC' so the rendered date matches the stored ISO date
regardless of viewer TZ; closes a latent bug in formatIsoDate too).
- New SQL fix script that retargets the user's 3-pending-items plan
from 2026-06-05 (Friday-keyed) to 2026-06-08 (upcoming Monday).
Idempotent + transaction-wrapped. Optional block for 2026-05-29.
No backend migration. No new dependencies. Deploy is git pull +
run the SQL fix + docker compose up -d --build backend frontend.
See Review/sprint7-verification.md for the full deploy + smoke flow.
Files:
- backend/app/services/orchestrator/runner.py:20-35
- backend/scripts/fix_2026_06_05_to_2026_06_08.sql (new)
- frontend/src/lib/utils.ts:43-130
- frontend/src/components/WeekRangeNav.tsx (new)
- frontend/src/pages/Dashboard.tsx (3 call sites + 1 segmented control)
- frontend/src/pages/ShoppingList.tsx (5 call sites + 2 segmented controls)
- Review/{sprint7-verification,ui-nielsen-audit,handoff-ui-audit}.md
- fix-ui-audit.md
- docs/HANDOFF.md
- .agent/{plan,context}.md
This commit is contained in:
@@ -58,3 +58,60 @@ R1 and R2 are independent and run in parallel. R3 cannot start until BOTH R1 ver
|
||||
- Auth scoping: bearer header is attached ONLY to `prod.swiftlyapi.net` requests, NOT to the public `luckysupermarkets.com` HTML page. Two `requests.Session` objects (one with default UA, one with the bearer header).
|
||||
- 401 detection: cannot use `BaseScraper._get` because it swallows HTTPError into a `None` return. The new client calls `session.get(...)` directly and checks `resp.status_code == 401` BEFORE `raise_for_status` to raise `SwiftlyAuthError`. Token in `.env.example` expires hourly per spec; on 401 the scraper aborts with a fixed error_message instructing the admin to refresh the token.
|
||||
- Idempotency key: `(source, external_id)` upserts. Migration 0005 adds `grocery_item.external_id` (nullable text, indexed; not unique because legacy R2-A rows lack one).
|
||||
|
||||
---
|
||||
|
||||
# Context — Sprint 7 (webui empty-meal-plan fix)
|
||||
|
||||
## Why Sprint 7 exists
|
||||
|
||||
User report 2026-06-05: "Latest meal plans were emails to me this morning, but when I go to the webui, the Meal Planner page is empty." Investigation found a date-semantics mismatch.
|
||||
|
||||
## Decisions (locked in for Sprint 7)
|
||||
|
||||
- **D1. "This week" = the upcoming Mon-Sun week.** The Friday email advertises the upcoming week; the plan is keyed by the upcoming Monday; the webui opens on the upcoming Monday. Past weeks accessible via the back-arrow. (User asked for a clickable `< Jun 8 — Jun 14 >` style nav, so the range is visible at a glance.)
|
||||
- **D2. Plan key changes from Friday to Monday.** All future plans are Monday-keyed. Existing 2026-06-05 plan migrated to 2026-06-08 via guarded SQL.
|
||||
- **D3. Email subject unchanged in form, changes in content.** `step_email` already uses `run.week_start_date` for the subject (verified `steps.py:305`). After D1, subject becomes "Meal plan for week of 2026-06-08" — natural Mon-Sun.
|
||||
- **D4. Frontend `isoMonday` renamed to `upcomingMonday`.** Same surface (Dashboard + ShoppingList). No backward-compat alias needed; the only callers are within our codebase.
|
||||
- **D5. New `WeekRangeNav` component is shared between Dashboard and ShoppingList.** Single source of truth for the visual + behavior.
|
||||
- **D6. The no-op `Generate Meal Plan` CTA at `Dashboard.tsx:415` is still out of scope.** F4 (plan-the-week) and the dead CTA solve different problems. Documented as a follow-up.
|
||||
|
||||
## Open questions to surface to the user, not to assume
|
||||
|
||||
- **Q1. Migrate the 2026-05-29 plan too?** It's also Friday-keyed. Operator can run a separate guarded UPDATE in the same SQL script. Default for now: include the statement but commented out; user uncomments if they want.
|
||||
- **Q2. Recency logic in the planner.** `_load_last_cooked` in `planner/generate.py:80-94` compares `MealPlan.week_start_date` across plans. After D2, all values are Mondays, so the comparison is symmetric and "days since last cooked" stays correct. No change needed. (Verified by reading the code.)
|
||||
- **Q3. Should the email subject line shift by one day (Thu instead of Fri)?** No — the scheduler still fires Fri 02:00..18:00 PT (verified `scheduler/__main__.py`). The deadline (vote by Fri 17:00) still makes sense. The plan key shifts to Mon, the email timing stays Fri. No scheduler change.
|
||||
- **Q4. Any URL bookmarked with `?week=2026-06-05`?** After the SQL fix, the plan moves to 2026-06-08. Any external link to `?week=2026-06-05` will hit "no plan for that week" (404-ish). Acceptable since the user uses the webui, not external links.
|
||||
|
||||
## Sprint 7 verification gate
|
||||
|
||||
- `cd frontend && npm run build` → green
|
||||
- `curl http://100.108.208.56:8082/api/meals?week_start=2026-06-08` (after deploy + SQL) → 3 pending items
|
||||
- Browser: open `/` (no `?week=` param) on deployment host → header shows `Week of Jun 8, 2026`, 3 meal cards visible
|
||||
- `curl http://100.108.208.56:8082/api/meals?week_start=2026-06-01` → null (current calendar week has no plan; expected)
|
||||
- `curl http://100.108.208.56:8082/api/meals?week_start=2026-05-29` → null if user opted in to migrate it, 3 items otherwise
|
||||
- `Review/sprint7-verification.md` is the source of truth for the deploy + smoke flow.
|
||||
|
||||
## Sprint 7 — does NOT touch
|
||||
|
||||
- The `extractErrorMessage` / `showApiError` flow (Sprint 4 F7) — unchanged.
|
||||
- The keyboard shortcuts (Sprint 5 F2) — unchanged. Note: `g d` still navigates to Dashboard at `upcomingMonday()`.
|
||||
- The bulk pantry add (Sprint 6 F3) — unchanged.
|
||||
- The plan-the-week (Sprint 6 F4) — unchanged. It still operates on the active plan regardless of week.
|
||||
- The undo-toast (Sprint 3 B12) — unchanged.
|
||||
- The aisle-migration (Sprint 2 / Sprint 5 fix) — no migration in S7.
|
||||
|
||||
## Key file:line references
|
||||
|
||||
- `backend/app/services/orchestrator/runner.py:20-24` — `_current_week_start()` (TO MODIFY)
|
||||
- `backend/app/scheduler/__main__.py:31-66` — Friday cron schedule (NO CHANGE)
|
||||
- `backend/app/services/orchestrator/steps.py:305` — `f"Meal plan for week of {run.week_start_date}"` (NO CHANGE; uses upstream value)
|
||||
- `frontend/src/lib/utils.ts:44-50` — `isoMonday()` (TO RENAME + CHANGE)
|
||||
- `frontend/src/pages/Dashboard.tsx:316-320` — default-week + navigateWeek (TO UPDATE)
|
||||
- `frontend/src/pages/ShoppingList.tsx:87-90` — same (TO UPDATE)
|
||||
- `frontend/src/pages/Dashboard.tsx:479-503` — inline week nav (TO REPLACE with `<WeekRangeNav>`)
|
||||
- `frontend/src/pages/ShoppingList.tsx:259-283` — same (TO REPLACE)
|
||||
- `frontend/src/components/` — new `WeekRangeNav.tsx` (TO ADD)
|
||||
- `backend/scripts/fix_2026_06_05_to_2026_06_08.sql` — new (TO ADD)
|
||||
- `Review/sprint7-verification.md` — new (TO ADD)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user