trihub-ernaehrung-backseatDevs/docs/contact.md

155 lines
8.2 KiB
Markdown

# Beraterkontakt (Simulation)
## Testseite starten
Wie bei der Buchung in drei Terminals starten:
```sh
npm run dev:mail
npm run dev:server
npm run dev
```
Öffne <http://localhost:5173/contact-test.html?berater-id=relindis-agethen>.
Der Button öffnet ein modales Fenster. Gib eine E-Mail aus einer vorhandenen
Buchung in `src/server/data/bookings.csv` ein, wähle einen Kontaktgrund und
bestätige den Simulationshinweis. Beim Klick auf „Anfrage senden & Termin wählen“
prüft der Server die E-Mail und versendet nur bei vorhandener Buchung. Bei Bedarf zuerst
über die bestehende Angebotsseite eine Testbuchung anlegen.
Nach dem Absenden müssen zwei getrennte Nachrichten in
<http://localhost:8025> erscheinen: an die Kundenadresse und beispielsweise
`relindis.agethen@tri-hub.de`. Der Browser wechselt anschließend zur vom Auftrag
vorgegebenen Microsoft-Bookings-Adresse. Es wird dort kein Termin automatisch
gebucht. Die Checkliste spricht von Microsoft Forms; umgesetzt ist der konkret
angegebene Bookings-Link.
## Checkliste
- [x] Kontaktgrund auswählbar: Coaching, Ernährungsplan, Wettkampfverpflegung,
Regeneration, Paketfragen, Termin und Sonstiges.
- [x] Getrennte Eingangsbestätigungen an Kunde und Berater, ausschließlich
simuliert über Mailpit, mit Kontaktgrund und Simulationshinweis.
- [x] Weiterleitung zur vorhandenen Microsoft-Bookings-Adresse nach Annahme
beider Nachrichten durch Mailpit; zusätzlicher Link als Rückfalloption.
- [x] Kontakt nur nach erfolgreichem E-Mail-Abgleich mit `bookings.csv`;
der Server prüft beim tatsächlichen Absenden erneut.
## Einbindung in Beraterprofile
Die Profilseite importiert `openContactDialog` aus
`src/features/contact/contact-dialog.js` und übergibt ausschließlich die ID:
```js
import { openContactDialog } from "./src/features/contact/contact-dialog.js";
button.addEventListener("click", () => {
openContactDialog({ advisorId: profile.id });
});
```
Den Importpfad relativ zur einbindenden Datei anpassen. Das Modul bringt die
Popup-Styles und die lokalen DM-Sans-Schriftdateien mit. Die vollständige
Einbauanleitung für die Profilseite steht in [contact-integration.md](contact-integration.md).
Die gemeinsame Datenquelle `src/shared/berater-daten.json` enthält ein Array
mit eindeutigen String-IDs und den Feldern `id`, `vorname`, `nachname`, `spitzname`, `email`. Die vorhandenen Einträge enthalten die Beraterprofile. Zusätzliche Profilfelder sind möglich. Die Datei
wird im Frontend eingebunden: ausschließlich öffentliche Profildaten eintragen.
`src/shared/berater.js` stellt `findAdvisor(advisorId)` bereit. Popup und Server
verwenden dieselbe Zuordnung. Der Server holt die Empfängeradresse ausschließlich
aus dieser JSON; vom Browser gesendete Namen oder Empfängeradressen werden
nicht übernommen. E-Mail-Adressen müssen gültig sein und auf `@tri-hub.de`
enden. Eine unbekannte ID wird abgelehnt und sperrt das Absenden im Popup.
Die Testseite liest den URL-Parameter `berater-id`, zum Beispiel
`contact-test.html?berater-id=maren-hoffmann`. Ohne Parameter verwendet sie
`relindis-agethen`. Nach Änderungen an der JSON den API-Server neu starten und für
das Deployment das Frontend neu bauen.
Das native `dialog` hält den Tastaturfokus im Popup. Escape und Schließen
bringen ihn zum auslösenden Button zurück. Während einer Anfrage ist Schließen
kurz gesperrt, damit der Versand nicht unbemerkt weiterläuft. Das Fenster ist
auf schmalen Bildschirmen scrollbar. Die E-Mail wird bei jedem Absenden geprüft;
ein separater Prüfbutton ist nicht erforderlich. Es gibt keine zusätzlichen
Laufzeitabhängigkeiten.
## API
Der vollständige OpenAPI-Vertrag steht in `src/server/swagger.json`.
Beide Endpunkte erwarten POST mit `Content-Type: application/json`, maximal
8 KiB. Fehler enthalten `error.code` und `error.message`.
| Endpunkt | Eingabe | Erfolg |
| ----------------------- | --------------------------------------------------- | ---------------------------------------------------------- |
| `/api/contact/validate` | `{ "email": "kunde@example.test" }` | 200: `{ "valid": true, "email": "kunde@example.test" }` |
| `/api/contact` | E-Mail, Berater-ID, Grund, Zustimmung (siehe unten) | 201: `simulated`, `emailStatus: "accepted"`, `redirectUrl` |
```json
{
"email": "kunde@example.test",
"advisorId": "relindis-agethen",
"reason": "coaching",
"simulationAccepted": true
}
```
400 bedeutet ungültige Eingaben, 403 keine passende Buchung, 405 falsche Methode,
413 zu große Anfrage, 415 falscher Medientyp, 503 nicht lesbarer CSV-Speicher oder ungültige Beraterkontaktdaten.
502 bedeutet, dass mindestens eine Mailannahme fehlgeschlagen oder unklar ist;
der Browser leitet dann nicht weiter. 500 bezeichnet einen unerwarteten Fehler.
Die CSV wird ausschließlich gelesen und bleibt unverändert. Der separate
Validierungsendpunkt bleibt für API-Nutzer verfügbar; das Popup verwendet
ausschließlich `/api/contact` mit integrierter Kundenprüfung.
## Grenzen der Simulation
Der E-Mail-Abgleich ignoriert Groß-/Kleinschreibung und äußere Leerzeichen.
Er bestätigt einen Eintrag in der Buchungsdatei, nicht den Besitz des Postfachs.
Die Prüfantwort ist kein Anmeldetoken und gibt keine Buchungsdetails zurück.
Vor einem öffentlichen Produktiveinsatz wären eine Anmeldung oder Bestätigung
per E-Mail sowie ein Schutz vor automatisierten Adressabfragen erforderlich.
Kontaktmails verwenden fest `127.0.0.1:1025`, unabhängig von einer eventuell
externen SMTP-Konfiguration des Buchungsablaufs. Mailpit muss deshalb auf
demselben Host laufen. Kunden- und Berateradresse sind simulierte Empfänger;
es erfolgt kein externer Versand. SMTP-Annahme ist keine reale Zustellbestätigung.
Kontaktanfragen werden nicht dauerhaft gespeichert und haben keinen
Idempotenzschlüssel. Doppelklicks sind während des Versands gesperrt. Bei
Verbindungsabbrüchen oder Teilfehlern kann bereits eine Nachricht vorliegen:
vor manuellem Wiederholen Mailpit prüfen. Es gibt keinen automatischen Neuversand.
## Prüfungen
- `npm test`: API, CSV-Abgleich, erneute Validierung beim Absenden, ungültige
Eingaben, SMTP-Empfänger und Teilfehler; isolierte temporäre CSV-Dateien.
- `npm run test:browser`: vollständiger Ablauf einschließlich abgefangener
externer Weiterleitung, unbekannter Adresse, geänderter E-Mail, Versandfehler,
mobiler Darstellung, Fokus und Escape; zusätzlich bestehende Buchungstests.
- `npm run build` und `git diff --check`.
- Lokaler SMTP-Smoke-Test mit Mailpit: beide Empfänger und Simulationshinweis
in den empfangenen Nachrichten geprüft.
Chromium muss für Playwright installiert sein (`npx playwright install chromium`).
In dieser Arbeitsumgebung liegt der Testbrowser unter `/tmp/trihub-playwright`;
hier mit `PLAYWRIGHT_BROWSERS_PATH=/tmp/trihub-playwright npm run test:browser` starten.
Der globale Formatcheck hat bereits bestehende Abweichungen in `src/main.js`
und `src/components/navigationsbar/README.md`; die Kontaktdateien sind formatiert.
Der bestehende Gesamt-Build meldet außerdem fehlende ältere Schriftdateien aus
dem globalen Stylesheet. Das Kontakt-Popup verwendet eigene lokale Schriftdateien.
### Ergebnis dieser Umsetzung
31 API-/Servicetests und alle vier Kontakt-Browsertests bestanden.
Build, lokale Mailpit-Prüfung und Formatierung der geänderten Dateien bestanden.
Die bestehenden Buchungs-Browsertests sind teilweise nicht mehr an das schon
vorhandene Buchungs-Popup angepasst: Sie suchen Formularfelder, ohne vorher
„Jetzt buchen“ zu klicken. Weitere Bestandsfälle erwarten einen nicht mehr vorhandenen Startseiten-Link
oder eine Seitennavigation, wo bereits ein Popup geöffnet wird. Beim ursprünglichen Gesamtlauf bestanden 7 von 15 Browsertests; die 8 Fehler
betrafen ausschließlich die unveränderten Buchungstests. Die gezielten
Kontakttests werden nach Anpassungen separat ausgeführt.
`git diff --check` ist für die eigenen Änderungen sauber; die zuvor bereits
geänderte `bookings.csv` erzeugt separat CRLF-Whitespace-Hinweise und wurde
von dieser Umsetzung nicht verändert.