// Online-Terminbuchung – Buchungsseite (html/terminbuchung.html). // Liest den gewählten Termin aus der URL (berater, datum, zeit), füllt die // Zusammenfassung, verwaltet die Anamnese-Tags und prüft das Formular. // Die Bestätigung wird nur auf der Seite angezeigt; es wird keine E-Mail versendet. // ----- Scroll-Reveal ----- (function () { const elements = document.querySelectorAll(".terminbuchung-seite .reveal"); if (!elements.length) return; const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (reduceMotion || !("IntersectionObserver" in window)) { elements.forEach(function (el) { el.classList.add("visible"); }); return; } const observer = new IntersectionObserver( function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { entry.target.classList.add("visible"); observer.unobserve(entry.target); } }); }, { threshold: 0.12, rootMargin: "0px 0px -40px 0px" } ); elements.forEach(function (el) { observer.observe(el); }); })(); // ----- Anamnese-Tags (Allergien & Unverträglichkeiten) ----- (function () { const input = document.getElementById("tag-input"); const addButton = document.getElementById("tag-add"); const liste = document.getElementById("tag-list"); if (!input || !addButton || !liste) return; const tags = []; const MAX_TAGS = 20; function tagHinzufuegen() { let wert = input.value.trim().replace(/^#+/, "").trim(); if (!wert) return; if (tags.length >= MAX_TAGS) { input.value = ""; return; } // Doppelte Einträge (groß/kleinschreibungsgleich) werden nicht aufgenommen. const existiert = tags.some(function (t) { return t.toLowerCase() === wert.toLowerCase(); }); if (!existiert) { tags.push(wert); } input.value = ""; zeichnen(); } function tagEntfernen(index) { tags.splice(index, 1); zeichnen(); } function zeichnen() { liste.innerHTML = ""; tags.forEach(function (wert, index) { const li = document.createElement("li"); li.className = "tb-tag"; const text = document.createElement("span"); text.textContent = wert; const entfernen = document.createElement("button"); entfernen.type = "button"; entfernen.setAttribute("aria-label", "Tag entfernen: " + wert); entfernen.innerHTML = "×"; entfernen.addEventListener("click", function () { tagEntfernen(index); }); li.appendChild(text); li.appendChild(entfernen); liste.appendChild(li); }); } addButton.addEventListener("click", tagHinzufuegen); input.addEventListener("keydown", function (event) { if (event.key === "Enter") { event.preventDefault(); tagHinzufuegen(); } }); // Für das E-Mail-Body: aktuelle Tag-Liste als Text. window.TerminbuchungTags = { holen: function () { return tags.slice(); }, leeren: function () { tags.length = 0; zeichnen(); } }; })(); // ----- Formularprüfung und Absenden ----- (function () { const form = document.getElementById("terminbuchung-formular"); if (!form) return; const bestaetigungDialog = document.getElementById("anfrage-bestaetigung"); const fields = { art: form.querySelector("[name='art']"), format: form.querySelector("[name='format']"), gespraech: form.querySelector("[name='gespraech']"), beschreibung: form.querySelector("[name='beschreibung']"), name: form.querySelector("[name='name']"), email: form.querySelector("[name='email']"), phone: form.querySelector("[name='phone']") }; const statusEl = document.getElementById("form-status"); const formularBereich = document.getElementById("formular"); const pruefBereich = document.getElementById("anfrage-pruefen"); const pruefDetails = document.getElementById("anfrage-pruefen-details"); const bearbeitenButton = document.getElementById("anfrage-bearbeiten"); const sendenButton = document.getElementById("anfrage-senden"); function wrap(el) { return el.closest(".field"); } function setFieldState(el, state) { const box = wrap(el); if (!box) return; box.classList.remove("has-error", "is-ok"); el.classList.remove("invalid", "valid"); if (state === "error") { box.classList.add("has-error"); el.classList.add("invalid"); } else if (state === "ok") { box.classList.add("is-ok"); el.classList.add("valid"); } } function validateName() { const ok = fields.name.value.trim().length > 0; setFieldState(fields.name, ok ? null : "error"); return ok; } function validateEmail() { const value = fields.email.value.trim(); const ok = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(value); setFieldState(fields.email, ok ? null : "error"); return ok; } function validateSelect(key) { const ok = fields[key].value !== ""; setFieldState(fields[key], ok ? null : "error"); return ok; } function allValid() { return ( terminIstGueltig() && fields.art.value !== "" && fields.format.value !== "" && fields.gespraech.value !== "" && fields.name.value.trim().length > 0 && /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(fields.email.value.trim()) ); } function updateSubmitState() { const submit = form.querySelector("[type='submit']"); if (submit) { submit.disabled = !allValid(); } } form.addEventListener("input", updateSubmitState); form.addEventListener("change", updateSubmitState); ["name", "email"].forEach(function (key) { fields[key].addEventListener("blur", function () { if (fields[key].value.trim() !== "" || form.dataset.touched === "true") { ({ name: validateName, email: validateEmail })[key](); } }); }); ["art", "format", "gespraech"].forEach(function (key) { fields[key].addEventListener("change", function () { validateSelect(key); }); }); // Nur ein eingetragener, noch zukünftiger Termin darf angefragt werden. const params = new URLSearchParams(window.location.search); const beraterId = params.get("berater"); const datum = params.get("datum"); const zeit = params.get("zeit"); let termin = null; function terminIstGueltig() { return termin !== null && termin.zeitpunkt > Date.now(); } function terminFehlerAnzeigen(text) { termin = null; const zusammenfassung = document.getElementById("termin-zusammenfassung"); zusammenfassung.classList.add("tb-summary--fehlend"); zusammenfassung.innerHTML = `
Bitte einen gültigen Termin wählen

