diff --git a/specs/frontend/j2-add-meal.html b/specs/frontend/j2-add-meal.html new file mode 100644 index 0000000..6358019 --- /dev/null +++ b/specs/frontend/j2-add-meal.html @@ -0,0 +1,913 @@ + + + + + + Recipe App — J2 Add to Plan · Screens C4–C6 + + + + +
+ +
+
+

J2 Add to Plan — Screens C4–C6

+

Supplemental spec · Bottom sheet recipe picker · Recipe quick actions · Day picker

+
+
+ Journey: J2 supplement
+ Screens: C4 · C5 · C6
+ Status: draft
+ Version: 1.0
+ Updated: 2026-04 +
+
+ +
+
J2
+
+

Add to plan — supplemental

+

Three missing flows: picking a recipe for an empty slot (C4), quick actions on recipe cards (C5), and the day picker when the recipe is already known (C6).

+
Two entry points: C1 "+" / empty slot → C4 · B1 "Zur Woche +" → C6
+
+
+ + +
+
Design rationale
+
+
+

Entry from planner (C4) — recipe unknown

+

User taps "+" or an empty slot in C1. The day is known; the recipe isn't. A bottom sheet slides up over the dimmed planner, showing variety-ranked suggestions and a search-filtered full library. On desktop the empty tile highlights and the detail panel transforms to a recipe picker — the calendar grid stays fully visible. No full-page navigation; same pattern as J4 swap.

+
+
+

Entry from recipe list (C5 → C6) — day unknown

+

User is browsing recipes and taps "Zur Woche +". The recipe is known; the day isn't. A compact day-picker sheet shows the week's slots. Empty slots have a dashed green border to invite selection. Selecting a filled slot shows an inline replace warning — no modal dialog. "Jetzt kochen" on the same card navigates directly to J3 cook mode.

+
+
+
+
+

Add ≠ Swap

+

J4 Swap starts from a filled slot and shows system-suggested replacements. C4/C6 add starts from an empty slot or a chosen recipe. Different intent — do not reuse the swap sheet component.

+
+
+

Mobile: always bottom sheet

+

No full-page navigation for either entry point. The planner or recipe list context stays visible behind the sheet at 40% opacity.

+
+
+

Desktop: panel transformation

+

The 216px detail panel switches between states. No modal overlay. The calendar grid or recipe grid remains fully interactive beside the picker.

+
+
+
+ + + +
+

Add meal — from planner

C4
+
Entry: tap "+" in C1 nav (pre-selects next empty day) or tap any empty slot chip/tile (pre-selects that day). Shows a bottom sheet with search + variety-ranked suggestions + full recipe list. Tapping a recipe immediately fills the slot — no confirmation, undo toast instead.
+
V1 · Bottom sheet (mobile & tablet) · V2 · Panel transformation (desktop)
+ +
+
Breakpoint 1 — Mobile · < 768px
+
+ +
+
9:41●●● WiFi 🔋
+
+ +
+
+
Diese Woche
+
+
+
+
+
+
+
+
+
Mo31
+
Di1
+
Mi2
+
Do3
+
Fr4
+
Sa5
+
So6
+
+
+ +
+
+
+
+
+
+
Rezept wählen
+
Samstag, 5. April
+
+
×
+
+ +
+
Empfohlen · Beste Abwechslung
+
+
+
Lachsfilet mit Gemüse
+
25 min · Einfach · Fisch
+ ↑ +2 Punkte +
+ +
+
+
+
Mushroom Risotto
+
50 min · Mittel · Vegetarisch
+ ↑ +2 Punkte +
+ +
+
+
+
Hähnchen-Curry
+
35 min · Einfach
+ ⚠ Fr ebenfalls Hähnchen +
+ +
+
Alle Rezepte
+
+
Beef Bourguignon
2h 30 · Aufwendig
+ +
+
+
Spaghetti Carbonara
20 min · Einfach
+ +
+
+
Tomatensuppe
30 min · Vegetarisch
+ +
+
+
+
+
+
+ +
+
+

Sheet structure

+

