# Sprint 6 — Deploy & smoke-check (F3 + F4)

**Goal:** verify the bulk pantry add (F3) and the plan-the-week button (F4) work end-to-end on `http://100.108.208.56:8082/`.

**Commit:** `8ad4ef6` (`feat(ui): bulk pantry add + plan-the-week button (Sprint 6 F3+F4)`)
**Sprint scope:** F3 (ShoppingList bulk add) + F4 (Dashboard plan-the-week button)
**Backend changes:** `pantry.py` (new bulk endpoint), `meals.py` (new fill-empty-slots endpoint), `schemas/__init__.py` (3 new schemas)
**Frontend changes:** `api/index.ts` (2 new bindings), `Dashboard.tsx` (Plan the week button + dropdown), `ShoppingList.tsx` (Add to pantry button)

## 1. Deploy

Sprint 6 has both backend and frontend changes. No migrations needed.

```bash
# On deployment host (100.108.224.12)
cd /path/to/MealPlanner
git pull
docker compose -f docker-compose.yml up -d --build backend frontend
```

## 2. Build verification (already green locally)

```
$ cd frontend && npm run build
> tsc && vite build
✓ 1894 modules transformed.
dist/index.html                   0.54 kB │ gzip:   0.31 kB
dist/assets/index-DBX6fwsX.css   38.10 kB │ gzip:   6.82 kB
dist/assets/index-CTd7xPlC.js   478.22 kB │ gzip: 147.53 kB
✓ built in 7.02s
```

tsc 0 errors, vite 0 errors.

## 3. Backend smoke (local dev, already verified)

```bash
# F3 — bulk pantry add with an unknown ingredient
curl -s -X POST http://100.108.208.56:8082/api/pantry/bulk \
  -H 'Content-Type: application/json' \
  -d '{"items":[{"ingredient_id":"00000000-0000-0000-0000-000000000001"}]}'
# Expected: {"added":0,"updated":0,"skipped":1,"results":[{"ingredient_id":"...","status":"skipped","id":null,"reason":"Unknown ingredient"}]}

# F4 — fill empty slots for dinners only
PLAN_ID=$(curl -s http://100.108.208.56:8082/api/meals | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id','') if d else '')")
curl -s -X POST "http://100.108.208.56:8082/api/meals/$PLAN_ID/fill-empty-slots" \
  -H 'Content-Type: application/json' \
  -d '{"meal_types":["dinner"]}'
# Expected: {"filled":[...],"failed":[]}  (filled has the newly-inserted dinner slots; failed is empty if there were empty dinner slots and recipes available)

# F4 — invalid meal_type
curl -s -X POST "http://100.108.208.56:8082/api/meals/$PLAN_ID/fill-empty-slots" \
  -H 'Content-Type: application/json' \
  -d '{"meal_types":["brunch"]}'
# Expected: {"filled":[],"failed":[{"day_of_week":0,"meal_type":"brunch","reason":"Unknown meal_type: brunch"}]}
```

## 4. UI smoke checks

### S6.A — F3 ShoppingList "Add checked to pantry"

1. Open `http://100.108.208.56:8082/shopping-list`.
2. Confirm a shopping list is displayed (current week has a plan).
3. Tick 3-5 checkboxes in different aisles. The header should show a primary `Add 5 to pantry` button next to the Reset button.
4. Click the `Add 5 to pantry` button.
5. Toast should appear: `Pantry: added 3, updated 1, skipped 1` (counts will vary).
6. Open `http://100.108.208.56:8082/pantry` in a new tab. The 4 successfully added items should be there.
7. Return to the ShoppingList. The 4 items should now be unchecked; the 1 skipped item (if any) should still be checked.

### S6.B — F3 partial-failure edge case

To force a "skipped" count, you'd need a shopping list item without an `ingredient_id`. The seed data may not include any. If your DB does have such an item:
- Tick one such item + one normal item.
- Click `Add 2 to pantry`.
- Toast should show `skipped 1` and the un-skipped item should be added.

If your DB has no such items, the toast will show only `added` / `updated` and that's correct.

### S6.C — F4 Dashboard "Plan the week"

1. Open `http://100.108.208.56:8082/`.
2. The header should show a primary `Plan the week` button (with a Sparkles icon and a chevron-down caret) next to the week-nav control.
3. Click the button. A dropdown should appear with two options:
   - `Dinners only` (subtitle: "Fill every empty dinner slot this week")
   - `All meals` (subtitle: "Fill every empty slot (breakfast, lunch, dinner) this week")
4. Click `Dinners only`. The button should show a spinner + `Planning…` and be disabled.
5. After 1-2 seconds, a toast should appear: `Planned N meal slots` (or `Planned N of M meal slots — X failed (e.g. <reason>)`).
6. The weekly grid should refresh and the previously-empty dinner slots should now show generated meals.
7. Click `Plan the week` again, choose `All meals`. The grid should fill in any remaining empty breakfast/lunch slots too.

### S6.D — F4 edge cases

- **No empty slots:** if every slot is already filled, the toast should say `No empty meal slots to fill`.
- **All recipes used:** the backend reuses recipes when the un-used pool is empty. If your family has very few recipes, the toast might say `Planned 21 of 21 meal slots` (with recipes repeated across days). That's expected.
- **Invalid meal_types:** the frontend only sends `dinner` or `breakfast`+`lunch`+`dinner`, so the backend's `Unknown meal_type` failure mode is not triggerable from the UI. (Documented in the backend test for completeness.)

### S6.E — Regression check: Sprints 1-5 still work

Quick spot-check:

- [ ] `/recommended` redirects to `/recipes/recommended` (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 shows empty meal slots (Sprint 1 B5)
- [ ] Dashboard delete shows the Undo toast (Sprint 3 B12)
- [ ] Failed mutations show a toast with the FastAPI detail (Sprint 4 F7)
- [ ] URL week selector: `?week=2026-05-15` works (Sprint 5 F5)
- [ ] Keyboard shortcuts: `g d` / `g p` / `?` (Sprint 5 F2)

## 5. Acceptance criteria

Sprint 6 is done when:
- [ ] `git pull` + container rebuild on the deployment host succeeds
- [ ] S6.A bulk pantry add works
- [ ] S6.C plan-the-week button works
- [ ] No regression in S6.E

## 6. Rollback

```bash
git revert 8ad4ef6
docker compose -f docker-compose.yml up -d --build backend frontend
```

No migrations to revert. No data loss possible — both endpoints only insert or upsert user-controlled data.
