159 lines
7.0 KiB
JavaScript
159 lines
7.0 KiB
JavaScript
import { packagesDetails } from "../../shared/packagesdetails.js";
|
||
import { mountBookingPage } from "../booking/booking-page.js";
|
||
import { formatMoney } from "../../shared/order-summary.js";
|
||
import { paymentNotice } from "../../shared/booking-copy.js";
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const container = document.getElementById("package-detail-container");
|
||
if (!container) return;
|
||
|
||
const params = new URLSearchParams(window.location.search);
|
||
const pkg = packagesDetails.find((item) => item.id === params.get("id"));
|
||
const requestedVariant = params.get("variant");
|
||
const invalidVariant =
|
||
requestedVariant &&
|
||
!pkg?.variants?.some(
|
||
(variant) => variant.variantId === requestedVariant,
|
||
);
|
||
|
||
if (!pkg || invalidVariant) {
|
||
container.innerHTML = `
|
||
<div class="package-card">
|
||
<h1>Paket nicht gefunden</h1>
|
||
<p>Bitte wähle ein gültiges Paket und eine passende Variante aus.</p>
|
||
<a href="/angebotsuebersicht.html" class="package-btn">Zur Übersicht</a>
|
||
</div>`;
|
||
return;
|
||
}
|
||
|
||
document.title = `${pkg.type} – Tri-Hub Ernährungsberatung`;
|
||
const variantsHtml = pkg.variants?.length
|
||
? `<div class="detail-box">
|
||
<label for="variant-select">Wähle deine Variante</label>
|
||
<select id="variant-select" class="variant-select">
|
||
${pkg.variants
|
||
.map(
|
||
(variant) => `
|
||
<option value="${variant.variantId}">
|
||
${variant.label} – ${formatMoney(variant.price, pkg.currency)}
|
||
</option>`,
|
||
)
|
||
.join("")}
|
||
</select>
|
||
</div>`
|
||
: "";
|
||
|
||
container.innerHTML = `
|
||
<header class="detail-heading">
|
||
<div class="package-type-container">
|
||
<span class="package-type">${pkg.type} · <span id="package-duration"></span></span>
|
||
${pkg.badge ? `<span class="package-highlight">${pkg.badge}</span>` : ""}
|
||
</div>
|
||
<h1>${pkg.title}</h1>
|
||
<p class="package-desc">${pkg.subtitle}</p>
|
||
</header>
|
||
<div class="detail-grid">
|
||
<div class="detail-content">
|
||
<section aria-labelledby="target-heading">
|
||
<h2 id="target-heading">Für wen ist dieses Paket ideal?</h2>
|
||
<ul class="detail-list">
|
||
${pkg.targetGroup.map((item) => `<li>${item}</li>`).join("")}
|
||
</ul>
|
||
</section>
|
||
<section aria-labelledby="features-heading">
|
||
<h2 id="features-heading">Das ist im Paket enthalten</h2>
|
||
<ul class="detail-list">
|
||
${pkg.includedFeatures.map((feature) => `<li>${feature}</li>`).join("")}
|
||
</ul>
|
||
</section>
|
||
<section aria-labelledby="steps-heading">
|
||
<h2 id="steps-heading">So läuft deine Begleitung ab</h2>
|
||
${pkg.processSteps
|
||
.map(
|
||
(step) => `
|
||
<div class="process-step">
|
||
<strong>${step.step}</strong>
|
||
<p class="detail-text">${step.text}</p>
|
||
</div>`,
|
||
)
|
||
.join("")}
|
||
</section>
|
||
</div>
|
||
<section class="package-checkout" aria-labelledby="checkout-heading">
|
||
<h2 id="checkout-heading">Dein ${pkg.type.toLowerCase().replace(/^./, (letter) => letter.toUpperCase())}-Paket</h2>
|
||
<div class="price-box" aria-live="polite">
|
||
<span class="price-amount" id="displayed-price"></span>
|
||
<span class="price-note">inkl. ${pkg.taxRate} % MwSt. · ${pkg.billingInterval}</span>
|
||
<p id="selected-duration" class="price-note"></p>
|
||
</div>
|
||
${variantsHtml}
|
||
<button id="proceed-to-booking-btn" class="package-btn primary-cta" type="button" aria-haspopup="dialog" aria-expanded="false" disabled>
|
||
Buchungsformular wird geladen …
|
||
</button>
|
||
<p class="booking-notice" id="booking-notice"></p>
|
||
<div id="booking-root"></div>
|
||
</section>
|
||
</div>`;
|
||
|
||
container.querySelector("#booking-notice").textContent = paymentNotice;
|
||
const variantSelect = container.querySelector("#variant-select");
|
||
if (variantSelect && requestedVariant)
|
||
variantSelect.value = requestedVariant;
|
||
const bookingBtn = container.querySelector("#proceed-to-booking-btn");
|
||
const bookingRoot = container.querySelector("#booking-root");
|
||
let cleanupBooking;
|
||
let activeDialog;
|
||
|
||
function prepareBooking() {
|
||
cleanupBooking?.();
|
||
activeDialog = null;
|
||
bookingBtn.disabled = true;
|
||
bookingBtn.textContent = "Buchungsformular wird geladen …";
|
||
bookingBtn.setAttribute("aria-expanded", "false");
|
||
bookingBtn.removeAttribute("aria-controls");
|
||
const variant = pkg.variants?.find(
|
||
(item) => item.variantId === variantSelect?.value,
|
||
);
|
||
const duration = variant
|
||
? `${variant.durationWeeks} Wochen Begleitung`
|
||
: pkg.durationLabel;
|
||
container.querySelector("#displayed-price").textContent = formatMoney(
|
||
variant?.price ?? pkg.price,
|
||
pkg.currency,
|
||
);
|
||
container.querySelector("#package-duration").textContent = duration;
|
||
container.querySelector("#selected-duration").textContent = duration;
|
||
cleanupBooking = mountBookingPage(bookingRoot, {
|
||
packageId: pkg.id,
|
||
variantId: variantSelect?.value,
|
||
});
|
||
}
|
||
|
||
// Booking lädt die Paketvorschau asynchron, auch bei einem erneuten Versuch.
|
||
const observer = new MutationObserver(() => {
|
||
const dialog = bookingRoot.querySelector(".booking__dialog");
|
||
if (!dialog || dialog === activeDialog) return;
|
||
activeDialog = dialog;
|
||
bookingBtn.disabled = false;
|
||
bookingBtn.textContent = "Jetzt buchen";
|
||
bookingBtn.setAttribute("aria-controls", dialog.id);
|
||
dialog.addEventListener("close", () => {
|
||
bookingBtn.setAttribute("aria-expanded", "false");
|
||
bookingBtn.focus();
|
||
});
|
||
});
|
||
observer.observe(bookingRoot, { childList: true, subtree: true });
|
||
prepareBooking();
|
||
variantSelect?.addEventListener("change", prepareBooking);
|
||
|
||
bookingBtn.addEventListener("click", () => {
|
||
if (!activeDialog || activeDialog.open) return;
|
||
activeDialog.showModal();
|
||
bookingBtn.setAttribute("aria-expanded", "true");
|
||
const result = activeDialog.querySelector(
|
||
".booking__result:not([hidden])",
|
||
);
|
||
(result || activeDialog.querySelector('[name="name"]')).focus();
|
||
});
|
||
});
|