Drag handle → title row (day date in muted subtitle) → close × → search input → two sections: "Empfohlen" (2–4 variety-ranked, sorted by variety_delta DESC) + "Alle Rezepte" (full library, filtered by search). Max height ~75vh — 3 suggestions visible without scrolling.

+
+
+

Variety badges

+

Green "↑ +N Punkte" when adding this recipe improves the variety score. Yellow "⚠ [reason]" when it creates a conflict but is still selectable. No badge in the "Alle Rezepte" section.

+
+
+

Background

+

Weekly planner dims to 30% opacity behind the sheet. The week strip is still legible — the empty Sa chip is visible with its dashed border, providing context for which slot is being filled.

+
+
+

Immediate write

+

Tapping "+ Wählen" writes to the slot immediately and dismisses the sheet. An undo toast appears for 4 seconds. No confirmation dialog — same pattern as J4.

+
+
+
+
+ +
+
Breakpoint 3 — Desktop · > 1024px
+
+

Panel in recipe-picker mode

+

Clicking an empty slot tile (or "+" in the toolbar without a day) opens the recipe picker in the right detail panel. The calendar grid remains fully visible and interactive. The empty tile highlights with a solid green border and "Wählen…" label to indicate which slot is being edited.

+
+
+
+
+ +
+
📅 Planer
+
📖 Rezepte
+
🛒 Einkauf
+
+
Variety score
8/10
+
+
+
+
+ Wochenplanung +
+ + 31 März – 6 Apr + +
+
+ +
+
+
+
+
Montag
31
Abendessen
Pasta al forno
30 min
+
Dienstag
1
Heute
Gegrillter Lachs
25 min
+
Mittwoch
2
Abendessen
Tomaten-Pasta
45 min
+
Donnerstag
3
Abendessen
Hähnchen-Stir-fry
25 min
+
Freitag
4
Abendessen
Hähnchen-Curry
40 min
+
Samstag
5
+
Wählen…
+
Sonntag
6
Abendessen
Linsensuppe
45 min
+
+
+ +
+
+
Sa, 5. April
Rezept wählen
+
×
+
+
+ +
+
Empfohlen
+
+
Lachsfilet mit Gemüse
+
25 min · Einfach · Fisch
+ ↑ +2 Punkte +
+
+
Mushroom Risotto
+
50 min · Mittel · Vegetarisch
+ ↑ +2 Punkte +
+
+
Hähnchen-Curry
+
35 min · Einfach
+ ⚠ Fr ebenfalls Hähnchen +
+
Alle Rezepte
+
Beef Bourguignon
2h 30 · Aufwendig
+
Spaghetti Carbonara
20 min · Einfach
+
Tomatensuppe
30 min · Vegetarisch
+
Rindereintopf
1h 20 · Mittel
+
+
+
+
+
+
+

Active empty tile

Clicked empty slot: solid green border (replaces dashed), green-tint bg, green-dark "+" icon and "Wählen…" label. Provides clear visual anchor for which slot the panel is operating on.

+

Panel header state

"Sa, 5. April" in Fraunces 14px / "Rezept wählen" in 9px muted below. Close × returns panel to idle state. If a filled day was previously selected, × returns to that day's detail view.

+

Clicking a recipe row

Immediate PATCH to slot. Panel switches to the day-detail view for the newly filled slot. No undo toast needed on desktop — the panel immediately shows the result with a "Swap" option if the user wants to change it.

+
+
+ +
+

C4 · Add meal from planner — implementation

