8.5 KiB
Paketbuchung
Ablauf
Die Angebotsübersicht führt zur Detailseite eines Pakets. Der Buchungsbutton
übergibt die Paket-ID und gegebenenfalls die gewählte Variante als URL-Parameter
an booking.html. Bestehende Links mit #/buchen/:packageId funktionieren weiterhin.
Booking lädt die Bestellübersicht vom Server. Sie enthält Paket und Variante,
Laufzeit, Leistungen, Abrechnung, Nettopreis, MwSt. und Gesamtbetrag. Die Preise
stammen aus src/shared/packagesdetails.js; Preisangaben des Browsers werden
nicht übernommen. Ohne Varianten-ID wird die erste Variante des Pakets gewählt.
Beim Absenden speichert der Server Kundendaten und den vollständigen Bestellstand in der CSV. Anschließend sendet er eine Bestätigung als Text und HTML mit Buchungsnummer, Datum, gebuchten Leistungen und Preisen. Änderungen am Katalog verändern bereits gespeicherte Bestellungen nicht.
Der Zahlungsvorgang ist weiterhin simuliert. Es erfolgt keine Abbuchung und keine verbindliche Beratungsbuchung. Darauf weisen Formular und E-Mail hin.
Lokal starten
Voraussetzung ist Node.js 24. Abhängigkeiten mit npm ci installieren und die
Werte aus .env.example in eine lokale .env übernehmen. Eine vorhandene
Konfiguration dabei erhalten.
In separaten Terminals starten:
npm run dev:mail: Mailpit mit SMTP auf Port 1025 und Weboberfläche auf 8025.npm run dev:server: Buchungs-API auf Port 3000.npm run dev: Frontend auf Port 5173;/apiwird an Node weitergeleitet.
Die Angebotsübersicht liegt unter http://localhost:5173/angebotsuebersicht.html, der Mailfänger unter http://localhost:8025. Mailpit ist im Dev Container enthalten.
npm run build erzeugt die Seiten in dist/, einschließlich der Paketdetailseite.
Für ein Deployment muss der Webserver /api an den Node-Server weiterleiten.
npm run start:server startet die API ohne automatischen Neustart.
Konfiguration
| Variable | Standardwert | Bedeutung |
|---|---|---|
HOST |
127.0.0.1 |
Bind-Adresse der API |
PORT |
3000 |
API-Port; bei Änderung auch den Vite-Proxy anpassen |
SMTP_HOST |
127.0.0.1 |
SMTP-Server |
SMTP_PORT |
1025 lokal, sonst 587 |
SMTP-Port |
SMTP_SECURE |
false |
Direktes TLS aktivieren |
SMTP_USER, SMTP_PASS |
leer | SMTP-Zugangsdaten; beide gemeinsam setzen |
SMTP_FROM |
Tri-Hub <buchung@example.test> |
Absender |
SMTP_ALLOW_EXTERNAL |
false |
Versand über externe SMTP-Server aktivieren |
Externe SMTP-Verbindungen erfordern TLS. Zugangsdaten gehören ausschließlich
in die Serverkonfiguration und dürfen kein VITE_-Präfix erhalten.
API
Der vollständige Vertrag steht in src/server/swagger.json.
| Endpunkt | Funktion |
|---|---|
GET /api/angebote |
Bestehende Kurzübersicht mit id, name und summary |
GET /api/packages |
Kurzübersicht im Swagger-Format |
GET /api/packages/{id} |
Paketdetails einschließlich Varianten und Leistungen |
POST /api/bookings/preview |
Bestellübersicht für packageId und optionale variantId; speichert keine Buchung |
POST /api/bookings |
Buchung speichern und Bestätigung versenden |
POST-Anfragen benötigen Content-Type: application/json. Beim Buchen ist
zusätzlich ein Idempotency-Key als UUID v4 erforderlich. Das Formular sendet
packageId, gegebenenfalls variantId, name und email.
Alternativ akzeptiert die API das Swagger-Objekt customer mit firstName,
lastName, email sowie optional phone, ageGroup und notes. Dabei muss
acceptedTerms den Wert true haben. Diese Angaben werden ebenfalls gespeichert.
Die Antwort enthält die Buchungsnummer, den Erstellungszeitpunkt, das gebuchte
Paket unter bookedPackage und den Versandstatus. Eine neue Buchung mit
SMTP-Annahme erhält HTTP 201, eine Wiederholung HTTP 200. Bei ausstehendem,
fehlgeschlagenem oder unklarem Versand wird HTTP 202 zurückgegeben.
Ungültige Angaben, Pakete und Varianten ergeben HTTP 400; ein unbekanntes Paket
am Detail-Endpunkt HTTP 404. Derselbe Anfrageschlüssel mit anderen Daten ergibt
HTTP 409. JSON-Anfragen sind auf 8 KiB begrenzt. Fehlerantworten enthalten
error.code, error.message und gegebenenfalls error.fields.
Speicherung und Wiederholungen
Die Buchungen liegen in src/server/data/bookings.csv. Neue Buchungsnummern
werden fortlaufend als TH-000001, TH-000002 usw. vergeben. Der höchste
vorhandene Wert bestimmt die nächste Nummer. Die CSV muss deshalb vollständig
erhalten bleiben; ältere UUID-Buchungsnummern werden weiterhin unterstützt.
bookedPackage speichert den Bestellstand als JSON, customer die optionalen
strukturierten Kundendaten und acceptedTerms die zugehörige Zustimmung.
CSV-Dateien mit dem früheren Spaltensatz bleiben lesbar und werden beim nächsten
Speichern erweitert. Für alte Buchungen ohne Bestellstand liefert die API
bookedPackage: null.
Lesen, Schreiben und Versand werden innerhalb einer Warteschlange abgearbeitet. Die Datei wird über eine temporäre Datei atomar ersetzt. Unterstützt wird ein Backend-Prozess auf einem lokalen Dateisystem. Mehrere Serverinstanzen benötigen einen gemeinsam abgesicherten Speicher. CSV-Zellen werden gegen die Auswertung als Tabellenformeln geschützt; Dateien erhalten den Zugriffsmodus 0600.
Wiederholte Anfragen mit demselben Schlüssel und denselben Daten liefern die vorhandene Buchung. Es wird keine zweite Bestellung angelegt. Die Auswahl einer anderen Variante mit demselben Schlüssel wird abgelehnt.
Das Formular merkt sich Anfrage und Bestellübersicht je Paket und Variante im
sessionStorage. Nach einem Verbindungsfehler bleiben die Angaben gesperrt,
damit dieselbe Anfrage erneut geprüft werden kann. Ein neuer Tab oder gelöschter
Sitzungsspeicher liegt außerhalb dieser Absicherung.
Versandstatus
| Status | Bedeutung |
|---|---|
pending |
Buchung gespeichert; Versand noch nicht gestartet. Dieselbe Anfrage kann den Versand fortsetzen. |
sending |
Versandabsicht gespeichert. Nach einem Prozessabbruch liefert die API unknown. |
accepted |
SMTP hat die Nachricht angenommen; die Zustellung ist noch nicht bestätigt. |
failed |
Versand fehlgeschlagen oder ausdrücklich abgelehnt. |
unknown |
SMTP-Annahme oder anschließende Statusspeicherung blieb unklar. |
Bei failed und unknown wird nicht automatisch erneut versendet. Vor einem
manuellen Neuversand muss der Mailserver anhand der Buchungsnummer geprüft werden.
CSV und SMTP bilden keine gemeinsame Transaktion.
Tests
npm test prüft Validierung, Preisberechnung, Varianten, CSV-Kompatibilität,
Wiederholungen und Versandfehler. npm run test:browser prüft den Weg von der
Angebotsübersicht über die Paketdetails bis zur Bestätigung, einschließlich
Premium-Jahresvariante, Tastaturbedienung und schmalem Bildschirm.
Die Tests verwenden die regulären Paketdaten, temporäre CSV-Dateien und einen
simulierten Mailversand. Playwright benötigt Chromium; die Installation erfolgt
mit npx playwright install --with-deps chromium. Die Ports 3000 und 5173 müssen
für die automatisch gestarteten Testserver frei sein.
Vor einem Commit außerdem npm run format:check, npm run build und
git diff --check ausführen. Für eine manuelle Mailprüfung eine Buchung mit einer
Adresse unter example.test anlegen und die Bestätigung in Mailpit kontrollieren.