Sprint 4 (F7 + F6) is now documented across the project: - Review/sprint4-verification.md: new 100-line deploy + smoke-check doc. Frontend-only deploy (git pull + docker compose up -d --build frontend). 5 smoke-check tables: A) success toasts still work for all 11 actions, B) error path shows FastAPI detail (network-down is the easiest test; full Pydantic 422 verification via curl + DevTools 'Edit and resend'), C) pre-flight toasts still fire without a network call, D) plan-status Badge has correct aria-label in DevTools, E) Sprint 1-3 regression spot-check. Includes rollback instructions (single-commit revert). - fix-ui-audit.md: new Sprint 4 section with full per-task notes (S4.1 F7 implementation details, S4.2 F6 aria-label, S4.3 verification gate). 'Done when (overall)' block updated to 4 sprints + 7 commits + 16 findings closed. No new commit in fix-ui-audit.md for the F8/F9 §Future addendum (those are noted in Review/handoff-ui-audit.md but live in the doc/proposals/ tree, not in the UI-audit plan). - Review/handoff-ui-audit.md: updated to a 4-sprint cycle. TL;DR table includes thed71b67arow, file-list includes the new verification doc, file-level diff summary gains 5 new rows for Sprint 4, §Future list now strikethroughs F6 and F7, and the Quick-start deploy commands list Sprint 4. - Review/ui-nielsen-audit.md: new Sprint 4 status block at the top with the F7/F6 changes, the no-backend-changes note, and a cross-ref to the new verification log. - docs/HANDOFF.md: Last-updated line bumped to 4 sprints / 7 commits / 16 findings. New 'Sprint 4' subsection in the 2026-06-03 session block. Commit table gained thed71b67arow. Files-modified list gained the lib/toast.tsx, App.tsx, and three pages changes for Sprint 4 (with B-tags preserved). No code changes; the 5 pre-existing WIP files (backend/app/api/ meals.py, recipes.py, schemas/recipe.py, frontend/src/api/index.ts, nginx/nginx.conf) are deliberately not staged.
7.1 KiB
Sprint 4 — Deploy & smoke-check (F7 + F6)
Goal: verify the global react-query error handler and plan-status aria-label work end-to-end on http://100.108.208.56:8082/.
Commit: d71b67a (feat(ui): global react-query error handler + plan-status a11y (Sprint 4 F7+F6))
Sprint scope: F7 + F6 (both small, both polish, no new deps)
Backend changes: none
Frontend changes: App.tsx, lib/toast.tsx, pages/Dashboard.tsx, Pantry.tsx, MealDetail.tsx
1. Deploy
This sprint is frontend-only — no backend rebuild, no migration, no schema change.
# On deployment host (100.108.224.12)
cd /path/to/MealPlanner
git pull
docker compose -f docker-compose.yml up -d --build frontend
docker compose logs -f frontend # wait for "Application startup complete"
Then sanity-check the bundle is the new one:
curl -s http://100.108.208.56:8082/ | grep -o 'index-[A-Za-z0-9_-]*\.js'
# Should contain a new hash (e.g. index-C_trtYuT.js → new one). Compare to the
# dist/assets/ output from the last `npm run build` on this machine.
2. Build verification (already green)
$ cd frontend && npm run build
> tsc && vite build
✓ 1891 modules transformed.
dist/index.html 0.54 kB │ gzip: 0.31 kB
dist/assets/index-DudtjbEb.css 37.14 kB │ gzip: 6.67 kB
dist/assets/index-C_trtYuT.js 466.14 kB │ gzip: 144.12 kB
✓ built in 7.27s
tsc 0 errors, vite 0 errors. (The C_trtYuT hash was from Sprint 3; the new bundle will have a different hash. Check the output of your local build for the actual filename.)
3. Smoke checks
S4.A — F7 happy path: success toasts still work
For each of the following actions, the success toast should fire with the same string as before:
| Action | Expected success toast |
|---|---|
| Dashboard: drag a meal to a different slot | "Meal moved" |
| Dashboard: click Approve on a pending meal | "Meal approved" |
| Dashboard: click Deny on a pending meal | "Meal denied" |
| Dashboard: click the trash icon on a meal | "Meal deleted" (with Undo button) |
| Dashboard: click Undo on the delete toast within 5s | "Slot filled with a new meal" |
| Dashboard: click Generate on an empty slot | "Meal generated" |
| Dashboard: send the weekly vote email | "queued" (or similar from res.data.status) |
| Pantry: add a new item | "Item added to pantry" |
| Pantry: remove an item | "Item removed" (with Undo button) |
| Pantry: click Undo on the remove toast within 5s | "Item restored" |
| MealDetail: submit feedback | "Feedback saved!" |
S4.B — F7 error path: backend errors show the FastAPI detail
The simplest way to trigger a backend error is to send an out-of-range day_of_week or an invalid meal_type. Without a frontend tweak, the easiest check is to stop the backend before exercising the actions:
docker compose stop backend
Then, in the browser, try each of the 7 Dashboard mutations. Each should show a toast with a message like Network Error or Failed to load data (the global handler's fallback), NOT the legacy "Failed to move meal" / "Failed to approve meal" / etc.
Then restart the backend and verify success paths still work:
docker compose start backend
For a real FastAPI 4xx (not a network error), the cleanest check is the /meals/<id>/move action with an invalid target. The frontend doesn't expose a way to send an invalid meal_type directly, so the smoke check is the network-down case. If you want to test the FastAPI 422 path, the pantry form does it naturally: type a 1-char name (the schema has min_length=1 → 1 char is fine, but try an empty name, which the frontend blocks, or use a recipe rating > 5 via DevTools).
For a controlled test, hit the API directly with curl:
# Should return 422 with a Pydantic detail array
curl -i -X POST http://100.108.208.56:8082/api/feedback \
-H 'Content-Type: application/json' \
-d '{"meal_plan_item_id":"00000000-0000-0000-0000-000000000000","rating":99}'
# Expected body: {"detail":[{"type":"greater_than_equal","loc":["body","rating"],"msg":"Input should be greater than or equal to 1",...}]}
Then in the MealDetail page for any meal, click "Submit feedback" with rating 5 stars. Then open DevTools → Network → find the POST → "Edit and resend" with rating: 99. The toast should show the first msg from the Pydantic array ("Input should be greater than or equal to 1") — not the legacy "Failed to save feedback. Please try again.".
S4.C — F7 pre-flight checks still work
These are the local checks we deliberately didn't route through the global handler:
| Action | Expected toast |
|---|---|
| Pantry: open the Add form, leave name empty, click Add | "Please enter an ingredient name" |
Pantry: try to remove an item whose ingredient_id is null (none in seed data, but visible in DevTools if you clear the field) |
"Cannot remove: missing ingredient link" |
Both should fire immediately (no network call), and no global error toast should follow.
S4.D — F6 plan-status aria-label
- Open the Dashboard (
http://100.108.208.56:8082/). - In the top-right of the meal-plan card, there is a status badge (e.g. "awaiting approval", "approved", or "draft").
- Open DevTools → Elements tab → find the badge → confirm it has
aria-label="Plan status: awaiting approval"(or whatever the status text is). - Optional: turn on a screen reader (VoiceOver on macOS:
Cmd+F5; NVDA on Windows). Tab to the badge. It should announce"Plan status: awaiting approval"(or similar).
S4.E — Regression check: Sprints 1-3 still work
Quick spot-check of the headline features from previous sprints:
/recommendedredirects to/recipes/recommended(Sprint 1 B4)/this-does-not-existrenders the NotFound page (Sprint 1 B4)- Recipe detail shows ingredients with proper spacing (Sprint 1 B1)
- Meal detail shows
$X.XX per serving(Sprint 1 B3) - Pantry aisle
<select>has all 9 canonical options (Sprint 2 B8) - Shopping list section headers are sentence-case (Sprint 2 B9)
- Recipes filter panel has Apply/Reset and active count badge (Sprint 2 B11)
- Dashboard mobile viewport (390 px) shows empty meal slots (Sprint 1 B5)
- Dashboard delete shows the Undo toast (Sprint 3 B12)
- Nav links have
aria-current="page"on the active route (Sprint 3 S3.5)
4. Acceptance criteria
Sprint 4 is done when:
git pull+docker compose up -d --build frontendon the deployment host succeeds- Browser at
http://100.108.208.56:8082/loads the new bundle - All S4.A success toasts still fire with the same text
- At least one S4.B error path is verified (network-down is the easiest)
- S4.C pre-flight toasts still fire without a network call
- S4.D DevTools confirms the plan-status badge has the right
aria-label - No regression in S4.E
5. Rollback
Sprint 4 is a single frontend commit. To roll back:
git revert d71b67a
docker compose -f docker-compose.yml up -d --build frontend
No DB migration to revert. No data loss possible — the only "writes" are user actions, and a backend call that fails simply doesn't write.