feat(ui): close 6 P1 audit findings + 1 bonus mobile fix (Sprint 2)

- Dashboard MealCard: title truncate -> line-clamp-2, image shrinks to
  40x40 on <md to give the title room (B6).
- MealDetail: hero reworked to normal flow with stronger gradient;
  description runs through new cleanDescription() helper that strips
  14 spoonacular SEO patterns and trims to the last full sentence.
  Raw description moved to a 'Notes from source' disclosure (B7).
- Pantry: free-text aisle/unit replaced with <Select> populated from
  the new PANTRY_AISLES canonical enum; ingredient name field marked
  required. New PANTRY_AISLES export + PantryAisle type in types (B8).
- backend: alembic 0015_normalize_pantry_aisles maps free-text
  ingredient.aisle and grocery_item.aisle to canonical labels in a
  single transaction; downgrade raises (restore from snapshot).
  backend/scripts/dry_run_aisle_migration.sql is the read-only
  preview helper.
- ShoppingList: human-readable AISLE_LABEL map replaces raw snake_case
  aisle keys; 3-col stat grid with compact mobile sizing (B9 + S3.3).
- Pantry table: role/aria-label region and a right-edge white
  gradient hint at mobile horizontal overflow (B10).
- Recipes: pending/applied filter split, Apply and Reset buttons,
  active-count chip on the Filters button, role=region + aria-label
  on the panel (B11).
- Review/sprint2-verification.md and fix-ui-audit.md updated.

Build: npm run build (tsc + vite) green. tsc emits 0 errors.

