docs(specs): add J9 variety score config user journey and variety page rework spec
- Adds J9 (Configure variety score) to userjourneys.html — new journey for tuning the algorithm per household dietary context (e.g. disabling protein penalties for vegetarian households); introduces screen E4 (Variety settings) - Adds specs/frontend/variety-page-rework.html with 3 design variations for the /planner/variety page rework: recipe-name pills, action rows (recommended), and week-grid with side panel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
841
specs/frontend/variety-page-rework.html
Normal file
841
specs/frontend/variety-page-rework.html
Normal file
@@ -0,0 +1,841 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Variety Page Rework · 3 Variationen</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500&family=DM+Sans:wght@300;400;500;600&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet"/>
|
||||
<!--
|
||||
spec:agent
|
||||
document: /planner/variety — Variety Page Rework, 3 Variationen
|
||||
version: 1.0
|
||||
journey: J4 Swap (adjacent)
|
||||
route: /planner/variety
|
||||
screen: C2
|
||||
variations: V1 Recipe Pills | V2 Action Rows | V3 Week Grid
|
||||
last-updated: 2026-04-09
|
||||
|
||||
PROBLEMS ADDRESSED:
|
||||
1. Warnings show day abbreviations ("MON, WED") — replace with recipe names
|
||||
2. No swap action reachable from warnings — add inline swap CTA per recipe
|
||||
3. Protein score is meat-centric for vegetarian households (backend concern, noted below)
|
||||
|
||||
FRONTEND-ONLY CHANGE (no backend schema changes required for items 1+2):
|
||||
weekPlan.slots has { dayOfWeek: "MON", recipe: { id, name } }
|
||||
tagRepeats.days[] contains day keys matching dayOfWeek
|
||||
→ build slotsByDay map frontend-side, look up recipeName + slotId per day
|
||||
→ swap CTA links to /planner?week={weekStart}&swap={slotId}
|
||||
|
||||
PROTEIN SCORE — VEGETARIAN HOUSEHOLDS (backend concern, TBD):
|
||||
Current: proteinDiversity = 10 - proteinRepeats * 2
|
||||
Problem: vegetarian protein sources (Tofu, Linsen, Ei) may repeat more than
|
||||
omnivore households; penalty of -2 per repeat is calibrated for meat variety.
|
||||
Backend discussed: tag filtering or weight adjustment needed.
|
||||
Frontend impact: if backend changes tagRepeats to exclude non-meat or adjusts score,
|
||||
the frontend ScoreBreakdownList label "Protein-Vielfalt" may need renaming.
|
||||
Until resolved: the rework does NOT change protein score display — only warnings.
|
||||
-->
|
||||
<style>
|
||||
:root {
|
||||
--color-page: #FAFAF7;
|
||||
--color-surface: #F5F4EE;
|
||||
--color-subtle: #EDECEA;
|
||||
--color-border: #D8D7D0;
|
||||
--color-text-muted: #6B6A63;
|
||||
--color-text: #1C1C18;
|
||||
--green-tint: #E8F5EA;
|
||||
--green-light: #AEDCB0;
|
||||
--green: #3D8C4A;
|
||||
--green-dark: #2E6E39;
|
||||
--yellow-tint: #FDF6D8;
|
||||
--yellow-light: #F9E08A;
|
||||
--yellow: #E8B400;
|
||||
--yellow-text: #8A6800;
|
||||
--color-error: #DC4C3E;
|
||||
--blue-tint: #E6F1FB;
|
||||
--blue: #185FA5;
|
||||
--font-display: 'Fraunces', Georgia, serif;
|
||||
--font-sans: 'DM Sans', system-ui, sans-serif;
|
||||
--font-mono: 'DM Mono', monospace;
|
||||
--radius-sm: 4px; --radius-md: 6px; --radius-lg: 10px; --radius-xl: 16px; --radius-full: 9999px;
|
||||
--shadow-card: 0 1px 3px rgba(28,28,24,.06), 0 1px 2px rgba(28,28,24,.04);
|
||||
--shadow-raised: 0 4px 12px rgba(28,28,24,.10), 0 2px 4px rgba(28,28,24,.05);
|
||||
}
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family: var(--font-sans); background: var(--color-page); color: var(--color-text); font-size: 14px; line-height: 1.6; }
|
||||
|
||||
.doc { max-width: 1040px; margin: 0 auto; padding: 48px 40px 96px; }
|
||||
.doc-header { padding-bottom: 28px; border-bottom: 1px solid var(--color-border); margin-bottom: 32px; display: flex; justify-content: space-between; align-items: flex-end; }
|
||||
.doc-header h1 { font-family: var(--font-display); font-size: 26px; font-weight: 500; letter-spacing: -0.02em; }
|
||||
.doc-header p { font-size: 13px; color: var(--color-text-muted); margin-top: 4px; }
|
||||
.doc-meta { font-family: var(--font-mono); font-size: 11px; color: var(--color-text-muted); text-align: right; line-height: 1.9; }
|
||||
.intro { font-size: 14px; line-height: 1.75; max-width: 680px; margin-bottom: 16px; }
|
||||
.section-label { font-size: 10px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-text-muted); padding-bottom: 10px; border-bottom: 1px solid var(--color-border); margin-bottom: 32px; margin-top: 56px; }
|
||||
|
||||
/* Notice box */
|
||||
.notice { background: var(--yellow-tint); border: 1px solid var(--yellow-light); border-radius: var(--radius-lg); padding: 14px 18px; margin-bottom: 40px; }
|
||||
.notice h3 { font-size: 12px; font-weight: 600; color: var(--yellow-text); margin-bottom: 4px; }
|
||||
.notice p { font-size: 12px; color: var(--yellow-text); line-height: 1.6; }
|
||||
.notice code { font-family: var(--font-mono); background: rgba(0,0,0,.07); padding: 1px 4px; border-radius: 3px; }
|
||||
|
||||
/* Variation sections */
|
||||
.variation { margin-bottom: 72px; }
|
||||
.var-header { display: flex; align-items: flex-start; gap: 20px; margin-bottom: 24px; }
|
||||
.var-num { font-family: var(--font-display); font-size: 44px; font-weight: 300; color: var(--yellow-light); line-height: 1; flex-shrink: 0; width: 56px; letter-spacing: -0.03em; }
|
||||
.var-meta { flex: 1; padding-top: 4px; }
|
||||
.var-title { font-size: 18px; font-weight: 500; letter-spacing: -0.01em; margin-bottom: 4px; }
|
||||
.var-desc { font-size: 13px; color: var(--color-text-muted); line-height: 1.6; max-width: 540px; }
|
||||
.var-tag { font-size: 10px; font-weight: 500; letter-spacing: 0.07em; text-transform: uppercase; padding: 3px 8px; border-radius: var(--radius-sm); background: var(--color-subtle); color: var(--color-text-muted); margin-top: 6px; display: inline-block; }
|
||||
.var-tag.rec { background: var(--green-tint); color: var(--green-dark); }
|
||||
.var-tag.amb { background: var(--blue-tint); color: var(--blue); }
|
||||
|
||||
/* Preview containers */
|
||||
.preview-pair { display: flex; gap: 24px; align-items: flex-start; margin-bottom: 20px; }
|
||||
.preview-d-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
|
||||
.preview-m-wrap { flex-shrink: 0; display: flex; flex-direction: column; gap: 6px; }
|
||||
.preview-label { font-size: 9px; font-weight: 500; letter-spacing: 0.09em; text-transform: uppercase; color: var(--color-text-muted); }
|
||||
.preview-d-clip { height: 340px; overflow: hidden; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-page); }
|
||||
.preview-d-scale { transform: scale(0.5); transform-origin: top left; width: 200%; }
|
||||
.preview-m-clip { width: 196px; height: 340px; overflow: hidden; border: 1.5px solid var(--color-border); border-radius: 24px; background: var(--color-page); }
|
||||
.preview-m-scale { transform: scale(0.5); transform-origin: top left; width: 200%; }
|
||||
|
||||
/* Notes */
|
||||
.notes { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: 14px 18px; }
|
||||
.notes-label { font-size: 9px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-text-muted); margin-bottom: 8px; }
|
||||
.notes ul { list-style: none; display: flex; flex-direction: column; gap: 4px; }
|
||||
.notes li { font-size: 12px; color: var(--color-text-muted); line-height: 1.5; display: flex; align-items: flex-start; gap: 8px; }
|
||||
.notes li::before { content: '→'; color: var(--green); font-weight: 500; flex-shrink: 0; }
|
||||
|
||||
/* ── AppShell chrome ── */
|
||||
.shell { display: flex; background: var(--color-page); font-family: var(--font-sans); overflow: hidden; }
|
||||
.sidebar { width: 224px; min-width: 224px; background: white; border-right: 1px solid var(--color-border); display: flex; flex-direction: column; }
|
||||
.sidebar-brand { padding: 14px 18px; border-bottom: 1px solid var(--color-border); }
|
||||
.sidebar-brand-row { display: flex; align-items: center; gap: 8px; }
|
||||
.sidebar-logo { width: 22px; height: 22px; background: var(--green); border-radius: var(--radius-sm); }
|
||||
.sidebar-app { font-family: var(--font-display); font-size: 15px; font-weight: 500; }
|
||||
.sidebar-household { font-size: 10px; color: var(--color-text-muted); margin-top: 1px; }
|
||||
.sidebar-nav { flex: 1; padding: 4px 8px; }
|
||||
.sidebar-group-label { font-size: 8px; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-text-muted); padding: 16px 12px 4px; }
|
||||
.sidebar-item { display: flex; align-items: center; gap: 8px; padding: 7px 12px; border-radius: var(--radius-md); font-size: 13px; color: var(--color-text); text-decoration: none; }
|
||||
.sidebar-item.active { background: var(--green-tint); color: var(--green-dark); font-weight: 500; }
|
||||
.sidebar-icon { width: 20px; text-align: center; font-size: 16px; }
|
||||
|
||||
/* Page chrome */
|
||||
.topbar { display: flex; align-items: center; gap: 12px; border-bottom: 1px solid var(--color-border); background: var(--color-page); padding: 14px 24px; }
|
||||
.topbar-back { font-size: 13px; color: var(--color-text-muted); text-decoration: none; }
|
||||
.topbar-sep { font-size: 13px; color: var(--color-text-muted); }
|
||||
.topbar-title { font-family: var(--font-display); font-size: 20px; font-weight: 300; }
|
||||
.main { flex: 1; padding: 24px; overflow: hidden; }
|
||||
|
||||
/* Score hero (shared across variations) */
|
||||
.score-hero { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin-bottom: 20px; }
|
||||
.score-num { font-family: var(--font-display); font-size: 64px; font-weight: 300; line-height: 1; letter-spacing: -0.03em; }
|
||||
.score-denom { font-family: var(--font-display); font-size: 24px; font-weight: 300; color: var(--color-text-muted); }
|
||||
.score-label { font-size: 13px; font-weight: 500; color: var(--yellow-text); }
|
||||
.score-bar { height: 6px; background: var(--color-subtle); border-radius: var(--radius-full); overflow: hidden; width: 160px; }
|
||||
.score-fill { height: 100%; border-radius: var(--radius-full); }
|
||||
.score-fill.good { background: var(--green-dark); }
|
||||
.score-fill.warn { background: var(--yellow); }
|
||||
|
||||
/* Sub-scores */
|
||||
.sub-scores { border: 1px solid var(--color-border); border-radius: var(--radius-lg); overflow: hidden; background: white; margin-bottom: 20px; }
|
||||
.sub-row { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-bottom: 1px solid var(--color-border); }
|
||||
.sub-row:last-child { border-bottom: none; }
|
||||
.sub-label { font-size: 13px; }
|
||||
.sub-val { font-size: 13px; font-weight: 500; color: var(--yellow-text); }
|
||||
.sub-val.ok { color: var(--green-dark); }
|
||||
|
||||
/* Section heading */
|
||||
.section-hd { font-size: 10px; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-text-muted); margin-bottom: 10px; }
|
||||
|
||||
/* ────────────────────────────────────────────
|
||||
V1: Recipe Pill Warning Cards
|
||||
──────────────────────────────────────────── */
|
||||
|
||||
.warn-card { background: var(--yellow-tint); border: 1px solid var(--yellow-light); border-radius: var(--radius-lg); padding: 14px 16px; margin-bottom: 10px; }
|
||||
.warn-title { font-size: 13px; font-weight: 500; color: var(--yellow-text); margin-bottom: 8px; }
|
||||
.pill-row { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.recipe-pill { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px 5px 12px; border-radius: var(--radius-full); background: white; border: 1px solid var(--yellow-light); font-size: 12px; font-weight: 500; color: var(--color-text); }
|
||||
.recipe-pill-day { font-size: 10px; color: var(--color-text-muted); font-weight: 400; }
|
||||
.pill-swap-btn { width: 22px; height: 22px; border-radius: var(--radius-full); background: var(--color-subtle); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 11px; color: var(--color-text-muted); flex-shrink: 0; }
|
||||
.pill-swap-btn:hover { background: var(--green-tint); color: var(--green-dark); }
|
||||
|
||||
/* ────────────────────────────────────────────
|
||||
V2: Action Rows
|
||||
──────────────────────────────────────────── */
|
||||
|
||||
/* Compact score header for V2 */
|
||||
.score-compact { display: flex; align-items: center; gap: 14px; padding: 14px 20px; background: white; border: 1px solid var(--color-border); border-radius: var(--radius-lg); margin-bottom: 20px; }
|
||||
.score-compact-num { font-family: var(--font-display); font-size: 36px; font-weight: 300; line-height: 1; }
|
||||
.score-compact-denom { font-family: var(--font-display); font-size: 16px; font-weight: 300; color: var(--color-text-muted); }
|
||||
.score-compact-right { flex: 1; }
|
||||
.score-compact-label { font-size: 12px; font-weight: 500; color: var(--yellow-text); margin-bottom: 4px; }
|
||||
.score-compact-bar { height: 5px; background: var(--color-subtle); border-radius: var(--radius-full); overflow: hidden; }
|
||||
.score-compact-fill { height: 100%; border-radius: var(--radius-full); background: var(--yellow); }
|
||||
|
||||
.action-row { display: flex; align-items: flex-start; gap: 14px; padding: 14px 16px; background: white; border: 1px solid var(--color-border); border-radius: var(--radius-lg); margin-bottom: 8px; }
|
||||
.action-icon { width: 32px; height: 32px; border-radius: var(--radius-md); background: var(--yellow-tint); display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; margin-top: 1px; }
|
||||
.action-body { flex: 1; }
|
||||
.action-title { font-size: 13px; font-weight: 500; margin-bottom: 6px; }
|
||||
.action-recipe-row { display: flex; align-items: center; justify-content: space-between; padding: 6px 10px; background: var(--color-subtle); border-radius: var(--radius-md); margin-bottom: 4px; }
|
||||
.action-recipe-name { font-size: 12px; font-weight: 500; }
|
||||
.action-recipe-day { font-size: 10px; color: var(--color-text-muted); margin-left: 4px; }
|
||||
.btn-swap { padding: 4px 10px; background: white; border: 1px solid var(--color-border); border-radius: var(--radius-md); font-size: 11px; font-weight: 500; color: var(--color-text-muted); cursor: pointer; white-space: nowrap; }
|
||||
.btn-swap:hover { border-color: var(--green-light); color: var(--green-dark); }
|
||||
|
||||
/* ────────────────────────────────────────────
|
||||
V3: Week Grid + Side Panel
|
||||
──────────────────────────────────────────── */
|
||||
|
||||
.v3-layout { display: flex; gap: 0; height: 680px; }
|
||||
.v3-main { flex: 1; padding: 24px; overflow-y: auto; }
|
||||
.v3-panel { width: 280px; min-width: 280px; border-left: 1px solid var(--color-border); background: white; padding: 20px; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
|
||||
.week-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; margin-bottom: 20px; }
|
||||
.day-col { display: flex; flex-direction: column; gap: 4px; }
|
||||
.day-header { font-size: 10px; font-weight: 500; color: var(--color-text-muted); text-align: center; padding-bottom: 4px; }
|
||||
.recipe-slot { border-radius: var(--radius-md); border: 1px solid var(--color-border); background: white; padding: 6px 5px; min-height: 52px; font-size: 10px; font-weight: 500; text-align: center; display: flex; align-items: center; justify-content: center; cursor: pointer; line-height: 1.3; }
|
||||
.recipe-slot.warn { border-color: var(--yellow); background: var(--yellow-tint); color: var(--yellow-text); box-shadow: 0 0 0 2px rgba(232,180,0,.25); }
|
||||
.recipe-slot.warn:hover { box-shadow: 0 0 0 2px var(--yellow); }
|
||||
.recipe-slot.selected { border-color: var(--green-dark); box-shadow: 0 0 0 2px var(--green-light); }
|
||||
.recipe-slot.empty { background: var(--color-subtle); color: var(--color-text-muted); font-weight: 400; font-size: 9px; }
|
||||
.warn-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--yellow); display: inline-block; margin-left: 3px; vertical-align: middle; }
|
||||
|
||||
.panel-score { display: flex; align-items: baseline; gap: 4px; margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--color-border); }
|
||||
.panel-score-num { font-family: var(--font-display); font-size: 48px; font-weight: 300; line-height: 1; }
|
||||
.panel-score-denom { font-family: var(--font-display); font-size: 18px; font-weight: 300; color: var(--color-text-muted); }
|
||||
.panel-warn-title { font-size: 13px; font-weight: 500; margin-bottom: 4px; }
|
||||
.panel-warn-desc { font-size: 12px; color: var(--color-text-muted); margin-bottom: 14px; line-height: 1.5; }
|
||||
.panel-recipe-entry { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; background: var(--color-subtle); border-radius: var(--radius-md); margin-bottom: 6px; }
|
||||
.panel-recipe-name { font-size: 12px; font-weight: 500; }
|
||||
.panel-recipe-day { font-size: 10px; color: var(--color-text-muted); }
|
||||
.btn-swap-primary { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 9px 16px; background: var(--green-dark); color: white; border-radius: var(--radius-md); font-size: 12px; font-weight: 500; border: none; cursor: pointer; width: 100%; margin-top: 12px; }
|
||||
.btn-swap-primary:hover { background: var(--green); }
|
||||
.panel-hint { font-size: 11px; color: var(--color-text-muted); margin-top: 8px; }
|
||||
.panel-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 8px; }
|
||||
.panel-empty-icon { font-size: 24px; opacity: .4; }
|
||||
.panel-empty-text { font-size: 12px; color: var(--color-text-muted); }
|
||||
|
||||
/* ── Mobile ── */
|
||||
.m-shell { display: flex; flex-direction: column; background: var(--color-page); }
|
||||
.m-topbar { display: flex; align-items: center; gap: 10px; border-bottom: 1px solid var(--color-border); background: var(--color-page); padding: 12px 16px; position: sticky; top: 0; z-index: 10; }
|
||||
.m-back { font-size: 20px; color: var(--color-text-muted); }
|
||||
.m-title { font-family: var(--font-display); font-size: 16px; font-weight: 300; }
|
||||
.m-content { flex: 1; padding: 16px; overflow-y: auto; }
|
||||
.m-score-hero { display: flex; align-items: baseline; gap: 4px; margin-bottom: 16px; }
|
||||
.m-score-num { font-family: var(--font-display); font-size: 52px; font-weight: 300; line-height: 1; }
|
||||
.m-score-denom { font-family: var(--font-display); font-size: 20px; font-weight: 300; color: var(--color-text-muted); }
|
||||
.m-score-bar { height: 5px; background: var(--color-subtle); border-radius: var(--radius-full); overflow: hidden; margin-bottom: 4px; }
|
||||
.m-score-fill { height: 100%; border-radius: var(--radius-full); background: var(--yellow); }
|
||||
.m-score-label { font-size: 12px; font-weight: 500; color: var(--yellow-text); margin-bottom: 16px; }
|
||||
.m-section-hd { font-size: 9px; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-text-muted); margin-bottom: 8px; }
|
||||
.m-warn-card { background: var(--yellow-tint); border: 1px solid var(--yellow-light); border-radius: var(--radius-lg); padding: 12px 14px; margin-bottom: 8px; }
|
||||
.m-warn-title { font-size: 12px; font-weight: 500; color: var(--yellow-text); margin-bottom: 6px; }
|
||||
.m-pill-row { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.m-pill { display: inline-flex; align-items: center; gap: 5px; padding: 4px 8px 4px 10px; background: white; border: 1px solid var(--yellow-light); border-radius: var(--radius-full); font-size: 11px; font-weight: 500; }
|
||||
.m-pill-swap { width: 18px; height: 18px; border-radius: 50%; background: var(--color-subtle); display: flex; align-items: center; justify-content: center; font-size: 10px; flex-shrink: 0; }
|
||||
.m-tabbar { display: flex; border-top: 1px solid var(--color-border); background: white; }
|
||||
.m-tab { flex: 1; display: flex; flex-direction: column; align-items: center; padding: 8px 4px 4px; font-size: 10px; color: var(--color-text-muted); gap: 2px; }
|
||||
.m-tab.active { color: var(--green-dark); }
|
||||
.m-tab-icon { font-size: 20px; }
|
||||
|
||||
/* Agent section */
|
||||
.agent-section { background: var(--color-text); color: #E8E8E2; padding: 40px 48px; margin-top: 64px; }
|
||||
.agent-section h2 { font-size: 10px; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: #6B6A63; margin-bottom: 4px; }
|
||||
.agent-section > p { font-size: 13px; color: #9A9990; margin-bottom: 28px; line-height: 1.6; max-width: 640px; }
|
||||
.spec-comment { font-family: var(--font-mono); font-size: 11px; color: #3A3A36; margin-bottom: 32px; line-height: 1.9; white-space: pre-wrap; }
|
||||
.agent-table { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: 11px; margin-bottom: 40px; }
|
||||
.agent-table thead tr { border-bottom: 1px solid #2A2A26; }
|
||||
.agent-table th { text-align: left; padding: 8px 14px; font-size: 9px; font-weight: 500; letter-spacing: 0.09em; text-transform: uppercase; color: #5A5A55; font-family: var(--font-sans); }
|
||||
.agent-table td { padding: 9px 14px; border-bottom: 1px solid #1E1E1A; vertical-align: top; line-height: 1.5; }
|
||||
.agent-table tr:last-child td { border-bottom: none; }
|
||||
.agent-table td:first-child { color: #7A7A72; white-space: nowrap; }
|
||||
.agent-table td:nth-child(2) { color: #E8E8E2; font-weight: 500; }
|
||||
.agent-table td:nth-child(3) { color: #5A5A55; }
|
||||
.group-row td { padding-top: 20px; font-family: var(--font-sans); font-size: 9px; font-weight: 500; letter-spacing: 0.09em; text-transform: uppercase; color: #3A3A36; border-bottom: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="doc">
|
||||
|
||||
<div class="doc-header">
|
||||
<div>
|
||||
<h1>Variety Page — Rework</h1>
|
||||
<p>3 Design-Variationen · Route: <code>/planner/variety</code></p>
|
||||
</div>
|
||||
<div class="doc-meta">
|
||||
screen: C2<br/>
|
||||
journey: J4<br/>
|
||||
version: 1.0<br/>
|
||||
date: 2026-04-09
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="intro">
|
||||
Zwei Kernprobleme werden adressiert: (1) Warnungen zeigen aktuell Wochentag-Kürzel ("MON, WED")
|
||||
statt Rezeptnamen — rein frontend-seitig lösbar über <code>weekPlan.slots</code>-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.
|
||||
</p>
|
||||
|
||||
<div class="notice">
|
||||
<h3>Protein-Score: Vegetarische Haushalte — Backend TBD</h3>
|
||||
<p>
|
||||
Die aktuelle Formel <code>proteinDiversity = 10 − repeats × 2</code> 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 <code>ScoreBreakdownList</code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<div class="section-label">V1 — Rezept-Pills in Warnkarten</div>
|
||||
|
||||
<div class="variation">
|
||||
<div class="var-header">
|
||||
<div class="var-num">1</div>
|
||||
<div class="var-meta">
|
||||
<div class="var-title">Rezept-Pills in Warnkarten</div>
|
||||
<div class="var-desc">Minimale Änderung an der bestehenden Seitenstruktur. Warnkarten zeigen statt "MON, WED" konkrete Rezept-Pills mit Tauschen-Button. Seitenaufbau und Score-Hero bleiben identisch.</div>
|
||||
<span class="var-tag">Vertraut · Geringer Aufwand</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-pair">
|
||||
<div class="preview-d-wrap">
|
||||
<div class="preview-label">Desktop</div>
|
||||
<div class="preview-d-clip">
|
||||
<div class="preview-d-scale">
|
||||
<div class="shell" style="min-height:680px;">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-brand"><div class="sidebar-brand-row"><div class="sidebar-logo"></div><span class="sidebar-app">Mealplan</span></div><div class="sidebar-household">Familie Raddatz</div></div>
|
||||
<div class="sidebar-nav">
|
||||
<div class="sidebar-group-label">Plan</div>
|
||||
<a class="sidebar-item active" href="#"><span class="sidebar-icon">📅</span>Planer</a>
|
||||
<a class="sidebar-item" href="#"><span class="sidebar-icon">🍽</span>Rezepte</a>
|
||||
<a class="sidebar-item" href="#"><span class="sidebar-icon">🛒</span>Einkauf</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">
|
||||
<div class="topbar">
|
||||
<a class="topbar-back" href="#">Planer</a>
|
||||
<span class="topbar-sep">/</span>
|
||||
<span class="topbar-title">Abwechslungs-Analyse</span>
|
||||
</div>
|
||||
<div class="main" style="display:flex;gap:32px;align-items:flex-start;overflow-y:auto;">
|
||||
<!-- Left -->
|
||||
<div style="flex:1;">
|
||||
<div class="score-hero">
|
||||
<div><span class="score-num" style="color:var(--yellow-text);">6.5</span><span class="score-denom">/10</span></div>
|
||||
<div class="score-bar" style="width:200px;"><div class="score-fill warn" style="width:65%;"></div></div>
|
||||
<div class="score-label">Verbesserbar</div>
|
||||
</div>
|
||||
<div class="section-hd">Bewertung im Detail</div>
|
||||
<div class="sub-scores">
|
||||
<div class="sub-row"><span class="sub-label">Quellen-Vielfalt</span><span class="sub-val">6/10</span></div>
|
||||
<div class="sub-row"><span class="sub-label">Zutaten-Überlappung</span><span class="sub-val ok">8/10</span></div>
|
||||
<div class="sub-row"><span class="sub-label">Aufwandsbalance</span><span class="sub-val ok">9/10</span></div>
|
||||
</div>
|
||||
<!-- Warnings with recipe pills -->
|
||||
<div class="section-hd" style="margin-top:20px;">Hinweise</div>
|
||||
|
||||
<div class="warn-card">
|
||||
<div class="warn-title">Tofu mehrfach diese Woche</div>
|
||||
<div class="pill-row">
|
||||
<span class="recipe-pill"><span class="recipe-pill-day">Mo</span>Tofu-Curry<button class="pill-swap-btn">↔</button></span>
|
||||
<span class="recipe-pill"><span class="recipe-pill-day">Mi</span>Tofu-Bowl<button class="pill-swap-btn">↔</button></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="warn-card">
|
||||
<div class="warn-title">Linsen in mehreren Gerichten</div>
|
||||
<div class="pill-row">
|
||||
<span class="recipe-pill"><span class="recipe-pill-day">Di</span>Linsen-Suppe<button class="pill-swap-btn">↔</button></span>
|
||||
<span class="recipe-pill"><span class="recipe-pill-day">Fr</span>Linsen-Dal<button class="pill-swap-btn">↔</button></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right -->
|
||||
<div style="width:280px;flex-shrink:0;">
|
||||
<div class="section-hd">Quellen-Verteilung</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:5px;margin-bottom:16px;">
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:3px;"><span style="font-size:9px;color:var(--color-text-muted);">Mo</span><div style="width:100%;height:40px;background:var(--yellow-tint);border:2px solid var(--yellow);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:600;color:var(--yellow-text);">TOF</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:3px;"><span style="font-size:9px;color:var(--color-text-muted);">Di</span><div style="width:100%;height:40px;background:var(--green-tint);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:600;color:var(--green-dark);">LIN</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:3px;"><span style="font-size:9px;color:var(--color-text-muted);">Mi</span><div style="width:100%;height:40px;background:var(--yellow-tint);border:2px solid var(--yellow);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:600;color:var(--yellow-text);">TOF</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:3px;"><span style="font-size:9px;color:var(--color-text-muted);">Do</span><div style="width:100%;height:40px;background:var(--green-tint);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:600;color:var(--green-dark);">GEM</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:3px;"><span style="font-size:9px;color:var(--color-text-muted);">Fr</span><div style="width:100%;height:40px;background:var(--yellow-tint);border:2px solid var(--yellow);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:600;color:var(--yellow-text);">LIN</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:3px;"><span style="font-size:9px;color:var(--color-text-muted);">Sa</span><div style="width:100%;height:40px;background:var(--color-subtle);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--color-text-muted);">—</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:3px;"><span style="font-size:9px;color:var(--color-text-muted);">So</span><div style="width:100%;height:40px;background:var(--color-subtle);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--color-text-muted);">—</div></div>
|
||||
</div>
|
||||
<div class="section-hd">Aufwandsverteilung</div>
|
||||
<div style="display:flex;height:16px;border-radius:var(--radius-full);overflow:hidden;gap:2px;">
|
||||
<div style="flex:3;background:var(--green-dark);"></div>
|
||||
<div style="flex:2;background:var(--yellow);"></div>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;margin-top:6px;font-size:10px;color:var(--color-text-muted);">
|
||||
<span>Einfach ×3</span><span>Mittel ×2</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-m-wrap">
|
||||
<div class="preview-label">Mobile</div>
|
||||
<div class="preview-m-clip">
|
||||
<div class="preview-m-scale">
|
||||
<div class="m-shell" style="min-height:680px;">
|
||||
<div class="m-topbar"><span class="m-back">‹</span><span class="m-title">Abwechslungs-Analyse</span></div>
|
||||
<div class="m-content">
|
||||
<div class="m-score-hero"><span class="m-score-num" style="color:var(--yellow-text);">6.5</span><span class="m-score-denom">/10</span></div>
|
||||
<div class="m-score-bar"><div class="m-score-fill" style="width:65%;"></div></div>
|
||||
<div class="m-score-label">Verbesserbar</div>
|
||||
<div class="m-section-hd">Hinweise</div>
|
||||
<div class="m-warn-card">
|
||||
<div class="m-warn-title">Tofu mehrfach diese Woche</div>
|
||||
<div class="m-pill-row">
|
||||
<span class="m-pill"><span style="font-size:9px;color:var(--color-text-muted);">Mo</span>Tofu-Curry<span class="m-pill-swap">↔</span></span>
|
||||
<span class="m-pill"><span style="font-size:9px;color:var(--color-text-muted);">Mi</span>Tofu-Bowl<span class="m-pill-swap">↔</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-warn-card">
|
||||
<div class="m-warn-title">Linsen in mehreren Gerichten</div>
|
||||
<div class="m-pill-row">
|
||||
<span class="m-pill"><span style="font-size:9px;color:var(--color-text-muted);">Di</span>Linsen-Suppe<span class="m-pill-swap">↔</span></span>
|
||||
<span class="m-pill"><span style="font-size:9px;color:var(--color-text-muted);">Fr</span>Linsen-Dal<span class="m-pill-swap">↔</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-tabbar">
|
||||
<div class="m-tab active"><div class="m-tab-icon">📅</div>Planer</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">🍽</div>Rezepte</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">🛒</div>Einkauf</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">⚙️</div>Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notes">
|
||||
<div class="notes-label">Notizen</div>
|
||||
<ul>
|
||||
<li>Kein Backend-Change nötig. Frontend mappt <code>tagRepeat.days[]</code> → <code>weekPlan.slots.find(s => s.dayOfWeek === day)</code> → <code>recipe.name</code></li>
|
||||
<li>Pill-Swap-Button (↔): navigiert zu <code>/planner?week={weekStart}&swap={slotId}</code> — öffnet RecipePicker für den betreffenden Slot</li>
|
||||
<li>Pill-Label links: Wochentag-Kürzel (Mo, Di, …) aus <code>dayOfWeek</code>-Mapping</li>
|
||||
<li>Wenn ein Slot leer ist (Rezept wurde bereits entfernt): Pill zeigt nur den Wochentag, kein Swap-Button</li>
|
||||
<li>Geringe Änderung: nur <code>VarietyWarningCards.svelte</code> + <code>variety.ts</code> anpassen; Rest der Seite bleibt</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<div class="section-label">V2 — Aktions-Zeilen (Action-first)</div>
|
||||
|
||||
<div class="variation">
|
||||
<div class="var-header">
|
||||
<div class="var-num">2</div>
|
||||
<div class="var-meta">
|
||||
<div class="var-title">Aktions-Zeilen</div>
|
||||
<div class="var-desc">Warnungen stehen oben, Score-Hero wird kompakt. Pro Warnung gibt es eine vollständige Rezept-Zeile mit Wochentag und dediziertem "Tauschen"-Button. Fokus auf sofortige Handlung statt auf Metrik-Verständnis.</div>
|
||||
<span class="var-tag rec">Empfohlen · Aktionsfokus</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-pair">
|
||||
<div class="preview-d-wrap">
|
||||
<div class="preview-label">Desktop</div>
|
||||
<div class="preview-d-clip">
|
||||
<div class="preview-d-scale">
|
||||
<div class="shell" style="min-height:680px;">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-brand"><div class="sidebar-brand-row"><div class="sidebar-logo"></div><span class="sidebar-app">Mealplan</span></div><div class="sidebar-household">Familie Raddatz</div></div>
|
||||
<div class="sidebar-nav">
|
||||
<div class="sidebar-group-label">Plan</div>
|
||||
<a class="sidebar-item active" href="#"><span class="sidebar-icon">📅</span>Planer</a>
|
||||
<a class="sidebar-item" href="#"><span class="sidebar-icon">🍽</span>Rezepte</a>
|
||||
<a class="sidebar-item" href="#"><span class="sidebar-icon">🛒</span>Einkauf</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">
|
||||
<div class="topbar">
|
||||
<a class="topbar-back" href="#">Planer</a>
|
||||
<span class="topbar-sep">/</span>
|
||||
<span class="topbar-title">Abwechslungs-Analyse</span>
|
||||
</div>
|
||||
<div class="main" style="overflow-y:auto;">
|
||||
<!-- Compact score -->
|
||||
<div class="score-compact">
|
||||
<div><span class="score-compact-num" style="color:var(--yellow-text);">6.5</span><span class="score-compact-denom">/10</span></div>
|
||||
<div class="score-compact-right">
|
||||
<div class="score-compact-label">Verbesserbar — 2 Hinweise</div>
|
||||
<div class="score-compact-bar"><div class="score-compact-fill" style="width:65%;"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;gap:24px;align-items:flex-start;">
|
||||
<div style="flex:1;">
|
||||
<div class="section-hd">Empfehlenswerte Tausche</div>
|
||||
|
||||
<!-- Action row 1 -->
|
||||
<div class="action-row">
|
||||
<div class="action-icon">🔄</div>
|
||||
<div class="action-body">
|
||||
<div class="action-title">Tofu mehrfach diese Woche</div>
|
||||
<div class="action-recipe-row">
|
||||
<span><span class="action-recipe-name">Tofu-Curry</span><span class="action-recipe-day">· Montag</span></span>
|
||||
<button class="btn-swap">Tauschen →</button>
|
||||
</div>
|
||||
<div class="action-recipe-row">
|
||||
<span><span class="action-recipe-name">Tofu-Bowl</span><span class="action-recipe-day">· Mittwoch</span></span>
|
||||
<button class="btn-swap">Tauschen →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action row 2 -->
|
||||
<div class="action-row">
|
||||
<div class="action-icon">🔄</div>
|
||||
<div class="action-body">
|
||||
<div class="action-title">Linsen in mehreren Gerichten</div>
|
||||
<div class="action-recipe-row">
|
||||
<span><span class="action-recipe-name">Linsen-Suppe</span><span class="action-recipe-day">· Dienstag</span></span>
|
||||
<button class="btn-swap">Tauschen →</button>
|
||||
</div>
|
||||
<div class="action-recipe-row">
|
||||
<span><span class="action-recipe-name">Linsen-Dal</span><span class="action-recipe-day">· Freitag</span></span>
|
||||
<button class="btn-swap">Tauschen →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible detail scores -->
|
||||
<details style="margin-top:16px;">
|
||||
<summary style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);cursor:pointer;list-style:none;padding:8px 0;">Bewertung im Detail ▾</summary>
|
||||
<div class="sub-scores" style="margin-top:10px;">
|
||||
<div class="sub-row"><span class="sub-label">Quellen-Vielfalt</span><span class="sub-val">6/10</span></div>
|
||||
<div class="sub-row"><span class="sub-label">Zutaten-Überlappung</span><span class="sub-val ok">8/10</span></div>
|
||||
<div class="sub-row"><span class="sub-label">Aufwandsbalance</span><span class="sub-val ok">9/10</span></div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div style="width:240px;flex-shrink:0;">
|
||||
<div class="section-hd">Quellen-Verteilung</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:4px;margin-bottom:12px;">
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:2px;"><span style="font-size:9px;color:var(--color-text-muted);">Mo</span><div style="width:100%;height:36px;background:var(--yellow-tint);border:2px solid var(--yellow);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:8px;font-weight:600;color:var(--yellow-text);">TOF</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:2px;"><span style="font-size:9px;color:var(--color-text-muted);">Di</span><div style="width:100%;height:36px;background:var(--green-tint);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:8px;font-weight:600;color:var(--green-dark);">LIN</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:2px;"><span style="font-size:9px;color:var(--color-text-muted);">Mi</span><div style="width:100%;height:36px;background:var(--yellow-tint);border:2px solid var(--yellow);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:8px;font-weight:600;color:var(--yellow-text);">TOF</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:2px;"><span style="font-size:9px;color:var(--color-text-muted);">Do</span><div style="width:100%;height:36px;background:var(--green-tint);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:8px;font-weight:600;color:var(--green-dark);">GEM</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:2px;"><span style="font-size:9px;color:var(--color-text-muted);">Fr</span><div style="width:100%;height:36px;background:var(--yellow-tint);border:2px solid var(--yellow);border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:8px;font-weight:600;color:var(--yellow-text);">LIN</div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:2px;"><span style="font-size:9px;color:var(--color-text-muted);">Sa</span><div style="width:100%;height:36px;background:var(--color-subtle);border-radius:3px;"></div></div>
|
||||
<div style="display:flex;flex-direction:column;align-items:center;gap:2px;"><span style="font-size:9px;color:var(--color-text-muted);">So</span><div style="width:100%;height:36px;background:var(--color-subtle);border-radius:3px;"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-m-wrap">
|
||||
<div class="preview-label">Mobile</div>
|
||||
<div class="preview-m-clip">
|
||||
<div class="preview-m-scale">
|
||||
<div class="m-shell" style="min-height:680px;">
|
||||
<div class="m-topbar"><span class="m-back">‹</span><span class="m-title">Abwechslungs-Analyse</span></div>
|
||||
<div class="m-content">
|
||||
<!-- Compact score mobile -->
|
||||
<div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:white;border:1px solid var(--color-border);border-radius:var(--radius-lg);margin-bottom:16px;">
|
||||
<div><span style="font-family:var(--font-display);font-size:32px;font-weight:300;color:var(--yellow-text);">6.5</span><span style="font-family:var(--font-display);font-size:14px;font-weight:300;color:var(--color-text-muted);">/10</span></div>
|
||||
<div style="flex:1;"><div style="font-size:11px;font-weight:500;color:var(--yellow-text);margin-bottom:4px;">Verbesserbar</div><div style="height:4px;background:var(--color-subtle);border-radius:99px;overflow:hidden;"><div style="width:65%;height:100%;background:var(--yellow);border-radius:99px;"></div></div></div>
|
||||
</div>
|
||||
<div class="m-section-hd">Empfehlenswerte Tausche</div>
|
||||
<!-- Action row mobile -->
|
||||
<div style="background:white;border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:12px;margin-bottom:8px;">
|
||||
<div style="font-size:12px;font-weight:500;margin-bottom:8px;">🔄 Tofu mehrfach diese Woche</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:6px 8px;background:var(--color-subtle);border-radius:var(--radius-md);margin-bottom:4px;"><span style="font-size:11px;font-weight:500;">Tofu-Curry <span style="color:var(--color-text-muted);font-weight:400;">Mo</span></span><button style="font-size:10px;font-weight:500;padding:3px 8px;background:white;border:1px solid var(--color-border);border-radius:var(--radius-md);color:var(--color-text-muted);">Tauschen →</button></div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:6px 8px;background:var(--color-subtle);border-radius:var(--radius-md);"><span style="font-size:11px;font-weight:500;">Tofu-Bowl <span style="color:var(--color-text-muted);font-weight:400;">Mi</span></span><button style="font-size:10px;font-weight:500;padding:3px 8px;background:white;border:1px solid var(--color-border);border-radius:var(--radius-md);color:var(--color-text-muted);">Tauschen →</button></div>
|
||||
</div>
|
||||
<div style="background:white;border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:12px;margin-bottom:8px;">
|
||||
<div style="font-size:12px;font-weight:500;margin-bottom:8px;">🔄 Linsen in mehreren Gerichten</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:6px 8px;background:var(--color-subtle);border-radius:var(--radius-md);margin-bottom:4px;"><span style="font-size:11px;font-weight:500;">Linsen-Suppe <span style="color:var(--color-text-muted);font-weight:400;">Di</span></span><button style="font-size:10px;font-weight:500;padding:3px 8px;background:white;border:1px solid var(--color-border);border-radius:var(--radius-md);color:var(--color-text-muted);">Tauschen →</button></div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:6px 8px;background:var(--color-subtle);border-radius:var(--radius-md);"><span style="font-size:11px;font-weight:500;">Linsen-Dal <span style="color:var(--color-text-muted);font-weight:400;">Fr</span></span><button style="font-size:10px;font-weight:500;padding:3px 8px;background:white;border:1px solid var(--color-border);border-radius:var(--radius-md);color:var(--color-text-muted);">Tauschen →</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-tabbar">
|
||||
<div class="m-tab active"><div class="m-tab-icon">📅</div>Planer</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">🍽</div>Rezepte</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">🛒</div>Einkauf</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">⚙️</div>Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notes">
|
||||
<div class="notes-label">Notizen</div>
|
||||
<ul>
|
||||
<li>Score-Hero wird kompakt: Zahl + Label + Balken in einer horizontal komprimierten Leiste oben</li>
|
||||
<li>Sub-Scores in aufklappbarem <code><details></code>-Element — zugänglich, kein JavaScript nötig</li>
|
||||
<li>Jeder "Tauschen"-Button navigiert zum Planer mit dem spezifischen Slot vorselektiert</li>
|
||||
<li>Wochentag als ausgeschriebenes Wort ("Montag") — nicht Kürzel — für bessere Lesbarkeit</li>
|
||||
<li>Mobile: Score-Hero bleibt kompakt oben, Action-Rows nehmen den Hauptraum ein</li>
|
||||
<li>Größerer Aufwand als V1: <code>VarietyWarningCards</code> grundlegend neu strukturieren</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<div class="section-label">V3 — Wochenraster mit Kontext-Panel</div>
|
||||
|
||||
<div class="variation">
|
||||
<div class="var-header">
|
||||
<div class="var-num">3</div>
|
||||
<div class="var-meta">
|
||||
<div class="var-title">Wochenraster mit Kontext-Panel</div>
|
||||
<div class="var-desc">Das bestehende Protein-Raster wird zum Haupt-Interface. Alle 7 Tage zeigen das vollständige Rezept. Problematische Slots sind gelb markiert — Klick öffnet das rechte Panel mit Erklärung und Swap-CTA.</div>
|
||||
<span class="var-tag amb">Ambitiös · Meiste Übersicht</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-pair">
|
||||
<div class="preview-d-wrap">
|
||||
<div class="preview-label">Desktop</div>
|
||||
<div class="preview-d-clip">
|
||||
<div class="preview-d-scale">
|
||||
<div class="shell" style="min-height:680px;">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-brand"><div class="sidebar-brand-row"><div class="sidebar-logo"></div><span class="sidebar-app">Mealplan</span></div><div class="sidebar-household">Familie Raddatz</div></div>
|
||||
<div class="sidebar-nav">
|
||||
<div class="sidebar-group-label">Plan</div>
|
||||
<a class="sidebar-item active" href="#"><span class="sidebar-icon">📅</span>Planer</a>
|
||||
<a class="sidebar-item" href="#"><span class="sidebar-icon">🍽</span>Rezepte</a>
|
||||
<a class="sidebar-item" href="#"><span class="sidebar-icon">🛒</span>Einkauf</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">
|
||||
<div class="topbar">
|
||||
<a class="topbar-back" href="#">Planer</a>
|
||||
<span class="topbar-sep">/</span>
|
||||
<span class="topbar-title">Abwechslungs-Analyse</span>
|
||||
<!-- Score badge in topbar -->
|
||||
<div style="margin-left:auto;display:flex;align-items:center;gap:8px;">
|
||||
<span style="font-size:11px;color:var(--color-text-muted);">Abwechslung</span>
|
||||
<span style="font-family:var(--font-display);font-size:20px;font-weight:300;color:var(--yellow-text);">6.5</span>
|
||||
<span style="font-family:var(--font-display);font-size:12px;color:var(--color-text-muted);">/10</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;flex:1;overflow:hidden;">
|
||||
<!-- Main: week grid -->
|
||||
<div class="v3-main">
|
||||
<div class="section-hd">Wochenübersicht — gelb markierte Gerichte haben Hinweise</div>
|
||||
<div class="week-grid">
|
||||
<!-- Mon - Tofu-Curry WARN -->
|
||||
<div class="day-col">
|
||||
<div class="day-header">Mo</div>
|
||||
<div class="recipe-slot warn selected">Tofu-Curry</div>
|
||||
</div>
|
||||
<!-- Tue - Linsen-Suppe WARN -->
|
||||
<div class="day-col">
|
||||
<div class="day-header">Di</div>
|
||||
<div class="recipe-slot warn">Linsen-Suppe</div>
|
||||
</div>
|
||||
<!-- Wed - Tofu-Bowl WARN -->
|
||||
<div class="day-col">
|
||||
<div class="day-header">Mi</div>
|
||||
<div class="recipe-slot warn">Tofu-Bowl</div>
|
||||
</div>
|
||||
<!-- Thu - Gemüse OK -->
|
||||
<div class="day-col">
|
||||
<div class="day-header">Do</div>
|
||||
<div class="recipe-slot">Gemüse-Stir-Fry</div>
|
||||
</div>
|
||||
<!-- Fri - Linsen-Dal WARN -->
|
||||
<div class="day-col">
|
||||
<div class="day-header">Fr</div>
|
||||
<div class="recipe-slot warn">Linsen-Dal</div>
|
||||
</div>
|
||||
<!-- Sat - empty -->
|
||||
<div class="day-col">
|
||||
<div class="day-header">Sa</div>
|
||||
<div class="recipe-slot empty">—</div>
|
||||
</div>
|
||||
<!-- Sun - empty -->
|
||||
<div class="day-col">
|
||||
<div class="day-header">So</div>
|
||||
<div class="recipe-slot empty">—</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-hd" style="margin-top:16px;">Aufwandsverteilung</div>
|
||||
<div style="display:flex;height:18px;border-radius:var(--radius-full);overflow:hidden;gap:2px;max-width:280px;">
|
||||
<div style="flex:3;background:var(--green-dark);"></div>
|
||||
<div style="flex:2;background:var(--yellow);"></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:16px;margin-top:6px;font-size:11px;color:var(--color-text-muted);">
|
||||
<span>Einfach ×3</span><span>Mittel ×2</span>
|
||||
</div>
|
||||
|
||||
<div class="sub-scores" style="margin-top:20px;max-width:360px;">
|
||||
<div class="sub-row"><span class="sub-label">Quellen-Vielfalt</span><span class="sub-val">6/10</span></div>
|
||||
<div class="sub-row"><span class="sub-label">Zutaten-Überlappung</span><span class="sub-val ok">8/10</span></div>
|
||||
<div class="sub-row"><span class="sub-label">Aufwandsbalance</span><span class="sub-val ok">9/10</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right panel: context for selected slot -->
|
||||
<div class="v3-panel">
|
||||
<div class="panel-score">
|
||||
<span class="panel-score-num" style="color:var(--yellow-text);">6.5</span>
|
||||
<span class="panel-score-denom">/10</span>
|
||||
</div>
|
||||
<div class="panel-warn-title">Tofu-Curry — Montag</div>
|
||||
<div class="panel-warn-desc">Tofu taucht diese Woche auch am Mittwoch auf (Tofu-Bowl). Ein Tausch würde die Quellen-Vielfalt verbessern.</div>
|
||||
<div class="section-hd">Andere betroffene Gerichte</div>
|
||||
<div class="panel-recipe-entry">
|
||||
<div><div class="panel-recipe-name">Tofu-Bowl</div><div class="panel-recipe-day">Mittwoch</div></div>
|
||||
</div>
|
||||
<button class="btn-swap-primary">↔ Tofu-Curry tauschen</button>
|
||||
<div class="panel-hint">Öffnet den Rezept-Picker für Montag.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-m-wrap">
|
||||
<div class="preview-label">Mobile (Tab-Navigation)</div>
|
||||
<div class="preview-m-clip">
|
||||
<div class="preview-m-scale">
|
||||
<div class="m-shell" style="min-height:680px;">
|
||||
<div class="m-topbar"><span class="m-back">‹</span><span class="m-title">Abwechslungs-Analyse</span><span style="margin-left:auto;font-family:var(--font-display);font-size:18px;font-weight:300;color:var(--yellow-text);">6.5<span style="font-size:12px;color:var(--color-text-muted);">/10</span></span></div>
|
||||
<div class="m-content">
|
||||
<!-- Tab switcher for mobile (Übersicht | Hinweise) -->
|
||||
<div style="display:flex;border:1px solid var(--color-border);border-radius:var(--radius-md);overflow:hidden;margin-bottom:14px;">
|
||||
<button style="flex:1;padding:7px;font-size:11px;font-weight:500;background:var(--color-subtle);color:var(--color-text-muted);border:none;">Übersicht</button>
|
||||
<button style="flex:1;padding:7px;font-size:11px;font-weight:500;background:var(--green-dark);color:white;border:none;">Hinweise (2)</button>
|
||||
</div>
|
||||
<!-- Hinweise tab active -->
|
||||
<div style="background:white;border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:14px;margin-bottom:10px;">
|
||||
<div style="font-size:12px;font-weight:500;margin-bottom:8px;color:var(--yellow-text);">Tofu mehrfach diese Woche</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:7px 10px;background:var(--yellow-tint);border:1px solid var(--yellow-light);border-radius:var(--radius-md);margin-bottom:5px;"><span style="font-size:11px;font-weight:500;">Tofu-Curry <span style="color:var(--color-text-muted);font-weight:400;">Mo</span></span><button style="font-size:10px;font-weight:500;padding:4px 8px;background:var(--green-dark);color:white;border:none;border-radius:var(--radius-md);">Tauschen</button></div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:7px 10px;background:var(--yellow-tint);border:1px solid var(--yellow-light);border-radius:var(--radius-md);"><span style="font-size:11px;font-weight:500;">Tofu-Bowl <span style="color:var(--color-text-muted);font-weight:400;">Mi</span></span><button style="font-size:10px;font-weight:500;padding:4px 8px;background:var(--green-dark);color:white;border:none;border-radius:var(--radius-md);">Tauschen</button></div>
|
||||
</div>
|
||||
<div style="background:white;border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:14px;">
|
||||
<div style="font-size:12px;font-weight:500;margin-bottom:8px;color:var(--yellow-text);">Linsen in mehreren Gerichten</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:7px 10px;background:var(--yellow-tint);border:1px solid var(--yellow-light);border-radius:var(--radius-md);margin-bottom:5px;"><span style="font-size:11px;font-weight:500;">Linsen-Suppe <span style="color:var(--color-text-muted);font-weight:400;">Di</span></span><button style="font-size:10px;font-weight:500;padding:4px 8px;background:var(--green-dark);color:white;border:none;border-radius:var(--radius-md);">Tauschen</button></div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:7px 10px;background:var(--yellow-tint);border:1px solid var(--yellow-light);border-radius:var(--radius-md);"><span style="font-size:11px;font-weight:500;">Linsen-Dal <span style="color:var(--color-text-muted);font-weight:400;">Fr</span></span><button style="font-size:10px;font-weight:500;padding:4px 8px;background:var(--green-dark);color:white;border:none;border-radius:var(--radius-md);">Tauschen</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-tabbar">
|
||||
<div class="m-tab active"><div class="m-tab-icon">📅</div>Planer</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">🍽</div>Rezepte</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">🛒</div>Einkauf</div>
|
||||
<div class="m-tab"><div class="m-tab-icon">⚙️</div>Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notes">
|
||||
<div class="notes-label">Notizen</div>
|
||||
<ul>
|
||||
<li>Wochenraster ersetzt das bisherige Protein-Grid (7 Spalten, Rezeptname statt Kürzel, größere Zellen)</li>
|
||||
<li>Gelber Slot = mindestens ein Hinweis vorhanden. Klick selektiert den Slot, Panel rechts aktualisiert sich.</li>
|
||||
<li>Panel zeigt: betroffenes Rezept + Wochentag + Erklärung + andere betroffene Slots + primären "Tauschen"-Button</li>
|
||||
<li>Score-Zahl wandert in die Topbar-Leiste (kompakt, immer sichtbar)</li>
|
||||
<li>Mobile: kein Panel — stattdessen Tab-Switcher "Übersicht | Hinweise (N)" mit aufklappbaren Einträgen</li>
|
||||
<li>Größter Umbau: <code>+page.svelte</code> Struktur und alle beteiligten Komponenten müssen neu aufgebaut werden</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ─── Agent section ─── -->
|
||||
<div class="agent-section">
|
||||
<h2>Maschinen-lesbare Spezifikation</h2>
|
||||
<p>Gilt für alle drei Variationen. Implementierungs-Details werden nach Variantenwahl konkretisiert.</p>
|
||||
|
||||
<pre class="spec-comment">
|
||||
/* 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
|
||||
* <details> 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
|
||||
*/
|
||||
</pre>
|
||||
|
||||
<table class="agent-table">
|
||||
<thead>
|
||||
<tr><th>Property</th><th>Value</th><th>Notes</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="group-row"><td colspan="3">Shared: Recipe Mapping</td></tr>
|
||||
<tr><td>data-source</td><td>weekPlan.slots[].dayOfWeek + recipe</td><td>already in page data</td></tr>
|
||||
<tr><td>swap-url</td><td>/planner?week={weekStart}&swap={slotId}</td><td>RecipePicker pre-selects slot</td></tr>
|
||||
<tr><td>day-long</td><td>MON→Montag, TUE→Dienstag…</td><td>for V2 display</td></tr>
|
||||
<tr><td>day-short</td><td>MON→Mo, TUE→Di…</td><td>for V1 pills + V3 grid</td></tr>
|
||||
<tr class="group-row"><td colspan="3">V1 Recipe Pills</td></tr>
|
||||
<tr><td>pill-padding</td><td>5px 10px 5px 12px</td><td>left more for text</td></tr>
|
||||
<tr><td>swap-btn-size</td><td>22×22px, border-radius 50%</td><td>within pill</td></tr>
|
||||
<tr><td>pill-bg</td><td>white, border --yellow-light</td><td>on yellow-tint card</td></tr>
|
||||
<tr class="group-row"><td colspan="3">V2 Action Rows</td></tr>
|
||||
<tr><td>score-compact-height</td><td>~64px</td><td>replaces 180px hero</td></tr>
|
||||
<tr><td>details-summary</td><td>native <details>, no JS</td><td>sub-scores hidden by default</td></tr>
|
||||
<tr><td>recipe-row-bg</td><td>--color-subtle</td><td>within white action card</td></tr>
|
||||
<tr class="group-row"><td colspan="3">V3 Week Grid</td></tr>
|
||||
<tr><td>slot-height</td><td>52px min</td><td>enough for 2-line recipe name</td></tr>
|
||||
<tr><td>warn-slot-ring</td><td>2px solid --yellow + yellow-tint bg</td><td>problem indicator</td></tr>
|
||||
<tr><td>selected-slot-ring</td><td>2px solid --green-dark</td><td>active selection</td></tr>
|
||||
<tr><td>panel-width</td><td>280px</td><td>fixed, right side</td></tr>
|
||||
<tr><td>mobile-tab-active-bg</td><td>--green-dark</td><td>selected tab button</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user