+
/* TWO ENTRY POINTS:
+ * 1. "+" button in C1 nav → pre-selects next empty day in week (Mon–Sun scan).
+ * 2. Empty day slot chip (mobile) / empty tile (desktop) → pre-selects that date.
+ *
+ * MOBILE: bottom sheet slides up, planner dims to 30% opacity behind.
+ * Sheet: drag handle + {day label} header + × + search input + two sections.
+ * "Empfohlen" section: GET /api/suggestions?week={id}&day={date}
+ *   → sorted by variety_delta DESC (unlike J4 which sorts by effort ASC).
+ *   → 2–4 items. Badge: green "↑ +N Punkte" if delta > 0, yellow "⚠ {reason}" if ≤ 0.
+ * "Alle Rezepte": GET /api/recipes?sort=name — no badge.
+ * Search input: client-side filter of visible list. Does not re-sort.
+ * "+ Wählen" tap: PATCH /api/week-plan/{weekId}/slots/{date} {recipe_id}
+ *   → dismiss sheet → undo toast 4s with "Rückgängig" link.
+ *
+ * DESKTOP: tap empty tile → detail panel enters recipe-picker state.
+ * Empty tile visual: solid green border, green-tint bg, "Wählen…" label.
+ * Panel state machine: idle | day-detail | recipe-picker | day-picker
+ * Clicking panel recipe row: PATCH → panel transitions to day-detail for that date.
+ * No undo toast on desktop (panel shows result immediately with Swap option). */
+ + + + + + + + + + + +
ElementValueNotes
Mobile sheet
Sheet max-height75vhDrag to dismiss
Dim opacityrgba(28,28,24,.4)Same as J4
Suggestion sortvariety_delta DESCDifferent from J4 (effort ASC)
Suggestion badge8px, green-tint or yellow-tintOnly in Empfohlen section
Write actionPATCH + undo toast 4sNo confirmation dialog
Desktop panel
Active empty tilesolid green border, green-tint bgReplaces dashed border
Panel width216px (unchanged)Same panel, different content state
Panel recipe clickPATCH → transition to day-detailNo toast needed
+
+
+ + + +
+

Recipe card — quick actions

C5
+
Recipe list cards (Recipes tab, B1) gain two quick action buttons below the tags row. "Jetzt kochen" navigates directly to J3 cook mode. "Zur Woche +" triggers the C6 day picker. Both buttons are always visible — no hover-only pattern, since touch-capable desktops don't have reliable hover.
+
V1 · Always-visible quick action row — mobile list and desktop grid
+ +
+
+
Mobile · Recipe list
+
+
9:41●●● WiFi 🔋
+
+
+
Rezepte
+
+
+
+
+ +
+
+
+
Spaghetti Carbonara
+
20 min Einfach Nudeln
+
+
+
+
Mushroom Risotto
+
50 min Mittel Vegetarisch
+
+
+
+
Lachsfilet mit Gemüse
+
25 min Einfach Fisch
+
+
+
+
+
📅
Planer
+
📖
Rezepte
+
🛒
Einkauf
+
⚙️
Settings
+
+
+
+
+ +
+
Desktop · Recipe grid
+
+
+ +
+
📅 Planer
+
📖 Rezepte
+
🛒 Einkauf
+
+
+
+
+
Rezepte
+
+ + +
+
+
+
+
+
Spaghetti Carbonara
+
Cremige römische Pasta — kein Sahne, nur Ei und Guanciale.
+
20 min Einfach Nudeln
+
+
+
+
Mushroom Risotto
+
Cremiges Risotto mit Pilzen, Parmesan und frischem Thymian.
+
50 min Mittel Vegetarisch
+
+
+
+
Lachsfilet mit Gemüse
+
Knusprig gebratener Lachs auf Ofengemüse.
+
25 min Einfach Fisch
+
+
+
+
+
+
+
+
+ +
+
+

Two actions, two visual weights

+

"Jetzt kochen" is filled green (primary). "Zur Woche +" is green-tint bg / green-dark text / green-light border (secondary). Same green family — clearly related, but different priority. Both are 10px mobile / 11px desktop, weight 500, radius-md.

+
+
+

Navigation targets

+

"Jetzt kochen" → navigate to /cook/{recipeId} (J3, no sheet or confirmation). "Zur Woche +" → open C6 day picker (bottom sheet mobile, panel transformation desktop). The Recipes tab stays active.

+
+
+ +
+

C5 · Recipe quick actions — implementation

