Files
Meal-Planner/Review/sprint4-verification.md
admin 62dfc1eb4a docs(review): Sprint 4 verification log + plan/handoff/audit updates
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 the d71b67a row, 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 the d71b67a row.
  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.
2026-06-03 19:39:35 -07:00

148 lines
7.1 KiB
Markdown

# 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.
```bash
# 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:
```bash
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:
```bash
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:
```bash
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:
```bash
# 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
1. Open the Dashboard (`http://100.108.208.56:8082/`).
2. In the top-right of the meal-plan card, there is a status badge (e.g. "awaiting approval", "approved", or "draft").
3. Open DevTools → Elements tab → find the badge → confirm it has `aria-label="Plan status: awaiting approval"` (or whatever the status text is).
4. 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:
- [ ] `/recommended` redirects to `/recipes/recommended` (Sprint 1 B4)
- [ ] `/this-does-not-exist` renders 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 frontend` on 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:
```bash
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.