Co-located audit + plan docs kept in sync: Review/ui-nielsen-audit.md
gains a Sprint 2 status block; fix-ui-audit.md has implementation
notes for each Sprint 2 task.
This commit is contained in:
2026-06-03 17:36:26 -07:00
parent 36038bb9cb
commit ccc70aaf72
12 changed files with 728 additions and 81 deletions
+52
View File
@@ -0,0 +1,52 @@
# Sprint 2 — Verification Log
**Date:** 2026-06-02
**Scope:** Six P1s from `Review/ui-nielsen-audit.md` plus the S3.3 mobile stat-grid fix.
**Build:** `cd frontend && npm run build` → green (0 tsc errors, 0 eslint warnings, dist emitted).
## Files changed
| File | Change |
|---|---|
| `frontend/src/pages/Dashboard.tsx` | B6: meal-card title `truncate``line-clamp-2`; image 56→40 on mobile. |
| `frontend/src/pages/MealDetail.tsx` | B7: hero rework (relative flow, gradient `from-black/80`, `line-clamp-2` description via `cleanDescription`); new "Notes from source" disclosure. |
| `frontend/src/lib/utils.ts` | B7: `cleanDescription(input, maxLen=280)` strips 14 spoonacular boilerplate patterns and trims to last sentence. |
| `frontend/src/pages/Pantry.tsx` | B8: aisle/unit → `<Select>` from canonical lists; ingredient name marked `*`; B10: scroll-hint gradient + `role="region"`. |
| `frontend/src/types/index.ts` | B8: `PANTRY_AISLES` enum + `PantryAisle` type. |
| `backend/alembic/versions/0015_normalize_pantry_aisles.py` | B8: migration — `CASE LOWER` mapping, temp-table backup, downgrade raises. |
| `backend/scripts/dry_run_aisle_migration.sql` | B8: read-only dry-run (counts rows that *would* change). |
| `frontend/src/pages/ShoppingList.tsx` | B9: `AISLE_LABEL` map + `aisleDisplay()`; S3.3: 3-col grid on all viewports with compact mobile sizing. |
| `frontend/src/pages/Recipes.tsx` | B11: `applied`/`pending` filter state, Apply/Reset buttons, active-count chip on Filters button, `role="region"`. |
## How to verify on the deployment host
```bash
cd /path/to/MealPlanner
git pull
# 1. Backend migration (one-off, dry-run first)
psql "$DATABASE_URL" -f backend/scripts/dry_run_aisle_migration.sql
docker compose exec backend alembic upgrade head
# 2. Frontend rebuild + restart
docker compose -f docker-compose.yml up -d --build frontend
# 3. Smoke-check the live site
curl -s -o /dev/null -w "%{http_code}\n" http://100.108.208.56:8082/
# Expect: 200
```
## Manual smoke checks
- `/` (mobile 390 px): Generate button visible on every empty slot; new meal title clamps to 2 lines without `B..`.
- `/meals/<id>`: title is readable, description is 1-2 lines, "Notes from source" disclosure shows the raw text.
- `/pantry` (add form): Unit and Aisle are dropdowns; ingredient name shows `*`.
- `/shopping-list`: section headers read "Meat & Seafood", "Produce", "Pantry", "Dairy & Eggs". On mobile, the 3 stat cards are side-by-side in one row.
- `/recipes`: open Filters, set 2, collapse — Filters button shows `Filters (2)`. Reset clears, chip disappears.
- `/pantry` (mobile 390 px): right-edge gradient hints that the table scrolls horizontally.
## Migration safety
- Run dry-run first to count affected rows per table.
- Migration runs in a single `upgrade()` call; temp backup tables persist for the session lifetime. To preserve backups beyond session, alter the migration to use real tables.
- `downgrade()` is intentionally `NotImplementedError`. Restore from a pre-migration snapshot if rollback is needed.
+20 -1
View File
@@ -19,7 +19,26 @@ The app looks polished on the surface (Tailwind palette, clean cards, working to
4. **`/recommended` returns a blank page** (missing route + no 404 catch-all in `App.tsx`).
5. **Mobile dashboard hides empty meal slots** (`Dashboard.tsx:164,219` — users on phones cannot *plan* meals, only view them).
> **Sprint 1 status (commit `f3e4a44`):** Items 1, 2, 3, 4, 5 all addressed in the frontend source. ⚠️ **Not yet deployed to the live server at `100.108.208.56:8082/`** — that host is a different machine (Tailscale IP `100.108.224.12`) than the one I edited on. The user must `git pull` and `docker compose up -d --build frontend` on the deployment host. Verification screenshots in `/tmp/opencode/mp-review/screenshots/fix-sprint1/`.
> **Sprint 1 status (commit `f3e4a44`, deployed by user 2026-06-02):** Items 1, 2, 3, 4, 5 all addressed in the frontend source. Live at `100.108.208.56:8082/`. Verification screenshots in `/tmp/opencode/mp-review/screenshots/fix-sprint1/`.
>
> **Sprint 2 status (commit pending, ready for deploy):** All six P1s plus the S3.3 mobile shopping-list stat-grid fix are addressed in source.
> - **B6** Dashboard `MealCard` title: `truncate` → `line-clamp-2`; image shrinks to 40×40 on `<md` to give title more room.
> - **B7** `MealDetail` hero: title/description no longer overlap; description stripped of spoonacular SEO copy via `lib/utils.cleanDescription`; raw text moved to a "Notes from source" disclosure.
> - **B8** `Pantry` aisle/unit: free-text → canonical `Select` from `PANTRY_AISLES` enum (`types/index.ts`). `Ingredient name` field now marked `*` required. Backend migration `0015_normalize_pantry_aisles.py` normalizes `ingredient.aisle` and `grocery_item.aisle` to canonical labels. Dry-run SQL helper at `backend/scripts/dry_run_aisle_migration.sql`.
> - **B9** `ShoppingList` aisle section headers now human-readable via `AISLE_LABEL` map; falls back to raw key for unknown values.
> - **B10** Mobile pantry table: right-edge white-to-transparent gradient overlay hints at horizontal overflow; container has `role="region"` + descriptive `aria-label`.
> - **B11** Recipes filters: refactored to `pending`/`applied` state with explicit Apply / Reset buttons. `Filters` button shows active-count chip when filters are set. Wrapped in `role="region" aria-label="Filters"`.
> - **S3.3** Shopping list stat cards: now `grid-cols-3` on all viewports with compact mobile sizing.
>
> Deployment command:
> ```bash
> # On the deployment host:
> git pull
> # Apply the backend migration (one-off):
> docker compose exec backend alembic upgrade head
> # Rebuild & restart frontend:
> docker compose -f docker-compose.yml up -d --build frontend
> ```
---