trihub-ernaehrung-advinters/js/terminbuchung-form.js

383 lines
14 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.

// 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 = "&times;";
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();
})();