mitgliederverwaltung/email-copy-feature.plan.md
Moritz e2ace3d2a8
All checks were successful
continuous-integration/drone/push Build is passing
feat: add bulk email copy for selected members (#230)
Copy selected members' emails to clipboard in 'First Last <email>' format
2025-12-02 10:02:58 +01:00

7.2 KiB

Bulk Email Copy Feature - Detaillierter Implementierungsplan

Aktueller Stand

Die Checkbox-Funktionalität existiert bereits vollständig:

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 <email>
  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 <email>" 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 <email>"
  • 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 <max@example.com>

Mehrere E-Mails:

Max Mustermann <max@example.com>; Erika Musterfrau <erika@example.com>; Hans Müller <hans@example.com>

Hinweis: Semikolon als Trennzeichen ist Standard für E-Mail-Clients (Outlook, Thunderbird, etc.)