# 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. )`). 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 `