From e84d8ae6b3bf4c2a329e9f61cb216923e1f3cab2 Mon Sep 17 00:00:00 2001 From: Majd Ibrahim <“3021285@stud.hs-mannheim.de”> Date: Fri, 25 Sep 2026 13:03:04 +0000 Subject: [PATCH] packages erweitert und swagger erstellt --- paket-details.html | 30 ++ src/features/angebote/angebotsuebersicht.js | 2 +- src/features/angebote/paket-details.js | 132 ++++++ src/server/swagger.json | 481 ++++++++++++++++++++ src/shared/packagesdetails.js | 149 ++++++ 5 files changed, 793 insertions(+), 1 deletion(-) create mode 100644 paket-details.html create mode 100644 src/features/angebote/paket-details.js create mode 100644 src/server/swagger.json create mode 100644 src/shared/packagesdetails.js diff --git a/paket-details.html b/paket-details.html new file mode 100644 index 0000000..c79f82d --- /dev/null +++ b/paket-details.html @@ -0,0 +1,30 @@ + + + + + + + Paket-Details – Tri-Hub Ernährungsberatung + + + + + + + +
+ ← Zurück zur Angebotsübersicht +
+ +
+
+ +
+
+ + + + + diff --git a/src/features/angebote/angebotsuebersicht.js b/src/features/angebote/angebotsuebersicht.js index 02f7852..77afcf9 100644 --- a/src/features/angebote/angebotsuebersicht.js +++ b/src/features/angebote/angebotsuebersicht.js @@ -47,7 +47,7 @@ export function renderAngebotsuebersicht() { - ${pkg.buttonText} + ${pkg.buttonText} `; diff --git a/src/features/angebote/paket-details.js b/src/features/angebote/paket-details.js new file mode 100644 index 0000000..790f481 --- /dev/null +++ b/src/features/angebote/paket-details.js @@ -0,0 +1,132 @@ +// src/features/angebote/paket-details.js +import { packagesDetails } from "../../shared/packagesdetails.js"; + +document.addEventListener("DOMContentLoaded", async () => { + const container = document.getElementById("package-detail-container"); + if (!container) return; + + // 1. ID aus der URL lesen (z.B. ?id=ernaehrung-standard) + const params = new URLSearchParams(window.location.search); + const packageId = params.get("id"); + + // 2. Passendes Paket in packagesdetails.js suchen + // (Später kann hier alternativ: await fetch(`/api/packages/${packageId}`) genutzt werden) + const pkg = packagesDetails.find((item) => item.id === packageId); + + if (!pkg) { + container.innerHTML = ` +
+

Paket nicht gefunden

+

Bitte wähle ein gültiges Angebot aus der Übersicht.

+ Zur Übersicht +
+ `; + return; + } + + // 3. Optional: Varianten-Auswahl rendern (für das Premium-Paket) + const variantsHtml = pkg.variants + ? `
+

Wähle deine Variante

+ +
` + : ""; + + // 4. Detailseite im Tri-Hub-Stil rendern + container.innerHTML = ` +
+
+ ${pkg.type} · ${pkg.durationLabel} + ${pkg.badge} +
+ +

${pkg.title}

+

${pkg.subtitle}

+ +
+ ${pkg.price.toFixed(2)} € + inkl. ${pkg.taxRate}% MwSt. (${pkg.billingInterval}) +
+ + ${variantsHtml} + +
+
+

Für wen ist dieses Paket ideal?

+
    + ${pkg.targetGroup.map((item) => `
  • ${item}
  • `).join("")} +
+
+ +
+

Das ist im Paket enthalten

+
    + ${pkg.includedFeatures.map((feat) => `
  • ${feat}
  • `).join("")} +
+
+ +
+

So läuft deine Begleitung ab

+ ${pkg.processSteps + .map( + (s) => ` +
+ ${s.step}: ${s.text} +
+ `, + ) + .join("")} +
+
+ + +
+ `; + + // 5. Preis aktualisieren, falls eine andere Variante (24 vs. 52 Wochen) gewählt wird + const variantSelect = document.getElementById("variant-select"); + const displayedPrice = document.getElementById("displayed-price"); + if (variantSelect && displayedPrice) { + variantSelect.addEventListener("change", (e) => { + const selectedOption = e.target.selectedOptions[0]; + const newPrice = parseFloat( + selectedOption.getAttribute("data-price"), + ); + displayedPrice.textContent = `${newPrice.toFixed(2)} €`; + }); + } + + // 6. Klick auf "Jetzt buchen" -> Daten für bookingService bereitstellen & weiterleiten + const bookingBtn = document.getElementById("proceed-to-booking-btn"); + bookingBtn.addEventListener("click", () => { + const selectedVariantId = variantSelect ? variantSelect.value : pkg.id; + const selectedVariant = pkg.variants + ? pkg.variants.find((v) => v.variantId === selectedVariantId) + : null; + + const bookingPayload = { + packageId: pkg.id, + variantId: selectedVariantId, + title: pkg.title, + type: pkg.type, + durationWeeks: selectedVariant + ? selectedVariant.durationWeeks + : pkg.durationWeeks, + price: selectedVariant ? selectedVariant.price : pkg.price, + currency: pkg.currency, + taxRate: pkg.taxRate, + summaryForBooking: pkg.summaryForBooking, + }; + + // Im sessionStorage zwischenspeichern + per URL an die Buchungsseite übergeben + sessionStorage.setItem( + "selectedPackageForBooking", + JSON.stringify(bookingPayload), + ); + window.location.href = `booking.html?id=${pkg.id}&variant=${selectedVariantId}`; + }); +}); diff --git a/src/server/swagger.json b/src/server/swagger.json new file mode 100644 index 0000000..f356c8b --- /dev/null +++ b/src/server/swagger.json @@ -0,0 +1,481 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Tri-Hub Ernährungsberatung & Booking API", + "description": "API-Spezifikation für die Angebotsübersicht (US2.1), die Paket-Detailansicht und den Buchungsservice (US2.4 Paket buchen).", + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://localhost:3000/api", + "description": "Lokaler Node.js Entwicklungsserver" + } + ], + "paths": { + "/packages": { + "get": { + "summary": "Alle Ernährungs-Pakete abrufen (Kurzübersicht)", + "description": "Liefert die Basisdaten aus packages.js für die Angebotsübersicht (US2.1).", + "tags": ["Packages"], + "responses": { + "200": { + "description": "Erfolgreiche Rückgabe der Paketliste", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PackageOverview" + } + } + } + } + } + } + } + }, + "/packages/{id}": { + "get": { + "summary": "Detaillierte Paketinformationen für Detailseite & Bestellprozess abrufen", + "description": "Liefert die ausführlichen Paketdaten aus packagesdetails.js (inkl. Preis, Laufzeit, Leistungen und Varianten), um sie auf der Detailseite und in der Bestellzusammenfassung des Booking-Service anzuzeigen.", + "tags": ["Packages", "Booking"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "Eindeutige ID des Pakets (z. B. ernaehrung-starter)", + "schema": { + "type": "string", + "example": "ernaehrung-standard" + } + } + ], + "responses": { + "200": { + "description": "Detaillierte Paketdaten erfolgreich geladen", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackageDetail" + } + } + } + }, + "404": { + "description": "Paket mit dieser ID wurde nicht gefunden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/bookings/preview": { + "post": { + "summary": "Preis- und Leistungsübersicht für den Bestellprozess berechnen", + "description": "Wird vom bookingService aufgerufen, um vor Abschluss der Buchung die relevanten Bestelldaten (Nettopreis, MwSt., Bruttopreis, gewählte Variante) validiert anzuzeigen.", + "tags": ["Booking"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingPreviewRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Berechnete Bestellübersicht für das Checkout-Formular", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingPreviewResponse" + } + } + } + }, + "400": { + "description": "Ungültige Paket- oder Varianten-ID", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/bookings": { + "post": { + "summary": "Paket verbindlich buchen (US2.4)", + "description": "Nimmt die ausgewählten Paketdetails und die Kundendaten entgegen, speichert die Buchung im bookingService und gibt eine Bestellbestätigung zurück.", + "tags": ["Booking"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingCreateRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Buchung erfolgreich angelegt (Bestellbestätigung)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingConfirmation" + } + } + } + }, + "400": { + "description": "Fehlende oder ungültige Eingabedaten", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "PackageOverview": { + "type": "object", + "required": [ + "id", + "type", + "title", + "description", + "fokus", + "anamnese", + "begleitung", + "buttonText" + ], + "properties": { + "id": { + "type": "string", + "example": "ernaehrung-standard" + }, + "type": { + "type": "string", + "example": "STANDARD" + }, + "title": { + "type": "string", + "example": "Für Best Ager mit ersten Erfahrungen und klaren Zielen" + }, + "description": { + "type": "string" + }, + "fokus": { + "type": "string" + }, + "anamnese": { + "type": "string" + }, + "begleitung": { + "type": "string" + }, + "buttonText": { + "type": "string", + "example": "Standard-Paket ansehen" + }, + "highlight": { + "type": "string", + "nullable": true, + "example": "BESONDERS PASSEND" + } + } + }, + "PackageVariant": { + "type": "object", + "properties": { + "variantId": { + "type": "string", + "example": "ernaehrung-premium-24" + }, + "label": { + "type": "string", + "example": "24-Wochen-Variante" + }, + "durationWeeks": { + "type": "integer", + "example": 24 + }, + "price": { + "type": "number", + "format": "float", + "example": 799.0 + } + } + }, + "PackageDetail": { + "type": "object", + "required": [ + "id", + "type", + "title", + "subtitle", + "durationWeeks", + "price", + "currency", + "taxRate", + "includedFeatures" + ], + "properties": { + "id": { + "type": "string", + "example": "ernaehrung-standard" + }, + "type": { + "type": "string", + "example": "STANDARD" + }, + "badge": { + "type": "string", + "example": "BESONDERS PASSEND" + }, + "title": { + "type": "string", + "example": "Für Best Ager mit ersten Erfahrungen und klaren Zielen" + }, + "subtitle": { + "type": "string" + }, + "durationWeeks": { + "type": "integer", + "example": 24 + }, + "durationLabel": { + "type": "string", + "example": "24 Wochen Begleitung" + }, + "price": { + "type": "number", + "format": "float", + "example": 449.0 + }, + "currency": { + "type": "string", + "example": "EUR" + }, + "billingInterval": { + "type": "string", + "example": "einmalig" + }, + "taxRate": { + "type": "integer", + "example": 19 + }, + "summaryForBooking": { + "type": "string", + "example": "Standard-Paket Sporternährung (24 Wochen Coaching)" + }, + "targetGroup": { + "type": "array", + "items": { + "type": "string" + } + }, + "includedFeatures": { + "type": "array", + "items": { + "type": "string" + } + }, + "processSteps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "step": { + "type": "string" + }, + "text": { + "type": "string" + } + } + } + }, + "variants": { + "type": "array", + "nullable": true, + "items": { + "$ref": "#/components/schemas/PackageVariant" + } + }, + "ctaButtonText": { + "type": "string", + "example": "Standard-Paket jetzt buchen" + } + } + }, + "BookingPreviewRequest": { + "type": "object", + "required": ["packageId"], + "properties": { + "packageId": { + "type": "string", + "example": "ernaehrung-premium" + }, + "variantId": { + "type": "string", + "nullable": true, + "example": "ernaehrung-premium-52" + } + } + }, + "BookingPreviewResponse": { + "type": "object", + "properties": { + "packageId": { + "type": "string", + "example": "ernaehrung-premium" + }, + "variantId": { + "type": "string", + "example": "ernaehrung-premium-52" + }, + "title": { + "type": "string", + "example": "Für maximale Individualität und intensive 1:1-Begleitung" + }, + "summaryForBooking": { + "type": "string" + }, + "durationWeeks": { + "type": "integer", + "example": 52 + }, + "netPrice": { + "type": "number", + "format": "float", + "example": 1175.63 + }, + "taxAmount": { + "type": "number", + "format": "float", + "example": 223.37 + }, + "grossPrice": { + "type": "number", + "format": "float", + "example": 1399.0 + }, + "currency": { + "type": "string", + "example": "EUR" + } + } + }, + "BookingCreateRequest": { + "type": "object", + "required": ["packageId", "customer", "acceptedTerms"], + "properties": { + "packageId": { + "type": "string", + "example": "ernaehrung-standard" + }, + "variantId": { + "type": "string", + "nullable": true, + "example": "ernaehrung-standard" + }, + "customer": { + "type": "object", + "required": ["firstName", "lastName", "email"], + "properties": { + "firstName": { + "type": "string", + "example": "Thomas" + }, + "lastName": { + "type": "string", + "example": "Müller" + }, + "email": { + "type": "string", + "format": "email", + "example": "thomas.mueller@example.de" + }, + "phone": { + "type": "string", + "example": "+49 170 1234567" + }, + "ageGroup": { + "type": "string", + "example": "50-59" + }, + "notes": { + "type": "string", + "example": "Vorbereitung auf meine erste Mitteldistanz im August." + } + } + }, + "acceptedTerms": { + "type": "boolean", + "example": true + } + } + }, + "BookingConfirmation": { + "type": "object", + "properties": { + "bookingId": { + "type": "string", + "example": "BK-2026-84920" + }, + "status": { + "type": "string", + "example": "CONFIRMED" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "bookedPackage": { + "$ref": "#/components/schemas/BookingPreviewResponse" + }, + "customerEmail": { + "type": "string", + "example": "thomas.mueller@example.de" + }, + "confirmationMessage": { + "type": "string", + "example": "Vielen Dank für deine Buchung! Deine Bestellbestätigung wurde per E-Mail versendet." + } + } + }, + "ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Paket nicht gefunden" + }, + "code": { + "type": "integer", + "example": 404 + } + } + } + } + } +} diff --git a/src/shared/packagesdetails.js b/src/shared/packagesdetails.js new file mode 100644 index 0000000..9518acb --- /dev/null +++ b/src/shared/packagesdetails.js @@ -0,0 +1,149 @@ +// src/shared/packagesdetails.js + +export const packagesDetails = [ + { + id: "ernaehrung-starter", + type: "STARTER", + badge: "EINSTIEG", + title: "Für den sicheren Einstieg in die Sporternährung", + subtitle: + "Strukturierte Grundlagen für Menschen 50+, die ihre Ernährung optimal auf das Triathlon-Training abstimmen wollen.", + durationWeeks: 12, + durationLabel: "12 Wochen Begleitung", + price: 249.0, + currency: "EUR", + billingInterval: "einmalig", + taxRate: 19, + summaryForBooking: + "Starter-Paket Sporternährung (12 Wochen Basis-Begleitung inkl. Anamnese & Basisplan)", + targetGroup: [ + "Du steigst neu in den Triathlon ein oder baust nach einer Pause wieder Struktur auf.", + "Du möchtest verstehen, wie du Energielevel und Regeneration über die Alltagsernährung steuerst.", + "Du suchst einen klaren, alltagstauglichen Plan ohne komplizierte Wissenschaft.", + ], + includedFeatures: [ + "Ausführliche Eingangs-Anamnese (60 Min. Video-Call) zu Gesundheit, Alltag und Zielen", + "Individueller Basis-Ernährungsplan abgestimmt auf deine Trainingswoche", + "Trink- und Hydrationsstrategie für Grundlageneinheiten", + "2x Check-in-Calls (je 30 Min.) zur Plananpassung im Verlauf der 12 Wochen", + "Rezept- und Einkaufsguide für schnelle, nährstoffreiche Mahlzeiten", + ], + processSteps: [ + { + step: "1. Anamnese & Status Quo", + text: "Auswertung deines 7-Tage-Ernährungsprotokolls und gemeinsames Erstgespräch.", + }, + { + step: "2. Dein individueller Plan", + text: "Erstellung deines alltagstauglichen Ernährungskonzepts für Trainingstage und Ruhetage.", + }, + { + step: "3. Umsetzung & Rückmeldung", + text: "Regelmäßige Check-ins, um das Tempo anzupassen und Überforderung zu vermeiden.", + }, + ], + ctaButtonText: "Starter-Paket jetzt buchen", + }, + { + id: "ernaehrung-standard", + type: "STANDARD", + badge: "BESONDERS PASSEND", + title: "Für Best Ager mit ersten Erfahrungen und klaren Zielen", + subtitle: + "Gezielte Ernährungssteuerung für Alltag, Belastungsspitzen und deinen nächsten Wettkampf.", + durationWeeks: 24, + durationLabel: "24 Wochen Begleitung", + price: 449.0, + currency: "EUR", + billingInterval: "einmalig", + taxRate: 19, + summaryForBooking: + "Standard-Paket Sporternährung (24 Wochen Coaching inkl. Wettkampf-Verpflegungsstrategie)", + targetGroup: [ + "Du trainierst bereits regelmäßig und möchtest deine Leistung über die Ernährung spürbar verbessern.", + "Du bereitest dich gezielt auf eine Kurz- oder Mitteldistanz vor.", + "Du möchtest Magen-Darm-Probleme im Training und Wettkampf sicher vermeiden.", + ], + includedFeatures: [ + "Umfassende Anamnese inkl. Analyse der bisherigen Trainings- und Wettkampfernährung", + "Dynamischer Ernährungsplan (gekoppelt an deine Trainingsphasen)", + "Spezifisches Carb-Loading- und Pacing-Konzept für den Wettkampftag", + "Monatliche 1:1 Video-Calls (6x 45 Min.) zur Feinjustierung", + "Gezieltes Magen-Training (Gut-Training) für Gels, Riegel und Sportgetränke", + "Support per Messenger bei Fragen im Trainingsalltag (Antwort innerhalb von 48h)", + ], + processSteps: [ + { + step: "1. Tiefen-Anamnese", + text: "Präzise Erfassung von Trainingsrealität, Erfahrung, Belastbarkeit und Verträglichkeiten.", + }, + { + step: "2. Periodisierte Planung", + text: "Abstimmung der Makro- und Mikronährstoffe auf deine konkreten Trainingsblöcke.", + }, + { + step: "3. Wettkampf-Simulation", + text: "Erprobung der Raceday-Strategie im Training für maximale Sicherheit am Starttag.", + }, + ], + ctaButtonText: "Standard-Paket jetzt buchen", + }, + { + id: "ernaehrung-premium", + type: "PREMIUM", + badge: "MAXIMALE INDIVIDUALITÄT", + title: "Für maximale Individualität und intensive 1:1-Begleitung", + subtitle: + "Enge Coaching-Beziehung, laufende Feinabstimmung und zwei wählbare Laufzeit-Varianten.", + durationWeeks: 24, + durationLabel: "24 oder 52 Wochen (wählbar)", + price: 799.0, + currency: "EUR", + billingInterval: "einmalig", + taxRate: 19, + variants: [ + { + variantId: "ernaehrung-premium-24", + label: "24-Wochen-Variante", + durationWeeks: 24, + price: 799.0, + }, + { + variantId: "ernaehrung-premium-52", + label: "52-Wochen-Variante (Jahresbegleitung)", + durationWeeks: 52, + price: 1399.0, + }, + ], + summaryForBooking: + "Premium-Paket Sporternährung (Intensive 1:1-Begleitung inkl. Raceday- & Regenerations-Steuerung)", + targetGroup: [ + "Du wünschst dir eine besonders enge Coaching-Beziehung mit kurzen Reaktionswegen.", + "Du möchtest Training, Ernährung, Blutwerte und Regeneration ganzheitlich abstimmen.", + "Du suchst maximale Verbindlichkeit für ein großes Saison-Ziel.", + ], + includedFeatures: [ + "Exklusive 90-Minuten-Start-Anamnese inkl. Auswertung vorhandener Blut-/Laborwerte", + "Wöchentliche Feinabstimmung des Ernährungsplans parallel zum Trainingsplan", + "Individueller Supplement- & Mikronährstoff-Plan für optimale Regeneration ab 50", + "14-tägige 1:1 Video-Calls sowie direkter Priority-Messenger-Support", + "Detailliertes Raceday-Playbook (Stunde-für-Stunde-Plan für vor, während und nach dem Rennen)", + "Direkte Abstimmung mit deinem Triathlon-Trainer bei Tri-Hub", + ], + processSteps: [ + { + step: "1. Ganzheitliches Onboarding", + text: "Gemeinsame Analyse von Gesundheitsdaten, Leistungszielen und Alltagsstruktur.", + }, + { + step: "2. Laufende Steuerung", + text: "Wöchentliche Anpassung an Trainingsumfang, Schlafqualität und Regeneration.", + }, + { + step: "3. Punktlandung am Wettkampftag", + text: "Individuelle Tapering- und Wettkampfernährung für deine persönliche Bestleistung.", + }, + ], + ctaButtonText: "Premium-Paket jetzt buchen", + }, +];