383 lines
14 KiB
JavaScript
383 lines
14 KiB
JavaScript
// 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 = `
|
||
<div class="tb-summary__item">
|
||
<span class="tb-summary__label">Kein Termin ausgewählt</span>
|
||
<p class="tb-summary__value" style="font-size: 0.95rem; font-weight: 400;">
|
||
Du hast noch keinen Termin im Kalender gewählt.
|
||
Gehe bitte zurück zur <a href="../index.html#berater">Startseite</a> und öffne dort über einen
|
||
Berater die Terminbuchung.
|
||
</p>
|
||
</div>
|
||
`;
|
||
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();
|
||
})();
|