All checks were successful
continuous-integration/drone/push Build is passing
Copy selected members' emails to clipboard in 'First Last <email>' format
235 lines
7.2 KiB
Markdown
235 lines
7.2 KiB
Markdown
# 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 <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.)
|