# Bulk Email Copy Feature - Detaillierter Implementierungsplan ## Aktueller Stand Die Checkbox-Funktionalität existiert bereits vollständig: - `select_member` und `select_all` Events in [`lib/mv_web/live/member_live/index.ex`](lib/mv_web/live/member_live/index.ex) (Zeilen 91-117) - Checkboxen im Template [`lib/mv_web/live/member_live/index.html.heex`](lib/mv_web/live/member_live/index.html.heex) (Zeilen 28-54) - `@selected_members` enthält die UUIDs der ausgewählten Mitglieder als Liste ## Gewählte Implementierung: JavaScript Hook mit LiveView Event **Ablauf:** 1. User wählt Mitglieder über Checkboxen aus 2. User klickt "E-Mail-Adressen kopieren" Button 3. LiveView Event `copy_emails` wird ausgelöst 4. Server filtert Member aus `@members` nach `@selected_members` 5. Server formatiert E-Mails im Format `Vorname Nachname ` 6. Server pusht `copy_to_clipboard` Event mit formatiertem String an Client 7. JavaScript Hook empfängt Event und kopiert via `navigator.clipboard.writeText()` 8. Server zeigt Flash-Nachricht mit Erfolgsbestätigung --- ## Implementierungsschritte ### Schritt 1: JavaScript Hook erstellen **Datei:** `assets/js/app.js` - Neuen Hook `CopyToClipboard` zum bestehenden `Hooks` Objekt hinzufügen - Hook lauscht auf `copy_to_clipboard` Event vom Server - Nutzt `navigator.clipboard.writeText()` API für das Kopieren - Fallback-Behandlung für Browser ohne Clipboard API (ältere Browser) - Fehlerbehandlung bei fehlgeschlagenem Kopieren ### Schritt 2: LiveView Event Handler implementieren **Datei:** `lib/mv_web/live/member_live/index.ex` - Neuen `handle_event("copy_emails", ...)` Callback hinzufügen - Member aus `@members` filtern, deren ID in `@selected_members` enthalten ist - Jeden Member im Format `"Vorname Nachname "` formatieren - Formatierte Strings mit `"; "` (Semikolon + Leerzeichen) verbinden - `push_event/3` nutzen um `copy_to_clipboard` Event zu senden - `put_flash/3` für Erfolgsbestätigung mit Anzahl der kopierten Adressen - Private Helper-Funktion für die E-Mail-Formatierung ### Schritt 3: UI Button hinzufügen **Datei:** `lib/mv_web/live/member_live/index.html.heex` - Button im Header-Bereich neben "New Member" Button platzieren - Button nur anzeigen wenn mindestens ein Mitglied ausgewählt ist (`:if` Bedingung) - `phx-hook="CopyToClipboard"` Attribut für JavaScript Hook Anbindung - `phx-click="copy_emails"` für Event-Auslösung - Icon: `hero-clipboard-document` oder `hero-envelope` - Button-Text mit Anzahl der ausgewählten Mitglieder anzeigen - Accessibility: `aria-label` für Screen Reader ### Schritt 4: Gettext Übersetzungen hinzufügen **Dateien:** - `priv/gettext/default.pot` - Template aktualisieren via `mix gettext.extract` - `priv/gettext/de/LC_MESSAGES/default.po` - Deutsche Übersetzungen - `priv/gettext/en/LC_MESSAGES/default.po` - Englische Übersetzungen (falls vorhanden) **Zu übersetzende Strings:** - Button-Text: "Copy Email Addresses" - Flash-Nachricht Erfolg: "Copied %{count} email address(es) to clipboard" - Flash-Nachricht Fehler: "No members selected" ### Schritt 5: Moduledoc aktualisieren **Datei:** `lib/mv_web/live/member_live/index.ex` - `@moduledoc` um neues Event `copy_emails` erweitern - Dokumentation der Funktionalität hinzufügen --- ## Edge Cases ### E1: Keine Mitglieder ausgewählt - Button wird nicht angezeigt (UI-seitig gelöst) - Falls Event dennoch ausgelöst wird: Error-Flash anzeigen, nichts kopieren ### E2: Ausgewählte Mitglieder nicht mehr in `@members` Liste - Kann passieren wenn Member zwischenzeitlich gelöscht wurde - Nur vorhandene Member verarbeiten, keine Fehler werfen - Flash zeigt tatsächliche Anzahl kopierter Adressen ### E3: Member ohne E-Mail-Adresse - Defensive Programmierung: Member ohne E-Mail überspringen ### E4: Member mit leerem Vor- oder Nachnamen - Defensive Programmierung: Leere Namen graceful behandeln ### E5: Sonderzeichen in Namen - Namen können Umlaute, Akzente, etc. enthalten - Keine Escaping nötig, da Text direkt in Zwischenablage kopiert wird - E-Mail-Clients verarbeiten Unicode korrekt ### E6: Sehr lange Liste (100+ Mitglieder) - String kann sehr lang werden - Clipboard API hat kein praktisches Limit - Kein spezielles Handling nötig ### E7: Browser unterstützt Clipboard API nicht - `navigator.clipboard` ist nicht in allen Browsern verfügbar - Fallback: `document.execCommand('copy')` (deprecated aber breit unterstützt) - Oder: Fehler-Flash anzeigen ### E8: Clipboard-Zugriff vom Browser blockiert - Moderne Browser können Clipboard-Zugriff einschränken - HTTPS erforderlich (in Produktion gegeben) - User muss ggf. Berechtigung erteilen - Fehlerbehandlung im Hook nötig ### E9: Parallel laufende Suche/Filter ändert `@members` - User wählt Mitglieder, dann ändert Suche die Liste - `@selected_members` bleibt erhalten, aber IDs passen nicht mehr zu `@members` - Nur noch vorhandene (angezeigte) Members werden kopiert - Entscheidung: Selection bei Suche beibehalten? ### E10: "Select All" nach Filterung - Wenn gefiltert und "Select All" geklickt, werden nur sichtbare Members ausgewählt - Bestehendes Verhalten, kein neues Problem --- ## Testplan ### Unit Tests (index.ex) **T1: copy_emails Event - Erfolgsfall** - Setup: 3 Members in `@members`, 2 davon in `@selected_members` - Assert: `push_event` wird mit korrektem String aufgerufen - Assert: Flash-Nachricht mit count=2 **T2: copy_emails Event - Keine Auswahl** - Setup: `@selected_members` ist leer - Assert: Kein `push_event` - Assert: Error-Flash oder keine Aktion **T3: copy_emails Event - Alle ausgewählt** - Setup: Alle Members in `@selected_members` - Assert: Alle E-Mails im Output-String **T4: E-Mail Formatierung** - Assert: Format ist `"Vorname Nachname "` - Assert: Mehrere E-Mails mit `"; "` getrennt **T5: Member mit Sonderzeichen im Namen** - Setup: Member mit Name "Müller-Lüdenscheidt" - Assert: Name wird korrekt übernommen **T6: Teilweise nicht vorhandene Member** - Setup: `@selected_members` enthält ID die nicht in `@members` ist - Assert: Nur vorhandene Members werden verarbeitet, kein Crash ### LiveView Integration Tests **T7: Button Sichtbarkeit** - Assert: Button nicht sichtbar wenn `@selected_members` leer - Assert: Button sichtbar wenn mindestens 1 Member ausgewählt **T8: Button zeigt korrekte Anzahl** - Setup: 3 Members ausgewählt - Assert: Button-Text enthält "(3)" **T9: Click löst Event aus** - Action: Click auf Copy-Button - Assert: `copy_emails` Event wird gesendet **T10: Vollständiger Flow** - Action: Member auswählen, Button klicken - Assert: Flash-Nachricht erscheint ## Zu ändernde Dateien | Datei | Änderungstyp | |-------|--------------| | `assets/js/app.js` | Hook hinzufügen | | `lib/mv_web/live/member_live/index.ex` | Event Handler + Helper | | `lib/mv_web/live/member_live/index.html.heex` | Button UI | | `priv/gettext/de/LC_MESSAGES/default.po` | Übersetzungen | | `test/mv_web/member_live/index_test.exs` | Tests | --- ## E-Mail Output Format **Einzelne E-Mail:** ``` Max Mustermann ``` **Mehrere E-Mails:** ``` Max Mustermann ; Erika Musterfrau ; Hans Müller ``` **Hinweis:** Semikolon als Trennzeichen ist Standard für E-Mail-Clients (Outlook, Thunderbird, etc.)