Wähle auf der Startseite einen Berater, einen verfügbaren Tag und eine Uhrzeit.

`; pruefBereich.hidden = true; formularBereich.hidden = false; sendenButton.disabled = true; showStatus("error", text); updateSubmitState(); } async function terminLaden() { if (!beraterId || !datum || !zeit) { terminFehlerAnzeigen("Bitte wähle zuerst einen Termin im Kalender."); return; } try { const [beraterDaten, terminDaten] = await Promise.all( ["../json/berater.json", "../json/terminbuchung.json"].map(async function (url) { const response = await fetch(url); if (!response.ok) throw new Error("HTTP " + response.status); return response.json(); }) ); const berater = beraterDaten.berater.find(function (eintrag) { return String(eintrag.id) === beraterId; }); const zeiten = terminDaten[beraterId]?.[datum]; const zeitpunkt = new Date(`${datum}T${zeit}:00`).getTime(); if (!berater || !Array.isArray(zeiten) || !zeiten.includes(zeit) || !(zeitpunkt > Date.now())) { terminFehlerAnzeigen("Dieser Termin ist nicht verfügbar oder bereits vergangen. Bitte wähle einen neuen Termin."); return; } const datumText = new Intl.DateTimeFormat("de-DE", { weekday: "long", day: "2-digit", month: "2-digit", year: "numeric" }).format(new Date(zeitpunkt)); termin = { zeitpunkt, berater: String(berater.name).trim(), text: datumText + " um " + zeit.replace(":", ".") + " Uhr" }; document.getElementById("tb-berater").textContent = termin.berater; document.getElementById("tb-datum").textContent = datumText; document.getElementById("tb-zeit").textContent = zeit.replace(":", ".") + " Uhr"; updateSubmitState(); } catch (error) { terminFehlerAnzeigen("Der Termin konnte nicht geprüft werden. Bitte lade die Seite erneut."); } } function labelVon(key) { const select = fields[key]; return select.options[select.selectedIndex].text; } function werteAnzeigen(werte) { pruefDetails.replaceChildren(); werte.forEach(function (eintrag) { const label = document.createElement("dt"); label.textContent = eintrag[0]; const wert = document.createElement("dd"); wert.textContent = eintrag[1] || "–"; pruefDetails.append(label, wert); }); } bearbeitenButton.addEventListener("click", function () { pruefBereich.hidden = true; formularBereich.hidden = false; formularBereich.scrollIntoView({ behavior: "smooth", block: "start" }); }); sendenButton.addEventListener("click", function () { if (!terminIstGueltig()) { terminFehlerAnzeigen("Der Termin ist nicht mehr gültig. Bitte wähle einen neuen Termin."); formularBereich.scrollIntoView({ block: "start" }); return; } bestaetigungDialog.showModal(); }); form.addEventListener("submit", function (event) { event.preventDefault(); form.dataset.touched = "true"; if (!terminIstGueltig()) { terminFehlerAnzeigen("Bitte wähle zuerst einen verfügbaren, zukünftigen Termin."); return; } const okArt = validateSelect("art"); const okFormat = validateSelect("format"); const okGespraech = validateSelect("gespraech"); const okName = validateName(); const okEmail = validateEmail(); if (!(okArt && okFormat && okGespraech && okName && okEmail)) { showStatus("error", "Bitte überprüfe die markierten Felder und versuche es erneut."); return; } const tagListe = window.TerminbuchungTags ? window.TerminbuchungTags.holen() : []; const beraterText = termin.berater; const terminText = termin.text; const anfrageDaten = [ ["Berater/in", beraterText], ["Termin", terminText], ["Art der Ernährungsberatung", labelVon("art")], ["Beratungsformat", labelVon("format")], ["Gesprächsart", labelVon("gespraech")], ["Kurzbeschreibung", fields.beschreibung.value.trim() || "–"], ["Anamnese (Allergien & Unverträglichkeiten)", tagListe.length ? tagListe.join(", ") : "–"], ["Name", fields.name.value.trim()], ["E-Mail", fields.email.value.trim()], ["Telefon", fields.phone.value.trim() || "–"] ]; werteAnzeigen(anfrageDaten); sendenButton.disabled = false; formularBereich.hidden = true; pruefBereich.hidden = false; pruefBereich.scrollIntoView({ behavior: "smooth", block: "start" }); }); function showStatus(kind, text) { if (!statusEl) return; statusEl.textContent = kind === "ok" ? "✓ " + text : "✗ " + text; statusEl.className = "form-status is-visible " + (kind === "ok" ? "form-status--ok" : "form-status--error"); } updateSubmitState(); terminLaden(); })();