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>
701 lines
40 KiB
HTML
701 lines
40 KiB
HTML
<!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 & 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><a></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 & Ö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 & Ö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>
|