Merge pull request 'packages erweitert und swagger erstellt' (#2) from feature/angebote-booking-integration-detailseiten into feature/angebote-booking-integration
Reviewed-on: #2feature/angebote-booking-integration
commit
4abf4aa46c
|
|
@ -0,0 +1,30 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de" class="dark">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Paket-Details – Tri-Hub Ernährungsberatung</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Playfair+Display:wght@600;700&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="/src/styles/packages.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header class="container">
|
||||||
|
<a href="angebotsuebersicht.html">← Zurück zur Angebotsübersicht</a>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="packages-section">
|
||||||
|
<div id="package-detail-container" class="detail-page-container">
|
||||||
|
<!-- Wird dynamisch durch paket-details.js gefüllt -->
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script type="module" src="/src/features/angebote/paket-details.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -47,7 +47,7 @@ export function renderAngebotsuebersicht() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- HIER ist der wichtige Link für deinen Kollegen: -->
|
<!-- HIER ist der wichtige Link für deinen Kollegen: -->
|
||||||
<a href="/booking.html#/buchen/${encodeURIComponent(pkg.id)}" class="package-btn">${pkg.buttonText}</a>
|
<a href="paket-details.html?id=${pkg.id}" class="package-btn">${pkg.buttonText}</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -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 = `
|
||||||
|
<div class="package-card">
|
||||||
|
<h2>Paket nicht gefunden</h2>
|
||||||
|
<p>Bitte wähle ein gültiges Angebot aus der Übersicht.</p>
|
||||||
|
<a href="angebotsuebersicht.html" class="package-btn">Zur Übersicht</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Optional: Varianten-Auswahl rendern (für das Premium-Paket)
|
||||||
|
const variantsHtml = pkg.variants
|
||||||
|
? `<div class="detail-box">
|
||||||
|
<h3>Wähle deine Variante</h3>
|
||||||
|
<select id="variant-select" class="variant-select">
|
||||||
|
${pkg.variants.map((v) => `<option value="${v.variantId}" data-price="${v.price}">${v.label} –${v.price.toFixed(2)} €</option>`).join("")}
|
||||||
|
</select>
|
||||||
|
</div>`
|
||||||
|
: "";
|
||||||
|
|
||||||
|
// 4. Detailseite im Tri-Hub-Stil rendern
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="package-card detail-view">
|
||||||
|
<div class="package-type-container">
|
||||||
|
<span class="package-type">${pkg.type} · ${pkg.durationLabel}</span>
|
||||||
|
<span class="package-highlight">${pkg.badge}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="package-title">${pkg.title}</h1>
|
||||||
|
<p class="package-desc">${pkg.subtitle}</p>
|
||||||
|
|
||||||
|
<div class="price-box">
|
||||||
|
<span class="price-amount" id="displayed-price">${pkg.price.toFixed(2)} €</span>
|
||||||
|
<span class="price-note">inkl. ${pkg.taxRate}% MwSt. (${pkg.billingInterval})</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${variantsHtml}
|
||||||
|
|
||||||
|
<div class="package-details-box">
|
||||||
|
<div class="detail-item">
|
||||||
|
<h3 class="detail-title">Für wen ist dieses Paket ideal?</h3>
|
||||||
|
<ul class="detail-list">
|
||||||
|
${pkg.targetGroup.map((item) => `<li>${item}</li>`).join("")}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-item">
|
||||||
|
<h3 class="detail-title">Das ist im Paket enthalten</h3>
|
||||||
|
<ul class="detail-list">
|
||||||
|
${pkg.includedFeatures.map((feat) => `<li>${feat}</li>`).join("")}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-item">
|
||||||
|
<h3 class="detail-title">So läuft deine Begleitung ab</h3>
|
||||||
|
${pkg.processSteps
|
||||||
|
.map(
|
||||||
|
(s) => `
|
||||||
|
<div class="process-step">
|
||||||
|
<strong>${s.step}:</strong> <span class="detail-text">${s.text}</span>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="proceed-to-booking-btn" class="package-btn primary-cta">
|
||||||
|
${pkg.ctaButtonText}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 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}`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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",
|
||||||
|
},
|
||||||
|
];
|
||||||
Loading…
Reference in New Issue