// 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.
// Zum Absenden wird wie auf der Kontaktseite das E-Mail-Programm geöffnet.
// ----- 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);
});
})();
// ----- Termin-Zusammenfassung aus der URL -----
(function () {
const params = new URLSearchParams(window.location.search);
const beraterId = params.get("berater");
const datum = params.get("datum");
const zeit = params.get("zeit");
const elBerater = document.getElementById("tb-berater");
const elDatum = document.getElementById("tb-datum");
const elZeit = document.getElementById("tb-zeit");
const zusammenfassung = document.getElementById("termin-zusammenfassung");
if (!zusammenfassung) return;
// Ohne Termin-Parameter (z. B. direkter Seitenaufruf): Hinweis statt Zusammenfassung.
if (!beraterId || !datum || !zeit) {
zusammenfassung.classList.add("tb-summary--fehlend");
zusammenfassung.innerHTML = `
Kein Termin ausgewählt
Du hast noch keinen Termin im Kalender gewählt.
Gehe bitte zurück zur Startseite und öffne dort über einen
Berater die Terminbuchung.
`;
return;
}
// Datum lesbar formatieren, z. B. "Montag, 05.10.2026"
const [jahr, monat, tag] = datum.split("-").map(Number);
if (!jahr || !monat || !tag) return;
const datumObjekt = new Date(jahr, monat - 1, tag);
const formatiert = new Intl.DateTimeFormat("de-DE", {
weekday: "long",
day: "2-digit",
month: "2-digit",
year: "numeric"
}).format(datumObjekt);
elDatum.textContent = formatiert;
elZeit.textContent = zeit.replace(":", ".") + " Uhr";
// Berater-Namen aus json/berater.json auflösen (ID stammt aus dem Kalender).
fetch("../json/berater.json")
.then(function (response) {
if (!response.ok) throw new Error("HTTP " + response.status);
return response.json();
})
.then(function (daten) {
const treffer = (daten.berater || []).find(function (b) {
return String(b.id) === String(beraterId);
});
elBerater.textContent = treffer ? String(treffer.name).trim() : "Unbekannter Berater";
})
.catch(function () {
elBerater.textContent = "Unbekannter Berater";
});
})();
// ----- 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 EMAIL_TARGET = "info@trihub.de";
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 sendenLink = 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 (
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);
});
});
// Termin-Angaben aus der URL für das E-Mail übernehmen.
const params = new URLSearchParams(window.location.search);
const datumRoh = params.get("datum") || "";
const zeitRoh = params.get("zeit") || "";
let terminText = "–";
if (datumRoh && zeitRoh) {
const [jahr, monat, tag] = datumRoh.split("-").map(Number);
if (jahr && monat && tag) {
const formatiert = new Intl.DateTimeFormat("de-DE", {
weekday: "long",
day: "2-digit",
month: "2-digit",
year: "numeric"
}).format(new Date(jahr, monat - 1, tag));
terminText = formatiert + " um " + zeitRoh.replace(":", ".") + " Uhr";
}
}
const elBerater = document.getElementById("tb-berater");
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" });
});
sendenLink.addEventListener("click", function (event) {
event.preventDefault();
bestaetigungDialog.showModal();
window.location.href = sendenLink.href;
});
form.addEventListener("submit", function (event) {
event.preventDefault();
form.dataset.touched = "true";
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 = elBerater ? elBerater.textContent.trim() : "–";
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() || "–"]
];
const body = [
"Terminanfrage – Trainingsplan & Beratung",
"",
"Termin: " + terminText,
"Berater/in: " + beraterText,
"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() || "–")
].join("\n");
werteAnzeigen(anfrageDaten);
sendenLink.href =
"mailto:" + EMAIL_TARGET +
"?subject=" + encodeURIComponent("Terminanfrage: " + terminText) +
"&body=" + encodeURIComponent(body);
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();
})();