Public Access
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 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.
This commit is contained in:
+18
-11
@@ -1,6 +1,6 @@
|
||||
# UI/UX Audit & Fix — Agent Handoff
|
||||
|
||||
You are taking over a 3-sprint UI/UX audit and fix cycle. All code changes are committed and build green. The user's deployment host (Tailscale `100.108.224.12`) is the only environment you should touch for verification — the local repo on this machine (`/home/peter/Projects/MealPlanner`) was the editing host; the running app lives elsewhere.
|
||||
You are taking over a 4-sprint UI/UX audit and fix cycle. All code changes are committed and build green. The user's deployment host (Tailscale `100.108.224.12`) is the only environment you should touch for verification — the local repo on this machine (`/home/peter/Projects/MealPlanner`) was the editing host; the running app lives elsewhere.
|
||||
|
||||
**Date of handoff: 2026-06-03.**
|
||||
|
||||
@@ -8,17 +8,18 @@ You are taking over a 3-sprint UI/UX audit and fix cycle. All code changes are c
|
||||
|
||||
## TL;DR
|
||||
|
||||
Three commits land all 14 audit findings from `Review/ui-nielsen-audit.md`:
|
||||
Four commits land all 14 audit findings + 2 of the §Future items:
|
||||
|
||||
| Sprint | Commit | Scope | Build | Deploy |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `f3e4a44` | 5 P0 blockers | ✅ green | ✅ deployed 2026-06-02 by user |
|
||||
| 2 | `ccc70aa` + `f5fb755` | 6 P1s + bonus S3.3 | ✅ green | ⚠️ not yet deployed |
|
||||
| 3 | `e90a9d6` | 3 P2s + a11y sweep | ✅ green | ⚠️ not yet deployed |
|
||||
| 4 | `d71b67a` | F7 global error handler + F6 plan-status aria-label | ✅ green | ⚠️ not yet deployed |
|
||||
|
||||
All work is on `main` ahead of `origin/main` (pre-existing WIP also present). All three sprints compile. **Sprint 1 is live. Sprints 2 and 3 are not yet live on `100.108.208.56:8082/`.**
|
||||
All work is on `main` ahead of `origin/main` (pre-existing WIP also present). All four sprints compile. **Sprint 1 is live. Sprints 2, 3, and 4 are not yet live on `100.108.208.56:8082/`.** Sprint 4 is frontend-only (no backend changes); Sprint 2 includes a backend migration; Sprints 3 and 4 are pure frontend.
|
||||
|
||||
**Next action:** the user runs the deploy commands in `Review/sprint2-verification.md` and `Review/sprint3-verification.md` on the deployment host, then smoke-checks per the checklists. After verification, any remaining items move to the §Future backlog in `fix-ui-audit.md`.
|
||||
**Next action:** the user runs the deploy commands in `Review/sprint2-verification.md`, `Review/sprint3-verification.md`, and `Review/sprint4-verification.md` on the deployment host, then smoke-checks per the checklists. After verification, any remaining items move to the §Future backlog in `fix-ui-audit.md` (F1 onboarding, F2 keyboard shortcuts, F3 bulk add, F4 plan-whole-week, F5 URL week selector, F8 Spoonacular enrichment, F9 Ollama matcher — F6 and F7 now done in Sprint 4).
|
||||
|
||||
---
|
||||
|
||||
@@ -30,17 +31,18 @@ All work is on `main` ahead of `origin/main` (pre-existing WIP also present). Al
|
||||
│ ├── ui-nielsen-audit.md # The audit (276 → ~340 lines). Status blocks per sprint at the top.
|
||||
│ ├── sprint2-verification.md # Deploy + smoke checks for Sprint 2 (BACKEND MIGRATION INCLUDED).
|
||||
│ ├── sprint3-verification.md # Deploy + smoke checks for Sprint 3 (frontend only).
|
||||
│ ├── sprint4-verification.md # Deploy + smoke checks for Sprint 4 (F7+F6, frontend only).
|
||||
│ ├── docs-claude.md # Prior docs adversarial review (not yours).
|
||||
│ ├── docs-gpt5.5.md # Prior docs adversarial review.
|
||||
│ ├── repo-gpt5.5.md # Prior repo adversarial review.
|
||||
│ ├── reviewconcensus.md # Prior review consensus.
|
||||
│ └── synthesis.md # Prior review synthesis.
|
||||
├── fix-ui-audit.md # The plan (232 lines). Implementation notes per task + commit refs.
|
||||
├── fix-ui-audit.md # The plan. Implementation notes per task + commit refs. Sprint 4 added.
|
||||
├── docs/HANDOFF.md # The project-wide handoff (don't duplicate — read for context).
|
||||
└── docs/ORIENTATION.md # Project orientation.
|
||||
```
|
||||
|
||||
**Read first**, in this order: `docs/ORIENTATION.md` → `docs/HANDOFF.md` → `Review/ui-nielsen-audit.md` (the audit) → `fix-ui-audit.md` (the plan) → the two `sprintN-verification.md` files.
|
||||
**Read first**, in this order: `docs/ORIENTATION.md` → `docs/HANDOFF.md` → `Review/ui-nielsen-audit.md` (the audit) → `fix-ui-audit.md` (the plan) → the three `sprintN-verification.md` files.
|
||||
|
||||
---
|
||||
|
||||
@@ -137,14 +139,14 @@ The user's flow is: read the smoke check, run the deploy, click around, mark eac
|
||||
|
||||
## What's NOT in scope (per audit §Future)
|
||||
|
||||
The audit intentionally deferred these. If the user asks, they become a new plan:
|
||||
F6 and F7 are done in Sprint 4. The audit intentionally deferred these. If the user asks, they become a new plan:
|
||||
- F1. Onboarding hints / tour (H10)
|
||||
- F2. Keyboard shortcuts (`/`, `g p`, `g s`, `n m`)
|
||||
- F3. Bulk add on Pantry/Shopping List (H7)
|
||||
- F4. Plan-the-whole-week button (H7)
|
||||
- F5. Persistent week selector in URL
|
||||
- F6. Badge a11y for color-only signals (generalized — the current badges are text+color)
|
||||
- F7. Global `react-query` `onError` toast handler
|
||||
- ~~F6. Badge a11y for color-only signals (generalized)~~ — done in Sprint 4
|
||||
- ~~F7. Global `react-query` `onError` toast handler~~ — done in Sprint 4
|
||||
- F8. Backend Spoonacular enrichment (separate proposal in `docs/proposals/`)
|
||||
- F9. Ollama LLM matcher (separate proposal in `docs/proposals/`)
|
||||
|
||||
@@ -176,6 +178,11 @@ The audit intentionally deferred these. If the user asks, they become a new plan
|
||||
| 3 | `frontend/src/pages/Pantry.tsx` | B12: reversible undo on remove |
|
||||
| 3 | `frontend/src/App.tsx` | B13 + S3.5: nav nowrap, aria-current, main id |
|
||||
| 3 | `frontend/src/components/ui/Badge.tsx` | S3.5: icon + aria-label props |
|
||||
| 4 | `frontend/src/lib/toast.tsx` | F7: extractErrorMessage + showApiError |
|
||||
| 4 | `frontend/src/App.tsx` | F7: QueryCache/MutationCache onError + queries defaultOptions |
|
||||
| 4 | `frontend/src/pages/Dashboard.tsx` | F7: removed 6 local onError; F6: plan-status aria-label |
|
||||
| 4 | `frontend/src/pages/Pantry.tsx` | F7: removed 3 local onError; undo now uses showApiError |
|
||||
| 4 | `frontend/src/pages/MealDetail.tsx` | F7: removed submitMutation.onError |
|
||||
|
||||
---
|
||||
|
||||
@@ -200,9 +207,9 @@ git log origin/main..HEAD --oneline
|
||||
# - Sprint 2: docker compose exec db psql -U mealplanner -d mealplanner \
|
||||
# -f /dev/stdin < backend/scripts/persist_aisle_backup.sql
|
||||
# - docker compose exec backend alembic upgrade head
|
||||
# - docker compose -f docker-compose.yml up -d --build frontend
|
||||
# - Sprint 3: docker compose -f docker-compose.yml up -d --build frontend
|
||||
# See Review/sprint{2,3}-verification.md for the full checklist.
|
||||
# - Sprint 4: docker compose -f docker-compose.yml up -d --build frontend
|
||||
# See Review/sprint{2,3,4}-verification.md for the full checklist.
|
||||
|
||||
# 4. If you need to take new screenshots after changes
|
||||
# /tmp/opencode/mp-review/shoot-sprint1.js is a working playwright harness.
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
# 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.
|
||||
@@ -50,7 +50,7 @@ The app looks polished on the surface (Tailwind palette, clean cards, working to
|
||||
> docker compose -f docker-compose.yml up -d --build frontend
|
||||
> ```
|
||||
|
||||
> **Sprint 3 status (commit pending, ready for deploy):** All P2s plus the a11y sweep.
|
||||
> **Sprint 3 status (commit `e90a9d6`, awaiting deploy):** All P2s plus the a11y sweep.
|
||||
> - **B12** Native `confirm()` deleted for both delete sites. `lib/toast.tsx` (renamed from `.ts` for JSX) gains a new `showToast.undo(message, onUndo, ms=5000)` helper. `Dashboard.handleDelete` captures the full item, deletes, then surfaces an Undo toast that re-fires `generateItem(planId, dayOfWeek, mealType)` to refill the slot. `Pantry.handleRemove` is fully reversible: re-adds via `pantry.add` with the original `ingredient_id`/`quantity`/`unit`. Per-row loading state via new `removeId` state.
|
||||
> - **B13** `Navigation` link text gets `whitespace-nowrap`; padding reduced to `px-2 sm:px-3` so all 4 links fit on one line down to ~360 px.
|
||||
> - **S3.4** Confirmed `ErrorBoundary` is already mounted at `App.tsx:42` (verified `components/ErrorBoundary.tsx`).
|
||||
@@ -63,6 +63,12 @@ The app looks polished on the surface (Tailwind palette, clean cards, working to
|
||||
> git pull
|
||||
> docker compose -f docker-compose.yml up -d --build frontend
|
||||
> ```
|
||||
>
|
||||
> **Sprint 4 status (commit `d71b67a`, awaiting deploy):** Two §Future items, both small, both polish.
|
||||
> - **F7** Global react-query error handler. `lib/toast.tsx` gains `extractErrorMessage(err, fallback)` and `showApiError(err, fallback)` that read FastAPI's `response.data.detail` (string or Pydantic 422 array) and produce a clean user-facing string. `App.tsx` wires `QueryCache({ onError })` and `MutationCache({ onError })` to `showApiError`, so any future mutation that forgets a local handler still surfaces its failure. 10 local try/catch toasts deleted across `Dashboard.tsx`, `Pantry.tsx`, `MealDetail.tsx`. Pre-flight client-side checks (empty name, missing ingredient link) deliberately kept local since they never reach the network. Default-options added: `queries: { retry: 1, refetchOnWindowFocus: false }` — closes the H9 "silent background refetch failure" finding.
|
||||
> - **F6** Plan-status Badge on the Dashboard header (draft / awaiting_approval / approved / rejected) now passes `aria-label="Plan status: <text>"` so screen readers announce both the category and the value. Matches the per-item approval-status pattern added in Sprint 3. **No other colour-only badges exist in the app** — every other `<Badge>` is either a count or a self-describing tag.
|
||||
> - **Backend changes:** none. Deploy is frontend-only.
|
||||
> - **Verification log:** `Review/sprint4-verification.md`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+31
-7
@@ -4,6 +4,7 @@ You are taking over a project in mid-flight. Read `docs/ORIENTATION.md` first fo
|
||||
|
||||
**Date of handoff: 2026-06-03. Last commits before handoff:**
|
||||
```
|
||||
d71b67a feat(ui): global react-query error handler + plan-status a11y (Sprint 4 F7+F6)
|
||||
427d8ac docs(review): add handoff document for UI audit work
|
||||
e90a9d6 feat(ui): close 3 P2 audit findings + a11y sweep (Sprint 3)
|
||||
f5fb755 fix(migration): simplify aisle migration + add persistent backup script
|
||||
@@ -298,7 +299,7 @@ Trust the tests. Trust the live runs. Don't trust prose claims that something is
|
||||
**Current open proposals:**
|
||||
- `docs/proposals/2026-05-23-feedback-driven-recipe-discovery.md` — pending user approval. No code yet (per the 2026-05-23 section below).
|
||||
|
||||
**Last updated: 2026-06-03** — UI/UX audit & fix cycle (Sprints 1, 2, 3) complete. 14 findings closed, code committed (`f3e4a44`, `ccc70aa`, `f5fb755`, `e90a9d6`), build green. Sprint 1 deployed; Sprints 2 and 3 awaiting deploy on the user's home server. Backend migration `0015_normalize_pantry_aisles.py` not yet run on prod (dry-run shows 21k rows would change on dev DB). Full UI-audit handoff at `Review/handoff-ui-audit.md`.
|
||||
**Last updated: 2026-06-03** — UI/UX audit & fix cycle (Sprints 1, 2, 3, 4) complete. 16 findings closed (5 P0 + 6 P1 + 3 P2 + 2 §Future), code committed across 7 commits (`f3e4a44`, `36038bb`, `ccc70aa`, `f5fb755`, `e90a9d6`, `427d8ac`, `d71b67a`), build green. Sprint 1 deployed; Sprints 2, 3, and 4 awaiting deploy on the user's home server (4 is frontend-only). Backend migration `0015_normalize_pantry_aisles.py` not yet run on prod (dry-run shows 21k rows would change on dev DB). Full UI-audit handoff at `Review/handoff-ui-audit.md`.
|
||||
|
||||
---
|
||||
|
||||
@@ -313,9 +314,10 @@ A full Nielsen-10-heuristics audit of the live deployment at `http://100.108.208
|
||||
- `fix-ui-audit.md` — the implementation plan, with per-task implementation notes
|
||||
- `Review/sprint2-verification.md` — Sprint 2 deploy + smoke-check checklist (includes the **backend migration** step)
|
||||
- `Review/sprint3-verification.md` — Sprint 3 deploy + smoke-check checklist (frontend only)
|
||||
- `Review/sprint4-verification.md` — Sprint 4 deploy + smoke-check checklist (F7 + F6, frontend only)
|
||||
- `Review/handoff-ui-audit.md` — focused handoff for a fresh agent continuing UI-audit work
|
||||
|
||||
**Commits on `main` (ahead of `origin/main` by 9 prior WIP commits plus these 6):**
|
||||
**Commits on `main` (ahead of `origin/main` by 9 prior WIP commits plus these 7):**
|
||||
|
||||
| Commit | Sprint | What |
|
||||
|---|---|---|
|
||||
@@ -325,6 +327,7 @@ A full Nielsen-10-heuristics audit of the live deployment at `http://100.108.208
|
||||
| `f5fb755` | 2 (fix) | Migration 0015 simplification + persistent backup script (`persist_aisle_backup.sql`) + corrected container-based deploy commands |
|
||||
| `e90a9d6` | 3 | 3 P2s + a11y: undo-toast (Dashboard refills slot; Pantry fully reversible), mobile nav nowrap, aria-current, `<main id="main-content">`, Badge `aria-label`/`icon` props |
|
||||
| `427d8ac` | (docs) | Review/handoff-ui-audit.md |
|
||||
| `d71b67a` | 4 | F7 global error handler (10 try/catch blocks deleted, QueryCache/MutationCache onError wired) + F6 plan-status aria-label |
|
||||
|
||||
**Critical Sprint 2 deploy note:** the user must run on the deployment host *after* `git pull`:
|
||||
|
||||
@@ -352,6 +355,26 @@ git pull
|
||||
docker compose -f docker-compose.yml up -d --build frontend
|
||||
```
|
||||
|
||||
### Sprint 4 — F7 (global error handler) + F6 (plan-status a11y)
|
||||
|
||||
The first wave of `fix-ui-audit.md` §Future work. Two small items, no new deps, no backend changes.
|
||||
|
||||
**F7 — `lib/toast.tsx` + `App.tsx` + 3 page refactors:**
|
||||
- New `extractErrorMessage(err, fallback)` and `showApiError(err, fallback)` helpers in `lib/toast.tsx`. The normalizer reads `err.response.data.detail` (string or Pydantic 422 array), then `err.message`, then the fallback. Closes the H9 "silent failure" finding for both queries (background refetches) and mutations.
|
||||
- `QueryClient` now created with `QueryCache({ onError: showApiError })` and `MutationCache({ onError: showApiError })`. Default options: `queries: { retry: 1, refetchOnWindowFocus: false }`.
|
||||
- 10 local try/catch toasts deleted across `Dashboard.tsx` (6: move/approve/deny/delete/generate + outer delete), `Pantry.tsx` (3: add/remove mutations + createIngredient), `MealDetail.tsx` (1: submitFeedback). 4 pre-flight client-side checks kept local (empty name, missing ingredient link, unresolved ingredient, "Failed to send vote emails" — that one is fire-and-forget via BackgroundTasks; see `Review/sprint4-verification.md` for the rationale).
|
||||
|
||||
**F6 — `Dashboard.tsx` plan-status Badge:**
|
||||
- Added `aria-label={\`Plan status: ${mealPlan.status.replace(/_/g, ' ')}\`}` to the badge that shows draft / awaiting_approval / approved / rejected. Matches the per-item approval-status pattern from Sprint 3. Audit of all other `<Badge>` call sites confirmed no further aria-label work needed — every other badge is either a count or a self-describing tag.
|
||||
|
||||
**Verification:** `npm run build` green. Live smoke per `Review/sprint4-verification.md` (network-down is the easiest way to verify F7; DevTools + VoiceOver for F6).
|
||||
|
||||
**Sprint 4 deploy is also frontend-only:**
|
||||
```bash
|
||||
git pull
|
||||
docker compose -f docker-compose.yml up -d --build frontend
|
||||
```
|
||||
|
||||
### Deployment-host vs dev-host (Tailscale gotcha)
|
||||
|
||||
This repo lives on a dev host (Tailscale `100.108.146.47`). The user's home server (Tailscale `100.108.224.12`) serves the live app at `100.108.208.56:8082`. The user's workflow is **commit locally, `git pull` on the deployment host, rebuild there**. Don't `docker compose up` on the local dev host expecting it to update the live site — it won't.
|
||||
@@ -366,6 +389,7 @@ Pre-existing bug: `.gitignore` line 17 is `lib/` (the Python ignore), and it cat
|
||||
Review/handoff-ui-audit.md # Focused UI-audit handoff
|
||||
Review/sprint2-verification.md # Deploy + smoke-check for Sprint 2
|
||||
Review/sprint3-verification.md # Deploy + smoke-check for Sprint 3
|
||||
Review/sprint4-verification.md # Deploy + smoke-check for Sprint 4 (F7+F6)
|
||||
Review/ui-nielsen-audit.md # (rewritten) Audit with status blocks per sprint
|
||||
fix-ui-audit.md # The plan, with per-task implementation notes
|
||||
frontend/src/pages/NotFound.tsx # 404 catch-all (B4)
|
||||
@@ -377,15 +401,15 @@ backend/scripts/persist_aisle_backup.sql # Persistent backup
|
||||
### Files modified by this session
|
||||
|
||||
```
|
||||
frontend/src/App.tsx # Routes, nav, aria
|
||||
frontend/src/App.tsx # Routes, nav, aria, QueryCache/MutationCache
|
||||
frontend/src/api/index.ts # (pre-existing WIP, not mine)
|
||||
frontend/src/components/ui/Badge.tsx # icon + aria-label props
|
||||
frontend/src/components/ui/EmptyState.tsx # optional to prop
|
||||
frontend/src/lib/toast.ts → toast.tsx # renamed for JSX; showToast.undo()
|
||||
frontend/src/lib/toast.ts → toast.tsx # renamed for JSX; showToast.undo() (B12), extractErrorMessage/showApiError (F7)
|
||||
frontend/src/lib/utils.ts # cleanDescription() (B7)
|
||||
frontend/src/pages/Dashboard.tsx # B5, B6, B12
|
||||
frontend/src/pages/MealDetail.tsx # B2, B3, B7
|
||||
frontend/src/pages/Pantry.tsx # B8, B10, B12
|
||||
frontend/src/pages/Dashboard.tsx # B5, B6, B12, F6 aria-label, F7 handler refactor
|
||||
frontend/src/pages/MealDetail.tsx # B2, B3, B7, F7 submitFeedback onError
|
||||
frontend/src/pages/Pantry.tsx # B8, B10, B12, F7 add/remove/createIngredient onError
|
||||
frontend/src/pages/RecipeDetail.tsx # B1
|
||||
frontend/src/pages/Recipes.tsx # B11
|
||||
frontend/src/pages/ShoppingList.tsx # B9, S3.3
|
||||
|
||||
+35
-3
@@ -215,6 +215,37 @@ Resolve the 14 issues (5 P0, 6 P1, 3 P2) from `Review/ui-nielsen-audit.md` in th
|
||||
|
||||
---
|
||||
|
||||
## Sprint 4 — Polish the error path (F7 + F6)
|
||||
|
||||
**Status (2026-06-03):** ✅ Both items implemented and committed (`d71b67a`). `npm run build` green. Awaiting deploy.
|
||||
|
||||
### S4.1 · F7 — Global react-query error toast handler
|
||||
- **Files:** `frontend/src/lib/toast.tsx`, `frontend/src/App.tsx`, `frontend/src/pages/Dashboard.tsx`, `Pantry.tsx`, `MealDetail.tsx`
|
||||
- **Change (one commit `d71b67a`):**
|
||||
1. **`lib/toast.tsx`** — added `extractErrorMessage(err, fallback)` and `showApiError(err, fallback)`. The normalizer reads `err.response.data.detail` when present (handles both `string` and Pydantic 422 `[{loc, msg, type}, ...]` array shapes), then falls back to `err.message`, then the supplied default. Never surfaces `"[object Object]"` or raw stack traces.
|
||||
2. **`App.tsx`** — `QueryClient` now created with `QueryCache({ onError })` and `MutationCache({ onError })` wired to `showApiError`. Added `defaultOptions.queries: { retry: 1, refetchOnWindowFocus: false }` so background-refetch failures (H9) are no longer silent.
|
||||
3. **`Dashboard.tsx`** — removed 6 local try/catch toasts (move / approve / deny / delete / generate + the outer delete handler). Kept `VoteEmailButton.handleSend` and `handleDelete`'s undo-callback with `showApiError(err, 'Failed to ...')` for action-specific fallback strings (these are user-initiated recovery paths where a contextual default is more useful than the bare FastAPI detail).
|
||||
4. **`Pantry.tsx`** — removed 3 local `onError` handlers (`addMutation`, `removeMutation`, `handleAdd`'s createIngredient path) and `handleRemove`'s outer catch. Kept 3 pre-flight client-side checks that never reach the network (missing ingredient link, empty name, unresolved ingredient). `handleRemove`'s undo callback now uses `showApiError` for the restore failure.
|
||||
5. **`MealDetail.tsx`** — removed `submitMutation.onError`. The local `"Failed to save feedback. Please try again."` is replaced by the actual FastAPI detail.
|
||||
- **Net effect:** 10 backend-error try/catch blocks deleted; error messages are now identical to what the backend actually says; any future mutation that forgets to add a local `onError` still gets surfaced.
|
||||
- **Backend audit (read-only):** Every `HTTPException(detail=...)` in the touched routes is human-friendly (e.g. `"Meal plan item not found"`, `"Slot already occupied"`, `"Family profile not found"`, `"ingredient name already exists"`). Pydantic 422s return arrays and the helper handles them. No detail message is technical/leaks internals.
|
||||
- **Verify:** `npm run build` green. Live smoke: pull `100.108.208.56` and try each of the 7 Dashboard mutations + the 4 Pantry/MealDetail mutations with the backend down or returning 4xx — every failure should show a toast with the FastAPI `detail` string, not the legacy `"Failed to ..."` default.
|
||||
- **Risk:** `sendVoteEmails` is fire-and-forget (`POST /orchestrate/email` returns 202 + `BackgroundTasks`; errors land in `WeeklyRun.error_message` not the HTTP response). The toast for that action will only ever show the success message or a network error. Keep the local fallback string for that one — it documents the intent.
|
||||
|
||||
### S4.2 · F6 — Plan-status Badge: `aria-label`
|
||||
- **File:** `frontend/src/pages/Dashboard.tsx:438`
|
||||
- **Change:** Added `aria-label={\`Plan status: ${mealPlan.status.replace(/_/g, ' ')}\`}` to the `<Badge>` that shows the meal-plan status (draft / awaiting_approval / approved / rejected). Matches the per-item approval-status pattern added in Sprint 3 (S3.5). A screen reader now announces `"Plan status: awaiting approval"` instead of just the colour-encoded `"awaiting approval"` text.
|
||||
- **Other `<Badge>` audit:** the only other call site with colour-encoded semantics is the per-item approval status (already handled in Sprint 3) and the `"Never suggest this recipe again"` badge on MealDetail (its visible text fully describes intent, so the colour is decorative). The "Spice N/5" warning badge on RecipeDetail is also self-describing. **No further aria-label work needed.**
|
||||
- **Verify:** VoiceOver/NVDA on the Dashboard header — the plan status badge announces with the category prefix.
|
||||
|
||||
### S4.3 · Sprint 4 verification gate
|
||||
- [x] `npm run build` green for Sprint 4 (tsc 0 errors, vite 0 errors).
|
||||
- [ ] Deploy verified (git pull on `100.108.224.12`, `docker compose up -d --build frontend` — no backend changes).
|
||||
- [ ] Smoke pass: 11 mutation failures show FastAPI `detail` (not legacy fallback); plan-status Badge announces correctly.
|
||||
- [ ] No regression in Sprint 1–3 fixes.
|
||||
|
||||
---
|
||||
|
||||
## Risks & mitigations
|
||||
- **R1 · Backend field `qty` vs `quantity`:** confirm with a one-line `curl` against `/api/meals/<id>` before renaming the type. If the API still returns `quantity`, use a shim `ing.qty ?? ing.quantity` rather than breaking other consumers.
|
||||
- **R2 · Pantry migration:** run against dev DB first; capture before/after row counts. **Do not** run on prod without the `--backup-table` step in place.
|
||||
@@ -227,8 +258,9 @@ Resolve the 14 issues (5 P0, 6 P1, 3 P2) from `Review/ui-nielsen-audit.md` in th
|
||||
## Done when (overall)
|
||||
- [x] Sprint 1: 5 P0 fixes — committed `f3e4a44`, deployed by user 2026-06-02.
|
||||
- [x] Sprint 2: 6 P1 fixes + 1 bonus S3.3 — committed `ccc70aa`, deploy helper `f5fb755`. Awaiting deploy.
|
||||
- [x] Sprint 3: 3 P2 fixes + a11y sweep — committed (this branch), awaiting deploy.
|
||||
- [x] `npm run build` green for all three sprints (tsc 0 errors, vite 0 errors).
|
||||
- [x] Sprint 3: 3 P2 fixes + a11y sweep — committed `e90a9d6`, awaiting deploy.
|
||||
- [x] Sprint 4: F7 (global error handler) + F6 (plan-status aria-label) — committed `d71b67a`, awaiting deploy. **No backend changes; deploy is frontend-only like Sprint 3.**
|
||||
- [x] `npm run build` green for all four sprints (tsc 0 errors, vite 0 errors).
|
||||
- [ ] Backend aisle-migration (`0015`) run on dev; row counts logged to `Review/sprint2-verification.md`.
|
||||
- [ ] Manual smoke pass on `http://100.108.208.56:8082/` per `Review/sprint2-verification.md`.
|
||||
- [ ] Manual smoke pass on `http://100.108.208.56:8082/` per `Review/sprint2-verification.md` (Sprint 1-3) and `Review/sprint4-verification.md` (Sprint 4).
|
||||
- [ ] No regressions in existing Playwright walkthrough.
|
||||
|
||||
Reference in New Issue
Block a user