# Sidebar Neuimplementierung - Schritt-für-Schritt Anleitung **Erstellt:** 2025-12-16 **Status:** Bereit zur Umsetzung **Strategie:** Sequenzielle Tasks mit frischem Kontext pro Task --- ## Übersicht Diese Anleitung zerlegt die komplexe Sidebar-Implementierung in 13 beherrschbare Subtasks. Jeder Task wird mit einem frischen Cursor-Agent im Auto-Mode (oder Sonnet 4.5 für komplexere Aufgaben) umgesetzt. **Ziel:** Saubere, wartbare Sidebar-Implementierung nahe am DaisyUI-Standard ohne Custom-Variants oder Speziallösungen. --- ## Task 1: Vorbereitung & Analyse **Agent:** Sonnet 4.5 **Geschätzte Dauer:** 10 Minuten ### Prompt: ``` Analysiere die aktuelle Sidebar-Implementierung in diesem Projekt und erstelle einen detaillierten Bericht: 1. Liste alle Dateien auf, die mit der Sidebar zusammenhängen 2. Dokumentiere die aktuelle Struktur (HTML, CSS, JavaScript) 3. Identifiziere alle Custom-CSS-Klassen und Variants 4. Dokumentiere die JavaScript-Hooks und ihre Funktionen 5. Erstelle eine Liste aller Dependencies (DaisyUI-Komponenten, Tailwind-Klassen) Speichere den Bericht als `docs/sidebar-analysis-current-state.md` Ziel: Vollständiges Verständnis des Ist-Zustands vor der Neuimplementierung. ``` ### Acceptance Criteria: - ✅ Alle Sidebar-bezogenen Dateien identifiziert - ✅ Aktuelle Implementierung dokumentiert - ✅ Custom CSS und JavaScript dokumentiert - ✅ Bericht als Markdown gespeichert --- ## Task 2: DaisyUI Drawer Pattern Recherche **Agent:** Sonnet 4.5 (wegen Web-Recherche) **Geschätzte Dauer:** 15 Minuten ### Prompt: ``` Recherchiere und dokumentiere das Standard DaisyUI Drawer Pattern: 1. Lies die DaisyUI Dokumentation für die `drawer` Komponente 2. Finde Best-Practice-Beispiele für responsive Sidebars mit DaisyUI 3. Dokumentiere, wie drawer-toggle funktioniert 4. Dokumentiere, wie drawer-open für Desktop funktioniert 5. Erstelle Code-Beispiele für: - Mobile Drawer (overlay) - Desktop Sidebar (persistent) - Kombination beider Speichere die Dokumentation als `docs/daisyui-drawer-pattern.md` Wichtig: Verwende KEINE custom CSS variants - nur Standard DaisyUI und Tailwind. ``` ### Acceptance Criteria: - ✅ DaisyUI Drawer Pattern dokumentiert - ✅ Mobile und Desktop Patterns verstanden - ✅ Code-Beispiele vorhanden - ✅ Dokumentation gespeichert --- ## Task 3: Anforderungsdefinition & Design **Agent:** Sonnet 4.5 **Geschätzte Dauer:** 20 Minuten ### Prompt: ``` Erstelle eine präzise Anforderungsspezifikation für die Sidebar basierend auf folgenden Anforderungen: ## Funktionale Anforderungen: 1. **Logo:** - Immer sichtbar, gleiche Größe (32px / size-8) - Sowohl im expanded als auch collapsed State - Keine zwei verschiedenen Logo-Elemente 2. **Toggle-Button:** - Nur auf Desktop sichtbar - Icon-Swap: Chevron-left (expanded) ↔ Chevron-right (collapsed) - Immer erreichbar 3. **Menü-Items:** - Expanded: Icons + Text-Labels - Collapsed: Nur Icons mit Tooltips (tooltip-right) - Einheitlicher Hover-Effekt 4. **Nested Menu "Beiträge":** - Expanded: Standard
mit - Collapsed: DaisyUI dropdown dropdown-right - Nur EIN Hover-Effekt, kein doppelter 5. **Footer:** - IMMER am unteren Ende der Sidebar (via Flexbox) - Theme-Toggle (immer sichtbar) - Language-Selector (nur expanded) - User-Menu mit Avatar (dropdown-top dropdown-end) - Avatar: Erste Buchstabe, zentriert 6. **State Persistence:** - localStorage: 'sidebar-expanded' - data-attribute: [data-sidebar-expanded="true"|"false"] 7. **Responsive:** - Mobile: Standard DaisyUI Drawer Overlay - Desktop: Fixed Sidebar mit smooth width transition ## Aufgaben: 1. Erstelle Wireframes (als ASCII-Art) für: - Desktop Expanded - Desktop Collapsed - Mobile mit Overlay 2. Liste alle benötigten DaisyUI-Komponenten auf 3. Definiere CSS-Strategie: - Nur Tailwind + DaisyUI - KEINE custom variants (@custom-variant) - State-Management via data-attribute selectors 4. Definiere State-Management-Strategie: - JavaScript Hook - localStorage - CSS reactions Speichere als `docs/sidebar-requirements-v2.md` ``` ### Acceptance Criteria: - ✅ Anforderungen klar dokumentiert - ✅ Wireframes erstellt - ✅ Komponenten-Liste vorhanden - ✅ CSS-Strategie definiert - ✅ State-Management definiert --- ## Task 4: CSS Foundation **Agent:** Auto-Mode **Geschätzte Dauer:** 15 Minuten ### Prompt: ``` Erstelle die CSS-Grundlage für die neue Sidebar in `assets/css/app.css`: ## Schritt 1: Aufräumen 1. Entferne ALLE bestehenden Custom CSS Variants für Sidebar: - @custom-variant is-drawer-open - @custom-variant is-drawer-close - Alle .is-drawer-* Regeln 2. Entferne alte Sidebar-spezifische Custom-Klassen ## Schritt 2: Neue CSS-Regeln erstellen Erstelle CSS basierend auf `[data-sidebar-expanded]` Attribut: ```css /* Desktop Sidebar Base */ .sidebar { @apply flex flex-col bg-base-200 min-h-screen; @apply transition-[width] duration-300 ease-in-out; width: 16rem; /* Expanded: w-64 */ } /* Collapsed State */ [data-sidebar-expanded="false"] .sidebar { width: 4rem; /* Collapsed: w-16 */ } /* Text Labels - Hide in Collapsed State */ .menu-label { @apply transition-all duration-200 whitespace-nowrap; } [data-sidebar-expanded="false"] .sidebar .menu-label { @apply opacity-0 w-0 overflow-hidden pointer-events-none; } /* Toggle Button Icon Swap */ .sidebar-collapsed-icon { @apply hidden; } [data-sidebar-expanded="false"] .sidebar-expanded-icon { @apply hidden; } [data-sidebar-expanded="false"] .sidebar-collapsed-icon { @apply block; } /* Menu Groups - Show/Hide Based on State */ .expanded-menu-group { @apply block; } .collapsed-menu-group { @apply hidden; } [data-sidebar-expanded="false"] .sidebar .expanded-menu-group { @apply hidden; } [data-sidebar-expanded="false"] .sidebar .collapsed-menu-group { @apply block; } /* Elements Only Visible in Expanded State */ .expanded-only { @apply block transition-opacity duration-200; } [data-sidebar-expanded="false"] .sidebar .expanded-only { @apply hidden; } /* Tooltip - Only Show in Collapsed State */ .sidebar .tooltip::before, .sidebar .tooltip::after { @apply opacity-0 pointer-events-none; } [data-sidebar-expanded="false"] .sidebar .tooltip:hover::before, [data-sidebar-expanded="false"] .sidebar .tooltip:hover::after { @apply opacity-100; } /* Menu Item Alignment */ [data-sidebar-expanded="false"] .sidebar .menu > li > a, [data-sidebar-expanded="false"] .sidebar .menu > li > button { @apply justify-center px-0; } ``` ## Schritt 3: Testen - Kompiliere CSS: `mix assets.build` - Prüfe auf Fehler - Stelle sicher, dass keine alten Custom-Variants mehr existieren Verwende AUSSCHLIESSLICH: - Tailwind @apply - Standard DaisyUI Klassen - CSS attribute selectors für state ``` ### Acceptance Criteria: - ✅ Alte Custom Variants entfernt - ✅ Neue CSS-Regeln erstellt - ✅ CSS kompiliert ohne Fehler - ✅ Nur Standard Tailwind/DaisyUI verwendet --- ## Task 5: Layout-Struktur **Agent:** Auto-Mode **Geschätzte Dauer:** 20 Minuten ### Prompt: ``` Implementiere die grundlegende Layout-Struktur für die Sidebar in `lib/mv_web/components/layouts.ex`: ## Anforderungen: 1. Nutze DaisyUI `drawer` + `drawer-open` Pattern 2. Ein Container für Mobile UND Desktop (keine Duplikate!) 3. `@inner_block` darf nur EINMAL gerendert werden 4. `data-sidebar-expanded` Attribut auf root 5. `phx-hook="SidebarState"` auf root 6. `id="main-sidebar"` auf main content (für Tests) ## Implementierung: Ersetze die bestehende `app/1` Funktion mit: ```heex def app(assigns) do club_name = get_club_name() assigns = assign(assigns, :club_name, club_name) ~H""" <%= if @current_user do %>
{render_slot(@inner_block)}
<% else %>
{render_slot(@inner_block)}
<% end %> <.flash_group flash={@flash} /> """ end ``` ## Wichtig: - @inner_block wird nur EINMAL gerendert (im drawer-content) - Mobile und Desktop teilen sich den gleichen main-content - Sidebar-Inhalt kommt in späteren Tasks ## Testen: 1. Kompiliere: `mix compile` 2. Starte Server: `mix phx.server` 3. Prüfe: Keine duplicate ID Fehler in Browser Console 4. Prüfe: Layout funktioniert responsive 5. Prüfe: Mobile Header erscheint nur auf Mobile ``` ### Acceptance Criteria: - ✅ Layout-Struktur implementiert - ✅ Keine duplicate IDs - ✅ @inner_block nur einmal gerendert - ✅ Responsive funktioniert - ✅ Kompiliert ohne Fehler --- ## Task 6: Sidebar Header Komponente **Agent:** Auto-Mode **Geschätzte Dauer:** 20 Minuten ### Prompt: ``` Implementiere die Sidebar-Header-Komponente in `lib/mv_web/components/layouts/sidebar.ex`: ## Anforderungen: 1. **Logo:** - Immer `size-8` (32px) - Immer sichtbar (kein Hide) - Nur EIN Logo-Element - Pfad: `/images/mila.svg` 2. **Club-Name:** - Text-Label mit CSS-Klasse `menu-label` - Wird via CSS ausgeblendet (collapsed) - `text-lg font-bold truncate` 3. **Toggle-Button:** - Nur auf Desktop sichtbar (responsive: `hidden lg:flex` oder `lg:block`) - DaisyUI-konforme Button-Variante wählen: * Option A: `btn btn-ghost btn-sm btn-square` (minimal, icon-only) * Option B: `btn btn-ghost btn-sm` (mit etwas Padding) * Option C: `btn btn-ghost btn-circle` (rund, falls besser zum Design passt) - Icon-Strategie (wähle die beste Variante): * Option A: Zwei Icons mit CSS-Klassen (`.sidebar-expanded-icon` / `.sidebar-collapsed-icon`) * Option B: Ein Icon mit CSS transform (rotate bei collapsed) * Option C: Ein Icon mit CSS content-swap (via ::before/::after) - Event-Handler (wähle passende Variante): * Option A: `onclick="toggleSidebar()"` (wenn global function vorhanden) * Option B: `phx-click="toggle_sidebar"` (wenn LiveView event) * Option C: `phx-hook="SidebarToggle"` (wenn Hook-basiert) - ARIA: `aria-label={gettext("Toggle sidebar")}` und `aria-expanded` (wird via JS gesetzt) ## Design-Überlegungen: - Button sollte sich harmonisch in den Header einfügen - Position: Rechts im Header (`ml-auto`) - Icon-Größe: `size-5` oder `size-4` (je nach Button-Größe) - Icon-Typ: Chevron (left/right) oder Arrow (left/right) - wähle das passendere - Hover-Effekt: Standard DaisyUI btn-ghost hover ## Implementierung: Erstelle `sidebar_header/1` Funktion mit: - Flexbox-Layout für Header (Logo + Name + Toggle) - Responsive Toggle-Button - Icon-Swap-Mechanismus (wähle beste Variante) - Korrekte ARIA-Attribute ## Empfehlung: Für DaisyUI-Konformität und Wartbarkeit: - Button: `btn btn-ghost btn-sm btn-square` (icon-only, minimal) - Icons: Zwei separate Icons mit CSS-Klassen (einfach, klar) - Event: `onclick="toggleSidebar()"` (wenn JS Hook vorhanden) ODER `phx-click` (wenn LiveView) ## Beispiel-Struktur (als Orientierung): ```elixir defp sidebar_header(assigns) do ~H"""
Mila Logo {@club_name} <%= unless @mobile do %> <% end %>
""" end ``` ## Integration in layouts.ex: Ersetze den Sidebar Placeholder in `layouts.ex`: ```heex ``` ## Testen: 1. Kompiliere: `mix compile` 2. Starte Server: `mix phx.server` 3. Prüfe: - Logo ist immer 32px groß - Toggle-Button erscheint nur auf Desktop - Button-Design passt zum Rest der Sidebar - Icon wechselt beim Toggle - Hover-Effekt funktioniert - ARIA-Attribute korrekt - Club-Name verschwindet beim Collapse (wenn toggleSidebar() funktioniert) ``` ### Acceptance Criteria: - ✅ sidebar_header Komponente erstellt - ✅ Logo immer gleich groß - ✅ Toggle-Button nur auf Desktop - ✅ DaisyUI-konforme Button-Klassen verwendet - ✅ Icon-Swap funktioniert (egal welche Variante gewählt wurde) - ✅ Event-Handler funktioniert - ✅ Design fügt sich harmonisch ein - ✅ Keine Layout-Breaks --- ## Task 7: Sidebar Navigation - Flat Items **Agent:** Auto-Mode **Geschätzte Dauer:** 25 Minuten ### Prompt: ``` Implementiere einfache Menü-Items (flat, ohne Nesting) in `lib/mv_web/components/layouts/sidebar.ex`: ## Menü-Items: - Members (`/members`) - Users (`/users`) - Custom Fields (`/custom_fields`) - Settings (Placeholder) ## Anforderungen: 1. **Expanded State:** - Icon + Text-Label - Standard DaisyUI menu hover 2. **Collapsed State:**gf - Nur Icon - Tooltip erscheint rechts (`tooltip-right`) - Tooltip-Text aus `data-tip` 3. **Hover-Effekt:** - Einheitlich (Standard DaisyUI menu) - Keine custom hover-styles 4. **Active State:** - Highlight für current_path (optional) ## Implementierung: Füge in `sidebar.ex` hinzu: ```elixir defp sidebar_menu(assigns) do ~H""" """ end attr :href, :string, required: true attr :icon, :string, required: true attr :label, :string, required: true defp menu_item(assigns) do ~H"""
  • <.link navigate={@href} class="flex items-center gap-3 tooltip tooltip-right" data-tip={@label} role="menuitem" > <.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" /> {@label}
  • """ end ``` Ersetze in `sidebar_content`: ```heex <%= if @current_user do %> <.sidebar_menu /> <% end %> ``` ## Testen: 1. Kompiliere und starte Server 2. Prüfe Expanded State: - Icons + Labels sichtbar - Hover funktioniert 3. Toggle zu Collapsed: - Nur Icons sichtbar - Tooltips erscheinen bei Hover - Tooltips zeigen richtigen Text 4. Prüfe: - Einheitlicher Hover-Effekt - Navigation funktioniert (Links klickbar) ``` ### Acceptance Criteria: - ✅ menu_item Komponente erstellt - ✅ 4 Menü-Items implementiert - ✅ Tooltips funktionieren (nur collapsed) - ✅ Icons sichtbar in beiden States - ✅ Hover-Effekt einheitlich - ✅ Navigation funktioniert --- ## Task 8: Sidebar Navigation - Nested Menu **Agent:** Sonnet 4.5 (komplexer) **Geschätzte Dauer:** 30 Minuten ### Prompt: ``` Implementiere das verschachtelte "Beiträge"-Menü (Contributions) mit ZWEI verschiedenen Darstellungen je nach State. ## Problem: Das Nested Menu muss sich anders verhalten als flat items: - **Expanded:** Nutzt
    mit für auf/zuklappbar - **Collapsed:** Nutzt DaisyUI dropdown für Flyout rechts vom Icon ## Anforderungen: 1. **Nur EIN Hover-Effekt** (nicht doppelt) 2. **Flyout erscheint rechts** vom Icon im collapsed state 3. **Smooth transitions** zwischen states 4. **Submenu-Items:** Beitragsarten, Einstellungen ## Implementierung: Füge in `sidebar.ex` hinzu: ```elixir attr :icon, :string, required: true attr :label, :string, required: true slot :inner_block, required: true defp menu_group(assigns) do ~H""" """ end attr :href, :string, required: true attr :label, :string, required: true defp menu_subitem(assigns) do ~H"""
  • <.link navigate={@href} role="menuitem"> {@label}
  • """ end ``` Füge in `sidebar_menu` nach den flat items hinzu: ```elixir <.menu_group icon="hero-currency-dollar" label={gettext("Contributions")} > <.menu_subitem href="/contribution_types" label={gettext("Contribution Types")} /> <.menu_subitem href="/contribution_settings" label={gettext("Settings")} /> ``` ## CSS-Prüfung: Stelle sicher, dass in `app.css` folgende Regeln existieren: ```css /* Expanded: Show details, hide dropdown */ .expanded-menu-group { @apply block; } .collapsed-menu-group { @apply hidden; } /* Collapsed: Hide details, show dropdown */ [data-sidebar-expanded="false"] .sidebar .expanded-menu-group { @apply hidden; } [data-sidebar-expanded="false"] .sidebar .collapsed-menu-group { @apply block; } ``` ## Testen: 1. **Expanded State:** - Details/Summary erscheint - Klick öffnet/schließt Submenu - Submenu-Items sind klickbar - NUR EIN Hover-Effekt auf summary 2. **Collapsed State:** - Dropdown erscheint - Flyout öffnet RECHTS vom Icon - Menu-Title "Contributions" erscheint - Submenu-Items sind klickbar - NUR EIN Hover-Effekt auf button 3. **Toggle zwischen States:** - Smooth Transition - Keine Glitches - Keine doppelten Elemente sichtbar ## Debugging: Falls Probleme auftreten: - Browser DevTools: Prüfe, welche Elemente .expanded-menu-group oder .collapsed-menu-group haben - Prüfe data-sidebar-expanded Attribut im HTML - Prüfe z-index des Dropdowns (z-50) - Prüfe, ob dropdown-right funktioniert ``` ### Acceptance Criteria: - ✅ menu_group und menu_subitem implementiert - ✅ Details funktioniert (expanded) - ✅ Dropdown funktioniert (collapsed) - ✅ Flyout erscheint rechts - ✅ Nur EIN Hover-Effekt - ✅ Keine visuellen Glitches --- ## Task 9: Sidebar Footer mit Flexbox **Agent:** Auto-Mode **Geschätzte Dauer:** 25 Minuten ### Prompt: ``` Implementiere den Sidebar-Footer mit korrekter Positionierung am unteren Ende. ## Flexbox-Struktur: Die Sidebar muss als Flexbox-Container funktionieren: 1. `.sidebar`: `flex flex-col` (bereits vorhanden) 2. Navigation: `flex-1` (nimmt verfügbaren Platz) 3. Footer: `mt-auto` (wird nach unten geschoben) ## Footer-Komponenten: 1. **Language Selector:** - Nur im expanded state sichtbar (`.expanded-only`) - DaisyUI select - Form mit POST zu `/locale` 2. **Theme Toggle:** - IMMER sichtbar - Horizontal: Sun Icon + Toggle + Moon Icon - DaisyUI toggle + theme-controller 3. **User Menu:** - DaisyUI dropdown - `dropdown-top dropdown-end` (öffnet nach oben) - Avatar: Erste Buchstabe, zentriert, rund - Email nur expanded - Dropdown: Profile + Logout ## Implementierung: ```elixir defp sidebar_footer(assigns) do ~H"""
    <.theme_toggle /> <.user_menu current_user={@current_user} />
    """ end defp theme_toggle(assigns) do ~H""" """ end defp user_menu(assigns) do ~H""" """ end ``` Ersetze in `sidebar_content` den Footer-Placeholder: ```heex <%= if @current_user do %> <.sidebar_footer current_user={@current_user} /> <% end %> ``` Prüfe, dass `.sidebar_menu` `flex-1` hat: ```heex