trihub-ernaehrung-backseatDevs/src/features/angebote/paket-details.js

159 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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();
});
});