+
/* Add two buttons to every recipe card in B1 (Recipes tab).
+ * Position: below the tags row, above any description/ingredients.
+ * "Jetzt kochen": navigate to /cook/{recipeId}. No confirmation. Primary style.
+ * "Zur Woche +": open C6 day-picker component. Secondary style.
+ * Both always visible — not hover-gated. Touch-capable desktops need always-visible actions.
+ * Mobile: equal-width flex row, font-size 10px, padding 5px 6px.
+ * Desktop grid: equal-width flex row, font-size 11px, padding 7px. */
+ + + + + + + +
TokenCook btnPlan btn
Backgroundvar(--green)var(--green-tint)
Text color#fffvar(--green-dark)
Bordernone1px var(--green-light)
Font size mobile10px10px
Font size desktop11px11px
Radiusvar(--radius-md)var(--radius-md)
+
+
+ + + +
+

Day picker — add from recipe

C6
+
Entry: tap "Zur Woche +" on any recipe card (C5). Recipe is already known; user picks the target day. Mobile: compact bottom sheet (~55vh) with week strip. Desktop: detail panel transforms to day picker. Empty slots have dashed green border to invite selection. Selecting a filled slot shows an inline replace warning — no modal dialog.
+
V1 · Empty slot selected (green confirm) · V2 · Filled slot selected (orange replace confirm)
+ +
+
Breakpoint 1 — Mobile · Both variants
+
+ + +
+
V1 · Empty slot (Sa)
+
+
9:41●●● WiFi 🔋
+
+
+
Rezepte
+
+
Spaghetti Carbonara
+
Mushroom Risotto
+
+
+
+
+
+
+
+
+
Mushroom Risotto
+
Zu welchem Tag hinzufügen?
+
+
×
+
+
+ 31 März – 6 Apr +
+ + +
+
+
+
Mo31
+
Di1
+
Mi2
+
Do3
+
Fr4
+
Sa5
+
So6
+
+
+ +
+
+
+
+
+
+ + +
+
V2 · Filled slot (Mi) — ersetzen?
+
+
9:41●●● WiFi 🔋
+
+
+
Rezepte
+
+
Mushroom Risotto
+
+
+
+
+
+
+
+
+
Mushroom Risotto
+
Zu welchem Tag hinzufügen?
+
+
×
+
+
+ 31 März – 6 Apr +
+ + +
+
+
+
Mo31
+
Di1
+
Mi2
+
Do3
+
Fr4
+
Sa5
+
So6
+
+
Ersetzt Tomaten-Pasta am Mittwoch. Undo möglich.
+
+ +
+
+
+
+
+
+ +
+
+

Slot states

+

Empty: dashed green-light border + green-tint bg (invites selection). Filled: solid color-border + surface bg + green dot. Today: yellow-tint border. Selected empty: 2px green-dark. Selected filled: 2px orange-dark + orange-tint → shows replace warning.

+
+
+

No replace dialog

+

The replace warning appears inline below the week strip. The confirm button turns orange. One tap replaces. An undo toast appears for 4 seconds. No modal dialog is shown — consistent with J4 swap.

+
+
+

Week navigation

+

‹ › buttons allow browsing to next/prev week if the current week has no empty slots. Default: shows current week.

+
+
+
+
+ +
+
Breakpoint 3 — Desktop · Panel day-picker mode
+
+
+
+ +
+
📅 Planer
+
📖 Rezepte
+
🛒 Einkauf
+
+
+
+
+
Rezepte
+ +
+
+ +
+
+
+
Spaghetti Carbonara
+
20 min Einfach
+
+
+
+
Mushroom Risotto
+
Cremiges Risotto mit Pilzen und Parmesan.
+
50 min Mittel Vegetarisch
+
+ + +
+
+
+
Lachsfilet
+
25 min Einfach
+
+
+
+
+ +
+
+
Mushroom Risotto
Tag für diese Woche wählen
+
×
+
+ +
+
31 März – 6 Apr
+
+
Mo31
+
Di1
+
Mi2
+
Do3
+
Fr4
+
Sa5
+
So6
+
+
+ +
+
Samstag, 5. April · leer
+ +
+ +
+
Variety-Vorschau
+
↑ Score: 8 → 9 · Neues Protein
+
+
+
+
+
+
+
+

Active card highlight

The card whose day picker is open: 2px green border, green-tint bg, "Zur Woche +" button becomes green filled "Tag wählen…". Other cards at 45% opacity to keep focus on the active recipe.

+

Variety preview (desktop only)

