J5 — Shopping list

Journey spec — Generate shopping list, shared checklist

v1.0
Journey
J5

Generate shopping list

Merge ingredients, filter staples. Shared with household.

C1 → D1 · Planner generates · All members add/remove/check off

Shopping list

D1
V1 Checklist with sources. Desktop: sidebar + topbar + two-column content — checklist on the left, a "This week's recipes" reference panel on the right that shows which recipes contributed which items. The panel is a page section (surface bg), not a card.
V1 · Checklist with sources — desktop: list left, recipe reference right
Mobile · 320px
10:24●●● WiFi 🔋
Shopping list
⚙️
Shared with household
5 items remaining
Cherry tomatoes
For: Tomato pasta
500g
Penne pasta
For: Tomato pasta
300g
Salmon fillet
For: Salmon teriyaki
400g
Jasmine rice
For: Teriyaki, Curry
500g
Soy sauce
For: Teriyaki, Stir-fry
4 tbsp
Coconut milk
For: Thai curry
400ml
Curry paste
For: Thai curry
2 tbsp
+ Add custom item
📅
Planner
📖
Recipes
🛒
Shopping
⚙️
Settings
Desktop · 1040px
Plan
📅Planner
📖Recipes
🛒Shopping
Shopping list
Shared with household
5 items remaining · 2 checked off
Cherry tomatoes
For: Tomato pasta
500g
Penne pasta
For: Tomato pasta
300g
Salmon fillet
For: Salmon teriyaki
400g
Jasmine rice
For: Salmon teriyaki, Thai curry
500g
Soy sauce
For: Salmon teriyaki, Stir-fry
4 tbsp
Checked off
Coconut milk
For: Thai curry
400ml
Curry paste
For: Thai curry
2 tbsp
+ Add custom item
This week's recipes
Tomato pasta
Tue · 2 ingredients on list
Salmon teriyaki
Wed · 3 ingredients on list
Thai green curry
Thu · 2 ingredients on list
Chicken stir-fry
Mon · shared soy sauce
Filtered staples
Olive oil · Garlic · Salt · Pepper · Rice · Pasta
Edit staples →

D1 · Shopping list

/* Desktop: 224px sidebar + topbar (title + shared badge) + 2-col content:
 *   Left (flex:1, page bg): remaining count + checklist rows + "checked off" section + add custom
 *   Right (280px, surface bg): recipe reference cards + filtered staples list + edit staples link
 * Recipe reference panel: page-section with surface bg, not a floating card.
 * Mobile: full-width checklist + shared banner + bottom tabs.
 * Sync model: server-authoritative — check/uncheck persists via form action, other users see changes on page refresh.
 * Both roles: planner + member can view and check off. Only planner can regenerate. */
ElementValueNotes
Desktop
Checklist areaflex:1, page bg, 20px 24px paddingRemaining items + divider + checked items
Recipe reference280px, surface bg, border-leftRecipe name + day + ingredient count. Filtered staples below.
Shared state
Banner (mobile)blue-tint, blue dot, radius-lg"Shared with household"
Badge (desktop)blue-tint pill in topbarCompact: dot + "Shared with household"

LLM instructions — J5 Shopping list journey

Authoritative implementation reference for the shopping list journey. Covers screen D1 and references A3/D3 (staples). Use this when building or modifying the shopping list feature.

1. Journey flow

/* J5 flow
 * C1 (week confirmed) → D1 (shopping list).
 * Actor: Planner generates the list. All household members shop (view, check off, add items).
 * Preconditions: J1 (recipes exist) + J2 (week is planned) for generating a list.
 *                J6 (household setup) for shared access.
 * Sync model: server-authoritative. Changes persist via form actions.
 *             Other users see updates on page refresh (no WebSocket/SSE). */

2. Screen D1 — Shopping list

/* Mobile layout:
 *   topbar (title + settings icon)
 *   + blue-tint shared banner ("Shared with household", blue dot)
 *   + checklist (unchecked items, then checked items below divider)
 *   + "+ Add custom item" link (blue-dark, centred)
 *   + bottom tabs (Planner | Recipes | Shopping [active] | Settings)
 *
 * Desktop layout:
 *   sidebar (224px, dsb) + topbar (dtb: title + blue-tint "Shared with household" badge)
 *   + split content area:
 *     Left: checklist (flex:1, page bg, 20px 24px padding)
 *       - eyebrow "N items remaining · N checked off"
 *       - unchecked rows
 *       - border-top divider → "Checked off" eyebrow (opacity .6) → checked rows
 *       - "+ Add custom item" link (12px, blue-dark, font-weight 500)
 *     Right: recipe reference panel (280px, surface bg, border-left, 20px padding)
 *       - eyebrow "This week's recipes"
 *       - recipe cards: page bg, border, radius-md, 10px padding
 *         - recipe name (13px/500) + day + ingredient count (10px muted)
 *       - border-top divider → "Filtered staples" eyebrow
 *         - staple names inline (11px muted, dot-separated)
 *         - "Edit staples →" link (11px, blue, font-weight 500) — links to D3
 *
 * Checklist row (.ck):
 *   checkbox (.ck-b, 22px, radius 4px, 2px border)
 *   + content (.ck-c): name (.ck-n, 14px) + source (.ck-s, 10px muted, "For: [recipe names]")
 *   + quantity (.ck-q, mono 12px muted, flex-shrink 0)
 *
 * Checked state (.ck.d):
 *   checkbox fills green with white checkmark
 *   name gets line-through + muted colour
 *   row moves below divider into "Checked off" section */

3. Shopping list generation

4. Sync model

/* Sync rules:
 * - Server-authoritative: all mutations (check, uncheck, add) go through form actions / API calls
 * - No WebSocket or SSE — other users see changes on page refresh
 * - Each check/uncheck is a single server round-trip (form action with use:enhance)
 * - Blue accent colour for shared-state UI:
 *     Mobile: blue-tint banner with blue dot ("Shared with household")
 *     Desktop: blue-tint badge in topbar with blue dot ("Shared with household") */

5. Custom items

6. Data operations

/* Generate list:
 *   SELECT ri.ingredient_id, i.name, SUM(ri.quantity), ri.unit
 *   FROM recipe_ingredient ri
 *   JOIN ingredient i ON ri.ingredient_id = i.id
 *   JOIN week_plan_slot wps ON wps.recipe_id = ri.recipe_id
 *   WHERE wps.week = :current_week
 *     AND i.is_staple = false
 *   GROUP BY ri.ingredient_id, i.name, ri.unit
 *
 * Check off:
 *   UPDATE shopping_list_item
 *   SET is_checked = true/false
 *   WHERE id = :item_id
 *
 * Add custom:
 *   INSERT INTO shopping_list_item (name, quantity, is_custom, is_checked, shopping_list_id)
 *   VALUES (:name, :quantity, true, false, :list_id) */

7. Design constraints

8. Preconditions

/* Precondition chain:
 * J1 (recipes exist) — cannot generate a shopping list without recipes
 * J2 (week is planned) — cannot generate a shopping list without planned meals
 * J6 (household setup) — required for shared access
 *
 * If no meals are planned: show empty state on D1 with prompt to plan the week first
 * If no household members: list works for solo planner, shared banner is hidden */