3 Design-Variationen · Route: /planner/variety
Zwei Kernprobleme werden adressiert: (1) Warnungen zeigen aktuell Wochentag-Kürzel ("MON, WED")
statt Rezeptnamen — rein frontend-seitig lösbar über weekPlan.slots-Mapping.
(2) Es gibt keine Swap-Aktion direkt aus den Warnungen heraus. Das Protein-Score-Problem
für vegetarische Haushalte ist ein Backend-Thema und separat zu behandeln.
Die aktuelle Formel proteinDiversity = 10 − repeats × 2 bestraft vegetarische
Proteinquellen (Tofu, Linsen, Ei) stärker als in omnivoren Haushalten üblich.
Frontend-seitig ändert sich das Label "Protein-Vielfalt" ggf. zu "Quellen-Vielfalt" sobald
das Backend die Score-Gewichtung anpasst. Bis dahin: keine Änderung an ScoreBreakdownList.
tagRepeat.days[] → weekPlan.slots.find(s => s.dayOfWeek === day) → recipe.name/planner?week={weekStart}&swap={slotId} — öffnet RecipePicker für den betreffenden SlotdayOfWeek-MappingVarietyWarningCards.svelte + variety.ts anpassen; Rest der Seite bleibt<details>-Element — zugänglich, kein JavaScript nötigVarietyWarningCards grundlegend neu strukturieren+page.svelte Struktur und alle beteiligten Komponenten müssen neu aufgebaut werdenGilt für alle drei Variationen. Implementierungs-Details werden nach Variantenwahl konkretisiert.
/* spec:rules — Variety Page Rework (alle Variationen)
*
* RECIPE NAME MAPPING (frontend, no backend change)
* Source: weekPlan.slots[] → { dayOfWeek: "MON"|"TUE"|..., recipe: { id, name } }
* tagRepeats[].days[] contains dayOfWeek keys (e.g. "MON")
* slotsByDay = Object.fromEntries(weekPlan.slots.map(s => [s.dayOfWeek, s]))
* recipeName = slotsByDay[day]?.recipe?.name ?? day
* slotId = slotsByDay[day]?.id
*
* SWAP NAVIGATION
* "Tauschen" button href: /planner?week={weekStart}&swap={slotId}
* weekStart available in page data
* slotId from weekPlan.slots mapping above
* Opens RecipePicker for that slot (existing functionality in planner page)
*
* DAY LABEL MAPPING (for display)
* MON → "Montag" TUE → "Dienstag" WED → "Mittwoch" THU → "Donnerstag"
* FRI → "Freitag" SAT → "Samstag" SUN → "Sonntag"
* Short: Mo, Di, Mi, Do, Fr, Sa, So
*
* EMPTY SLOT HANDLING
* If slotsByDay[day] is undefined: show day key only, no swap button
* This can happen if slot was deleted since varietyScore was computed
*
* PROTEIN SCORE — VEGETARIAN NOTE
* Label "Protein-Vielfalt" in ScoreBreakdownList may change to "Quellen-Vielfalt"
* pending backend decision on scoring weight adjustment.
* No frontend change required until backend ships the updated score.
*
* VARIATION-SPECIFIC
* V1: Modify VarietyWarningCards + Warning type (add slots: { day, recipeName, slotId }[])
* computeWarnings() now returns slots[] instead of string days[]
* V2: Restructure VarietyWarningCards to ActionRows; VarietyScoreHero → compact variant
* for sub-scores (no JS needed)
* V3: Replace protein grid with full week grid (recipe names); add side panel component
* Mobile: tab switcher (Übersicht | Hinweise) using $state activeTab
*/
| Property | Value | Notes |
|---|---|---|
| Shared: Recipe Mapping | ||
| data-source | weekPlan.slots[].dayOfWeek + recipe | already in page data |
| swap-url | /planner?week={weekStart}&swap={slotId} | RecipePicker pre-selects slot |
| day-long | MON→Montag, TUE→Dienstag… | for V2 display |
| day-short | MON→Mo, TUE→Di… | for V1 pills + V3 grid |
| V1 Recipe Pills | ||
| pill-padding | 5px 10px 5px 12px | left more for text |
| swap-btn-size | 22×22px, border-radius 50% | within pill |
| pill-bg | white, border --yellow-light | on yellow-tint card |
| V2 Action Rows | ||
| score-compact-height | ~64px | replaces 180px hero |
| details-summary | native <details>, no JS | sub-scores hidden by default |
| recipe-row-bg | --color-subtle | within white action card |
| V3 Week Grid | ||
| slot-height | 52px min | enough for 2-line recipe name |
| warn-slot-ring | 2px solid --yellow + yellow-tint bg | problem indicator |
| selected-slot-ring | 2px solid --green-dark | active selection |
| panel-width | 280px | fixed, right side |
| mobile-tab-active-bg | --green-dark | selected tab button |