# Sprint 3 — Verification Log

**Date:** 2026-06-03
**Scope:** Three P2s (B12, B13, B14) + a11y sweep (S3.5) + ErrorBoundary confirmation (S3.4 — no code change).
**Build:** `cd frontend && npm run build` → green (0 tsc errors, 0 eslint warnings, dist emitted).

## Files changed

| File | Change |
|---|---|
| `frontend/src/lib/toast.ts` → `toast.tsx` | New `showToast.undo(message, onUndo, ms=5000)` helper. Renamed to `.tsx` for JSX support. |
| `frontend/src/pages/Dashboard.tsx` | B12: `handleDelete` captures full `MealPlanItem`; on undo calls `meals.generateItem(planId, dayOfWeek, mealType)`. Status Badge gets explicit `aria-label`. |
| `frontend/src/pages/Pantry.tsx` | B12: `handleRemove` is fully reversible; calls `pantry.add` with original `ingredient_id`/`quantity`/`unit` on undo. New `removeId` state for per-row loading. |
| `frontend/src/App.tsx` | B13: nav `whitespace-nowrap` + `px-2 sm:px-3`. S3.5: `aria-current="page"` on active link; `<nav aria-label="Primary">`; `<main id="main-content">`. |
| `frontend/src/components/ui/Badge.tsx` | S3.5: optional `icon` and `aria-label` props. |
| `Review/ui-nielsen-audit.md`, `fix-ui-audit.md` | Updated with Sprint 3 status. |

## How to deploy

```bash
cd ~/MealPlanner
git pull
docker compose -f docker-compose.yml up -d --build frontend
```

No backend changes in Sprint 3. No migration.

## Manual smoke checks

- **Dashboard** (mobile 360 px): all 4 nav links fit on one line. Tab to Meal Planner link → `aria-current="page"` is announced.
- **Dashboard** (delete flow): click a meal's `X` button → toast appears "Meal deleted" with **Undo**. Click Undo within 5s → a new meal fills the slot. Let the toast expire → slot stays empty.
- **Pantry** (delete flow): click Remove on a row → toast appears "Item removed" with **Undo**. Click Undo within 5s → the original item is back with its original quantity/unit. Per-row spinner visible only on the clicked row.
- **A11y** (DevTools): `<main id="main-content">` is the skip-link target. `<nav aria-label="Primary">`. Active nav link has `aria-current="page"`. Approval-status Badge in meal card has `aria-label="Approval status: approved"` etc.
- **Meal card (mobile 360 px)**: confirm the title is still 2-line clamped (Sprint 2 fix intact) and the approve-status pill is color + text + aria-label (no color-only signal).

## Notes / Caveats

- `react-hot-toast` 2.6's `ToastOptions` does not expose `onClose` or `onDismiss`. The `undo` helper relies solely on the user clicking the Undo button during the 5s window — there is no "expire callback". This is documented in `lib/toast.tsx`. The previous `confirm()` had the same effective behavior (the user could click Cancel within the dialog).
- For Dashboard delete, the Undo fills the slot with a freshly generated recipe (likely different from the deleted one). The original recipe cannot be restored without backend support for a "rebuild from snapshot" endpoint. This trade-off is documented in the plan's §R4.
- Per-row loading state in Pantry: the `Remove` button shows a spinner only on the row being deleted, not all rows simultaneously.
