Files
mealprep/specs/frontend/e1-settings-kachel.html
Marcel Raddatz 6dd0b7ac93 docs(specs): add final frontend specs for members and settings Kachel views
Finalised implementation specs for /members (E2) and /settings (E1)
pages using the chosen Kachel (card grid) variation. Members spec
covers 6 states including role-change inline control and remove
confirmation dialog; notes backend gaps (DELETE/PATCH member
endpoints). Settings spec covers hub layout, D3 staples sub-page,
hover and empty states.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:06:11 +02:00

701 lines
40 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>E1 — Einstellungen · Kachel-Ansicht · Finale Spezifikation</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: E1 Einstellungen Kachel-Ansicht, Finale Spezifikation
version: 1.0
journey: J8 Edit pantry staples
routes: /settings (E1 hub) → /household/staples?ctx=settings (D3)
screens: E1, D3
chosen-variation: V2 Kachel-Ansicht (Card sections)
last-updated: 2026-04-09
NAVIGATION STRUCTURE:
E1 (/settings) → Hub with 3 cards:
Card 1 "Vorräte" → navigates to D3 (/household/staples?ctx=settings)
Card 2 "Mitglieder" → navigates to E2 (/members)
Card 3 "Profil" → navigates to /profile (not yet implemented)
DATA:
Vorräte count: derived from GET /v1/ingredient-categories response
(count ingredients where isStaple === true)
Mitglieder count: from layout data (locals.haushalt via GET /v1/households/mine/members)
Profil name/email: from locals.benutzer
NOTE: D3 = A3. StaplesManager component is reused with context="settings".
StaplesManager renders categories as StapleChip pill grids, NOT checkboxes.
Auto-save on toggle (debounced PATCH 300ms). No save button.
-->
<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-text: #8A6800;
--color-error: #DC4C3E;
--blue-tint: #E6F1FB;
--blue: #185FA5;
--blue-dark: #0C447C;
--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 layout ── */
.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: 48px; 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; }
.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; }
.intro { font-size: 14px; line-height: 1.75; max-width: 640px; margin-bottom: 40px; }
/* ── State sections ── */
.state { margin-bottom: 64px; }
.state-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 20px; }
.state-id { font-family: var(--font-mono); font-size: 10px; font-weight: 500; background: var(--color-subtle); color: var(--color-text-muted); padding: 2px 8px; border-radius: var(--radius-sm); white-space: nowrap; margin-top: 3px; }
.state-title { font-size: 16px; font-weight: 500; letter-spacing: -0.01em; }
.state-desc { font-size: 13px; color: var(--color-text-muted); margin-top: 2px; max-width: 540px; }
/* ── Preview containers ── */
.preview-wrap { 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; min-height: 100vh; background: var(--color-page); font-family: var(--font-sans); }
.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-item:not(.active):hover { background: var(--color-subtle); }
.sidebar-icon { width: 20px; text-align: center; font-size: 16px; }
/* ── Page content ── */
.page-content { flex: 1; padding: 32px 40px; }
.page-title { font-family: var(--font-display); font-size: 24px; font-weight: 500; letter-spacing: -0.02em; margin-bottom: 4px; }
.page-subtitle { font-size: 13px; color: var(--color-text-muted); margin-bottom: 28px; }
/* ── Settings card grid ── */
.settings-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; }
.settings-grid-bottom { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px; }
/* ── Setting card ── */
.setting-card { background: white; border: 1px solid var(--color-border); border-radius: var(--radius-xl); padding: 24px; box-shadow: var(--shadow-card); cursor: pointer; text-decoration: none; color: inherit; display: flex; flex-direction: column; }
.setting-card:hover { box-shadow: var(--shadow-raised); border-color: #C0BFB8; }
.setting-card.primary { border-left: 3px solid var(--green-dark); }
.setting-card.primary:hover { border-left-color: var(--green-dark); }
.card-icon { font-size: 22px; margin-bottom: 12px; }
.card-stat { font-family: var(--font-display); font-size: 36px; font-weight: 500; letter-spacing: -0.02em; color: var(--green-dark); line-height: 1; margin-bottom: 2px; }
.card-stat-label { font-size: 11px; color: var(--color-text-muted); margin-bottom: 12px; }
.card-title { font-size: 15px; font-weight: 500; margin-bottom: 4px; }
.card-desc { font-size: 12px; color: var(--color-text-muted); line-height: 1.5; flex: 1; }
.card-cta { margin-top: 16px; display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; color: var(--green-dark); }
.card-cta-secondary { margin-top: 16px; display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; color: var(--color-text-muted); }
.card-meta { font-size: 12px; color: var(--color-text-muted); margin-bottom: 4px; }
/* ── D3 Staples page chrome ── */
.breadcrumb { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--color-text-muted); margin-bottom: 20px; }
.breadcrumb a { color: var(--color-text-muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--color-text); }
.breadcrumb-sep { font-size: 10px; }
/* ── Staple chips ── */
.category-block { margin-bottom: 24px; }
.category-name { font-size: 10px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-text-muted); margin-bottom: 10px; }
.chip-wrap { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { padding: 5px 12px; border-radius: var(--radius-full); border: 1px solid var(--color-border); font-size: 12px; font-weight: 500; cursor: pointer; white-space: nowrap; }
.chip.on { background: var(--green-dark); color: white; border-color: var(--green-dark); }
.chip.off { background: transparent; color: var(--color-text-muted); }
.chip.off:hover { border-color: var(--green-light); color: var(--green-dark); }
.save-note { font-size: 11px; color: var(--color-text-muted); margin-top: 16px; font-style: italic; }
/* ── Mobile shell ── */
.m-shell { display: flex; flex-direction: column; background: var(--color-page); }
.m-header { padding: 16px; background: white; border-bottom: 1px solid var(--color-border); }
.m-header-title { font-size: 16px; font-weight: 500; }
.m-content { flex: 1; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.m-card { background: white; border: 1px solid var(--color-border); border-radius: var(--radius-xl); padding: 16px; box-shadow: var(--shadow-card); }
.m-card.primary { border-left: 3px solid var(--green-dark); }
.m-card-stat { font-family: var(--font-display); font-size: 28px; font-weight: 500; color: var(--green-dark); line-height: 1; margin-bottom: 2px; }
.m-card-stat-label { font-size: 10px; color: var(--color-text-muted); margin-bottom: 8px; }
.m-card-title { font-size: 14px; font-weight: 500; margin-bottom: 3px; }
.m-card-desc { font-size: 11px; color: var(--color-text-muted); }
.m-card-cta { margin-top: 12px; font-size: 11px; font-weight: 500; color: var(--green-dark); }
.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">
<!-- Header -->
<div class="doc-header">
<div>
<h1>E1 — Einstellungen</h1>
<p>Kachel-Ansicht · Finale Spezifikation · Route: <code>/settings</code><code>/household/staples?ctx=settings</code></p>
</div>
<div class="doc-meta">
screens: E1, D3<br/>
journey: J8<br/>
variation: Kachel (V2)<br/>
version: 1.0<br/>
date: 2026-04-09
</div>
</div>
<p class="intro">
Die Einstellungsseite dient als Hub mit drei Kacheln: Vorräte (primäre Aktion, navigiert zu D3),
Mitglieder (navigiert zu E2) und Profil. Die Vorräte-Kachel zeigt die aktive Zutatenanzahl als
Display-Font-Zahl. D3 verwendet die bestehende StaplesManager-Komponente mit <code>context="settings"</code>.
</p>
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section-label">S1 — Hub-Ansicht (E1 /settings)</div>
<div class="state">
<div class="state-header">
<div class="state-id">S1</div>
<div>
<div class="state-title">Einstellungs-Hub — drei Kacheln</div>
<div class="state-desc">Vorräte-Kachel (2fr, primär mit grünem Akzentstreifen), Mitglieder-Kachel (1fr), Profil-Kachel (1fr). Desktop 2-spaltig oben, dann 2-spaltig unten.</div>
</div>
</div>
<div class="preview-wrap">
<div class="preview-d-wrap">
<div class="preview-label">Desktop</div>
<div class="preview-d-clip">
<div class="preview-d-scale">
<div class="shell">
<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" 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 class="sidebar-group-label">Haushalt</div>
<a class="sidebar-item" href="#"><span class="sidebar-icon">👥</span>Mitglieder</a>
<a class="sidebar-item active" href="#"><span class="sidebar-icon">⚙️</span>Einstellungen</a>
</div>
</div>
<div class="page-content">
<div class="page-title">Einstellungen</div>
<div class="page-subtitle">Familie Raddatz</div>
<div class="settings-grid">
<!-- Vorräte card (2fr, primary) -->
<a class="setting-card primary" href="#">
<div class="card-icon">🥫</div>
<div class="card-stat">14</div>
<div class="card-stat-label">von 32 Zutaten als Vorrat markiert</div>
<div class="card-title">Vorräte</div>
<div class="card-desc">Lege fest, welche Zutaten immer zu Hause sind. Sie werden beim Einkaufen automatisch herausgefiltert.</div>
<div class="card-cta">Vorräte bearbeiten →</div>
</a>
<!-- Mitglieder card (1fr) -->
<a class="setting-card" href="#">
<div class="card-icon">👥</div>
<div class="card-title">Mitglieder</div>
<div class="card-meta" style="margin-top:4px;">3 Mitglieder</div>
<div class="card-desc" style="margin-top:8px;">Haushaltsmitglieder einladen, Rollen verwalten.</div>
<div class="card-cta-secondary">Mitglieder verwalten →</div>
</a>
</div>
<div class="settings-grid-bottom">
<!-- Profil card -->
<a class="setting-card" href="#">
<div class="card-icon">👤</div>
<div class="card-title">Profil</div>
<div class="card-meta" style="margin-top:4px;">Marcel R.</div>
<div class="card-desc" style="margin-top:8px;">Name und E-Mail-Adresse anpassen.</div>
<div class="card-cta-secondary">Profil bearbeiten →</div>
</a>
<!-- Placeholder / future -->
<div style="border: 1.5px dashed var(--color-border); border-radius: var(--radius-xl); padding: 24px; display:flex; align-items:center; justify-content:center; color: var(--color-text-muted); font-size: 12px;">Weitere Einstellungen folgen</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-header"><div class="m-header-title">Einstellungen</div></div>
<div class="m-content">
<div class="m-card primary">
<div class="m-card-stat">14</div>
<div class="m-card-stat-label">von 32 Vorräten aktiv</div>
<div class="m-card-title">Vorräte</div>
<div class="m-card-desc">Welche Zutaten hast du immer zu Hause?</div>
<div class="m-card-cta">Vorräte bearbeiten →</div>
</div>
<div class="m-card">
<div class="m-card-title">👥 Mitglieder</div>
<div class="m-card-desc" style="margin-top:4px;">3 Mitglieder · Einladen &amp; Rollen</div>
<div class="m-card-cta">Verwalten →</div>
</div>
<div class="m-card">
<div class="m-card-title">👤 Profil</div>
<div class="m-card-desc" style="margin-top:4px;">Marcel R.</div>
<div class="m-card-cta">Bearbeiten →</div>
</div>
</div>
<div class="m-tabbar">
<div class="m-tab"><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 active"><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>Vorräte-Kachel: <code>grid-column: span 1</code> aber <code>2fr</code> Spaltenbreite im 2-Spalten-Grid. Grüner Linksstreifen (<code>border-left: 3px solid --green-dark</code>).</li>
<li>Stat-Zahl: Anzahl Zutaten mit <code>isStaple === true</code>, aus dem gleichen Load-Call der D3-Seite</li>
<li>Mitglieder-Karte: Anzahl aus <code>locals.haushalt</code> oder separatem API-Call; navigiert zu <code>/members</code></li>
<li>Profil-Karte: Name aus <code>locals.benutzer.name</code>; Zielseite <code>/profile</code> (noch nicht implementiert — Link disabled oder Placeholder)</li>
<li>Hover: <code>box-shadow: --shadow-raised</code>, leicht dunklerer Border</li>
<li>Alle Kacheln sind <code>&lt;a&gt;</code>-Tags für korrekte Navigation und Accessibility</li>
<li>Mobile: Kacheln stapeln sich vertikal in voller Breite, kein Grid</li>
</ul>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section-label">S2 — Vorräte-Seite (D3 /household/staples?ctx=settings)</div>
<div class="state">
<div class="state-header">
<div class="state-id">S2</div>
<div>
<div class="state-title">D3 — Vorräte bearbeiten (StaplesManager, context="settings")</div>
<div class="state-desc">Navigiert man von der Vorräte-Kachel aus, erscheint die bestehende StaplesManager-Komponente mit Breadcrumb zurück zu Einstellungen.</div>
</div>
</div>
<div class="preview-wrap">
<div class="preview-d-wrap">
<div class="preview-label">Desktop</div>
<div class="preview-d-clip">
<div class="preview-d-scale">
<div class="shell">
<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" 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 class="sidebar-group-label">Haushalt</div>
<a class="sidebar-item" href="#"><span class="sidebar-icon">👥</span>Mitglieder</a>
<a class="sidebar-item active" href="#"><span class="sidebar-icon">⚙️</span>Einstellungen</a>
</div>
</div>
<div class="page-content">
<!-- Breadcrumb -->
<div class="breadcrumb">
<a href="#">← Einstellungen</a>
<span class="breadcrumb-sep">/</span>
<span>Vorräte</span>
</div>
<div class="page-title">Vorräte</div>
<div class="page-subtitle">Markierte Zutaten werden beim Einkaufen herausgefiltert.</div>
<!-- StaplesManager content (context="settings") -->
<div class="category-block">
<div class="category-name">Gewürze &amp; Öle</div>
<div class="chip-wrap">
<span class="chip on">Salz</span>
<span class="chip on">Pfeffer</span>
<span class="chip on">Olivenöl</span>
<span class="chip off">Paprika</span>
<span class="chip off">Kreuzkümmel</span>
<span class="chip on">Knoblauch</span>
<span class="chip off">Chili</span>
</div>
</div>
<div class="category-block">
<div class="category-name">Grundnahrung</div>
<div class="chip-wrap">
<span class="chip on">Reis</span>
<span class="chip off">Nudeln</span>
<span class="chip on">Mehl</span>
<span class="chip on">Zucker</span>
<span class="chip off">Linsen</span>
<span class="chip off">Hülsenfrüchte</span>
</div>
</div>
<div class="category-block">
<div class="category-name">Kühlschrank</div>
<div class="chip-wrap">
<span class="chip on">Butter</span>
<span class="chip on">Eier</span>
<span class="chip off">Milch</span>
<span class="chip off">Käse</span>
<span class="chip off">Joghurt</span>
</div>
</div>
<div class="save-note">Änderungen werden automatisch gespeichert. Gilt ab der nächsten Einkaufsliste.</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-header">
<div style="font-size:11px;color:var(--color-text-muted);margin-bottom:2px;">← Einstellungen</div>
<div class="m-header-title">Vorräte</div>
</div>
<div class="m-content" style="gap:16px;">
<div>
<div class="category-name">Gewürze &amp; Öle</div>
<div class="chip-wrap">
<span class="chip on" style="font-size:11px;">Salz</span>
<span class="chip on" style="font-size:11px;">Pfeffer</span>
<span class="chip on" style="font-size:11px;">Olivenöl</span>
<span class="chip off" style="font-size:11px;">Paprika</span>
<span class="chip on" style="font-size:11px;">Knoblauch</span>
</div>
</div>
<div>
<div class="category-name">Grundnahrung</div>
<div class="chip-wrap">
<span class="chip on" style="font-size:11px;">Reis</span>
<span class="chip off" style="font-size:11px;">Nudeln</span>
<span class="chip on" style="font-size:11px;">Mehl</span>
<span class="chip on" style="font-size:11px;">Zucker</span>
</div>
</div>
</div>
<div class="m-tabbar">
<div class="m-tab"><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 active"><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>Breadcrumb "← Einstellungen" navigiert zurück zu <code>/settings</code></li>
<li>"Einstellungen" bleibt in der Sidebar aktiv (kein eigener Nav-Eintrag für Vorräte)</li>
<li>StaplesManager-Komponente unverändert mit <code>context="settings"</code> (3-spaltig auf md+)</li>
<li>Kein Speichern-Button. Hinweistext "Änderungen werden automatisch gespeichert." unter den Chips</li>
<li>Mobile: Chips statt 3-spaltig 1-spaltig (volle Breite), Flex-Wrap bleibt bestehen</li>
<li>D3 hat eigene <code>+page.server.ts</code> die <code>+page.svelte</code> bei <code>/household/staples</code> gibt es bereits</li>
</ul>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section-label">S3 — Hover-Zustand der Kacheln</div>
<div class="state">
<div class="state-header">
<div class="state-id">S3</div>
<div>
<div class="state-title">Kachel-Hover — visuelles Feedback</div>
<div class="state-desc">Alle Kacheln sind anklickbare Links. Hover hebt die Kachel visuell an.</div>
</div>
</div>
<div class="preview-wrap">
<div class="preview-d-wrap">
<div class="preview-label">Desktop — Vorräte-Kachel im Hover</div>
<div class="preview-d-clip">
<div class="preview-d-scale">
<div class="shell">
<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">Haushalt</div>
<a class="sidebar-item" href="#"><span class="sidebar-icon">👥</span>Mitglieder</a>
<a class="sidebar-item active" href="#"><span class="sidebar-icon">⚙️</span>Einstellungen</a>
</div>
</div>
<div class="page-content">
<div class="page-title">Einstellungen</div>
<div class="page-subtitle">Familie Raddatz</div>
<div class="settings-grid">
<!-- Hovered Vorräte card -->
<a class="setting-card primary" href="#" style="box-shadow:var(--shadow-raised);border-color:#C0BFB8;cursor:pointer;">
<div class="card-icon">🥫</div>
<div class="card-stat">14</div>
<div class="card-stat-label">von 32 Zutaten als Vorrat markiert</div>
<div class="card-title">Vorräte</div>
<div class="card-desc">Lege fest, welche Zutaten immer zu Hause sind.</div>
<div class="card-cta">Vorräte bearbeiten →</div>
</a>
<a class="setting-card" href="#">
<div class="card-icon">👥</div>
<div class="card-title">Mitglieder</div>
<div class="card-meta" style="margin-top:4px;">3 Mitglieder</div>
<div class="card-desc" style="margin-top:8px;">Haushaltsmitglieder einladen, Rollen verwalten.</div>
<div class="card-cta-secondary">Mitglieder verwalten →</div>
</a>
</div>
<div class="settings-grid-bottom">
<a class="setting-card" href="#"><div class="card-icon">👤</div><div class="card-title">Profil</div><div class="card-meta" style="margin-top:4px;">Marcel R.</div><div class="card-desc" style="margin-top:8px;">Name und E-Mail anpassen.</div><div class="card-cta-secondary">Profil bearbeiten →</div></a>
<div style="border:1.5px dashed var(--color-border);border-radius:var(--radius-xl);padding:24px;display:flex;align-items:center;justify-content:center;color:var(--color-text-muted);font-size:12px;">Weitere folgen</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="notes">
<div class="notes-label">Notizen</div>
<ul>
<li>Hover: <code>box-shadow: --shadow-raised</code> + <code>border-color: #C0BFB8</code></li>
<li>Vorräte-Kachel behält den grünen Linksstreifen auch im Hover</li>
<li>Transition: <code>box-shadow 150ms ease, border-color 150ms ease</code></li>
<li>Cursor: <code>pointer</code> auf allen Kacheln</li>
<li>Focus-visible: <code>outline: 2px solid --green-dark; outline-offset: 2px</code></li>
</ul>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section-label">S4 — Leerer Zustand (kein Vorrat gesetzt)</div>
<div class="state">
<div class="state-header">
<div class="state-id">S4</div>
<div>
<div class="state-title">Vorräte-Kachel bei 0 aktiven Vorräten</div>
<div class="state-desc">Wenn noch kein Vorrat gesetzt wurde, zeigt die Kachel eine Einladung zur Aktion statt der Zahl.</div>
</div>
</div>
<div class="preview-wrap">
<div class="preview-d-wrap">
<div class="preview-label">Desktop — 0 Vorräte</div>
<div class="preview-d-clip">
<div class="preview-d-scale">
<div class="shell">
<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">Haushalt</div>
<a class="sidebar-item" href="#"><span class="sidebar-icon">👥</span>Mitglieder</a>
<a class="sidebar-item active" href="#"><span class="sidebar-icon">⚙️</span>Einstellungen</a>
</div>
</div>
<div class="page-content">
<div class="page-title">Einstellungen</div>
<div class="page-subtitle">Familie Raddatz</div>
<div class="settings-grid">
<a class="setting-card primary" href="#">
<div class="card-icon">🥫</div>
<!-- Empty state: no big number, instead prompt -->
<div style="font-size:13px;color:var(--color-text-muted);margin-bottom:8px;">Noch keine Vorräte eingerichtet</div>
<div class="card-title">Vorräte</div>
<div class="card-desc">Lege fest, welche Zutaten immer zu Hause sind. Sie werden beim Einkaufen automatisch herausgefiltert.</div>
<div class="card-cta">Jetzt einrichten →</div>
</a>
<a class="setting-card" href="#">
<div class="card-icon">👥</div>
<div class="card-title">Mitglieder</div>
<div class="card-meta" style="margin-top:4px;">1 Mitglied</div>
<div class="card-desc" style="margin-top:8px;">Haushaltsmitglieder einladen, Rollen verwalten.</div>
<div class="card-cta-secondary">Mitglieder verwalten →</div>
</a>
</div>
<div class="settings-grid-bottom">
<a class="setting-card" href="#"><div class="card-icon">👤</div><div class="card-title">Profil</div><div class="card-meta" style="margin-top:4px;">Marcel R.</div><div class="card-cta-secondary" style="margin-top:8px;">Bearbeiten →</div></a>
<div style="border:1.5px dashed var(--color-border);border-radius:var(--radius-xl);padding:24px;display:flex;align-items:center;justify-content:center;color:var(--color-text-muted);font-size:12px;">Weitere folgen</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="notes">
<div class="notes-label">Notizen</div>
<ul>
<li>Wenn <code>stapleCount === 0</code>: Stat-Zahl weglassen, stattdessen "Noch keine Vorräte eingerichtet" in muted</li>
<li>CTA-Text ändert sich: "Jetzt einrichten →" statt "Vorräte bearbeiten →"</li>
<li>Kachel navigiert weiterhin zu D3 — StaplesManager lädt immer, unabhängig vom Count</li>
</ul>
</div>
</div>
<!-- ─── Machine-readable agent section ─── -->
<div class="agent-section">
<h2>Maschinen-lesbare Spezifikation</h2>
<p>Diese Sektion enthält verbindliche Implementierungsregeln für den Coding-Agenten.</p>
<pre class="spec-comment">
/* spec:rules — E1 Einstellungen Kachel
*
* ROUTE: /settings (E1 hub)
* DATA LOAD (page.server.ts):
* - GET /v1/ingredient-categories to count stapleCount
* stapleCount = sum of ingredients where isStaple === true
* - member count available from layout data (locals.haushalt)
* or fetch GET /v1/households/mine/members and count length
* - profile name from locals.benutzer.name
*
* LAYOUT: E1 HUB
* grid: 2 columns (2fr 1fr) top row + 2 columns (1fr 1fr) bottom row; gap 16px
* mobile: single column, full-width cards, gap 12px
*
* CARD: all cards are <a> tags (href to target route)
* border-radius: --radius-xl
* border: 1px solid --color-border
* bg: white
* padding: 24px desktop / 16px mobile
* hover: box-shadow --shadow-raised, border-color #C0BFB8
* transition: box-shadow 150ms ease, border-color 150ms ease
* cursor: pointer
* focus-visible: outline 2px solid --green-dark, offset 2px
*
* VORRÄTE CARD (primary)
* border-left: 3px solid --green-dark
* stat number: font-family --font-display, font-size 36px, color --green-dark
* stat label: "von {total} Zutaten als Vorrat markiert", 11px, --color-text-muted
* empty state (stapleCount === 0): hide stat, show "Noch keine Vorräte eingerichtet"
* cta: "Vorräte bearbeiten →" (empty: "Jetzt einrichten →")
* href: /household/staples?ctx=settings
*
* MITGLIEDER CARD
* meta: "{memberCount} Mitglieder"
* href: /members
*
* PROFIL CARD
* meta: locals.benutzer.name
* href: /profile (not yet implemented — render as disabled or placeholder)
*
* ROUTE: /household/staples?ctx=settings (D3)
* component: StaplesManager with context="settings" (already exists)
* breadcrumb: "← Einstellungen" linking back to /settings
* sidebar: "Einstellungen" stays active (no separate nav item for staples)
* no save button — StaplesManager auto-saves via debounced PATCH 300ms
* hint text below grid: "Änderungen werden automatisch gespeichert. Gilt ab der nächsten Einkaufsliste."
* grid: 3-col on md+ (context="settings" already sets this in StaplesManager)
*
* CHIP STYLES (for reference — rendered by StapleChip, do NOT reimplement)
* selected: bg --green-dark, color white, border-color --green-dark
* unselected: bg transparent, color --color-text-muted, border 1px solid --color-border
* hover unselected: border-color --green-light, color --green-dark
*
* CATEGORY LABEL TYPOGRAPHY
* font-size: 10px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase
* color: --color-text-muted; margin-bottom: 10px
*/
</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">E1 Hub Layout</td></tr>
<tr><td>grid-desktop</td><td>2fr 1fr / 1fr 1fr</td><td>top row / bottom row</td></tr>
<tr><td>grid-mobile</td><td>1fr</td><td>full-width stack</td></tr>
<tr><td>gap</td><td>16px desktop / 12px mobile</td><td></td></tr>
<tr class="group-row"><td colspan="3">Vorräte Card</td></tr>
<tr><td>stat-font</td><td>--font-display, 36px, --green-dark</td><td>Fraunces</td></tr>
<tr><td>accent-border</td><td>border-left: 3px solid --green-dark</td><td>primary indicator</td></tr>
<tr><td>stat-source</td><td>count isStaple=true from /v1/ingredient-categories</td><td>load in page.server.ts</td></tr>
<tr><td>empty-state</td><td>hide stat; show muted text</td><td>when stapleCount === 0</td></tr>
<tr><td>href</td><td>/household/staples?ctx=settings</td><td>D3 route</td></tr>
<tr class="group-row"><td colspan="3">D3 Staples Page</td></tr>
<tr><td>component</td><td>StaplesManager context="settings"</td><td>existing, do not modify</td></tr>
<tr><td>breadcrumb</td><td>← Einstellungen → /settings</td><td>above page title</td></tr>
<tr><td>active-nav</td><td>Einstellungen in sidebar</td><td>not a separate nav entry</td></tr>
<tr><td>save-hint</td><td>"Änderungen werden automatisch gespeichert."</td><td>below chip grid</td></tr>
<tr><td>debounce</td><td>300ms (in StaplesManager)</td><td>do not add extra debounce</td></tr>
</tbody>
</table>
</div>
</div>
</body>
</html>