Compare commits
4 Commits
6dd0b7ac93
...
e3066ec3e5
| Author | SHA1 | Date | |
|---|---|---|---|
| e3066ec3e5 | |||
| bd1604fc1d | |||
| c297403506 | |||
| fa4a4c9ef7 |
625
specs/frontend/c3-variety-rework-v1-spec.html
Normal file
625
specs/frontend/c3-variety-rework-v1-spec.html
Normal file
@@ -0,0 +1,625 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Recipe App — C3 Abwechslungs-Analyse · Implementierungsspezifikation V1</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"/>
|
||||
<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:#F2C12E;--yellow-dark:#C49610;--yellow-text:#8A6800;
|
||||
--blue-tint:#E6F1FB;--blue-light:#A4CFF4;--blue:#2D7DD2;--blue-dark:#185FA5;
|
||||
--purple-tint:#EEEDFE;--purple-light:#CECBF6;--purple:#534AB7;--purple-dark:#3C3489;
|
||||
--orange-tint:#FEF0E6;--orange:#E8862A;
|
||||
--red-tint:#FDECEA;--red:#DC4C3E;--red-dark:#B03328;
|
||||
--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;
|
||||
--shadow-overlay:0 8px 32px rgba(28,28,24,.12),0 2px 8px rgba(28,28,24,.06);
|
||||
}
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:var(--font-sans);background:#DDDBD5;color:var(--color-text);font-size:14px;line-height:1.6;}
|
||||
.doc{max-width:1100px;margin:0 auto;padding:48px 40px 120px;}
|
||||
|
||||
.doc-header{background:var(--color-page);border-radius:var(--radius-xl) var(--radius-xl) 0 0;padding:40px 40px 28px;margin:-48px -40px 48px;display:flex;justify-content:space-between;align-items:flex-end;border-bottom:1px solid var(--color-border);}
|
||||
.doc-header h1{font-family:var(--font-display);font-size:26px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
||||
.doc-header p{font-size:13px;color:var(--color-text-muted);}
|
||||
.doc-meta{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);text-align:right;line-height:1.9;}
|
||||
.pill{display:inline-block;padding:2px 8px;border-radius:var(--radius-sm);font-size:10px;font-weight:500;letter-spacing:.05em;}
|
||||
.pill-ready{background:var(--green-tint);color:var(--green-dark);}
|
||||
.pill-warn{background:var(--yellow-tint);color:var(--yellow-text);}
|
||||
|
||||
.section{margin-bottom:56px;}
|
||||
.section-label{font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:20px;}
|
||||
|
||||
.prose{font-size:13px;color:var(--color-text-muted);line-height:1.7;max-width:720px;margin-bottom:16px;}
|
||||
.prose strong{color:var(--color-text);font-weight:500;}
|
||||
|
||||
/* Code blocks */
|
||||
.code{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);padding:16px 20px;font-family:var(--font-mono);font-size:12px;line-height:1.7;overflow-x:auto;margin-bottom:16px;white-space:pre;}
|
||||
.code .cm{color:var(--color-text-muted);}
|
||||
.code .kw{color:var(--purple);}
|
||||
.code .ty{color:var(--blue-dark);}
|
||||
.code .st{color:var(--green-dark);}
|
||||
.code .nu{color:var(--orange);}
|
||||
|
||||
/* Tables */
|
||||
.tbl{width:100%;border-collapse:collapse;font-size:12px;background:var(--color-surface);border-radius:var(--radius-lg);overflow:hidden;border:1px solid var(--color-border);margin-bottom:16px;}
|
||||
.tbl thead tr{background:var(--color-subtle);border-bottom:1px solid var(--color-border);}
|
||||
.tbl th{text-align:left;padding:10px 14px;font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);}
|
||||
.tbl td{padding:9px 14px;border-bottom:1px solid var(--color-subtle);vertical-align:top;}
|
||||
.tbl tr:last-child td{border-bottom:none;}
|
||||
.tbl td:first-child{font-weight:500;color:var(--color-text-muted);white-space:nowrap;font-size:11px;}
|
||||
.tbl td.mono{font-family:var(--font-mono);font-size:11px;}
|
||||
|
||||
/* Callout boxes */
|
||||
.box{border-radius:var(--radius-lg);padding:16px 20px;margin-bottom:16px;}
|
||||
.box-lbl{font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;}
|
||||
.box ul{list-style:none;display:flex;flex-direction:column;gap:5px;}
|
||||
.box li{font-size:12px;line-height:1.5;display:flex;align-items:flex-start;gap:8px;}
|
||||
.box li::before{font-weight:500;flex-shrink:0;}
|
||||
.box-y{background:var(--yellow-tint);border:1px solid var(--yellow-light);}
|
||||
.box-y .box-lbl,.box-y li::before{color:var(--yellow-text);}
|
||||
.box-y li{color:var(--yellow-text);}
|
||||
.box-g{background:var(--green-tint);border:1px solid var(--green-light);}
|
||||
.box-g .box-lbl,.box-g li::before{color:var(--green-dark);}
|
||||
.box-g li{color:var(--green-dark);}
|
||||
.box-b{background:var(--blue-tint);border:1px solid var(--blue-light);}
|
||||
.box-b .box-lbl,.box-b li::before{color:var(--blue-dark);}
|
||||
.box-b li{color:var(--blue-dark);}
|
||||
.box ul.checks li::before{content:'✓';}
|
||||
.box ul.arrows li::before{content:'→';}
|
||||
|
||||
/* State cards */
|
||||
.state-card{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;margin-bottom:12px;}
|
||||
.state-head{background:var(--color-subtle);padding:10px 16px;border-bottom:1px solid var(--color-border);display:flex;align-items:center;gap:10px;}
|
||||
.state-id{font-family:var(--font-mono);font-size:11px;font-weight:500;color:var(--color-text-muted);}
|
||||
.state-title{font-size:13px;font-weight:500;}
|
||||
.state-body{padding:14px 16px;font-size:12px;line-height:1.7;}
|
||||
|
||||
/* Device frames (compact preview) */
|
||||
.prev-row{display:flex;gap:32px;align-items:flex-start;flex-wrap:wrap;margin-bottom:16px;}
|
||||
.prev-col{display:flex;flex-direction:column;align-items:center;gap:8px;}
|
||||
.bp-lbl{font-family:var(--font-mono);font-size:10px;color:var(--color-text-muted);}
|
||||
.phone{width:300px;flex-shrink:0;background:var(--color-page);border-radius:32px;overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.08);border:5px solid #1C1C18;}
|
||||
.pst{padding:8px 16px 0;display:flex;justify-content:space-between;align-items:center;font-size:10px;background:var(--color-page);}
|
||||
.pst b{font-weight:600;font-size:11px;}
|
||||
|
||||
/* Warning card preview */
|
||||
.wcard{border-radius:8px;border:1px solid var(--yellow-light);background:var(--yellow-tint);overflow:hidden;margin-bottom:8px;}
|
||||
.wcard:last-child{margin-bottom:0;}
|
||||
.wcard-hd{padding:9px 14px;border-bottom:1px solid var(--yellow-light);}
|
||||
.wcard-hd-t{font-size:13px;font-weight:500;color:var(--yellow-text);}
|
||||
.wcard-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:9px 14px;border-bottom:1px solid rgba(249,224,138,.4);}
|
||||
.wcard-row:last-child{border-bottom:none;}
|
||||
.wcard-left{display:flex;align-items:center;gap:8px;min-width:0;}
|
||||
.wcard-day{font-size:11px;font-weight:600;color:var(--yellow-text);width:20px;flex-shrink:0;}
|
||||
.wcard-recipe{font-size:13px;color:var(--color-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
||||
.wcard-swap{font-size:12px;font-weight:500;color:var(--yellow-text);white-space:nowrap;flex-shrink:0;}
|
||||
|
||||
.divider{border:none;border-top:1px solid var(--color-border);margin:40px 0;}
|
||||
|
||||
/* File diff style */
|
||||
.diff{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);font-family:var(--font-mono);font-size:12px;line-height:1.6;overflow-x:auto;margin-bottom:16px;}
|
||||
.diff-file{padding:8px 16px;background:var(--color-subtle);border-bottom:1px solid var(--color-border);font-size:11px;font-weight:500;color:var(--color-text-muted);}
|
||||
.diff-body{padding:12px 16px;white-space:pre;}
|
||||
.diff-add{color:var(--green-dark);background:rgba(61,140,74,.06);}
|
||||
.diff-rem{color:var(--red-dark);background:rgba(220,76,62,.06);}
|
||||
.diff-ctx{color:var(--color-text-muted);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="doc">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="doc-header">
|
||||
<div>
|
||||
<h1>C3 — Abwechslungs-Analyse · Implementierungsspezifikation</h1>
|
||||
<p>Recipe App · Variation V1 "Erweiterte Karten" · Rezeptnamen + Tausch-Links in Warnkarten</p>
|
||||
</div>
|
||||
<div class="doc-meta">
|
||||
<span class="pill pill-ready">Final</span><br>
|
||||
Erstellt: 2026-04<br>
|
||||
Screen: C3<br>
|
||||
Bezug: c3-variety-rework.html
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 1. ÜBERBLICK ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">1 · Überblick</div>
|
||||
<p class="prose">Die Seite <strong>/planner/variety</strong> zeigt derzeit Warnkarten mit technischen Tages-Codes (<code style="font-family:var(--font-mono);font-size:11px;">MON, WED — erwäge einen Tausch</code>). Der Planer muss manuell nachschlagen, welches Gericht an diesen Tagen eingeplant ist, und dann zurück zum Planer navigieren um es zu tauschen.</p>
|
||||
<p class="prose"><strong>V1 "Erweiterte Karten"</strong> löst dies mit minimalem Umbauaufwand: Die Warnkarten erhalten eine strukturierte Zeile pro betroffenem Tag — mit Wochentag-Abkürzung, Rezeptname und direktem "Tauschen →" Link. Score-Hero, Bewertungsdetails und das Gesamt-Layout bleiben unverändert.</p>
|
||||
|
||||
<div class="box box-b">
|
||||
<div class="box-lbl">Scope</div>
|
||||
<ul class="arrows">
|
||||
<li>Kein neues Backend-Endpoint — alle nötigen Daten sind bereits im weekPlan-Load vorhanden</li>
|
||||
<li>Kein Layout-Umbau — nur VarietyWarningCards.svelte und die Datenvorbereitung in +page.svelte ändern sich</li>
|
||||
<li>Protein-Grid und EffortBar bleiben wie bisher (Desktop)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 2. PROBLEM IM DETAIL ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">2 · Aktueller Ist-Zustand und Problem</div>
|
||||
|
||||
<table class="tbl">
|
||||
<thead><tr><th>Element</th><th>Aktuell</th><th>Soll (V1)</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Warnkarte Inhalt</td>
|
||||
<td><code style="font-family:var(--font-mono);font-size:11px;">title + explanation (String)</code></td>
|
||||
<td>Strukturierte Zeilen: Wochentag · Rezeptname · Tauschen-Link</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tages-Angabe</td>
|
||||
<td>API-Code <code style="font-family:var(--font-mono);font-size:11px;">MON, WED</code></td>
|
||||
<td>Abkürzung <code style="font-family:var(--font-mono);font-size:11px;">Mo, Mi</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rezeptname</td>
|
||||
<td>Fehlt</td>
|
||||
<td>Aus <code style="font-family:var(--font-mono);font-size:11px;">weekPlan.slots[].recipe.name</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tausch-Navigation</td>
|
||||
<td>Fehlt — Nutzer verlässt die Seite manuell</td>
|
||||
<td><code style="font-family:var(--font-mono);font-size:11px;">/planner?week={weekStart}&swap={slotId}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Datenbasis</td>
|
||||
<td><code style="font-family:var(--font-mono);font-size:11px;">computeWarnings()</code> aus variety.ts</td>
|
||||
<td>Inline <code style="font-family:var(--font-mono);font-size:11px;">$derived.by()</code> in +page.svelte, direkt aus API-Daten</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 3. DATENFLUSS ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">3 · Datenfluss</div>
|
||||
|
||||
<p class="prose">Alle nötigen Daten werden bereits im Server-Load geladen. Kein neuer API-Call erforderlich.</p>
|
||||
|
||||
<table class="tbl">
|
||||
<thead><tr><th>Quelle</th><th>Feld</th><th>Verwendung</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>weekPlan.slots[]</td>
|
||||
<td class="mono">{ id, dayOfWeek, recipe: { id, name } }</td>
|
||||
<td>Aufbau der <code style="font-family:var(--font-mono);font-size:11px;">slotsByDay</code>-Map: DayCode → { slotId, recipeName }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>varietyScore.tagRepeats[]</td>
|
||||
<td class="mono">{ tagType, tagName, days: string[] }</td>
|
||||
<td>Warnkarten für wiederholte Tags (Protein, Cuisine). days[] enthält API-Codes: "MON", "TUE" …</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>varietyScore.ingredientOverlaps[]</td>
|
||||
<td class="mono">{ ingredientName, days: string[] }</td>
|
||||
<td>Warnkarten für Zutaten-Überschneidungen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>varietyScore.duplicatesInPlan[]</td>
|
||||
<td class="mono">string[] (Rezeptnamen)</td>
|
||||
<td>Warnkarte: "X doppelt geplant". Alle Slots mit diesem Rezeptnamen liefern die Items.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>data.weekStart</td>
|
||||
<td class="mono">string (YYYY-MM-DD)</td>
|
||||
<td>Swap-URL-Parameter</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="prose">Tag-Code → Abkürzung Mapping (konstant):</p>
|
||||
<div class="code"><span class="cm">// Day code → German short label</span>
|
||||
<span class="kw">const</span> DAY_SHORT: Record<<span class="ty">string</span>, <span class="ty">string</span>> = {
|
||||
MON: <span class="st">'Mo'</span>, TUE: <span class="st">'Di'</span>, WED: <span class="st">'Mi'</span>,
|
||||
THU: <span class="st">'Do'</span>, FRI: <span class="st">'Fr'</span>, SAT: <span class="st">'Sa'</span>, SUN: <span class="st">'So'</span>
|
||||
};</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 4. TYPEN ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">4 · Typen</div>
|
||||
|
||||
<p class="prose">Die bestehende <code style="font-family:var(--font-mono);font-size:11px;">VarietyWarningCards.svelte</code> definiert bereits die korrekten Interfaces. Diese bleiben unverändert:</p>
|
||||
|
||||
<div class="code"><span class="cm">// In VarietyWarningCards.svelte (bereits vorhanden, nicht ändern)</span>
|
||||
<span class="kw">interface</span> <span class="ty">WarningItem</span> {
|
||||
dayShort: <span class="ty">string</span>; <span class="cm">// 'Mo', 'Di', …</span>
|
||||
recipeName: <span class="ty">string</span>; <span class="cm">// aus weekPlan.slots[].recipe.name</span>
|
||||
slotId: <span class="ty">number</span>; <span class="cm">// für Swap-Link</span>
|
||||
}
|
||||
|
||||
<span class="kw">interface</span> <span class="ty">ActionWarning</span> {
|
||||
title: <span class="ty">string</span>; <span class="cm">// z.B. "Tofu mehrfach diese Woche"</span>
|
||||
items: <span class="ty">WarningItem</span>[]; <span class="cm">// eine Zeile pro betroffenem Tag</span>
|
||||
}</div>
|
||||
|
||||
<p class="prose">Die alte <code style="font-family:var(--font-mono);font-size:11px;">Warning</code>-Schnittstelle aus <code style="font-family:var(--font-mono);font-size:11px;">variety.ts</code> (<code style="font-family:var(--font-mono);font-size:11px;">{ title, explanation }</code>) wird nicht mehr verwendet.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 5. IMPLEMENTIERUNG ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">5 · Implementierung</div>
|
||||
|
||||
<p class="prose">Es gibt drei Änderungen:</p>
|
||||
|
||||
<!-- 5.1 slotsByDay -->
|
||||
<div class="state-card">
|
||||
<div class="state-head">
|
||||
<div class="state-id">5.1</div>
|
||||
<div class="state-title">+page.svelte — slotsByDay Map aufbauen</div>
|
||||
</div>
|
||||
<div class="state-body">
|
||||
<p style="margin-bottom:10px;">Füge direkt nach den bestehenden <code style="font-family:var(--font-mono);font-size:11px;">$derived</code>-Deklarationen hinzu:</p>
|
||||
<div class="code" style="margin-bottom:0"><span class="kw">const</span> DAY_SHORT: Record<<span class="ty">string</span>, <span class="ty">string</span>> = {
|
||||
MON: <span class="st">'Mo'</span>, TUE: <span class="st">'Di'</span>, WED: <span class="st">'Mi'</span>,
|
||||
THU: <span class="st">'Do'</span>, FRI: <span class="st">'Fr'</span>, SAT: <span class="st">'Sa'</span>, SUN: <span class="st">'So'</span>
|
||||
};
|
||||
|
||||
<span class="cm">// dayOfWeek (API code) → { slotId, recipeName }</span>
|
||||
<span class="kw">let</span> slotsByDay = $derived.by(() => {
|
||||
<span class="kw">const</span> map: Record<<span class="ty">string</span>, { slotId: <span class="ty">number</span>; recipeName: <span class="ty">string</span> }> = {};
|
||||
<span class="kw">for</span> (<span class="kw">const</span> slot <span class="kw">of</span> weekPlan?.slots ?? []) {
|
||||
<span class="kw">if</span> (slot.dayOfWeek && slot.recipe?.name && slot.id) {
|
||||
map[slot.dayOfWeek] = { slotId: slot.id, recipeName: slot.recipe.name };
|
||||
}
|
||||
}
|
||||
<span class="kw">return</span> map;
|
||||
});</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5.2 actionWarnings -->
|
||||
<div class="state-card">
|
||||
<div class="state-head">
|
||||
<div class="state-id">5.2</div>
|
||||
<div class="state-title">+page.svelte — actionWarnings ersetzen computeWarnings()</div>
|
||||
</div>
|
||||
<div class="state-body">
|
||||
<p style="margin-bottom:10px;">Ersetze den bestehenden <code style="font-family:var(--font-mono);font-size:11px;">let warnings = $derived.by(() => computeWarnings(…))</code>-Block vollständig:</p>
|
||||
<div class="code" style="margin-bottom:0"><span class="kw">interface</span> <span class="ty">WarningItem</span> { dayShort: <span class="ty">string</span>; recipeName: <span class="ty">string</span>; slotId: <span class="ty">number</span>; }
|
||||
<span class="kw">interface</span> <span class="ty">ActionWarning</span> { title: <span class="ty">string</span>; items: <span class="ty">WarningItem</span>[]; }
|
||||
|
||||
<span class="kw">let</span> actionWarnings = $derived.by((): <span class="ty">ActionWarning</span>[] => {
|
||||
<span class="kw">const</span> result: <span class="ty">ActionWarning</span>[] = [];
|
||||
<span class="kw">const</span> vs = varietyScore;
|
||||
<span class="kw">if</span> (!vs) <span class="kw">return</span> result;
|
||||
|
||||
<span class="cm">// Tag repeats (protein, cuisine, …)</span>
|
||||
<span class="kw">for</span> (<span class="kw">const</span> repeat <span class="kw">of</span> vs.tagRepeats ?? []) {
|
||||
<span class="kw">if</span> ((repeat.days?.length ?? <span class="nu">0</span>) < <span class="nu">2</span>) <span class="kw">continue</span>;
|
||||
<span class="kw">const</span> items: <span class="ty">WarningItem</span>[] = (repeat.days ?? [])
|
||||
.map((day) => {
|
||||
<span class="kw">const</span> slot = slotsByDay[day];
|
||||
<span class="kw">return</span> slot
|
||||
? { dayShort: DAY_SHORT[day] ?? day, recipeName: slot.recipeName, slotId: slot.slotId }
|
||||
: <span class="kw">null</span>;
|
||||
})
|
||||
.filter((x): x is <span class="ty">WarningItem</span> => x !== <span class="kw">null</span>);
|
||||
<span class="kw">if</span> (items.length > <span class="nu">0</span>) {
|
||||
result.push({ title: `${repeat.tagName} mehrfach diese Woche`, items });
|
||||
}
|
||||
}
|
||||
|
||||
<span class="cm">// Ingredient overlaps</span>
|
||||
<span class="kw">for</span> (<span class="kw">const</span> overlap <span class="kw">of</span> vs.ingredientOverlaps ?? []) {
|
||||
<span class="kw">if</span> ((overlap.days?.length ?? <span class="nu">0</span>) < <span class="nu">2</span>) <span class="kw">continue</span>;
|
||||
<span class="kw">const</span> items: <span class="ty">WarningItem</span>[] = (overlap.days ?? [])
|
||||
.map((day) => {
|
||||
<span class="kw">const</span> slot = slotsByDay[day];
|
||||
<span class="kw">return</span> slot
|
||||
? { dayShort: DAY_SHORT[day] ?? day, recipeName: slot.recipeName, slotId: slot.slotId }
|
||||
: <span class="kw">null</span>;
|
||||
})
|
||||
.filter((x): x is <span class="ty">WarningItem</span> => x !== <span class="kw">null</span>);
|
||||
<span class="kw">if</span> (items.length > <span class="nu">0</span>) {
|
||||
result.push({ title: `${overlap.ingredientName} in mehreren Gerichten`, items });
|
||||
}
|
||||
}
|
||||
|
||||
<span class="cm">// Duplicate recipes — find all slots with that recipe name</span>
|
||||
<span class="kw">for</span> (<span class="kw">const</span> name <span class="kw">of</span> vs.duplicatesInPlan ?? []) {
|
||||
<span class="kw">const</span> items: <span class="ty">WarningItem</span>[] = Object.entries(slotsByDay)
|
||||
.filter(([, s]) => s.recipeName === name)
|
||||
.map(([day, s]) => ({ dayShort: DAY_SHORT[day] ?? day, recipeName: s.recipeName, slotId: s.slotId }));
|
||||
<span class="kw">if</span> (items.length > <span class="nu">0</span>) {
|
||||
result.push({ title: `${name} doppelt geplant`, items });
|
||||
}
|
||||
}
|
||||
|
||||
<span class="kw">return</span> result;
|
||||
});</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5.3 Template update -->
|
||||
<div class="state-card">
|
||||
<div class="state-head">
|
||||
<div class="state-id">5.3</div>
|
||||
<div class="state-title">+page.svelte — Template: warnings → actionWarnings, weekStart übergeben</div>
|
||||
</div>
|
||||
<div class="state-body">
|
||||
<p style="margin-bottom:10px;">An beiden Stellen im Template (Mobile + Desktop) ersetzen:</p>
|
||||
<div class="diff">
|
||||
<div class="diff-file">+page.svelte (Mobile, ~Zeile 110 / Desktop, ~Zeile 222)</div>
|
||||
<div class="diff-body"><span class="diff-rem">- {#if warnings.length > 0}</span>
|
||||
<span class="diff-rem">- <VarietyWarningCards {warnings} /></span>
|
||||
<span class="diff-add">+ {#if actionWarnings.length > 0}</span>
|
||||
<span class="diff-add">+ <VarietyWarningCards warnings={actionWarnings} {weekStart} /></span></div>
|
||||
</div>
|
||||
<p style="font-size:12px;color:var(--color-text-muted);">Achtung: <code style="font-family:var(--font-mono);font-size:11px;">weekStart</code> ist für die Swap-URL erforderlich und muss explizit übergeben werden.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5.4 Import cleanup -->
|
||||
<div class="state-card">
|
||||
<div class="state-head">
|
||||
<div class="state-id">5.4</div>
|
||||
<div class="state-title">+page.svelte — Import aufräumen</div>
|
||||
</div>
|
||||
<div class="state-body">
|
||||
<p style="margin-bottom:10px;">Entferne den nicht mehr genutzten Import:</p>
|
||||
<div class="diff">
|
||||
<div class="diff-file">+page.svelte (Script-Block, oben)</div>
|
||||
<div class="diff-body"><span class="diff-rem">- import { computeSubScores, computeWarnings } from '$lib/planner/variety';</span>
|
||||
<span class="diff-add">+ import { computeSubScores } from '$lib/planner/variety';</span></div>
|
||||
</div>
|
||||
<p style="font-size:12px;color:var(--color-text-muted);">computeSubScores wird noch für die Score-Breakdown-Anzeige genutzt.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 6. KOMPONENTE: VarietyWarningCards ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">6 · VarietyWarningCards.svelte — bereits korrekt</div>
|
||||
|
||||
<p class="prose">Die Komponente wurde bereits auf das neue <code style="font-family:var(--font-mono);font-size:11px;">ActionWarning</code>-Format aktualisiert. <strong>Keine Änderung erforderlich.</strong> Zur Referenz die erwartete Props-Schnittstelle:</p>
|
||||
|
||||
<div class="code"><span class="cm">// Props (bereits implementiert)</span>
|
||||
<span class="kw">let</span> { warnings, weekStart }: {
|
||||
warnings: <span class="ty">ActionWarning</span>[];
|
||||
weekStart: <span class="ty">string</span>;
|
||||
} = $props();</div>
|
||||
|
||||
<p class="prose">Die Komponente rendert für jede Warnung:</p>
|
||||
<ul style="font-size:12px;color:var(--color-text-muted);margin-left:20px;margin-bottom:16px;line-height:1.9;">
|
||||
<li>Gelbe Karte (<code style="font-family:var(--font-mono);font-size:11px;">border: yellow-light, bg: yellow-tint</code>) mit Header-Zeile (Titel)</li>
|
||||
<li>Pro Item: Zeile mit Wochentag-Abkürzung (W=20px, fixed) · Rezeptname (truncate) · "Tauschen →" Link (rechts)</li>
|
||||
<li>Swap-URL: <code style="font-family:var(--font-mono);font-size:11px;">/planner?week={weekStart}&swap={item.slotId}</code></li>
|
||||
</ul>
|
||||
|
||||
<!-- Visual preview -->
|
||||
<div class="prev-row">
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Warnkarte · Referenz-Darstellung</div>
|
||||
<div style="width:340px;">
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Tofu mehrfach diese Woche</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mo</span><span class="wcard-recipe">Tofu-Gemüse-Pfanne</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Paprika in mehreren Gerichten</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Di</span><span class="wcard-recipe">Paprika-Linsen-Eintopf</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 7. EDGE CASES ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">7 · Edge Cases</div>
|
||||
|
||||
<table class="tbl">
|
||||
<thead><tr><th>Fall</th><th>Verhalten</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Tag im tagRepeat hat keinen Slot</td>
|
||||
<td>Filter-Schritt (.filter(x => x !== null)) entfernt das Item. Warnkarte erscheint nur wenn ≥1 Item vorhanden.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>weekPlan hat keine Slots (leere Woche)</td>
|
||||
<td>slotsByDay ist {}, actionWarnings ist []. Keine Warnkarten sichtbar.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>varietyScore ist null</td>
|
||||
<td>Bestehende {#if !varietyScore}-Guard greift — actionWarnings wird nie gerendert.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Slot hat kein Rezept (slot.recipe === null)</td>
|
||||
<td>slot.recipe?.name ist undefined → Slot wird nicht in slotsByDay aufgenommen.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>duplicatesInPlan: Rezeptname kommt in slotsByDay nicht vor</td>
|
||||
<td>items ist leer → Warnkarte wird nicht gepusht.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Unbekannter Tag-Code (z.B. zukünftige API-Erweiterung)</td>
|
||||
<td>DAY_SHORT[day] ?? day — Fallback auf den rohen Code.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sehr langer Rezeptname</td>
|
||||
<td>CSS truncate auf .wcard-recipe — kein Überlauf, Swap-Link bleibt sichtbar.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 8. ABNAHMEKRITERIEN ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">8 · Abnahmekriterien</div>
|
||||
|
||||
<div class="box box-g">
|
||||
<div class="box-lbl">Acceptance Criteria</div>
|
||||
<ul class="checks">
|
||||
<li>AC-1: Warnkarte zeigt pro betroffenem Tag eine eigene Zeile (nicht mehr einen langen Erklärungstext)</li>
|
||||
<li>AC-2: Jede Zeile enthält die deutsche Wochentag-Abkürzung (Mo, Di, Mi, Do, Fr, Sa, So)</li>
|
||||
<li>AC-3: Jede Zeile enthält den Namen des eingeplanten Rezepts</li>
|
||||
<li>AC-4: Jede Zeile enthält einen "Tauschen →" Link, der zu /planner?week={weekStart}&swap={slotId} führt</li>
|
||||
<li>AC-5: Tags mit nur einem betroffenen Tag (days.length < 2) erzeugen keine Warnkarte</li>
|
||||
<li>AC-6: Score-Hero, Bewertungsdetails und Protein-Grid (Desktop) bleiben unverändert</li>
|
||||
<li>AC-7: Wenn varietyScore null ist, werden keine Warnkarten gerendert (leere-Woche-State bleibt)</li>
|
||||
<li>AC-8: Der Import von computeWarnings ist entfernt, TypeScript kompiliert fehlerfrei</li>
|
||||
<li>AC-9: Auf Mobilgerät sind Tausch-Links touch-freundlich (mind. 44px Zeilenhöhe)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="box box-y">
|
||||
<div class="box-lbl">Nicht in Scope</div>
|
||||
<ul class="arrows">
|
||||
<li>Neues Backend-Endpoint — alle Daten kommen aus dem bestehenden Load</li>
|
||||
<li>Layout-Umbau der Seite — Score bleibt oben, Warnungen unten wie bisher</li>
|
||||
<li>Protein-Grid oder EffortBar Änderungen</li>
|
||||
<li>computeSubScores aus variety.ts — bleibt unverändert</li>
|
||||
<li>Entfernen von computeWarnings aus variety.ts (Funktion bleibt, wird nur nicht mehr aufgerufen)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── 9. DATEIEN ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">9 · Betroffene Dateien</div>
|
||||
|
||||
<table class="tbl">
|
||||
<thead><tr><th>Datei</th><th>Änderung</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="mono">frontend/src/routes/(app)/planner/variety/+page.svelte</td>
|
||||
<td>DAY_SHORT-Konstante, slotsByDay-Derived, actionWarnings-Derived, Template-Update (2×), Import-Bereinigung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">frontend/src/lib/planner/VarietyWarningCards.svelte</td>
|
||||
<td>Keine — bereits auf ActionWarning-Format aktualisiert</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">frontend/src/lib/planner/variety.ts</td>
|
||||
<td>Keine — computeWarnings bleibt (ungenutzt, aber nicht entfernen um Regressions-Risiko zu vermeiden)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── LLM AGENT REGION ── -->
|
||||
<div class="section">
|
||||
<div class="section-label">LLM-Agent-Lesbereich</div>
|
||||
<p class="prose">Dieser Abschnitt enthält maschinenlesbare Regeln für einen KI-Agenten der die Implementierung durchführt.</p>
|
||||
|
||||
<div class="code"><span class="cm">SCREEN: C3 /planner/variety
|
||||
VARIATION: V1 "Erweiterte Karten"
|
||||
STATUS: Final spec — ready for implementation
|
||||
|
||||
FILES TO MODIFY:
|
||||
frontend/src/routes/(app)/planner/variety/+page.svelte
|
||||
|
||||
FILES NOT TO MODIFY:
|
||||
frontend/src/lib/planner/VarietyWarningCards.svelte (already correct)
|
||||
frontend/src/lib/planner/variety.ts (keep computeWarnings, remove only import)
|
||||
|
||||
STEP 1 — Add DAY_SHORT constant (in <script> block, after imports):
|
||||
const DAY_SHORT: Record<string, string> = {
|
||||
MON: 'Mo', TUE: 'Di', WED: 'Mi',
|
||||
THU: 'Do', FRI: 'Fr', SAT: 'Sa', SUN: 'So'
|
||||
};
|
||||
|
||||
STEP 2 — Add slotsByDay derived (after $derived declarations for weekPlan, etc.):
|
||||
let slotsByDay = $derived.by(() => {
|
||||
const map: Record<string, { slotId: number; recipeName: string }> = {};
|
||||
for (const slot of weekPlan?.slots ?? []) {
|
||||
if (slot.dayOfWeek && slot.recipe?.name && slot.id) {
|
||||
map[slot.dayOfWeek] = { slotId: slot.id, recipeName: slot.recipe.name };
|
||||
}
|
||||
}
|
||||
return map;
|
||||
});
|
||||
|
||||
STEP 3 — Define inline interfaces + actionWarnings derived:
|
||||
interface WarningItem { dayShort: string; recipeName: string; slotId: number; }
|
||||
interface ActionWarning { title: string; items: WarningItem[]; }
|
||||
|
||||
let actionWarnings = $derived.by((): ActionWarning[] => {
|
||||
const result: ActionWarning[] = [];
|
||||
const vs = varietyScore;
|
||||
if (!vs) return result;
|
||||
|
||||
for (const repeat of vs.tagRepeats ?? []) {
|
||||
if ((repeat.days?.length ?? 0) < 2) continue;
|
||||
const items: WarningItem[] = (repeat.days ?? [])
|
||||
.map((day) => {
|
||||
const slot = slotsByDay[day];
|
||||
return slot ? { dayShort: DAY_SHORT[day] ?? day, recipeName: slot.recipeName, slotId: slot.slotId } : null;
|
||||
})
|
||||
.filter((x): x is WarningItem => x !== null);
|
||||
if (items.length > 0) result.push({ title: `${repeat.tagName} mehrfach diese Woche`, items });
|
||||
}
|
||||
|
||||
for (const overlap of vs.ingredientOverlaps ?? []) {
|
||||
if ((overlap.days?.length ?? 0) < 2) continue;
|
||||
const items: WarningItem[] = (overlap.days ?? [])
|
||||
.map((day) => {
|
||||
const slot = slotsByDay[day];
|
||||
return slot ? { dayShort: DAY_SHORT[day] ?? day, recipeName: slot.recipeName, slotId: slot.slotId } : null;
|
||||
})
|
||||
.filter((x): x is WarningItem => x !== null);
|
||||
if (items.length > 0) result.push({ title: `${overlap.ingredientName} in mehreren Gerichten`, items });
|
||||
}
|
||||
|
||||
for (const name of vs.duplicatesInPlan ?? []) {
|
||||
const items: WarningItem[] = Object.entries(slotsByDay)
|
||||
.filter(([, s]) => s.recipeName === name)
|
||||
.map(([day, s]) => ({ dayShort: DAY_SHORT[day] ?? day, recipeName: s.recipeName, slotId: s.slotId }));
|
||||
if (items.length > 0) result.push({ title: `${name} doppelt geplant`, items });
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
STEP 4 — Replace template occurrences (both mobile and desktop sections):
|
||||
OLD: {#if warnings.length > 0} / <VarietyWarningCards {warnings} />
|
||||
NEW: {#if actionWarnings.length > 0} / <VarietyWarningCards warnings={actionWarnings} {weekStart} />
|
||||
|
||||
STEP 5 — Fix import:
|
||||
OLD: import { computeSubScores, computeWarnings } from '$lib/planner/variety';
|
||||
NEW: import { computeSubScores } from '$lib/planner/variety';
|
||||
|
||||
INVARIANTS (do not change):
|
||||
- VarietyScoreHero, ScoreBreakdownList, EffortBar remain untouched
|
||||
- Desktop protein grid (proteinByDay) remains untouched
|
||||
- Layout structure (score top, warnings bottom) stays identical
|
||||
- No new server load or API calls</span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
790
specs/frontend/c3-variety-rework.html
Normal file
790
specs/frontend/c3-variety-rework.html
Normal file
@@ -0,0 +1,790 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Recipe App — C3 Abwechslungs-Analyse · 3 Mockup-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"/>
|
||||
<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:#F2C12E;--yellow-dark:#C49610;--yellow-text:#8A6800;
|
||||
--blue-tint:#E6F1FB;--blue-light:#A4CFF4;--blue:#2D7DD2;--blue-dark:#185FA5;
|
||||
--purple-tint:#EEEDFE;--purple-light:#CECBF6;--purple:#534AB7;--purple-dark:#3C3489;
|
||||
--orange-tint:#FEF0E6;--orange:#E8862A;
|
||||
--red-tint:#FDECEA;--red:#DC4C3E;--red-dark:#B03328;
|
||||
--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;
|
||||
--shadow-overlay:0 8px 32px rgba(28,28,24,.12),0 2px 8px rgba(28,28,24,.06);
|
||||
--shadow-card:0 1px 3px rgba(28,28,24,.06),0 1px 2px rgba(28,28,24,.04);
|
||||
}
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:var(--font-sans);background:#DDDBD5;color:var(--color-text);font-size:14px;line-height:1.6;}
|
||||
.doc{max-width:1200px;margin:0 auto;padding:48px 40px 120px;}
|
||||
|
||||
/* Header */
|
||||
.doc-header{background:var(--color-page);border-radius:var(--radius-xl) var(--radius-xl) 0 0;padding:40px 40px 28px;margin:-48px -40px 48px;display:flex;justify-content:space-between;align-items:flex-end;border-bottom:1px solid var(--color-border);}
|
||||
.doc-header h1{font-family:var(--font-display);font-size:26px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
||||
.doc-header p{font-size:13px;color:var(--color-text-muted);}
|
||||
.doc-meta{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);text-align:right;line-height:1.9;}
|
||||
.pill{display:inline-block;padding:2px 8px;border-radius:var(--radius-sm);font-size:10px;font-weight:500;letter-spacing:.05em;}
|
||||
.pill-draft{background:var(--yellow-tint);color:var(--yellow-text);}
|
||||
.pill-rec{background:var(--green-tint);color:var(--green-dark);}
|
||||
|
||||
/* Section */
|
||||
.section{margin-bottom:80px;}
|
||||
.section-label{font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:24px;}
|
||||
.prose{font-size:13px;color:var(--color-text-muted);line-height:1.7;max-width:720px;margin-bottom:24px;}
|
||||
|
||||
/* Variation header */
|
||||
.var-head{padding:20px 24px;border-radius:var(--radius-xl);margin-bottom:32px;display:flex;align-items:flex-start;gap:16px;}
|
||||
.var-num{font-family:var(--font-display);font-size:48px;font-weight:300;line-height:1;opacity:.5;flex-shrink:0;}
|
||||
.var-id{font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;margin-bottom:4px;}
|
||||
.var-title{font-family:var(--font-display);font-size:20px;font-weight:500;letter-spacing:-.02em;margin-bottom:6px;}
|
||||
.var-desc{font-size:13px;line-height:1.6;max-width:600px;}
|
||||
.var-y{background:var(--yellow-tint);border:1px solid var(--yellow-light);}
|
||||
.var-y .var-num,.var-y .var-id{color:var(--yellow-dark);}
|
||||
.var-y .var-desc{color:var(--yellow-text);}
|
||||
.var-g{background:var(--green-tint);border:1px solid var(--green-light);}
|
||||
.var-g .var-num,.var-g .var-id{color:var(--green);}
|
||||
.var-g .var-desc{color:var(--green-dark);}
|
||||
.var-p{background:var(--purple-tint);border:1px solid var(--purple-light);}
|
||||
.var-p .var-num,.var-p .var-id{color:var(--purple);}
|
||||
.var-p .var-desc{color:var(--purple-dark);}
|
||||
|
||||
/* Device frames */
|
||||
.previews{display:flex;gap:40px;flex-wrap:wrap;justify-content:center;align-items:flex-start;margin-bottom:28px;}
|
||||
.prev-col{display:flex;flex-direction:column;align-items:center;gap:10px;}
|
||||
.bp-lbl{font-family:var(--font-mono);font-size:10px;color:var(--color-text-muted);}
|
||||
.phone{width:320px;flex-shrink:0;background:var(--color-page);border-radius:36px;overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.08);border:6px solid #1C1C18;}
|
||||
.pst{padding:10px 20px 0;display:flex;justify-content:space-between;align-items:center;font-size:11px;background:var(--color-page);}
|
||||
.pst b{font-weight:600;font-size:12px;}
|
||||
.desk{width:100%;max-width:1040px;background:var(--color-page);border-radius:var(--radius-xl);overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.06);display:flex;min-height:500px;}
|
||||
|
||||
/* Nav chrome */
|
||||
.mtb{padding:10px 16px;background:var(--color-page);border-bottom:1px solid var(--color-border);display:flex;align-items:center;gap:10px;}
|
||||
.mtb-back{font-size:20px;color:var(--color-text-muted);line-height:1;}
|
||||
.mtb-t{font-family:var(--font-display);font-size:18px;font-weight:300;letter-spacing:-.02em;flex:1;}
|
||||
.mbt{border-top:1px solid var(--color-border);background:var(--color-surface);padding:8px 16px 28px;display:flex;justify-content:space-around;flex-shrink:0;}
|
||||
.mt-i{display:flex;flex-direction:column;align-items:center;gap:2px;}
|
||||
.mt-ic{width:20px;height:20px;border-radius:4px;background:var(--color-subtle);font-size:11px;display:flex;align-items:center;justify-content:center;}
|
||||
.mt-i.a .mt-ic{background:var(--yellow-tint);}
|
||||
.mt-l{font-size:9px;font-weight:500;color:var(--color-text-muted);}
|
||||
.mt-i.a .mt-l{color:var(--yellow-text);}
|
||||
/* Desktop sidebar */
|
||||
.dsb{width:224px;flex-shrink:0;background:var(--color-surface);border-right:1px solid var(--color-border);display:flex;flex-direction:column;}
|
||||
.dsb-logo{padding:20px 16px 16px;border-bottom:1px solid var(--color-border);}
|
||||
.dsb-lm{display:flex;align-items:center;gap:8px;margin-bottom:2px;}
|
||||
.dsb-ic{width:24px;height:24px;border-radius:5px;background:var(--green);font-size:12px;display:flex;align-items:center;justify-content:center;}
|
||||
.dsb-nm{font-family:var(--font-display);font-size:16px;font-weight:500;letter-spacing:-.02em;}
|
||||
.dsb-sub{font-size:10px;color:var(--color-text-muted);padding-left:32px;}
|
||||
.dsb-nav{padding:12px 10px;flex:1;}
|
||||
.dsb-nl{font-size:8px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);padding:0 8px;margin-bottom:4px;}
|
||||
.dsb-ni{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:var(--radius-md);font-size:13px;color:var(--color-text-muted);margin-bottom:2px;}
|
||||
.dsb-ni.a{background:var(--yellow-tint);color:var(--yellow-text);font-weight:500;}
|
||||
.dsb-nc{font-size:13px;width:18px;text-align:center;}
|
||||
.dm{flex:1;display:flex;flex-direction:column;min-width:0;}
|
||||
.dtb{padding:14px 24px;border-bottom:1px solid var(--color-border);display:flex;align-items:center;gap:8px;flex-shrink:0;}
|
||||
.dtb-bc{font-size:12px;color:var(--color-text-muted);}
|
||||
.dtb-t{font-family:var(--font-display);font-size:18px;font-weight:300;letter-spacing:-.02em;}
|
||||
.dmc{padding:24px;flex:1;overflow-y:auto;}
|
||||
|
||||
/* Shared components */
|
||||
.score-num{font-family:var(--font-display);font-weight:300;letter-spacing:-.02em;line-height:1;}
|
||||
.prog{height:6px;border-radius:3px;background:var(--color-border);overflow:hidden;margin-top:8px;}
|
||||
.prog-fill{height:100%;border-radius:3px;background:var(--yellow);}
|
||||
|
||||
/* Warning card styles */
|
||||
.wcard{border-radius:var(--radius-lg);border:1px solid var(--yellow-light);background:var(--yellow-tint);overflow:hidden;margin-bottom:8px;}
|
||||
.wcard:last-child{margin-bottom:0;}
|
||||
.wcard-hd{padding:10px 14px;border-bottom:1px solid var(--yellow-light);}
|
||||
.wcard-hd-t{font-size:13px;font-weight:500;color:var(--yellow-text);}
|
||||
.wcard-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:9px 14px;border-bottom:1px solid rgba(249,224,138,.4);}
|
||||
.wcard-row:last-child{border-bottom:none;}
|
||||
.wcard-left{display:flex;align-items:baseline;gap:8px;min-width:0;}
|
||||
.wcard-day{font-size:11px;font-weight:600;color:var(--yellow-text);width:20px;flex-shrink:0;}
|
||||
.wcard-recipe{font-size:13px;color:var(--color-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
||||
.wcard-swap{font-size:12px;font-weight:500;color:var(--yellow-text);white-space:nowrap;flex-shrink:0;}
|
||||
.wcard-swap:hover{text-decoration:underline;}
|
||||
|
||||
/* Score breakdown rows */
|
||||
.sb-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 0;border-bottom:1px solid var(--color-subtle);}
|
||||
.sb-row:last-child{border-bottom:none;}
|
||||
.sb-label{font-size:12px;color:var(--color-text-muted);}
|
||||
.sb-val{font-family:var(--font-mono);font-size:12px;font-weight:500;}
|
||||
|
||||
/* Collapsible details */
|
||||
.det summary{font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);cursor:default;list-style:none;display:flex;align-items:center;justify-content:space-between;}
|
||||
.det summary::after{content:'▾';font-size:11px;}
|
||||
.det[open] summary::after{content:'▴';}
|
||||
.det-body{padding-top:8px;}
|
||||
|
||||
/* Notes block */
|
||||
.notes{border-radius:var(--radius-lg);padding:16px 20px;margin-top:20px;}
|
||||
.notes-lbl{font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;}
|
||||
.notes ul{list-style:none;display:flex;flex-direction:column;gap:5px;}
|
||||
.notes li{font-size:12px;line-height:1.5;display:flex;align-items:flex-start;gap:8px;}
|
||||
.notes li::before{content:'→';font-weight:500;flex-shrink:0;}
|
||||
.notes-y{background:var(--yellow-tint);border:1px solid var(--yellow-light);}
|
||||
.notes-y .notes-lbl,.notes-y li::before{color:var(--yellow-text);}
|
||||
.notes-y li{color:var(--yellow-text);}
|
||||
.notes-g{background:var(--green-tint);border:1px solid var(--green-light);}
|
||||
.notes-g .notes-lbl,.notes-g li::before{color:var(--green-dark);}
|
||||
.notes-g li{color:var(--green-dark);}
|
||||
.notes-p{background:var(--purple-tint);border:1px solid var(--purple-light);}
|
||||
.notes-p .notes-lbl,.notes-p li::before{color:var(--purple-dark);}
|
||||
.notes-p li{color:var(--purple-dark);}
|
||||
|
||||
.divider{border:none;border-top:1px solid var(--color-border);margin:48px 0;}
|
||||
|
||||
/* Comparison table */
|
||||
.ct{width:100%;border-collapse:collapse;font-size:13px;background:var(--color-surface);border-radius:var(--radius-lg);overflow:hidden;border:1px solid var(--color-border);}
|
||||
.ct thead tr{background:var(--color-subtle);border-bottom:1px solid var(--color-border);}
|
||||
.ct th{text-align:left;padding:10px 16px;font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);}
|
||||
.ct td{padding:10px 16px;border-bottom:1px solid var(--color-subtle);font-size:12px;vertical-align:top;}
|
||||
.ct tr:last-child td{border-bottom:none;}
|
||||
.ct td:first-child{font-weight:500;font-size:11px;color:var(--color-text-muted);white-space:nowrap;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="doc">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="doc-header">
|
||||
<div>
|
||||
<h1>C3 — Abwechslungs-Analyse · Rework</h1>
|
||||
<p>Recipe App · 3 Mockup-Variationen · Aktuell: technische Tages-Codes, keine Rezeptnamen, kein direkter Tausch</p>
|
||||
</div>
|
||||
<div class="doc-meta">
|
||||
<span class="pill pill-draft">Entwurf</span><br>
|
||||
Erstellt: 2026-04<br>
|
||||
Variationen: 3<br>
|
||||
Screen: C3
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Context -->
|
||||
<div class="section">
|
||||
<div class="section-label">Problem</div>
|
||||
<p class="prose">Die aktuelle Seite zeigt Warnungen wie <strong style="font-family:var(--font-mono);font-size:12px;">"MON, WED — erwäge einen Tausch"</strong>. Der Planer muss selbst nachschlagen, welches Gericht an Montag und Mittwoch geplant ist, und dann manuell zum Planer navigieren um zu tauschen. Zwei Probleme:</p>
|
||||
<p class="prose"><strong>1. Keine Rezeptnamen</strong> — Tag-Codes statt echter Gerichte. <strong>2. Kein direkter Tausch</strong> — der Planer muss die Seite verlassen, zurück zum Planer, das richtige Gericht suchen und dann tauschen.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ═══════════════════════════════════════
|
||||
V1 — ERWEITERTE KARTEN
|
||||
════════════════════════════════════════ -->
|
||||
<div class="section">
|
||||
<div class="var-head var-y">
|
||||
<div class="var-num">V1</div>
|
||||
<div>
|
||||
<div class="var-id">Variation 1</div>
|
||||
<div class="var-title">Erweiterte Karten</div>
|
||||
<div class="var-desc">Minimale Änderung: bestehende gelbe Karten bleiben, aber der Text wird durch strukturierte Zeilen ersetzt — eine pro betroffenem Gericht, mit Wochentag, Rezeptname und "Tauschen →" Link. Score-Bereich und Layout bleiben unverändert.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="previews">
|
||||
|
||||
<!-- Mobile V1 -->
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Mobile · 320px</div>
|
||||
<div class="phone">
|
||||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||||
<!-- topbar -->
|
||||
<div class="mtb">
|
||||
<div class="mtb-back">‹</div>
|
||||
<div class="mtb-t">Abwechslungs-Analyse</div>
|
||||
</div>
|
||||
<div style="flex:1;overflow-y:auto;padding:20px 16px 16px;">
|
||||
|
||||
<!-- Score hero -->
|
||||
<div style="margin-bottom:24px;">
|
||||
<div style="display:flex;align-items:baseline;gap:8px;">
|
||||
<span class="score-num" style="font-size:56px;color:var(--color-text);">5.8</span>
|
||||
<span style="font-size:16px;color:var(--color-text-muted);">/ 10</span>
|
||||
<span style="font-size:14px;font-weight:500;color:var(--yellow-text);margin-left:4px;">Verbesserbar</span>
|
||||
</div>
|
||||
<div class="prog" style="width:120px;"><div class="prog-fill" style="width:58%;"></div></div>
|
||||
</div>
|
||||
|
||||
<!-- Sub-scores -->
|
||||
<div style="margin-bottom:24px;">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:10px;">Bewertung im Detail</div>
|
||||
<div class="sb-row"><span class="sb-label">Quellen-Vielfalt</span><span class="sb-val" style="color:var(--red-dark);">4 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Zutaten-Überschneidung</span><span class="sb-val" style="color:var(--yellow-text);">7 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Aufwandsbalance</span><span class="sb-val" style="color:var(--green-dark);">8 / 10</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Warnings — V1 style: same card structure, but rows inside -->
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:10px;">Hinweise</div>
|
||||
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Tofu mehrfach diese Woche</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mo</span><span class="wcard-recipe">Tofu-Gemüse-Pfanne</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Paprika in mehreren Gerichten</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Di</span><span class="wcard-recipe">Paprika-Linsen-Eintopf</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="mbt">
|
||||
<div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Plan</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Einkauf</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🍳</div><div class="mt-l">Rezepte</div></div>
|
||||
<div class="mt-i a"><div class="mt-ic">📊</div><div class="mt-l">Analyse</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop V1 -->
|
||||
<div class="prev-col" style="flex:1;min-width:580px;">
|
||||
<div class="bp-lbl">Desktop · 1040px</div>
|
||||
<div class="desk">
|
||||
<div class="dsb">
|
||||
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥦</div><div class="dsb-nm">Mealprep</div></div><div class="dsb-sub">Familie Raddatz</div></div>
|
||||
<div class="dsb-nav">
|
||||
<div class="dsb-nl">Planung</div>
|
||||
<div class="dsb-ni a"><span class="dsb-nc">📅</span> Wochenplan</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkaufsliste</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🍳</span> Rezepte</div>
|
||||
<div class="dsb-nl" style="margin-top:12px;">Haushalt</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">⚙️</span> Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dm">
|
||||
<div class="dtb">
|
||||
<span class="dtb-bc">Planer /</span>
|
||||
<span class="dtb-t">Abwechslungs-Analyse</span>
|
||||
</div>
|
||||
<div class="dmc">
|
||||
<!-- 2-col: left score + breakdown, right warnings -->
|
||||
<div style="display:flex;gap:32px;">
|
||||
<!-- Left -->
|
||||
<div style="flex:1;">
|
||||
<div style="display:flex;align-items:baseline;gap:8px;margin-bottom:12px;">
|
||||
<span class="score-num" style="font-size:72px;color:var(--color-text);">5.8</span>
|
||||
<span style="font-size:18px;color:var(--color-text-muted);">/ 10</span>
|
||||
<span style="font-size:14px;font-weight:500;color:var(--yellow-text);margin-left:4px;">Verbesserbar</span>
|
||||
</div>
|
||||
<div class="prog" style="width:200px;"><div class="prog-fill" style="width:58%;"></div></div>
|
||||
<div style="margin-top:20px;">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:10px;">Bewertung im Detail</div>
|
||||
<div class="sb-row"><span class="sb-label">Quellen-Vielfalt</span><span class="sb-val" style="color:var(--red-dark);">4 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Zutaten-Überschneidung</span><span class="sb-val" style="color:var(--yellow-text);">7 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Aufwandsbalance</span><span class="sb-val" style="color:var(--green-dark);">8 / 10</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right: warnings -->
|
||||
<div style="width:340px;flex-shrink:0;">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:10px;">Hinweise</div>
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Tofu mehrfach diese Woche</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mo</span><span class="wcard-recipe">Tofu-Gemüse-Pfanne</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Paprika in mehreren Gerichten</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Di</span><span class="wcard-recipe">Paprika-Linsen-Eintopf</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notes notes-y">
|
||||
<div class="notes-lbl">Design-Notizen V1</div>
|
||||
<ul>
|
||||
<li>Geringster Umbauaufwand — nur VarietyWarningCards.svelte ändert sich, keine Layout-Umstrukturierung.</li>
|
||||
<li>Behält die bekannte Score-Hierarchie bei: Zahl oben, dann Detail, dann Hinweise.</li>
|
||||
<li>Schwachstelle: Hinweise sind trotzdem am Ende der Seite versteckt — auf kurzen Telefon-Bildschirmen muss gescrollt werden, bevor der Planer die Tausch-Links sieht.</li>
|
||||
<li>Die Sub-Scores bleiben immer sichtbar, auch wenn der Planer nur die Tausch-Aktionen braucht.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="divider"/>
|
||||
|
||||
|
||||
<!-- ═══════════════════════════════════════
|
||||
V2 — AKTIONS-LISTE (EMPFOHLEN)
|
||||
════════════════════════════════════════ -->
|
||||
<div class="section">
|
||||
<div class="var-head var-g">
|
||||
<div class="var-num">V2</div>
|
||||
<div>
|
||||
<div class="var-id">Variation 2 · Empfohlen</div>
|
||||
<div class="var-title">Aktions-Liste</div>
|
||||
<div class="var-desc">Hinweise rücken nach oben — direkt unter dem Score. Der Planer sieht sofort, was zu tun ist. Sub-Scores wandern in ein ausklappbares "Bewertung im Detail" (native <details>, kein JS). Kompakterer Score-Hero gibt Hinweisen mehr Raum.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="previews">
|
||||
|
||||
<!-- Mobile V2 -->
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Mobile · 320px</div>
|
||||
<div class="phone">
|
||||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||||
<div class="mtb">
|
||||
<div class="mtb-back">‹</div>
|
||||
<div class="mtb-t">Abwechslungs-Analyse</div>
|
||||
</div>
|
||||
<div style="flex:1;overflow-y:auto;padding:16px;">
|
||||
|
||||
<!-- Compact score strip -->
|
||||
<div style="display:flex;align-items:center;gap:12px;padding:14px 16px;background:var(--color-surface);border-radius:var(--radius-lg);border:1px solid var(--color-border);margin-bottom:16px;">
|
||||
<div>
|
||||
<span class="score-num" style="font-size:40px;color:var(--color-text);">5.8</span>
|
||||
<span style="font-size:13px;color:var(--color-text-muted);margin-left:4px;">/ 10</span>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<div style="font-size:12px;font-weight:500;color:var(--yellow-text);margin-bottom:4px;">Verbesserbar</div>
|
||||
<div class="prog"><div class="prog-fill" style="width:58%;"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warnings — primary content -->
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:8px;">2 Hinweise</div>
|
||||
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Tofu mehrfach diese Woche</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mo</span><span class="wcard-recipe">Tofu-Gemüse-Pfanne</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wcard" style="margin-bottom:16px;">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Paprika in mehreren Gerichten</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Di</span><span class="wcard-recipe">Paprika-Linsen-Eintopf</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sub-scores — collapsed -->
|
||||
<details class="det" style="border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:10px 14px;background:var(--color-surface);">
|
||||
<summary>Bewertung im Detail</summary>
|
||||
<div class="det-body">
|
||||
<div class="sb-row"><span class="sb-label">Quellen-Vielfalt</span><span class="sb-val" style="color:var(--red-dark);">4 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Zutaten-Überschneidung</span><span class="sb-val" style="color:var(--yellow-text);">7 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Aufwandsbalance</span><span class="sb-val" style="color:var(--green-dark);">8 / 10</span></div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
</div>
|
||||
<div class="mbt">
|
||||
<div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Plan</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Einkauf</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🍳</div><div class="mt-l">Rezepte</div></div>
|
||||
<div class="mt-i a"><div class="mt-ic">📊</div><div class="mt-l">Analyse</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop V2 -->
|
||||
<div class="prev-col" style="flex:1;min-width:580px;">
|
||||
<div class="bp-lbl">Desktop · 1040px</div>
|
||||
<div class="desk">
|
||||
<div class="dsb">
|
||||
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥦</div><div class="dsb-nm">Mealprep</div></div><div class="dsb-sub">Familie Raddatz</div></div>
|
||||
<div class="dsb-nav">
|
||||
<div class="dsb-nl">Planung</div>
|
||||
<div class="dsb-ni a"><span class="dsb-nc">📅</span> Wochenplan</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkaufsliste</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🍳</span> Rezepte</div>
|
||||
<div class="dsb-nl" style="margin-top:12px;">Haushalt</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">⚙️</span> Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dm">
|
||||
<div class="dtb">
|
||||
<span class="dtb-bc">Planer /</span>
|
||||
<span class="dtb-t">Abwechslungs-Analyse</span>
|
||||
</div>
|
||||
<div class="dmc">
|
||||
<!-- Top: compact score strip + effort -->
|
||||
<div style="display:flex;gap:16px;align-items:center;padding:16px;background:var(--color-surface);border-radius:var(--radius-lg);border:1px solid var(--color-border);margin-bottom:24px;">
|
||||
<div>
|
||||
<span class="score-num" style="font-size:52px;color:var(--color-text);">5.8</span>
|
||||
<span style="font-size:14px;color:var(--color-text-muted);margin-left:6px;">/ 10</span>
|
||||
</div>
|
||||
<div style="flex:1;">
|
||||
<div style="font-size:13px;font-weight:500;color:var(--yellow-text);margin-bottom:4px;">Verbesserbar — 2 Hinweise</div>
|
||||
<div class="prog"><div class="prog-fill" style="width:58%;"></div></div>
|
||||
</div>
|
||||
<!-- Sub-scores inline on desktop -->
|
||||
<div style="border-left:1px solid var(--color-border);padding-left:16px;display:flex;gap:16px;">
|
||||
<div style="text-align:center;">
|
||||
<div style="font-family:var(--font-display);font-size:20px;font-weight:300;color:var(--red-dark);">4</div>
|
||||
<div style="font-size:10px;color:var(--color-text-muted);">Quellen</div>
|
||||
</div>
|
||||
<div style="text-align:center;">
|
||||
<div style="font-family:var(--font-display);font-size:20px;font-weight:300;color:var(--yellow-text);">7</div>
|
||||
<div style="font-size:10px;color:var(--color-text-muted);">Zutaten</div>
|
||||
</div>
|
||||
<div style="text-align:center;">
|
||||
<div style="font-family:var(--font-display);font-size:20px;font-weight:300;color:var(--green-dark);">8</div>
|
||||
<div style="font-size:10px;color:var(--color-text-muted);">Aufwand</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Warnings full-width -->
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:10px;">Hinweise</div>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Tofu mehrfach diese Woche</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mo</span><span class="wcard-recipe">Tofu-Gemüse-Pfanne</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wcard">
|
||||
<div class="wcard-hd"><div class="wcard-hd-t">Paprika in mehreren Gerichten</div></div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Di</span><span class="wcard-recipe">Paprika-Linsen-Eintopf</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
<div class="wcard-row">
|
||||
<div class="wcard-left"><span class="wcard-day">Mi</span><span class="wcard-recipe">Tofu-Curry mit Reis</span></div>
|
||||
<span class="wcard-swap">Tauschen →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notes notes-g">
|
||||
<div class="notes-lbl">Design-Notizen V2</div>
|
||||
<ul>
|
||||
<li>Hinweise erscheinen direkt unter dem Score — kein Scrollen nötig auf typischen Telefon-Bildschirmen.</li>
|
||||
<li>Kompakter Score-Strip auf Mobile spart ~80px gegenüber dem aktuellen großen Hero — mehr Raum für die eigentlichen Tausch-Aktionen.</li>
|
||||
<li>Desktop: Sub-Scores werden als kompakte Zahlen-Spalte in die Score-Leiste integriert — kein separater Abschnitt mehr nötig.</li>
|
||||
<li>Native <details> auf Mobile braucht kein JavaScript; funktioniert auch ohne hydration.</li>
|
||||
<li>"2 Hinweise" im Score-Strip auf Desktop gibt dem Planer sofort Kontext, ohne zu scrollen.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="divider"/>
|
||||
|
||||
|
||||
<!-- ═══════════════════════════════════════
|
||||
V3 — HINWEISE ZUERST
|
||||
════════════════════════════════════════ -->
|
||||
<div class="section">
|
||||
<div class="var-head var-p">
|
||||
<div class="var-num">V3</div>
|
||||
<div>
|
||||
<div class="var-id">Variation 3</div>
|
||||
<div class="var-title">Hinweise zuerst</div>
|
||||
<div class="var-desc">Invertiertes Layout: die Seite öffnet mit den konkreten Problem-Karten — groß und klar. Score und Breakdown erscheinen darunter als unterstützende Information. Jede Warnung ist eine eigenständige "Aufgaben-Karte" mit prominentem Tausch-Button statt Link.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="previews">
|
||||
|
||||
<!-- Mobile V3 -->
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Mobile · 320px</div>
|
||||
<div class="phone">
|
||||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||||
<div class="mtb">
|
||||
<div class="mtb-back">‹</div>
|
||||
<div class="mtb-t">Abwechslungs-Analyse</div>
|
||||
</div>
|
||||
<div style="flex:1;overflow-y:auto;padding:16px;">
|
||||
|
||||
<!-- Problem cards — full width, prominent -->
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:10px;">Was zu tun ist</div>
|
||||
|
||||
<!-- Problem card 1 -->
|
||||
<div style="border:1px solid var(--yellow-light);border-radius:var(--radius-xl);overflow:hidden;margin-bottom:10px;background:var(--yellow-tint);">
|
||||
<div style="padding:12px 14px;border-bottom:1px solid var(--yellow-light);">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--yellow-text);margin-bottom:2px;">Quellen-Wiederholung</div>
|
||||
<div style="font-size:14px;font-weight:500;color:var(--color-text);">Tofu an 2 Tagen</div>
|
||||
</div>
|
||||
<!-- Row 1 -->
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;border-bottom:1px solid rgba(249,224,138,.5);">
|
||||
<div style="display:flex;flex-direction:column;gap:1px;">
|
||||
<div style="font-size:10px;font-weight:600;color:var(--yellow-text);">Montag</div>
|
||||
<div style="font-size:13px;font-weight:500;color:var(--color-text);">Tofu-Gemüse-Pfanne</div>
|
||||
</div>
|
||||
<a href="#" style="font-size:12px;font-weight:600;padding:6px 12px;background:var(--yellow-text);color:#fff;border-radius:var(--radius-md);white-space:nowrap;text-decoration:none;">Tauschen</a>
|
||||
</div>
|
||||
<!-- Row 2 -->
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;">
|
||||
<div style="display:flex;flex-direction:column;gap:1px;">
|
||||
<div style="font-size:10px;font-weight:600;color:var(--yellow-text);">Mittwoch</div>
|
||||
<div style="font-size:13px;font-weight:500;color:var(--color-text);">Tofu-Curry mit Reis</div>
|
||||
</div>
|
||||
<a href="#" style="font-size:12px;font-weight:600;padding:6px 12px;background:var(--yellow-text);color:#fff;border-radius:var(--radius-md);white-space:nowrap;text-decoration:none;">Tauschen</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Problem card 2 -->
|
||||
<div style="border:1px solid var(--yellow-light);border-radius:var(--radius-xl);overflow:hidden;margin-bottom:16px;background:var(--yellow-tint);">
|
||||
<div style="padding:12px 14px;border-bottom:1px solid var(--yellow-light);">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--yellow-text);margin-bottom:2px;">Zutaten-Überschneidung</div>
|
||||
<div style="font-size:14px;font-weight:500;color:var(--color-text);">Paprika an 2 aufeinanderfolgenden Tagen</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;border-bottom:1px solid rgba(249,224,138,.5);">
|
||||
<div style="display:flex;flex-direction:column;gap:1px;">
|
||||
<div style="font-size:10px;font-weight:600;color:var(--yellow-text);">Dienstag</div>
|
||||
<div style="font-size:13px;font-weight:500;color:var(--color-text);">Paprika-Linsen-Eintopf</div>
|
||||
</div>
|
||||
<a href="#" style="font-size:12px;font-weight:600;padding:6px 12px;background:var(--yellow-text);color:#fff;border-radius:var(--radius-md);white-space:nowrap;text-decoration:none;">Tauschen</a>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;">
|
||||
<div style="display:flex;flex-direction:column;gap:1px;">
|
||||
<div style="font-size:10px;font-weight:600;color:var(--yellow-text);">Mittwoch</div>
|
||||
<div style="font-size:13px;font-weight:500;color:var(--color-text);">Tofu-Curry mit Reis</div>
|
||||
</div>
|
||||
<a href="#" style="font-size:12px;font-weight:600;padding:6px 12px;background:var(--yellow-text);color:#fff;border-radius:var(--radius-md);white-space:nowrap;text-decoration:none;">Tauschen</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Score — secondary, at bottom -->
|
||||
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:14px 16px;">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:8px;">Gesamt-Score</div>
|
||||
<div style="display:flex;align-items:baseline;gap:8px;margin-bottom:8px;">
|
||||
<span class="score-num" style="font-size:36px;color:var(--color-text);">5.8</span>
|
||||
<span style="font-size:13px;color:var(--color-text-muted);">/ 10 · Verbesserbar</span>
|
||||
</div>
|
||||
<div class="prog"><div class="prog-fill" style="width:58%;"></div></div>
|
||||
<div class="det" style="margin-top:10px;">
|
||||
<details>
|
||||
<summary style="font-size:11px;color:var(--color-text-muted);cursor:default;">Aufschlüsselung anzeigen</summary>
|
||||
<div style="padding-top:8px;">
|
||||
<div class="sb-row"><span class="sb-label">Quellen-Vielfalt</span><span class="sb-val" style="color:var(--red-dark);">4 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Zutaten-Überschneidung</span><span class="sb-val" style="color:var(--yellow-text);">7 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Aufwandsbalance</span><span class="sb-val" style="color:var(--green-dark);">8 / 10</span></div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="mbt">
|
||||
<div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Plan</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Einkauf</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🍳</div><div class="mt-l">Rezepte</div></div>
|
||||
<div class="mt-i a"><div class="mt-ic">📊</div><div class="mt-l">Analyse</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop V3 -->
|
||||
<div class="prev-col" style="flex:1;min-width:580px;">
|
||||
<div class="bp-lbl">Desktop · 1040px</div>
|
||||
<div class="desk">
|
||||
<div class="dsb">
|
||||
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥦</div><div class="dsb-nm">Mealprep</div></div><div class="dsb-sub">Familie Raddatz</div></div>
|
||||
<div class="dsb-nav">
|
||||
<div class="dsb-nl">Planung</div>
|
||||
<div class="dsb-ni a"><span class="dsb-nc">📅</span> Wochenplan</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkaufsliste</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🍳</span> Rezepte</div>
|
||||
<div class="dsb-nl" style="margin-top:12px;">Haushalt</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">⚙️</span> Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dm">
|
||||
<div class="dtb">
|
||||
<span class="dtb-bc">Planer /</span>
|
||||
<span class="dtb-t">Abwechslungs-Analyse</span>
|
||||
</div>
|
||||
<div class="dmc">
|
||||
<div style="display:grid;grid-template-columns:1fr 240px;gap:24px;">
|
||||
<!-- Left: problem cards -->
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:10px;">Was zu tun ist</div>
|
||||
|
||||
<div style="border:1px solid var(--yellow-light);border-radius:var(--radius-xl);overflow:hidden;margin-bottom:10px;background:var(--yellow-tint);">
|
||||
<div style="padding:10px 16px;border-bottom:1px solid var(--yellow-light);">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--yellow-text);margin-bottom:1px;">Quellen-Wiederholung</div>
|
||||
<div style="font-size:14px;font-weight:500;color:var(--color-text);">Tofu an 2 Tagen</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 16px;border-bottom:1px solid rgba(249,224,138,.5);">
|
||||
<div style="display:flex;align-items:baseline;gap:10px;">
|
||||
<span style="font-size:11px;font-weight:600;color:var(--yellow-text);width:60px;">Montag</span>
|
||||
<span style="font-size:13px;font-weight:500;color:var(--color-text);">Tofu-Gemüse-Pfanne</span>
|
||||
</div>
|
||||
<a href="#" style="font-size:12px;font-weight:600;padding:6px 14px;background:var(--yellow-text);color:#fff;border-radius:var(--radius-md);white-space:nowrap;text-decoration:none;">Tauschen</a>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 16px;">
|
||||
<div style="display:flex;align-items:baseline;gap:10px;">
|
||||
<span style="font-size:11px;font-weight:600;color:var(--yellow-text);width:60px;">Mittwoch</span>
|
||||
<span style="font-size:13px;font-weight:500;color:var(--color-text);">Tofu-Curry mit Reis</span>
|
||||
</div>
|
||||
<a href="#" style="font-size:12px;font-weight:600;padding:6px 14px;background:var(--yellow-text);color:#fff;border-radius:var(--radius-md);white-space:nowrap;text-decoration:none;">Tauschen</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border:1px solid var(--yellow-light);border-radius:var(--radius-xl);overflow:hidden;background:var(--yellow-tint);">
|
||||
<div style="padding:10px 16px;border-bottom:1px solid var(--yellow-light);">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--yellow-text);margin-bottom:1px;">Zutaten-Überschneidung</div>
|
||||
<div style="font-size:14px;font-weight:500;color:var(--color-text);">Paprika an 2 aufeinanderfolgenden Tagen</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 16px;border-bottom:1px solid rgba(249,224,138,.5);">
|
||||
<div style="display:flex;align-items:baseline;gap:10px;">
|
||||
<span style="font-size:11px;font-weight:600;color:var(--yellow-text);width:60px;">Dienstag</span>
|
||||
<span style="font-size:13px;font-weight:500;color:var(--color-text);">Paprika-Linsen-Eintopf</span>
|
||||
</div>
|
||||
<a href="#" style="font-size:12px;font-weight:600;padding:6px 14px;background:var(--yellow-text);color:#fff;border-radius:var(--radius-md);white-space:nowrap;text-decoration:none;">Tauschen</a>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 16px;">
|
||||
<div style="display:flex;align-items:baseline;gap:10px;">
|
||||
<span style="font-size:11px;font-weight:600;color:var(--yellow-text);width:60px;">Mittwoch</span>
|
||||
<span style="font-size:13px;font-weight:500;color:var(--color-text);">Tofu-Curry mit Reis</span>
|
||||
</div>
|
||||
<a href="#" style="font-size:12px;font-weight:600;padding:6px 14px;background:var(--yellow-text);color:#fff;border-radius:var(--radius-md);white-space:nowrap;text-decoration:none;">Tauschen</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: score panel -->
|
||||
<div>
|
||||
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;margin-bottom:12px;">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:8px;">Score</div>
|
||||
<div style="display:flex;align-items:baseline;gap:6px;margin-bottom:8px;">
|
||||
<span class="score-num" style="font-size:40px;color:var(--color-text);">5.8</span>
|
||||
<span style="font-size:13px;color:var(--color-text-muted);">/ 10</span>
|
||||
</div>
|
||||
<div style="font-size:12px;font-weight:500;color:var(--yellow-text);margin-bottom:6px;">Verbesserbar</div>
|
||||
<div class="prog"><div class="prog-fill" style="width:58%;"></div></div>
|
||||
</div>
|
||||
<div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;">
|
||||
<div style="font-size:10px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:10px;">Aufschlüsselung</div>
|
||||
<div class="sb-row"><span class="sb-label">Quellen</span><span class="sb-val" style="color:var(--red-dark);">4 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Zutaten</span><span class="sb-val" style="color:var(--yellow-text);">7 / 10</span></div>
|
||||
<div class="sb-row"><span class="sb-label">Aufwand</span><span class="sb-val" style="color:var(--green-dark);">8 / 10</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notes notes-p">
|
||||
<div class="notes-lbl">Design-Notizen V3</div>
|
||||
<ul>
|
||||
<li>Klarer Fokus: Das erste, was der Planer sieht, ist "Was zu tun ist" — keine Score-Hierarchie die von der Aktion ablenkt.</li>
|
||||
<li>Prominente "Tauschen"-Buttons (gefüllt, dunkelgelb) statt Links — erhöht die Tipp-Fläche auf Mobile und macht die Aktion offensichtlicher.</li>
|
||||
<li>Voller Wochentag ("Montag" statt "Mo") — lesbarer, besonders auf Desktop.</li>
|
||||
<li>Schwachstelle: Wenn es keine Hinweise gibt (Score ≥ 9), wirkt die Seite leer — der Score müsste dann nach oben rücken. Erfordert einen separaten Empty-State.</li>
|
||||
<li>Höherer Umbauaufwand gegenüber V1 und V2 — die Page-Struktur ändert sich grundlegend.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="divider"/>
|
||||
|
||||
<!-- Comparison -->
|
||||
<div class="section">
|
||||
<div class="section-label">Vergleich</div>
|
||||
<table class="ct">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Kriterium</th>
|
||||
<th style="color:var(--yellow-text);">V1 Erweiterte Karten</th>
|
||||
<th style="color:var(--green-dark);">V2 Aktions-Liste ★</th>
|
||||
<th style="color:var(--purple-dark);">V3 Hinweise zuerst</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Rezeptnamen sichtbar</td>
|
||||
<td>✓ Ja</td>
|
||||
<td>✓ Ja</td>
|
||||
<td>✓ Ja, prominent</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Direkter Tausch</td>
|
||||
<td>Link</td>
|
||||
<td>Link</td>
|
||||
<td>Button (größere Tap-Fläche)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hinweise sichtbar ohne Scrollen</td>
|
||||
<td>Nein (Score + Breakdown zuerst)</td>
|
||||
<td>Ja (direkt unter kompaktem Score)</td>
|
||||
<td>Ja (ganz oben)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Umbauaufwand</td>
|
||||
<td>Niedrig</td>
|
||||
<td>Mittel</td>
|
||||
<td>Hoch</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Layout-Änderung</td>
|
||||
<td>Keine</td>
|
||||
<td>Score kompakter, Details kollabierbar</td>
|
||||
<td>Grundlegende Umstrukturierung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Empfehlung</td>
|
||||
<td>Wenn schnelle Lieferung Prio</td>
|
||||
<td><strong>Empfohlen ★</strong></td>
|
||||
<td>Wenn Aktions-Fokus Prio</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
981
specs/frontend/e4-variety-settings-kachel.html
Normal file
981
specs/frontend/e4-variety-settings-kachel.html
Normal file
@@ -0,0 +1,981 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Recipe App — E4 Vielfalt-Einstellungen · Implementierungsspezifikation</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"/>
|
||||
<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;--green-deeper:#1E4A26;--yellow-tint:#FDF6D8;--yellow-light:#F9E08A;--yellow:#F2C12E;--yellow-dark:#C49610;--yellow-text:#8A6800;--blue-tint:#E6F1FB;--blue-light:#A4CFF4;--blue:#2D7DD2;--blue-dark:#185FA5;--purple-tint:#EEEDFE;--purple-light:#CECBF6;--purple:#534AB7;--purple-dark:#3C3489;--orange-tint:#FEF0E6;--orange:#E8862A;--orange-dark:#B46820;--red-tint:#FDECEA;--red:#DC4C3E;--red-dark:#B03328;--color-error:#DC4C3E;--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;--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,.08),0 2px 4px rgba(28,28,24,.04);--shadow-overlay:0 8px 32px rgba(28,28,24,.12),0 2px 8px rgba(28,28,24,.06);}
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:var(--font-sans);background:#E8E7E2;color:var(--color-text);font-size:14px;line-height:1.6;}
|
||||
.doc{max-width:1200px;margin:0 auto;padding:48px 40px 120px;}
|
||||
.doc-header{display:flex;justify-content:space-between;align-items:flex-end;padding-bottom:28px;border-bottom:1px solid var(--color-border);margin-bottom:48px;background:var(--color-page);margin:-48px -40px 48px;padding:48px 40px 28px;border-radius:var(--radius-xl) var(--radius-xl) 0 0;}
|
||||
.doc-header h1{font-family:var(--font-display);font-size:28px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
||||
.doc-header p{font-size:13px;color:var(--color-text-muted);}
|
||||
.doc-meta{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);text-align:right;line-height:1.9;}
|
||||
.pill{display:inline-block;padding:2px 8px;border-radius:var(--radius-sm);font-size:10px;font-weight:500;letter-spacing:.05em;background:var(--green-tint);color:var(--green-dark);}
|
||||
.section{margin-bottom:64px;}
|
||||
.section-title{font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:24px;}
|
||||
.prose{font-size:13px;color:var(--color-text-muted);line-height:1.65;max-width:720px;margin-bottom:20px;}
|
||||
/* Journey header */
|
||||
.jh{padding:20px 24px;border-radius:var(--radius-xl);margin-bottom:40px;display:flex;align-items:center;gap:16px;}
|
||||
.jh .jn{font-family:var(--font-display);font-size:48px;font-weight:300;line-height:1;opacity:.5;}
|
||||
.jh h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
||||
.jh p{font-size:13px;line-height:1.5;}.jh .fl{font-family:var(--font-mono);font-size:11px;margin-top:6px;opacity:.7;}
|
||||
.jh-p{background:var(--purple-tint);border:1px solid var(--purple-light);}.jh-p .jn{color:var(--purple);}.jh-p p,.jh-p .fl{color:var(--purple-dark);}
|
||||
/* Screen block */
|
||||
.scr{margin-bottom:56px;}
|
||||
.scr-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;}
|
||||
.scr-head h3{font-family:var(--font-display);font-size:20px;font-weight:500;letter-spacing:-.02em;}
|
||||
.scr-id{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);padding:2px 8px;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-page);}
|
||||
.scr-desc{font-size:12px;color:var(--color-text-muted);line-height:1.6;max-width:720px;margin-bottom:6px;}
|
||||
.scr-var{font-size:11px;color:var(--color-text-muted);margin-bottom:20px;}.scr-var strong{color:var(--color-text);}
|
||||
/* Device frames */
|
||||
.previews{display:flex;gap:32px;flex-wrap:wrap;justify-content:center;align-items:flex-start;margin-bottom:20px;}
|
||||
.prev-col{display:flex;flex-direction:column;align-items:center;gap:10px;}
|
||||
.bp-lbl{font-family:var(--font-mono);font-size:10px;color:var(--color-text-muted);}
|
||||
.phone{width:320px;flex-shrink:0;background:var(--color-page);border-radius:36px;overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.07);display:flex;flex-direction:column;border:6px solid #1C1C18;}
|
||||
.pst{padding:10px 20px 0;display:flex;justify-content:space-between;align-items:center;font-size:12px;background:var(--color-page);}.pst b{font-weight:600;}.pst span{font-size:10px;}
|
||||
.pb{flex:1;overflow-y:auto;display:flex;flex-direction:column;}
|
||||
.desk{width:100%;max-width:1040px;background:var(--color-page);border-radius:var(--radius-xl);overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.06);display:flex;min-height:520px;}
|
||||
/* Agent spec block */
|
||||
.agent{background:var(--color-text);color:#E8E8E2;padding:24px;border-radius:var(--radius-lg);margin-top:20px;}
|
||||
.agent h4{font-size:9px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:#5A5A55;margin-bottom:12px;}
|
||||
.agent pre{font-family:var(--font-mono);font-size:10px;color:#444440;margin-bottom:16px;line-height:1.8;white-space:pre-wrap;}
|
||||
.at{width:100%;border-collapse:collapse;font-family:var(--font-mono);font-size:10px;}
|
||||
.at thead tr{border-bottom:1px solid #2A2A26;}.at th{text-align:left;padding:6px 10px;font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#5A5A55;font-family:var(--font-sans);}.at td{padding:5px 10px;border-bottom:1px solid #1E1E1A;vertical-align:top;line-height:1.5;}.at tr:last-child td{border-bottom:none;}.at td:first-child{color:#7A7A72;}.at td:nth-child(2){color:#E8E8E2;font-weight:500;}.at td:nth-child(3){color:#5A5A55;}.at .grp td{padding-top:14px;font-family:var(--font-sans);font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#3A3A36;}
|
||||
/* LLM section */
|
||||
.llm{background:var(--color-page);border:2px solid var(--green);border-radius:var(--radius-xl);padding:32px 40px;margin-top:64px;}
|
||||
.llm h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:8px;color:var(--green-dark);}
|
||||
.llm h3{font-size:14px;font-weight:600;margin:20px 0 8px;color:var(--color-text);}
|
||||
.llm p,.llm li{font-size:13px;color:var(--color-text-muted);line-height:1.65;}
|
||||
.llm ul,.llm ol{padding-left:20px;margin-bottom:12px;}
|
||||
.llm li{margin-bottom:4px;}
|
||||
.llm code{font-family:var(--font-mono);font-size:11px;background:var(--color-surface);padding:1px 5px;border-radius:3px;}
|
||||
.llm table{width:100%;border-collapse:collapse;margin:12px 0;font-size:12px;}
|
||||
.llm th,.llm td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--color-border);}
|
||||
.llm th{font-weight:500;color:var(--color-text);font-size:11px;text-transform:uppercase;letter-spacing:.05em;}
|
||||
.llm td{color:var(--color-text-muted);}
|
||||
/* Shared nav chrome */
|
||||
.mtb{padding:10px 16px;background:var(--color-page);border-bottom:1px solid var(--color-border);display:flex;align-items:center;gap:10px;}
|
||||
.mtb-back{font-size:12px;color:var(--color-text-muted);display:flex;align-items:center;gap:4px;flex-shrink:0;}
|
||||
.mtb-t{font-family:var(--font-display);font-size:18px;font-weight:500;letter-spacing:-.02em;flex:1;}
|
||||
.mbt{border-top:1px solid var(--color-border);background:var(--color-surface);padding:8px 16px 28px;display:flex;justify-content:space-around;flex-shrink:0;}
|
||||
.mt-i{display:flex;flex-direction:column;align-items:center;gap:2px;}
|
||||
.mt-ic{width:20px;height:20px;border-radius:4px;background:var(--color-subtle);display:flex;align-items:center;justify-content:center;font-size:11px;}
|
||||
.mt-i.a .mt-ic{background:var(--green-tint);}
|
||||
.mt-l{font-size:9px;font-weight:500;color:var(--color-text-muted);}
|
||||
.mt-i.a .mt-l{color:var(--green-dark);}
|
||||
.dsb{width:224px;flex-shrink:0;background:var(--color-surface);border-right:1px solid var(--color-border);display:flex;flex-direction:column;}
|
||||
.dsb-logo{padding:20px 16px 16px;border-bottom:1px solid var(--color-border);}
|
||||
.dsb-lm{display:flex;align-items:center;gap:8px;margin-bottom:2px;}
|
||||
.dsb-ic{width:24px;height:24px;border-radius:5px;background:var(--green);display:flex;align-items:center;justify-content:center;font-size:12px;}
|
||||
.dsb-nm{font-family:var(--font-display);font-size:16px;font-weight:500;letter-spacing:-.02em;}
|
||||
.dsb-sub{font-size:10px;color:var(--color-text-muted);padding-left:32px;}
|
||||
.dsb-nav{padding:12px 10px;flex:1;}
|
||||
.dsb-nl{font-size:8px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);padding:0 8px;margin-bottom:4px;}
|
||||
.dsb-ni{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:var(--radius-md);font-size:13px;color:var(--color-text-muted);margin-bottom:2px;}
|
||||
.dsb-ni.a{background:var(--green-tint);color:var(--green-dark);font-weight:500;}
|
||||
.dsb-nc{font-size:13px;width:18px;text-align:center;}
|
||||
.dm{flex:1;display:flex;flex-direction:column;min-width:0;}
|
||||
.dtb{padding:14px 24px;border-bottom:1px solid var(--color-border);display:flex;justify-content:space-between;align-items:center;flex-shrink:0;}
|
||||
.dtb-t{font-family:var(--font-display);font-size:18px;font-weight:500;letter-spacing:-.02em;}
|
||||
.dtb-bc{font-size:12px;color:var(--color-text-muted);display:flex;align-items:center;gap:6px;margin-bottom:2px;}
|
||||
.dtb-bc span{color:var(--color-border);}
|
||||
.dmc{padding:24px;flex:1;overflow-y:auto;}
|
||||
/* UI components */
|
||||
.tog{display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-bottom:1px solid var(--color-subtle);}
|
||||
.tog:last-child{border-bottom:none;}
|
||||
.tog-l{display:flex;flex-direction:column;gap:2px;}
|
||||
.tog-name{font-size:13px;font-weight:500;}
|
||||
.tog-hint{font-size:11px;color:var(--color-text-muted);}
|
||||
.tog-sw{width:36px;height:20px;border-radius:10px;background:var(--green);flex-shrink:0;position:relative;}
|
||||
.tog-sw::after{content:'';position:absolute;width:16px;height:16px;border-radius:50%;background:#fff;top:2px;right:2px;box-shadow:0 1px 3px rgba(0,0,0,.2);}
|
||||
.tog-sw.off{background:var(--color-border);}
|
||||
.tog-sw.off::after{right:auto;left:2px;}
|
||||
.seg{display:flex;border:1px solid var(--color-border);border-radius:var(--radius-md);overflow:hidden;background:var(--color-surface);}
|
||||
.seg-o{flex:1;text-align:center;font-size:11px;font-weight:500;padding:6px 0;color:var(--color-text-muted);}
|
||||
.seg-o.a{background:var(--color-page);color:var(--color-text);box-shadow:var(--shadow-card);}
|
||||
.seg-o.a-r{background:var(--red-tint);color:var(--red-dark);}
|
||||
.seg-o.a-g{background:var(--green-tint);color:var(--green-dark);}
|
||||
.grp{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;margin-bottom:12px;}
|
||||
.grp-hd{padding:10px 14px;border-bottom:1px solid var(--color-border);background:var(--color-subtle);}
|
||||
.grp-hd-t{font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);}
|
||||
.grp-b{padding:0 14px;}
|
||||
.wr{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--color-subtle);}
|
||||
.wr:last-child{border-bottom:none;}
|
||||
.wr-l{display:flex;flex-direction:column;gap:1px;min-width:0;}
|
||||
.wr-name{font-size:12px;font-weight:500;}
|
||||
.wr-sub{font-size:10px;color:var(--color-text-muted);}
|
||||
/* Context chips */
|
||||
.ctx-chips{display:flex;gap:8px;margin-bottom:16px;}
|
||||
.ctx-chip{flex:1;padding:14px 12px;border-radius:var(--radius-xl);border:1.5px solid var(--color-border);background:var(--color-surface);display:flex;flex-direction:column;gap:3px;cursor:default;}
|
||||
.ctx-chip.sel{border-color:var(--green-light);background:var(--green-tint);}
|
||||
.ctx-chip.ind{border-color:var(--purple-light);background:var(--purple-tint);}
|
||||
.ctx-em{font-size:18px;}
|
||||
.ctx-name{font-size:12px;font-weight:600;color:var(--color-text);}
|
||||
.ctx-chip.sel .ctx-name{color:var(--green-dark);}
|
||||
.ctx-chip.ind .ctx-name{color:var(--purple-dark);}
|
||||
.ctx-sub{font-size:10px;color:var(--color-text-muted);line-height:1.3;}
|
||||
.ctx-chip.sel .ctx-sub{color:var(--green-dark);opacity:.7;}
|
||||
.ctx-chip.ind .ctx-sub{color:var(--purple-dark);opacity:.7;}
|
||||
/* Summary pills */
|
||||
.s-pills{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:14px;}
|
||||
.s-pill{font-size:10px;font-weight:500;padding:3px 8px;border-radius:20px;display:flex;align-items:center;gap:3px;}
|
||||
.s-pill.on{background:var(--green-tint);color:var(--green-dark);}
|
||||
.s-pill.off{background:var(--color-subtle);color:var(--color-text-muted);}
|
||||
.s-pill.warn{background:var(--yellow-tint);color:var(--yellow-text);}
|
||||
/* Accordion */
|
||||
.acc{border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;margin-bottom:12px;}
|
||||
.acc-hd{padding:12px 14px;display:flex;justify-content:space-between;align-items:center;background:var(--color-surface);}
|
||||
.acc-hd-t{font-size:13px;font-weight:500;}
|
||||
.acc-hd-r{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--color-text-muted);}
|
||||
.acc-b{padding:14px;border-top:1px solid var(--color-border);background:var(--color-page);}
|
||||
/* Score preview */
|
||||
.score-banner{background:var(--color-text);border-radius:var(--radius-lg);padding:14px 16px;margin-bottom:14px;display:flex;align-items:center;justify-content:space-between;gap:12px;}
|
||||
.score-banner-l{}
|
||||
.score-banner-label{font-size:10px;color:#6B6A63;margin-bottom:2px;}
|
||||
.score-banner-val{font-family:var(--font-display);font-size:30px;font-weight:300;letter-spacing:-.02em;color:#E8E8E2;line-height:1;}
|
||||
.score-banner-sub{font-size:10px;margin-top:3px;}
|
||||
.score-banner-up{color:#6FCF97;}
|
||||
.score-banner-neutral{color:#6B6A63;}
|
||||
.score-banner-r{font-size:28px;opacity:.7;}
|
||||
/* Summary detail rows (desktop right column) */
|
||||
.sum-rows{display:flex;flex-direction:column;gap:5px;}
|
||||
.sum-row{display:flex;justify-content:space-between;align-items:center;padding:7px 10px;border-radius:var(--radius-md);font-size:12px;}
|
||||
.sum-row.on{background:var(--green-tint);}
|
||||
.sum-row.off{background:var(--color-subtle);}
|
||||
.sum-row.warn{background:var(--yellow-tint);}
|
||||
.sum-row-name{font-weight:500;}
|
||||
.sum-row.on .sum-row-name{color:var(--green-dark);}
|
||||
.sum-row.off .sum-row-name{color:var(--color-text-muted);}
|
||||
.sum-row.warn .sum-row-name{color:var(--yellow-text);}
|
||||
.sum-row-val{font-size:10px;font-weight:500;}
|
||||
.sum-row.on .sum-row-val{color:var(--green-dark);}
|
||||
.sum-row.off .sum-row-val{color:var(--color-text-muted);}
|
||||
.sum-row.warn .sum-row-val{color:var(--yellow-text);}
|
||||
/* Reset link */
|
||||
.reset-link{font-size:12px;color:var(--red-dark);padding:10px 0;display:block;text-align:center;}
|
||||
/* Divider */
|
||||
.sec-lbl{font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:8px;}
|
||||
/* Modal overlay */
|
||||
.overlay{position:relative;border-radius:var(--radius-xl);overflow:hidden;}
|
||||
.modal-backdrop{position:absolute;inset:0;background:rgba(28,28,24,.4);display:flex;align-items:center;justify-content:center;padding:24px;}
|
||||
.modal{background:var(--color-page);border-radius:var(--radius-xl);padding:24px;width:100%;max-width:280px;box-shadow:var(--shadow-overlay);}
|
||||
.modal-title{font-family:var(--font-display);font-size:18px;font-weight:500;letter-spacing:-.02em;margin-bottom:8px;}
|
||||
.modal-body{font-size:13px;color:var(--color-text-muted);line-height:1.6;margin-bottom:20px;}
|
||||
.modal-acts{display:flex;flex-direction:column;gap:8px;}
|
||||
.btn-dest{padding:11px 16px;border-radius:var(--radius-md);background:var(--red);color:#fff;font-weight:500;font-size:13px;text-align:center;}
|
||||
.btn-ghost{padding:11px 16px;border-radius:var(--radius-md);background:var(--color-surface);border:1px solid var(--color-border);color:var(--color-text-muted);font-weight:500;font-size:13px;text-align:center;}
|
||||
/* E1 hub card */
|
||||
.hub-grid{display:grid;grid-template-columns:2fr 1fr;gap:12px;margin-bottom:12px;}
|
||||
.hub-card{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-xl);padding:16px;}
|
||||
.hub-card.primary{border-left:3px solid var(--green-dark);}
|
||||
.hub-card.variety{border-left:3px solid var(--purple);}
|
||||
.hub-stat{font-family:var(--font-display);font-size:36px;font-weight:300;letter-spacing:-.02em;line-height:1;margin-bottom:4px;}
|
||||
.hub-stat.green{color:var(--green-dark);}
|
||||
.hub-stat.purple{color:var(--purple);}
|
||||
.hub-name{font-size:12px;font-weight:500;margin-bottom:2px;}
|
||||
.hub-sub{font-size:11px;color:var(--color-text-muted);}
|
||||
.hub-arr{font-size:12px;color:var(--color-text-muted);margin-top:10px;}
|
||||
/* Settings hub bottom row */
|
||||
.hub-row{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="doc">
|
||||
|
||||
<!-- ── Header ── -->
|
||||
<div class="doc-header">
|
||||
<div>
|
||||
<h1>E4 — Vielfalt-Einstellungen</h1>
|
||||
<p>Implementierungsspezifikation · V2 Kontext-Preset · Journey J9</p>
|
||||
</div>
|
||||
<div class="doc-meta">
|
||||
<span class="pill">v1.0</span><br>
|
||||
Screens: E1 (Update) + E4<br>
|
||||
States: 5<br>
|
||||
Rolle: Planer only
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Journey context ── -->
|
||||
<div class="jh jh-p">
|
||||
<div class="jn">J9</div>
|
||||
<div>
|
||||
<h2>Vielfalt-Algorithmus konfigurieren</h2>
|
||||
<p>Planer passt Bewertungsregeln an den Haushaltskontext an — primär das Deaktivieren der Protein-Prüfung für vegetarische Haushalte.</p>
|
||||
<div class="fl">E1 → E4 → C3 · Planer only · Auto-Save · Reset benötigt Bestätigung</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ════════════════════════════════════════
|
||||
E1 — SETTINGS HUB UPDATE
|
||||
═════════════════════════════════════════ -->
|
||||
<div class="section">
|
||||
<div class="section-title">E1 — Settings-Hub (Update)</div>
|
||||
<p class="prose">Der bestehende Settings-Hub (E1) erhält eine dritte Kachel: "Vielfalt-Einstellungen". Die Kachel zeigt den aktuellen Vielfalt-Score als Kennzahl. Das Grid-Layout wird von 2-spaltig zu einem Mix aus Hauptkachel oben und zwei gleichbreiten Kacheln unten angepasst.</p>
|
||||
|
||||
<!-- S0: E1 Hub -->
|
||||
<div class="scr">
|
||||
<div class="scr-head"><h3>S0 · Settings-Hub mit Vielfalt-Kachel</h3><span class="scr-id">E1</span></div>
|
||||
<div class="scr-desc">Die neue Vielfalt-Kachel erscheint in der unteren Reihe neben der Haushalt-Kachel. Zeigt den aktuellen Score als lila Kennzahl. Bei Score < 6.0 färbt sich die Kennzahl orange als Aufmerksamkeitshinweis.</div>
|
||||
<div class="scr-var"><strong>Änderung gegenüber E1 v1:</strong> dritte Kachel + Grid-Anpassung. Vorräte-Kachel bleibt primär (2fr oben).</div>
|
||||
|
||||
<div class="previews">
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Mobile · 320px</div>
|
||||
<div class="phone">
|
||||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||||
<div class="pb">
|
||||
<div class="mtb"><div class="mtb-t">Einstellungen</div></div>
|
||||
<div style="padding:16px;flex:1;">
|
||||
<!-- Vorräte (primary, full width) -->
|
||||
<div class="hub-card primary" style="margin-bottom:12px;">
|
||||
<div class="hub-stat green">12</div>
|
||||
<div class="hub-name">Vorräte</div>
|
||||
<div class="hub-sub">Zutaten immer vorrätig</div>
|
||||
<div class="hub-arr">Bearbeiten →</div>
|
||||
</div>
|
||||
<!-- Bottom row: 2 cards -->
|
||||
<div class="hub-row">
|
||||
<div class="hub-card">
|
||||
<div class="hub-stat" style="font-size:28px;color:var(--blue-dark);">3</div>
|
||||
<div class="hub-name">Haushalt</div>
|
||||
<div class="hub-sub">Mitglieder</div>
|
||||
<div class="hub-arr">Verwalten →</div>
|
||||
</div>
|
||||
<div class="hub-card variety">
|
||||
<div class="hub-stat purple" style="font-size:28px;">7.4</div>
|
||||
<div class="hub-name">Vielfalt</div>
|
||||
<div class="hub-sub">Diese Woche</div>
|
||||
<div class="hub-arr">Einstellungen →</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mbt">
|
||||
<div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Plan</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Einkauf</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🍳</div><div class="mt-l">Rezepte</div></div>
|
||||
<div class="mt-i a"><div class="mt-ic">⚙️</div><div class="mt-l">Einstellungen</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="prev-col" style="flex:1;min-width:600px;">
|
||||
<div class="bp-lbl">Desktop · 1040px</div>
|
||||
<div class="desk">
|
||||
<div class="dsb">
|
||||
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥦</div><div class="dsb-nm">Mealprep</div></div><div class="dsb-sub">Familie Raddatz</div></div>
|
||||
<div class="dsb-nav">
|
||||
<div class="dsb-nl">Planung</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">📅</span> Wochenplan</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkaufsliste</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🍳</span> Rezepte</div>
|
||||
<div class="dsb-nl" style="margin-top:12px;">Haushalt</div>
|
||||
<div class="dsb-ni a"><span class="dsb-nc">⚙️</span> Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dm">
|
||||
<div class="dtb"><div class="dtb-t">Einstellungen</div></div>
|
||||
<div class="dmc">
|
||||
<div style="max-width:640px;">
|
||||
<!-- Vorräte primary -->
|
||||
<div class="hub-card primary" style="margin-bottom:16px;display:flex;align-items:center;justify-content:space-between;">
|
||||
<div>
|
||||
<div class="hub-stat green">12</div>
|
||||
<div class="hub-name">Vorräte</div>
|
||||
<div class="hub-sub">Zutaten, die immer vorrätig sind und nicht auf die Einkaufsliste kommen</div>
|
||||
</div>
|
||||
<div style="font-size:13px;font-weight:500;color:var(--green-dark);">Bearbeiten →</div>
|
||||
</div>
|
||||
<!-- Bottom row: 2 cards -->
|
||||
<div class="hub-row">
|
||||
<div class="hub-card" style="display:flex;align-items:center;justify-content:space-between;">
|
||||
<div>
|
||||
<div class="hub-stat" style="font-size:28px;color:var(--blue-dark);">3</div>
|
||||
<div class="hub-name">Haushalt</div>
|
||||
<div class="hub-sub">Mitglieder & Rollen</div>
|
||||
</div>
|
||||
<div style="font-size:13px;font-weight:500;color:var(--blue-dark);">Verwalten →</div>
|
||||
</div>
|
||||
<div class="hub-card variety" style="display:flex;align-items:center;justify-content:space-between;">
|
||||
<div>
|
||||
<div class="hub-stat purple" style="font-size:28px;">7.4</div>
|
||||
<div class="hub-name">Vielfalt-Einstellungen</div>
|
||||
<div class="hub-sub">Algorithmus anpassen</div>
|
||||
</div>
|
||||
<div style="font-size:13px;font-weight:500;color:var(--purple-dark);">Einstellungen →</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="agent">
|
||||
<h4>E1 Hub Update · S0</h4>
|
||||
<pre>/* E1 grid: Vorräte (full width, 2fr, border-left: 3px solid --green-dark) on top row.
|
||||
* Bottom row: 2 equal columns — Haushalt + Vielfalt-Einstellungen.
|
||||
* Vielfalt card: border-left: 3px solid --purple. Stat color: --purple (7.4).
|
||||
* If score < 6.0: stat color switches to --orange (Aufmerksamkeit) with no other change.
|
||||
* Score value: load from GET /v1/week-plans?weekStart=current → GET /v1/week-plans/{id}/variety-score.
|
||||
* If no current plan: show "–" as stat value, sub: "Kein Plan".
|
||||
* Tap/click Vielfalt card → navigate to E4. */</pre>
|
||||
<table class="at"><thead><tr><th>Element</th><th>Wert</th><th>Notizen</th></tr></thead><tbody>
|
||||
<tr class="grp"><td colspan="3">Vielfalt-Kachel</td></tr>
|
||||
<tr><td>Kennzahl</td><td>varietyScore.score, 1 Dezimalstelle</td><td>Farbe: --purple normal, --orange wenn < 6.0</td></tr>
|
||||
<tr><td>Label</td><td>Vielfalt-Einstellungen</td><td>Desktop; Mobile: "Vielfalt"</td></tr>
|
||||
<tr><td>Sub-Label</td><td>"Diese Woche" / "Kein Plan" / "–"</td><td>Kein Plan = kein weekPlan für aktuelle Woche</td></tr>
|
||||
<tr><td>Rand</td><td>border-left: 3px solid --purple</td><td>Analog zu Vorräte → --green-dark</td></tr>
|
||||
<tr><td>Aktion</td><td>Tap → navigate /settings/variety</td><td>Route: +page.svelte unter (app)/settings/variety/</td></tr>
|
||||
<tr class="grp"><td colspan="3">Grid-Layout</td></tr>
|
||||
<tr><td>Mobile</td><td>Vorräte fullwidth + grid-template-columns: 1fr 1fr unten</td><td>Gap: 12px</td></tr>
|
||||
<tr><td>Desktop</td><td>Vorräte fullwidth + grid-template-columns: 1fr 1fr unten</td><td>Max-width: 640px, gap: 16px</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ════════════════════════════════════════
|
||||
S1 — DEFAULT (KEIN CUSTOM-CONFIG)
|
||||
═════════════════════════════════════════ -->
|
||||
<div class="section">
|
||||
<div class="section-title">E4 — Vielfalt-Einstellungen · States</div>
|
||||
|
||||
<div class="scr">
|
||||
<div class="scr-head"><h3>S1 · Standard (kein Custom-Config)</h3><span class="scr-id">E4</span></div>
|
||||
<div class="scr-desc">Erster Aufruf, kein haushaltsindividueller Config-Eintrag. Omnivor-Chip ist ausgewählt (Default-Zustand). Score-Preview zeigt den aktuellen tatsächlichen Score — keine Simulation nötig, da noch nichts geändert wurde. Hinweis-Text erklärt kurz den Zweck der Seite.</div>
|
||||
<div class="scr-var"><strong>S1</strong> · Omnivor selected · Score-Preview = aktueller Score · Erweiterte Einstellungen eingeklappt</div>
|
||||
|
||||
<div class="previews">
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Mobile · 320px</div>
|
||||
<div class="phone">
|
||||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||||
<div class="pb">
|
||||
<div class="mtb"><div class="mtb-back">← Einstellungen</div><div class="mtb-t">Vielfalt</div></div>
|
||||
<div style="padding:16px;flex:1;overflow-y:auto;">
|
||||
<div style="font-size:12px;color:var(--color-text-muted);line-height:1.6;margin-bottom:14px;">Passe den Algorithmus an deinen Haushalt an. Änderungen werden sofort übernommen.</div>
|
||||
|
||||
<div class="sec-lbl">Haushaltskontext</div>
|
||||
<div class="ctx-chips">
|
||||
<div class="ctx-chip sel">
|
||||
<div class="ctx-em">🥩</div>
|
||||
<div class="ctx-name">Omnivor</div>
|
||||
<div class="ctx-sub">Alle Regeln aktiv</div>
|
||||
</div>
|
||||
<div class="ctx-chip">
|
||||
<div class="ctx-em">🥦</div>
|
||||
<div class="ctx-name">Vegetarisch</div>
|
||||
<div class="ctx-sub">Protein deaktiviert</div>
|
||||
</div>
|
||||
<div class="ctx-chip">
|
||||
<div class="ctx-em">🌱</div>
|
||||
<div class="ctx-name">Vegan</div>
|
||||
<div class="ctx-sub">Protein deaktiviert</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-lbl">Aktive Regeln</div>
|
||||
<div class="s-pills">
|
||||
<div class="s-pill on">✓ Protein</div>
|
||||
<div class="s-pill on">✓ Küche</div>
|
||||
<div class="s-pill on">✓ Zutaten · Mittel</div>
|
||||
<div class="s-pill on">✓ Letzte Wochen · Mittel</div>
|
||||
<div class="s-pill warn">⚠ Duplikate · Hoch</div>
|
||||
</div>
|
||||
|
||||
<div class="acc">
|
||||
<div class="acc-hd">
|
||||
<div class="acc-hd-t">Erweiterte Einstellungen</div>
|
||||
<div class="acc-hd-r">▸</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="score-banner">
|
||||
<div class="score-banner-l">
|
||||
<div class="score-banner-label">Aktueller Score</div>
|
||||
<div class="score-banner-val">7.4</div>
|
||||
<div class="score-banner-sub score-banner-neutral">Keine Änderungen</div>
|
||||
</div>
|
||||
<div class="score-banner-r">📊</div>
|
||||
</div>
|
||||
|
||||
<div class="reset-link" style="color:var(--color-text-muted);">Bereits Standard-Einstellungen</div>
|
||||
</div>
|
||||
<div class="mbt">
|
||||
<div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Plan</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Einkauf</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🍳</div><div class="mt-l">Rezepte</div></div>
|
||||
<div class="mt-i a"><div class="mt-ic">⚙️</div><div class="mt-l">Einstellungen</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="prev-col" style="flex:1;min-width:600px;">
|
||||
<div class="bp-lbl">Desktop · 1040px</div>
|
||||
<div class="desk">
|
||||
<div class="dsb">
|
||||
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥦</div><div class="dsb-nm">Mealprep</div></div><div class="dsb-sub">Familie Raddatz</div></div>
|
||||
<div class="dsb-nav">
|
||||
<div class="dsb-nl">Planung</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">📅</span> Wochenplan</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkaufsliste</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🍳</span> Rezepte</div>
|
||||
<div class="dsb-nl" style="margin-top:12px;">Haushalt</div>
|
||||
<div class="dsb-ni a"><span class="dsb-nc">⚙️</span> Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dm">
|
||||
<div class="dtb"><div><div class="dtb-bc">Einstellungen <span>›</span> Vielfalt-Einstellungen</div><div class="dtb-t">Vielfalt-Einstellungen</div></div></div>
|
||||
<div class="dmc">
|
||||
<div style="display:grid;grid-template-columns:1fr 280px;gap:24px;max-width:800px;">
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--color-text-muted);line-height:1.6;margin-bottom:16px;">Passe den Algorithmus an deinen Haushaltskontext an. Änderungen werden sofort übernommen und wirken sich auf den nächsten Score-Abruf aus.</div>
|
||||
<div class="sec-lbl">Haushaltskontext</div>
|
||||
<div class="ctx-chips" style="margin-bottom:20px;">
|
||||
<div class="ctx-chip sel"><div class="ctx-em">🥩</div><div class="ctx-name">Omnivor</div><div class="ctx-sub">Alle Regeln aktiv</div></div>
|
||||
<div class="ctx-chip"><div class="ctx-em">🥦</div><div class="ctx-name">Vegetarisch</div><div class="ctx-sub">Protein deaktiviert</div></div>
|
||||
<div class="ctx-chip"><div class="ctx-em">🌱</div><div class="ctx-name">Vegan</div><div class="ctx-sub">Protein deaktiviert</div></div>
|
||||
</div>
|
||||
<div class="acc">
|
||||
<div class="acc-hd"><div class="acc-hd-t">Erweiterte Einstellungen</div><div class="acc-hd-r">▸</div></div>
|
||||
</div>
|
||||
<div class="reset-link" style="text-align:left;color:var(--color-text-muted);">Bereits Standard-Einstellungen</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="score-banner">
|
||||
<div>
|
||||
<div class="score-banner-label">Aktueller Score</div>
|
||||
<div class="score-banner-val">7.4 <span style="font-size:13px;opacity:.5;">/ 10</span></div>
|
||||
<div class="score-banner-sub score-banner-neutral">Keine Änderungen aktiv</div>
|
||||
</div>
|
||||
<div class="score-banner-r">📊</div>
|
||||
</div>
|
||||
<div class="sec-lbl">Aktive Regeln</div>
|
||||
<div class="sum-rows">
|
||||
<div class="sum-row on"><span class="sum-row-name">Protein</span><span class="sum-row-val">Mittel</span></div>
|
||||
<div class="sum-row on"><span class="sum-row-name">Küche</span><span class="sum-row-val">Mittel</span></div>
|
||||
<div class="sum-row on"><span class="sum-row-name">Zutaten</span><span class="sum-row-val">Niedrig</span></div>
|
||||
<div class="sum-row on"><span class="sum-row-name">Letzte Wochen</span><span class="sum-row-val">Mittel</span></div>
|
||||
<div class="sum-row warn"><span class="sum-row-name">Duplikate</span><span class="sum-row-val">Hoch</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="agent">
|
||||
<h4>E4 · S1 Default</h4>
|
||||
<pre>/* Load: GET /v1/households/mine/variety-config → 404 if no custom config.
|
||||
* On 404: use defaults (Omnivor preset), show Omnivor chip as selected.
|
||||
* Score banner: show actual GET /v1/week-plans/{id}/variety-score (no simulation).
|
||||
* "Bereits Standard-Einstellungen" replaces reset link if no custom config exists.
|
||||
* Accordion: closed. */</pre>
|
||||
<table class="at"><thead><tr><th>Element</th><th>Wert</th><th>Notizen</th></tr></thead><tbody>
|
||||
<tr class="grp"><td colspan="3">Laden</td></tr>
|
||||
<tr><td>Config-Load</td><td>GET /v1/households/mine/variety-config</td><td>404 → Defaults verwenden, Omnivor selected</td></tr>
|
||||
<tr><td>Score-Load</td><td>GET /v1/week-plans/{id}/variety-score</td><td>Nur wenn weekPlan existiert; sonst Score-Banner ausblenden</td></tr>
|
||||
<tr class="grp"><td colspan="3">Kontext-Chips</td></tr>
|
||||
<tr><td>Omnivor</td><td>repeatTagTypes: ["protein","cuisine"], alle Gewichte Standard</td><td>Default-Preset = backend defaults</td></tr>
|
||||
<tr><td>Vegetarisch</td><td>repeatTagTypes: ["cuisine"], wTagRepeat Standard</td><td>Protein deaktiviert</td></tr>
|
||||
<tr><td>Vegan</td><td>repeatTagTypes: ["cuisine"], wTagRepeat Standard</td><td>Identisch zu Vegetarisch in v1</td></tr>
|
||||
<tr><td>Individuell</td><td>Erscheint automatisch wenn Advanced abweicht vom Preset</td><td>Kein manuell wählbarer Chip — nur automatisch</td></tr>
|
||||
<tr class="grp"><td colspan="3">Score-Banner (S1)</td></tr>
|
||||
<tr><td>Wert</td><td>Aktueller Score (keine Simulation)</td><td>Label: "Aktueller Score"</td></tr>
|
||||
<tr><td>Sub-Label</td><td>"Keine Änderungen"</td><td>Neutral-Farbe (#6B6A63)</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- S2: Vegetarisch selected -->
|
||||
<div class="scr">
|
||||
<div class="scr-head"><h3>S2 · Vegetarisch ausgewählt — Score-Simulation</h3><span class="scr-id">E4</span></div>
|
||||
<div class="scr-desc">Planer tippt auf "Vegetarisch". Config wird sofort per PATCH gespeichert. Score-Banner lädt die simulierte Punktzahl: wie würde der aktuelle Plan mit der neuen Config abschneiden. Delta wird grün hervorgehoben. Protein-Pill wechselt zu "off". Erweiterte Einstellungen zeigt Protein-Toggle als deaktiviert.</div>
|
||||
<div class="scr-var"><strong>S2</strong> · Vegetarisch selected · Score-Preview = simuliert · Protein-Pill = off</div>
|
||||
|
||||
<div class="previews">
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Mobile · 320px</div>
|
||||
<div class="phone">
|
||||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||||
<div class="pb">
|
||||
<div class="mtb"><div class="mtb-back">← Einstellungen</div><div class="mtb-t">Vielfalt</div></div>
|
||||
<div style="padding:16px;flex:1;overflow-y:auto;">
|
||||
<div style="font-size:12px;color:var(--color-text-muted);line-height:1.6;margin-bottom:14px;">Passe den Algorithmus an deinen Haushalt an. Änderungen werden sofort übernommen.</div>
|
||||
|
||||
<div class="sec-lbl">Haushaltskontext</div>
|
||||
<div class="ctx-chips">
|
||||
<div class="ctx-chip"><div class="ctx-em">🥩</div><div class="ctx-name">Omnivor</div><div class="ctx-sub">Alle Regeln aktiv</div></div>
|
||||
<div class="ctx-chip sel"><div class="ctx-em">🥦</div><div class="ctx-name">Vegetarisch</div><div class="ctx-sub">Protein deaktiviert</div></div>
|
||||
<div class="ctx-chip"><div class="ctx-em">🌱</div><div class="ctx-name">Vegan</div><div class="ctx-sub">Protein deaktiviert</div></div>
|
||||
</div>
|
||||
|
||||
<div class="sec-lbl">Aktive Regeln</div>
|
||||
<div class="s-pills">
|
||||
<div class="s-pill off">– Protein</div>
|
||||
<div class="s-pill on">✓ Küche</div>
|
||||
<div class="s-pill on">✓ Zutaten · Mittel</div>
|
||||
<div class="s-pill on">✓ Letzte Wochen · Mittel</div>
|
||||
<div class="s-pill warn">⚠ Duplikate · Hoch</div>
|
||||
</div>
|
||||
|
||||
<div class="acc">
|
||||
<div class="acc-hd"><div class="acc-hd-t">Erweiterte Einstellungen</div><div class="acc-hd-r">▸</div></div>
|
||||
</div>
|
||||
|
||||
<div class="score-banner">
|
||||
<div class="score-banner-l">
|
||||
<div class="score-banner-label">Mit diesen Einstellungen</div>
|
||||
<div class="score-banner-val">8.9</div>
|
||||
<div class="score-banner-sub score-banner-up">↑ +1.5 gegenüber vorher</div>
|
||||
</div>
|
||||
<div class="score-banner-r">📈</div>
|
||||
</div>
|
||||
|
||||
<div class="reset-link">Auf Standard zurücksetzen</div>
|
||||
</div>
|
||||
<div class="mbt">
|
||||
<div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Plan</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Einkauf</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🍳</div><div class="mt-l">Rezepte</div></div>
|
||||
<div class="mt-i a"><div class="mt-ic">⚙️</div><div class="mt-l">Einstellungen</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="prev-col" style="flex:1;min-width:600px;">
|
||||
<div class="bp-lbl">Desktop · 1040px</div>
|
||||
<div class="desk">
|
||||
<div class="dsb">
|
||||
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥦</div><div class="dsb-nm">Mealprep</div></div><div class="dsb-sub">Familie Raddatz</div></div>
|
||||
<div class="dsb-nav">
|
||||
<div class="dsb-nl">Planung</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">📅</span> Wochenplan</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkaufsliste</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🍳</span> Rezepte</div>
|
||||
<div class="dsb-nl" style="margin-top:12px;">Haushalt</div>
|
||||
<div class="dsb-ni a"><span class="dsb-nc">⚙️</span> Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dm">
|
||||
<div class="dtb"><div><div class="dtb-bc">Einstellungen <span>›</span> Vielfalt-Einstellungen</div><div class="dtb-t">Vielfalt-Einstellungen</div></div></div>
|
||||
<div class="dmc">
|
||||
<div style="display:grid;grid-template-columns:1fr 280px;gap:24px;max-width:800px;">
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--color-text-muted);line-height:1.6;margin-bottom:16px;">Passe den Algorithmus an deinen Haushaltskontext an. Änderungen werden sofort übernommen und wirken sich auf den nächsten Score-Abruf aus.</div>
|
||||
<div class="sec-lbl">Haushaltskontext</div>
|
||||
<div class="ctx-chips" style="margin-bottom:20px;">
|
||||
<div class="ctx-chip"><div class="ctx-em">🥩</div><div class="ctx-name">Omnivor</div><div class="ctx-sub">Alle Regeln aktiv</div></div>
|
||||
<div class="ctx-chip sel"><div class="ctx-em">🥦</div><div class="ctx-name">Vegetarisch</div><div class="ctx-sub">Protein deaktiviert</div></div>
|
||||
<div class="ctx-chip"><div class="ctx-em">🌱</div><div class="ctx-name">Vegan</div><div class="ctx-sub">Protein deaktiviert</div></div>
|
||||
</div>
|
||||
<div class="acc">
|
||||
<div class="acc-hd"><div class="acc-hd-t">Erweiterte Einstellungen</div><div class="acc-hd-r">▸</div></div>
|
||||
</div>
|
||||
<div class="reset-link" style="text-align:left;">Auf Standard zurücksetzen</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="score-banner">
|
||||
<div>
|
||||
<div class="score-banner-label">Mit diesen Einstellungen</div>
|
||||
<div class="score-banner-val">8.9 <span style="font-size:13px;opacity:.5;">/ 10</span></div>
|
||||
<div class="score-banner-sub score-banner-up">↑ +1.5 gegenüber vorher</div>
|
||||
</div>
|
||||
<div class="score-banner-r">📈</div>
|
||||
</div>
|
||||
<div class="sec-lbl">Aktive Regeln</div>
|
||||
<div class="sum-rows">
|
||||
<div class="sum-row off"><span class="sum-row-name">Protein</span><span class="sum-row-val">Deaktiviert</span></div>
|
||||
<div class="sum-row on"><span class="sum-row-name">Küche</span><span class="sum-row-val">Mittel</span></div>
|
||||
<div class="sum-row on"><span class="sum-row-name">Zutaten</span><span class="sum-row-val">Niedrig</span></div>
|
||||
<div class="sum-row on"><span class="sum-row-name">Letzte Wochen</span><span class="sum-row-val">Mittel</span></div>
|
||||
<div class="sum-row warn"><span class="sum-row-name">Duplikate</span><span class="sum-row-val">Hoch</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="agent">
|
||||
<h4>E4 · S2 Vegetarisch</h4>
|
||||
<pre>/* On chip tap (Vegetarisch):
|
||||
* 1. Optimistic UI: swap selected chip, update pills, update sum-rows immediately.
|
||||
* 2. PATCH /v1/households/mine/variety-config { repeatTagTypes: ["cuisine"],
|
||||
* wTagRepeat: 1.5, wIngredientOverlap: 0.3, wRecentRepeat: 1.0, wPlanDuplicate: 2.0 }
|
||||
* 3. On PATCH success: fire GET /v1/week-plans/{id}/variety-score?simulate=true
|
||||
* with same config body → update score-banner with simulated score + delta.
|
||||
* 4. On PATCH error: rollback to previous chip selection + show toast "Fehler beim Speichern".
|
||||
* Score-Banner during load: show spinner in place of val. */</pre>
|
||||
<table class="at"><thead><tr><th>Element</th><th>Wert</th><th>Notizen</th></tr></thead><tbody>
|
||||
<tr class="grp"><td colspan="3">Score-Banner (S2)</td></tr>
|
||||
<tr><td>Label</td><td>"Mit diesen Einstellungen"</td><td>Statt "Aktueller Score"</td></tr>
|
||||
<tr><td>Delta</td><td>"↑ +X.X gegenüber vorher"</td><td>Grün (#6FCF97) wenn positiv; rot wenn negativ; neutral wenn = 0</td></tr>
|
||||
<tr><td>Simulation-Endpoint</td><td>POST /v1/week-plans/{id}/variety-score/simulate</td><td>Body: VarietyScoreConfig-Felder. Neuer Endpoint nötig (Backend-Task).</td></tr>
|
||||
<tr><td>Kein Plan</td><td>Score-Banner ausblenden</td><td>Kein simulierter Score ohne Plan möglich</td></tr>
|
||||
<tr class="grp"><td colspan="3">Chip-Preset Vegetarisch</td></tr>
|
||||
<tr><td>repeatTagTypes</td><td>["cuisine"]</td><td>Protein entfernt</td></tr>
|
||||
<tr><td>wTagRepeat</td><td>1.5 (Standard)</td><td>Unverändert</td></tr>
|
||||
<tr><td>wIngredientOverlap</td><td>0.3 (Standard)</td><td>Unverändert</td></tr>
|
||||
<tr><td>wRecentRepeat</td><td>1.0 (Standard)</td><td>Unverändert</td></tr>
|
||||
<tr><td>wPlanDuplicate</td><td>2.0 (Standard)</td><td>Unverändert</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- S3: Erweiterte Einstellungen -->
|
||||
<div class="scr">
|
||||
<div class="scr-head"><h3>S3 · Erweiterte Einstellungen geöffnet</h3><span class="scr-id">E4</span></div>
|
||||
<div class="scr-desc">Planer öffnet das Accordion "Erweiterte Einstellungen". Er sieht Segmented Controls (Niedrig / Mittel / Hoch) für jeden Gewichts-Parameter. Ändert er einen Wert, der nicht mehr dem aktuellen Preset entspricht, erscheint automatisch ein vierter Chip "Individuell" (lila) und ersetzt den aktiven Preset-Chip. Score-Banner aktualisiert sich nach jeder Änderung.</div>
|
||||
<div class="scr-var"><strong>S3</strong> · Erweiterte Einstellungen offen · "Individuell"-Chip erschienen (Planer hat Zutaten-Gewicht angepasst)</div>
|
||||
|
||||
<div class="previews">
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Mobile · 320px</div>
|
||||
<div class="phone">
|
||||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||||
<div class="pb">
|
||||
<div class="mtb"><div class="mtb-back">← Einstellungen</div><div class="mtb-t">Vielfalt</div></div>
|
||||
<div style="padding:16px;flex:1;overflow-y:auto;">
|
||||
<div class="sec-lbl">Haushaltskontext</div>
|
||||
<div class="ctx-chips" style="flex-wrap:wrap;">
|
||||
<div class="ctx-chip" style="flex:1;min-width:60px;"><div class="ctx-em">🥩</div><div class="ctx-name">Omnivor</div></div>
|
||||
<div class="ctx-chip" style="flex:1;min-width:60px;"><div class="ctx-em">🥦</div><div class="ctx-name">Vegetarisch</div></div>
|
||||
<div class="ctx-chip" style="flex:1;min-width:60px;"><div class="ctx-em">🌱</div><div class="ctx-name">Vegan</div></div>
|
||||
<div class="ctx-chip ind" style="flex:1;min-width:60px;"><div class="ctx-em">✦</div><div class="ctx-name">Individuell</div></div>
|
||||
</div>
|
||||
|
||||
<div class="s-pills" style="margin-top:10px;">
|
||||
<div class="s-pill off">– Protein</div>
|
||||
<div class="s-pill on">✓ Küche</div>
|
||||
<div class="s-pill on">✓ Zutaten · Hoch</div>
|
||||
<div class="s-pill on">✓ Letzte Wochen · Mittel</div>
|
||||
<div class="s-pill warn">⚠ Duplikate · Hoch</div>
|
||||
</div>
|
||||
|
||||
<div class="acc">
|
||||
<div class="acc-hd"><div class="acc-hd-t">Erweiterte Einstellungen</div><div class="acc-hd-r">▾</div></div>
|
||||
<div class="acc-b">
|
||||
<div style="font-size:10px;color:var(--color-text-muted);margin-bottom:10px;">Protein ist über den Kontext deaktiviert. Die übrigen Gewichte kannst du hier anpassen.</div>
|
||||
<div class="wr">
|
||||
<div class="wr-l"><div class="wr-name">Küche</div><div class="wr-sub">Tag-Wiederholung</div></div>
|
||||
<div class="seg" style="width:108px"><div class="seg-o">Niedrig</div><div class="seg-o a">Mittel</div><div class="seg-o">Hoch</div></div>
|
||||
</div>
|
||||
<div class="wr">
|
||||
<div class="wr-l"><div class="wr-name">Zutaten</div><div class="wr-sub">Überschneidung</div></div>
|
||||
<div class="seg" style="width:108px"><div class="seg-o">Niedrig</div><div class="seg-o">Mittel</div><div class="seg-o a-r">Hoch</div></div>
|
||||
</div>
|
||||
<div class="wr">
|
||||
<div class="wr-l"><div class="wr-name">Letzte Wochen</div><div class="wr-sub">Kochverlauf</div></div>
|
||||
<div class="seg" style="width:108px"><div class="seg-o">Niedrig</div><div class="seg-o a">Mittel</div><div class="seg-o">Hoch</div></div>
|
||||
</div>
|
||||
<div class="wr">
|
||||
<div class="wr-l"><div class="wr-name">Duplikate</div><div class="wr-sub">Im Plan</div></div>
|
||||
<div class="seg" style="width:108px"><div class="seg-o">Niedrig</div><div class="seg-o">Mittel</div><div class="seg-o a-r">Hoch</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="score-banner">
|
||||
<div class="score-banner-l">
|
||||
<div class="score-banner-label">Mit diesen Einstellungen</div>
|
||||
<div class="score-banner-val">8.1</div>
|
||||
<div class="score-banner-sub score-banner-up">↑ +0.7 gegenüber vorher</div>
|
||||
</div>
|
||||
<div class="score-banner-r">📈</div>
|
||||
</div>
|
||||
<div class="reset-link">Auf Standard zurücksetzen</div>
|
||||
</div>
|
||||
<div class="mbt">
|
||||
<div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Plan</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Einkauf</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🍳</div><div class="mt-l">Rezepte</div></div>
|
||||
<div class="mt-i a"><div class="mt-ic">⚙️</div><div class="mt-l">Einstellungen</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="prev-col" style="flex:1;min-width:600px;">
|
||||
<div class="bp-lbl">Desktop · 1040px</div>
|
||||
<div class="desk">
|
||||
<div class="dsb">
|
||||
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥦</div><div class="dsb-nm">Mealprep</div></div><div class="dsb-sub">Familie Raddatz</div></div>
|
||||
<div class="dsb-nav">
|
||||
<div class="dsb-nl">Planung</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">📅</span> Wochenplan</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🛒</span> Einkaufsliste</div>
|
||||
<div class="dsb-ni"><span class="dsb-nc">🍳</span> Rezepte</div>
|
||||
<div class="dsb-nl" style="margin-top:12px;">Haushalt</div>
|
||||
<div class="dsb-ni a"><span class="dsb-nc">⚙️</span> Einstellungen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dm">
|
||||
<div class="dtb"><div><div class="dtb-bc">Einstellungen <span>›</span> Vielfalt-Einstellungen</div><div class="dtb-t">Vielfalt-Einstellungen</div></div></div>
|
||||
<div class="dmc">
|
||||
<div style="display:grid;grid-template-columns:1fr 280px;gap:24px;max-width:800px;">
|
||||
<div>
|
||||
<div class="sec-lbl">Haushaltskontext</div>
|
||||
<div class="ctx-chips" style="margin-bottom:20px;">
|
||||
<div class="ctx-chip"><div class="ctx-em">🥩</div><div class="ctx-name">Omnivor</div><div class="ctx-sub">Alle Regeln</div></div>
|
||||
<div class="ctx-chip"><div class="ctx-em">🥦</div><div class="ctx-name">Vegetarisch</div><div class="ctx-sub">Protein aus</div></div>
|
||||
<div class="ctx-chip"><div class="ctx-em">🌱</div><div class="ctx-name">Vegan</div><div class="ctx-sub">Protein aus</div></div>
|
||||
<div class="ctx-chip ind"><div class="ctx-em">✦</div><div class="ctx-name">Individuell</div><div class="ctx-sub">Benutzerdefiniert</div></div>
|
||||
</div>
|
||||
<div class="acc">
|
||||
<div class="acc-hd"><div class="acc-hd-t">Erweiterte Einstellungen</div><div class="acc-hd-r">▾</div></div>
|
||||
<div class="acc-b">
|
||||
<div style="font-size:11px;color:var(--color-text-muted);margin-bottom:12px;">Protein ist über den Haushaltskontext deaktiviert. Passe die Stärke der übrigen Regeln an.</div>
|
||||
<div class="wr">
|
||||
<div class="wr-l"><div class="wr-name">Küchen-Wiederholung</div><div class="wr-sub">Gleiche Küche an aufeinanderfolgenden Tagen</div></div>
|
||||
<div class="seg" style="width:160px"><div class="seg-o">Niedrig</div><div class="seg-o a">Mittel</div><div class="seg-o">Hoch</div></div>
|
||||
</div>
|
||||
<div class="wr">
|
||||
<div class="wr-l"><div class="wr-name">Zutaten-Überschneidung</div><div class="wr-sub">Gleiche Zutaten an aufeinanderfolgenden Tagen</div></div>
|
||||
<div class="seg" style="width:160px"><div class="seg-o">Niedrig</div><div class="seg-o">Mittel</div><div class="seg-o a-r">Hoch</div></div>
|
||||
</div>
|
||||
<div class="wr">
|
||||
<div class="wr-l"><div class="wr-name">Letzte Wochen</div><div class="wr-sub">Kochverlauf (14 Tage)</div></div>
|
||||
<div class="seg" style="width:160px"><div class="seg-o">Niedrig</div><div class="seg-o a">Mittel</div><div class="seg-o">Hoch</div></div>
|
||||
</div>
|
||||
<div class="wr">
|
||||
<div class="wr-l"><div class="wr-name">Doppelte Rezepte</div><div class="wr-sub">Gleiches Rezept mehrfach im Plan</div></div>
|
||||
<div class="seg" style="width:160px"><div class="seg-o">Niedrig</div><div class="seg-o">Mittel</div><div class="seg-o a-r">Hoch</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="reset-link" style="text-align:left;">Auf Standard zurücksetzen</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="score-banner">
|
||||
<div>
|
||||
<div class="score-banner-label">Mit diesen Einstellungen</div>
|
||||
<div class="score-banner-val">8.1 <span style="font-size:13px;opacity:.5;">/ 10</span></div>
|
||||
<div class="score-banner-sub score-banner-up">↑ +0.7 gegenüber vorher</div>
|
||||
</div>
|
||||
<div class="score-banner-r">📈</div>
|
||||
</div>
|
||||
<div class="sec-lbl">Aktive Regeln</div>
|
||||
<div class="sum-rows">
|
||||
<div class="sum-row off"><span class="sum-row-name">Protein</span><span class="sum-row-val">Deaktiviert</span></div>
|
||||
<div class="sum-row on"><span class="sum-row-name">Küche</span><span class="sum-row-val">Mittel</span></div>
|
||||
<div class="sum-row warn"><span class="sum-row-name">Zutaten</span><span class="sum-row-val">Hoch ↑</span></div>
|
||||
<div class="sum-row on"><span class="sum-row-name">Letzte Wochen</span><span class="sum-row-val">Mittel</span></div>
|
||||
<div class="sum-row warn"><span class="sum-row-name">Duplikate</span><span class="sum-row-val">Hoch</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="agent">
|
||||
<h4>E4 · S3 Erweiterte Einstellungen</h4>
|
||||
<pre>/* Accordion öffnet sich per Click/Tap auf acc-hd. Keine Animation nötig — display toggle reicht.
|
||||
* Erweiterte Einstellungen zeigt NUR aktive Tag-Typen als Gewichts-Rows.
|
||||
* Wenn Protein deaktiviert (über Preset): Protein-Row wird in acc-b NICHT angezeigt.
|
||||
* "Individuell"-Chip: erscheint automatisch wenn die Kombination repeatTagTypes+weights
|
||||
* nicht exakt einem der drei Presets entspricht. Kein manueller Auslöser.
|
||||
* Gewichts-Änderung → PATCH → Score-Simulation → Banner-Update.
|
||||
* Debounce der Simulation: 300ms nach letzter Interaktion. */</pre>
|
||||
<table class="at"><thead><tr><th>Element</th><th>Wert</th><th>Notizen</th></tr></thead><tbody>
|
||||
<tr class="grp"><td colspan="3">Gewicht-Mapping</td></tr>
|
||||
<tr><td>Niedrig</td><td>Faktor 0.5 × Standard-Gewicht</td><td>wTagRepeat: 0.75, wIngredient: 0.15, wRecent: 0.5, wDuplicate: 1.0</td></tr>
|
||||
<tr><td>Mittel</td><td>Faktor 1.0 (Standard)</td><td>wTagRepeat: 1.5, wIngredient: 0.3, wRecent: 1.0, wDuplicate: 2.0</td></tr>
|
||||
<tr><td>Hoch</td><td>Faktor 1.5 × Standard-Gewicht</td><td>wTagRepeat: 2.25, wIngredient: 0.45, wRecent: 1.5, wDuplicate: 3.0</td></tr>
|
||||
<tr class="grp"><td colspan="3">Individuell-Chip</td></tr>
|
||||
<tr><td>Trigger</td><td>Wenn gespeicherter Config ≠ Omnivor, Vegetarisch, oder Vegan Preset</td><td>Lila Border + Hintergrund</td></tr>
|
||||
<tr><td>Symbol</td><td>✦ (U+2726)</td><td>Statt Emoji</td></tr>
|
||||
<tr><td>Label</td><td>Individuell</td><td>Nicht anklickbar — nur Status-Indikator</td></tr>
|
||||
<tr class="grp"><td colspan="3">Simulation-Debounce</td></tr>
|
||||
<tr><td>Delay</td><td>300ms</td><td>Nach letzter Segmented-Control-Interaktion</td></tr>
|
||||
<tr><td>Während Laden</td><td>Score-Wert zeigt Spinner (CSS animation)</td><td>Kein Skeleton — nur val-Bereich</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- S4: Reset confirmation -->
|
||||
<div class="scr">
|
||||
<div class="scr-head"><h3>S4 · Reset-Bestätigung</h3><span class="scr-id">E4</span></div>
|
||||
<div class="scr-desc">Planer tippt "Auf Standard zurücksetzen". Ein Dialog erscheint und benennt explizit, was zurückgesetzt wird. Bestätigung löscht den Custom-Config-Eintrag (DELETE) und stellt die Omnivor-Defaults wieder her. Kein Backdrop-Dismiss — der Planer muss explizit wählen.</div>
|
||||
<div class="scr-var"><strong>S4</strong> · Modal über S2-Zustand · Backdrop nicht anklickbar · Mobile: Bottom Sheet</div>
|
||||
|
||||
<div class="previews">
|
||||
<div class="prev-col">
|
||||
<div class="bp-lbl">Mobile · 320px (Bottom Sheet)</div>
|
||||
<div class="phone">
|
||||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||||
<div class="pb">
|
||||
<!-- Blurred background state -->
|
||||
<div class="mtb"><div class="mtb-back">← Einstellungen</div><div class="mtb-t">Vielfalt</div></div>
|
||||
<div style="padding:16px;flex:1;overflow-y:auto;opacity:.35;pointer-events:none;">
|
||||
<div class="ctx-chips">
|
||||
<div class="ctx-chip sel"><div class="ctx-em">🥦</div><div class="ctx-name">Vegetarisch</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Bottom sheet -->
|
||||
<div style="background:var(--color-page);border-radius:20px 20px 0 0;padding:20px;border-top:1px solid var(--color-border);box-shadow:0 -4px 24px rgba(0,0,0,.12);">
|
||||
<div style="width:36px;height:4px;background:var(--color-border);border-radius:2px;margin:0 auto 16px;"></div>
|
||||
<div style="font-family:var(--font-display);font-size:18px;font-weight:500;margin-bottom:8px;">Auf Standard zurücksetzen?</div>
|
||||
<div style="font-size:12px;color:var(--color-text-muted);line-height:1.6;margin-bottom:16px;">Alle individuellen Einstellungen werden gelöscht. Der Algorithmus verwendet dann wieder:<br><br>• Protein: Aktiv<br>• Küche: Aktiv<br>• Alle Gewichte: Mittel</div>
|
||||
<div class="btn-dest" style="margin-bottom:8px;">Zurücksetzen</div>
|
||||
<div class="btn-ghost">Abbrechen</div>
|
||||
</div>
|
||||
<div class="mbt">
|
||||
<div class="mt-i"><div class="mt-ic">📅</div><div class="mt-l">Plan</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🛒</div><div class="mt-l">Einkauf</div></div>
|
||||
<div class="mt-i"><div class="mt-ic">🍳</div><div class="mt-l">Rezepte</div></div>
|
||||
<div class="mt-i a"><div class="mt-ic">⚙️</div><div class="mt-l">Einstellungen</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="prev-col" style="flex:1;min-width:600px;">
|
||||
<div class="bp-lbl">Desktop · 1040px (Centered Modal)</div>
|
||||
<div class="desk overlay">
|
||||
<div class="dsb" style="opacity:.35;">
|
||||
<div class="dsb-logo"><div class="dsb-lm"><div class="dsb-ic">🥦</div><div class="dsb-nm">Mealprep</div></div></div>
|
||||
</div>
|
||||
<div class="dm" style="opacity:.35;">
|
||||
<div class="dtb"><div class="dtb-t">Vielfalt-Einstellungen</div></div>
|
||||
<div class="dmc"><div class="ctx-chips"><div class="ctx-chip sel"><div class="ctx-em">🥦</div><div class="ctx-name">Vegetarisch</div></div></div></div>
|
||||
</div>
|
||||
<div class="modal-backdrop">
|
||||
<div class="modal">
|
||||
<div class="modal-title">Auf Standard zurücksetzen?</div>
|
||||
<div class="modal-body">Alle individuellen Einstellungen werden gelöscht. Der Algorithmus verwendet dann wieder die Standard-Werte:<br><br>
|
||||
<strong>Protein-Prüfung:</strong> Aktiv<br>
|
||||
<strong>Küchen-Vielfalt:</strong> Aktiv<br>
|
||||
<strong>Alle Gewichte:</strong> Mittel</div>
|
||||
<div class="modal-acts">
|
||||
<div class="btn-dest">Zurücksetzen</div>
|
||||
<div class="btn-ghost">Abbrechen</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="agent">
|
||||
<h4>E4 · S4 Reset-Bestätigung</h4>
|
||||
<pre>/* Reset-Link Tap → Dialog öffnet (kein Backdrop-Dismiss, kein Escape-Dismiss).
|
||||
* "Zurücksetzen" → DELETE /v1/households/mine/variety-config
|
||||
* On success: optimistic reset von UI zu S1 (Omnivor), Score-Banner zeigt echten Score.
|
||||
* On error: Toast "Fehler beim Zurücksetzen".
|
||||
* Mobile: Bottom Sheet (position:fixed, bottom 0, border-radius 20px 20px 0 0).
|
||||
* Desktop: centered modal, backdrop rgba(28,28,24,0.4), max-width 380px. */</pre>
|
||||
<table class="at"><thead><tr><th>Element</th><th>Wert</th><th>Notizen</th></tr></thead><tbody>
|
||||
<tr class="grp"><td colspan="3">Dialog-Inhalt</td></tr>
|
||||
<tr><td>Titel</td><td>"Auf Standard zurücksetzen?"</td><td>Fraunces 18px (Mobile), 20px (Desktop)</td></tr>
|
||||
<tr><td>Body</td><td>Auflistung der zurückgesetzten Werte</td><td>Muss konkret benennen: Protein aktiv, Küche aktiv, alle Gewichte Mittel</td></tr>
|
||||
<tr><td>Primär-Aktion</td><td>"Zurücksetzen" → DELETE</td><td>Hintergrund: --red, Text: weiß</td></tr>
|
||||
<tr><td>Sekundär-Aktion</td><td>"Abbrechen"</td><td>Ghost-Button, schließt Dialog</td></tr>
|
||||
<tr class="grp"><td colspan="3">API</td></tr>
|
||||
<tr><td>Endpoint</td><td>DELETE /v1/households/mine/variety-config</td><td>Löscht Custom-Config-Row; Backend fällt auf Defaults zurück</td></tr>
|
||||
<tr><td>On Success</td><td>UI reset zu S1</td><td>Omnivor chip selected, Score-Banner: echter Score</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ════════════════════════════════════════
|
||||
LLM / AGENT REGION
|
||||
═════════════════════════════════════════ -->
|
||||
<div class="llm">
|
||||
<h2>Maschinenlesbare Spezifikation — E4 Vielfalt-Einstellungen</h2>
|
||||
|
||||
<h3>Screens</h3>
|
||||
<table>
|
||||
<thead><tr><th>Screen</th><th>Route</th><th>Zugriff</th><th>Zweck</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>E1 (Update)</td><td>/settings</td><td>Planer</td><td>Settings-Hub: dritte Kachel "Vielfalt-Einstellungen" mit aktuellem Score</td></tr>
|
||||
<tr><td>E4</td><td>/settings/variety</td><td>Planer only</td><td>Vielfalt-Algorithmus per Kontext-Preset und Feineinstellungen konfigurieren</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>States</h3>
|
||||
<table>
|
||||
<thead><tr><th>State</th><th>Trigger</th><th>Beschreibung</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>S0</td><td>E1 load</td><td>Settings-Hub zeigt Score-Kachel (lila Kennzahl)</td></tr>
|
||||
<tr><td>S1</td><td>E4 load, kein Custom-Config</td><td>Omnivor chip selected, Score = aktueller echter Score, Reset-Link = deaktiviert/neutral</td></tr>
|
||||
<tr><td>S2</td><td>Preset-Chip tap</td><td>Chip wechselt, PATCH, Score-Simulation lädt und zeigt Delta</td></tr>
|
||||
<tr><td>S3</td><td>Accordion öffnen + Gewicht ändern</td><td>Individuell-Chip erscheint, Score-Simulation mit Debounce 300ms</td></tr>
|
||||
<tr><td>S4</td><td>Reset-Link tap</td><td>Modal/Bottom Sheet — Bestätigung vor DELETE</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>API-Endpoints (neu + bestehend)</h3>
|
||||
<table>
|
||||
<thead><tr><th>Method</th><th>Endpoint</th><th>Neu?</th><th>Zweck</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>GET</td><td>/v1/households/mine/variety-config</td><td>Neu</td><td>Aktuellen Config laden; 404 = Defaults verwenden</td></tr>
|
||||
<tr><td>PATCH</td><td>/v1/households/mine/variety-config</td><td>Neu</td><td>Config speichern (auto-save bei jedem Preset/Gewicht-Wechsel)</td></tr>
|
||||
<tr><td>DELETE</td><td>/v1/households/mine/variety-config</td><td>Neu</td><td>Custom-Config löschen, Backend fällt auf Defaults zurück</td></tr>
|
||||
<tr><td>POST</td><td>/v1/week-plans/{id}/variety-score/simulate</td><td>Neu</td><td>Score simulieren mit temporärem Config-Body (nicht persistiert)</td></tr>
|
||||
<tr><td>GET</td><td>/v1/week-plans/{id}/variety-score</td><td>Bestehend</td><td>Aktuellen Score laden (für S1 Banner + E1 Kachel)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Kontext-Preset Mapping</h3>
|
||||
<table>
|
||||
<thead><tr><th>Preset</th><th>repeatTagTypes</th><th>wTagRepeat</th><th>wIngredientOverlap</th><th>wRecentRepeat</th><th>wPlanDuplicate</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Omnivor (Default)</td><td>["protein","cuisine"]</td><td>1.5</td><td>0.3</td><td>1.0</td><td>2.0</td></tr>
|
||||
<tr><td>Vegetarisch</td><td>["cuisine"]</td><td>1.5</td><td>0.3</td><td>1.0</td><td>2.0</td></tr>
|
||||
<tr><td>Vegan</td><td>["cuisine"]</td><td>1.5</td><td>0.3</td><td>1.0</td><td>2.0</td></tr>
|
||||
<tr><td>Individuell</td><td>Beliebig (≠ obige Presets)</td><td>Beliebig</td><td>Beliebig</td><td>Beliebig</td><td>Beliebig</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Gewicht-Preset Mapping</h3>
|
||||
<table>
|
||||
<thead><tr><th>Stufe</th><th>Faktor</th><th>wTagRepeat</th><th>wIngredientOverlap</th><th>wRecentRepeat</th><th>wPlanDuplicate</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Niedrig</td><td>×0.5</td><td>0.75</td><td>0.15</td><td>0.5</td><td>1.0</td></tr>
|
||||
<tr><td>Mittel</td><td>×1.0</td><td>1.5</td><td>0.3</td><td>1.0</td><td>2.0</td></tr>
|
||||
<tr><td>Hoch</td><td>×1.5</td><td>2.25</td><td>0.45</td><td>1.5</td><td>3.0</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Implementierungsregeln (für Agenten)</h3>
|
||||
<ul>
|
||||
<li>E4 ist nur für <code>rolle === 'planer'</code> zugänglich. Mitglieder werden auf E1 redirected.</li>
|
||||
<li>Auto-Save auf jede Preset- oder Gewicht-Änderung. Kein expliziter Speichern-Button.</li>
|
||||
<li>Optimistic Update: UI wechselt sofort; Rollback mit Toast bei API-Fehler.</li>
|
||||
<li>Score-Simulation: Debounce 300ms. Während Laden: Spinner im Score-Wert-Bereich (nicht Skeleton).</li>
|
||||
<li>"Individuell"-Chip ist nicht anklickbar — er ist ein reiner Status-Indikator.</li>
|
||||
<li>Reset-Bestätigung: Backdrop-Dismiss deaktiviert (nicht schließbar durch Klick/Tap auf Overlay).</li>
|
||||
<li>Mobile Reset: Bottom Sheet mit Handle-Bar (36×4px, --color-border, border-radius 2px). Kein Backdrop-Dismiss.</li>
|
||||
<li>Desktop Reset: Zentriertes Modal, max-width 380px. Backdrop rgba(28,28,24,0.4).</li>
|
||||
<li>E1 Vielfalt-Kachel: Score < 6.0 → Kennzahl in --orange; Score ≥ 6.0 → Kennzahl in --purple.</li>
|
||||
<li>E4-Route: <code>(app)/settings/variety/+page.svelte</code>. Load-Funktion: <code>+page.server.ts</code> → Promise.all([GET variety-config, GET variety-score]).</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1138
specs/frontend/e4-variety-settings.html
Normal file
1138
specs/frontend/e4-variety-settings.html
Normal file
File diff suppressed because it is too large
Load Diff
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>
|
||||
1606
specs/userjourneys.html
Normal file
1606
specs/userjourneys.html
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user