Below the confirm button the panel shows the projected score change and reason: "↑ Score: 8 → 9 · Neues Protein". Omitted on mobile to keep the sheet compact. Calls GET /api/variety/preview?add={recipeId}&date={date}.

+

Replace on desktop

Same V2 pattern: clicking a filled day chip turns the confirm button orange and shows inline warning "Ersetzt [recipe] am [day]." Panel width is enough to show both elements without overlap.

+
+
+ +
+

C6 · Day picker — implementation

+
/* Entry: tap "Zur Woche +" on recipe card. Recipe ID is known; date is unknown.
+ * Mobile: compact bottom sheet (~55vh). Background: recipe list at 28% opacity.
+ * Sheet: drag handle + recipe name header + week label + ‹ › week nav + 7-day strip + confirm.
+ *
+ * DAY CHIP STATES (7 chips, current week):
+ *   .empty   → dashed green-light border, green-tint bg (invite selection)
+ *   .filled  → solid color-border, surface bg, green dot below date number
+ *   .today   → solid yellow border, yellow-tint bg, yellow-text label
+ *   .sel-empty  → 2px green-dark border, green-tint bg → confirm btn = green
+ *   .sel-filled → 2px orange-dark border, orange-tint bg → show dp-warn → confirm btn = orange
+ *
+ * Confirm empty: PATCH /api/week-plan/{weekId}/slots/{date} {recipe_id}
+ *   → dismiss sheet → undo toast 4s "Rückgängig"
+ * Confirm filled: same PATCH (server replaces existing recipe_id)
+ *   → dismiss sheet → undo toast 4s "Rückgängig"
+ *
+ * Desktop panel state: 'day-picker'
+ *   Active recipe card: 2px green border, green-tint bg, button text "Tag wählen…" (green filled)
+ *   Other cards: opacity 0.45
+ *   Panel adds variety-preview section below confirm: GET /api/variety/preview?add={id}&date={date}
+ *   Confirm → panel transitions to day-detail for newly filled date.
+ *
+ * Week navigation: ‹ › loads prev/next week's slots from GET /api/week-plan/{weekId±1}
+ * Default: current week. If current week fully filled, auto-advance to next week. */
+ + + + + + +
StateBorderBackgroundConfirm btn
emptydashed green-lightgreen-tint
filledsolid color-bordercolor-surface
todaysolid yellowyellow-tint
sel-empty2px green-darkgreen-tintgreen
sel-filled2px orange-darkorange-tintorange + dp-warn
+
+
+ + + +
+

LLM Implementation Notes — J2 Add to Plan (C4–C6)

+ +

1. Screens and entry points

+

C4: User knows the day, not the recipe. Entry: "+" or empty slot tap in C1. C5: Recipe card in B1 gains two quick action buttons. C6: User knows the recipe, not the day. Entry: "Zur Woche +" on C5 card. All three flows share the same backend write: PATCH /api/week-plan/{weekId}/slots/{date} { recipe_id }.

+ +

2. C4 vs J4 — different sort, same sheet pattern

+

Both use a bottom sheet over dimmed content. The difference: J4 swap suggestions are sorted effort ASC (easiest first, because mid-week changes happen under stress). C4 suggestions are sorted variety_delta DESC (best variety impact first, because this is deliberate weekly planning).

+ +

3. Panel state machine (desktop)

+

The 216px right panel can be in four states: idle (no selection) → day-detail (filled day selected in C1) → recipe-picker (C4 mode) → day-picker (C6 mode). The "×" close button always returns to the previous state. Successful pick transitions to day-detail for the affected date.

+ +

4. Tap counts

+ + +

5. Undo

+

All flows use DELETE /api/week-plan/{weekId}/slots/{date} when "Rückgängig" is tapped in the 4-second toast. Toast is dismissed on navigation. On desktop, no toast for C4 (panel shows result immediately with Swap option in the day-detail view).

+ +

6. C5 "Jetzt kochen" — no back-stack issue

+

Navigating to /cook/{recipeId} from the recipe list is a standard route push. The recipe list is available via the back button or bottom nav. Do not open cook mode in a sheet or modal — it is a full-screen experience per J3 spec.

+
+ +
+ +