feat: D1 — Shopping list (Issue #30) #43
@@ -132,7 +132,7 @@
|
||||
<div class="doc-header">
|
||||
<div>
|
||||
<h1>J5 — Shopping list</h1>
|
||||
<p>Journey spec — Generate shopping list, real-time shared checklist</p>
|
||||
<p>Journey spec — Generate shopping list, shared checklist</p>
|
||||
</div>
|
||||
<div class="doc-meta">
|
||||
v1.0<br/>
|
||||
@@ -143,12 +143,12 @@
|
||||
<!-- ═══ J5 SHOPPING ═══ -->
|
||||
<div class="jh jh-b">
|
||||
<div class="jn">J5</div>
|
||||
<div><h2>Generate shopping list</h2><p>Merge ingredients, filter staples. Always live and shared with household.</p><div class="fl">C1 → D1 (always live) · Planner generates · All members add/remove/check off</div></div>
|
||||
<div><h2>Generate shopping list</h2><p>Merge ingredients, filter staples. Shared with household.</p><div class="fl">C1 → D1 · Planner generates · All members add/remove/check off</div></div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ D1 SHOPPING LIST ═══ -->
|
||||
<div class="scr" id="d1">
|
||||
<div class="scr-head"><h3>Shopping list (live)</h3><span class="scr-id">D1</span></div>
|
||||
<div class="scr-head"><h3>Shopping list</h3><span class="scr-id">D1</span></div>
|
||||
<div class="scr-desc">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.</div>
|
||||
<div class="scr-var"><strong>V1 · Checklist with sources</strong> — desktop: list left, recipe reference right</div>
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<div style="padding:8px 12px;">
|
||||
<div style="background:var(--blue-tint);border:1px solid var(--blue-light);border-radius:var(--radius-lg);padding:8px 10px;display:flex;align-items:center;gap:8px;margin-bottom:10px;">
|
||||
<div style="width:8px;height:8px;border-radius:50%;background:var(--blue);"></div>
|
||||
<div style="font-size:11px;color:var(--blue-dark);">Shared with household · 2 members online</div>
|
||||
<div style="font-size:11px;color:var(--blue-dark);">Shared with household</div>
|
||||
</div>
|
||||
<div class="eye" style="margin-bottom:4px;">5 items remaining</div>
|
||||
<div style="padding:0 4px;">
|
||||
@@ -188,7 +188,7 @@
|
||||
<div class="dsb-nav"><div><div class="dsb-nl">Plan</div><div class="dsb-ni"><span class="dsb-nc">📅</span>Planner</div><div class="dsb-ni"><span class="dsb-nc">📖</span>Recipes</div><div class="dsb-ni a"><span class="dsb-nc">🛒</span>Shopping</div></div></div>
|
||||
</div>
|
||||
<div class="dm">
|
||||
<div class="dtb"><div class="dtb-t">Shopping list</div><div class="dtb-r"><div style="background:var(--blue-tint);border:1px solid var(--blue-light);border-radius:var(--radius-md);padding:5px 12px;font-size:11px;color:var(--blue-dark);display:flex;align-items:center;gap:6px;"><div style="width:6px;height:6px;border-radius:50%;background:var(--blue);"></div>2 members online</div></div></div>
|
||||
<div class="dtb"><div class="dtb-t">Shopping list</div><div class="dtb-r"><div style="background:var(--blue-tint);border:1px solid var(--blue-light);border-radius:var(--radius-md);padding:5px 12px;font-size:11px;color:var(--blue-dark);display:flex;align-items:center;gap:6px;"><div style="width:8px;height:8px;border-radius:50%;background:var(--blue);"></div>Shared with household</div></div></div>
|
||||
<div style="flex:1;display:flex;overflow:hidden;">
|
||||
<!-- Left: checklist -->
|
||||
<div style="flex:1;padding:20px 24px;overflow-y:auto;">
|
||||
@@ -237,20 +237,20 @@
|
||||
</div>
|
||||
<div class="agent">
|
||||
<h4>D1 · Shopping list</h4>
|
||||
<pre>/* Desktop: 224px sidebar + topbar (title + shared status badge) + 2-col content:
|
||||
<pre>/* 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.
|
||||
* Real-time sync: is_checked updates broadcast to all connected clients.
|
||||
* 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. */</pre>
|
||||
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
||||
<tr class="grp"><td colspan="3">Desktop</td></tr>
|
||||
<tr><td>Checklist area</td><td>flex:1, page bg, 20px 24px padding</td><td>Remaining items + divider + checked items</td></tr>
|
||||
<tr><td>Recipe reference</td><td>280px, surface bg, border-left</td><td>Recipe name + day + ingredient count. Filtered staples below.</td></tr>
|
||||
<tr class="grp"><td colspan="3">Shared state</td></tr>
|
||||
<tr><td>Banner (mobile)</td><td>blue-tint, blue dot, radius-lg</td><td>"Shared · N online"</td></tr>
|
||||
<tr><td>Badge (desktop)</td><td>blue-tint pill in topbar</td><td>Compact: dot + "N members online"</td></tr>
|
||||
<tr><td>Banner (mobile)</td><td>blue-tint, blue dot, radius-lg</td><td>"Shared with household"</td></tr>
|
||||
<tr><td>Badge (desktop)</td><td>blue-tint pill in topbar</td><td>Compact: dot + "Shared with household"</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -269,22 +269,23 @@
|
||||
|
||||
<h3>1. Journey flow</h3>
|
||||
<pre>/* J5 flow
|
||||
* C1 (week confirmed) → D1 (shopping list, always live).
|
||||
* 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.
|
||||
* There is NO draft/publish workflow — the list is always live. */</pre>
|
||||
* Sync model: server-authoritative. Changes persist via form actions.
|
||||
* Other users see updates on page refresh (no WebSocket/SSE). */</pre>
|
||||
|
||||
<h3>2. Screen D1 — Shopping list (live shared)</h3>
|
||||
<h3>2. Screen D1 — Shopping list</h3>
|
||||
<pre>/* Mobile layout:
|
||||
* topbar (title + settings icon)
|
||||
* + blue-tint shared banner ("Shared with household · N members online", blue dot)
|
||||
* + 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 "N members online" badge)
|
||||
* 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"
|
||||
@@ -319,15 +320,14 @@
|
||||
<li>Filtered staples are listed in the recipe reference panel (desktop) for transparency</li>
|
||||
</ul>
|
||||
|
||||
<h3>4. Real-time sync</h3>
|
||||
<pre>/* Real-time rules:
|
||||
* - is_checked updates broadcast to ALL connected clients instantly
|
||||
* - "N members online" indicator shows who is currently viewing the shopping list
|
||||
* - Prevents double-buying when multiple family members shop simultaneously or at different times
|
||||
* - Blue accent colour for all shared-state UI:
|
||||
* Mobile: blue-tint banner with blue dot
|
||||
* Desktop: blue-tint badge in topbar with blue dot
|
||||
* - WebSocket or SSE for real-time — implementation choice, but must be instant */</pre>
|
||||
<h3>4. Sync model</h3>
|
||||
<pre>/* 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") */</pre>
|
||||
|
||||
<h3>5. Custom items</h3>
|
||||
<ul>
|
||||
@@ -352,22 +352,20 @@
|
||||
* UPDATE shopping_list_item
|
||||
* SET is_checked = true/false
|
||||
* WHERE id = :item_id
|
||||
* → broadcast change to all connected clients via real-time channel
|
||||
*
|
||||
* Add custom:
|
||||
* INSERT INTO shopping_list_item (name, quantity, is_custom, is_checked, shopping_list_id)
|
||||
* VALUES (:name, :quantity, true, false, :list_id)
|
||||
* → broadcast new item to all connected clients */</pre>
|
||||
* VALUES (:name, :quantity, true, false, :list_id) */</pre>
|
||||
|
||||
<h3>7. Design constraints</h3>
|
||||
<ul>
|
||||
<li>List is ALWAYS live — no draft/publish workflow, no approval step</li>
|
||||
<li>List is shared — all household members see the same list on refresh</li>
|
||||
<li>Both planner and member can: view, check off, add custom items, remove items</li>
|
||||
<li>Only planner can: generate list, regenerate list</li>
|
||||
<li>"Edit staples" link navigates to D3 (same component as A3 — build once, reference from two entry points)</li>
|
||||
<li>CalDAV export is future scope (E3) — do not build in v1</li>
|
||||
<li>Recipe reference panel is a page section with surface bg — NOT a floating card</li>
|
||||
<li>Blue accent colour is reserved for shared/collaborative state indicators</li>
|
||||
<li>Blue accent colour for shared-state banner/badge ("Shared with household")</li>
|
||||
<li>Checked items must visually separate from unchecked via a divider and "Checked off" label</li>
|
||||
</ul>
|
||||
|
||||
@@ -375,7 +373,7 @@
|
||||
<pre>/* 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 (multiple members online)
|
||||
* 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 */</pre>
|
||||
|
||||
Reference in New Issue
Block a user