Compare commits
7 Commits
US-03-01-O
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
d5565f8561 | |
|
|
92a5306eda | |
|
|
87bc147ab3 | |
|
|
1620bf4f89 | |
|
|
af6c9511d7 | |
|
|
eefe5b228e | |
|
|
81413423d8 |
|
|
@ -134,7 +134,7 @@
|
||||||
|
|
||||||
.beratung-bereich .btn-accent {
|
.beratung-bereich .btn-accent {
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: #03242b;
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.beratung-bereich .btn-accent:hover {
|
.beratung-bereich .btn-accent:hover {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@
|
||||||
|
|
||||||
/* Empfehlung kommt aus der JSON-Datei; die Auswahlklasse setzt leistungen.js beim Klick. */
|
/* Empfehlung kommt aus der JSON-Datei; die Auswahlklasse setzt leistungen.js beim Klick. */
|
||||||
.pakete .paket-karte--empfohlen {
|
.pakete .paket-karte--empfohlen {
|
||||||
border-color: 1px solid rgba(248, 250, 252, 0.08);
|
|
||||||
box-shadow: 0 0 0 1px var(--accent-soft);
|
box-shadow: 0 0 0 1px var(--accent-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,15 +203,17 @@
|
||||||
|
|
||||||
/* ---------- Paketauswahl und Tastaturfokus ---------- */
|
/* ---------- Paketauswahl und Tastaturfokus ---------- */
|
||||||
.pakete .paket-karte__cta {
|
.pakete .paket-karte__cta {
|
||||||
|
display: inline-block;
|
||||||
font-family: var(--font-body);
|
font-family: var(--font-body);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: var(--background);
|
background: var(--accent);
|
||||||
border: 1px solid rgba(248, 250, 252, 0.08);
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 0.85rem 1rem;
|
padding: 0.85rem 1rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
transition: filter 0.15s ease;
|
transition: filter 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: min(560px, 100%);
|
width: min(560px, 100%);
|
||||||
|
/* Bei kleinen Fenstern bleibt der gesamte Dialog erreichbar. */
|
||||||
|
max-height: calc(100dvh - 48px);
|
||||||
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
background-color: #111a1e;
|
background-color: #111a1e;
|
||||||
border: 2px solid rgb(255 255 255 / 9%);
|
border: 2px solid rgb(255 255 255 / 9%);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
|
|
@ -1145,4 +1149,4 @@
|
||||||
.tb-overlay * {
|
.tb-overlay * {
|
||||||
transition-duration: 0.01ms !important;
|
transition-duration: 0.01ms !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="reveal">
|
<div class="reveal">
|
||||||
<h3 style="font-size: 1.25rem; margin-bottom: 24px;">Kontaktinformationen</h3>
|
<h3 style="font-size: 1.25rem; margin-bottom: 24px; margin-top: 38px;">Kontaktinformationen</h3>
|
||||||
<div class="info-stack">
|
<div class="info-stack">
|
||||||
<div class="info-card">
|
<div class="info-card">
|
||||||
<div class="info-card__icon" aria-hidden="true">✉</div>
|
<div class="info-card__icon" aria-hidden="true">✉</div>
|
||||||
|
|
@ -238,7 +238,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer-legal">
|
<div class="footer-legal">
|
||||||
<span>© 2026 Tri-hub GmbH</span>
|
<span>© 2026 TRI-HUB GmbH</span>
|
||||||
<span>
|
<span>
|
||||||
<a href="https://tri-hub.de/impressum">Impressum</a> ·
|
<a href="https://tri-hub.de/impressum">Impressum</a> ·
|
||||||
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
|
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,6 @@
|
||||||
<p class="hero__description">Volle Betreuung für ambitionierte Athletinnen und Athleten ab 50. Wir stehen dir wöchentlich zur Seite, stimmen deine Nahrungsergänzung ab und begleiten dich direkt an deinem Wettkampftag.</p>
|
<p class="hero__description">Volle Betreuung für ambitionierte Athletinnen und Athleten ab 50. Wir stehen dir wöchentlich zur Seite, stimmen deine Nahrungsergänzung ab und begleiten dich direkt an deinem Wettkampftag.</p>
|
||||||
<div class="paket-detail__actions">
|
<div class="paket-detail__actions">
|
||||||
<a class="btn btn-accent" href="#anfrage">Premium-Paket ansehen <span aria-hidden="true">↗</span></a>
|
<a class="btn btn-accent" href="#anfrage">Premium-Paket ansehen <span aria-hidden="true">↗</span></a>
|
||||||
<a class="paket-detail__text-link" href="#leistungen">Was ist enthalten? <span aria-hidden="true">↓</span></a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -58,7 +58,6 @@
|
||||||
<p class="hero__description">Laufende Betreuung für Triathletinnen und Triathleten, die regelmäßig trainieren. Wir passen deine Ernährung flexibel an deine Trainingsphasen an und entwickeln deine Wettkampfstrategie.</p>
|
<p class="hero__description">Laufende Betreuung für Triathletinnen und Triathleten, die regelmäßig trainieren. Wir passen deine Ernährung flexibel an deine Trainingsphasen an und entwickeln deine Wettkampfstrategie.</p>
|
||||||
<div class="paket-detail__actions">
|
<div class="paket-detail__actions">
|
||||||
<a class="btn btn-accent" href="#anfrage">Standard-Paket ansehen <span aria-hidden="true">↗</span></a>
|
<a class="btn btn-accent" href="#anfrage">Standard-Paket ansehen <span aria-hidden="true">↗</span></a>
|
||||||
<a class="paket-detail__text-link" href="#leistungen">Was ist enthalten? <span aria-hidden="true">↓</span></a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -58,7 +58,6 @@
|
||||||
<p class="hero__description">Du möchtest deine Ernährung bewusster auf dein Training abstimmen? Wir begleiten deinen Einstieg – mit einem persönlichen Gespräch, einem Plan für dich und konkreten Ideen für deinen Alltag.</p>
|
<p class="hero__description">Du möchtest deine Ernährung bewusster auf dein Training abstimmen? Wir begleiten deinen Einstieg – mit einem persönlichen Gespräch, einem Plan für dich und konkreten Ideen für deinen Alltag.</p>
|
||||||
<div class="paket-detail__actions">
|
<div class="paket-detail__actions">
|
||||||
<a class="btn btn-accent" href="#anfrage">Starter-Paket ansehen <span aria-hidden="true">↗</span></a>
|
<a class="btn btn-accent" href="#anfrage">Starter-Paket ansehen <span aria-hidden="true">↗</span></a>
|
||||||
<a class="paket-detail__text-link" href="#leistungen">Was ist enthalten? <span aria-hidden="true">↓</span></a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,6 @@
|
||||||
aria-label="Menü öffnen">☰</button>
|
aria-label="Menü öffnen">☰</button>
|
||||||
<nav class="site-navigation__links" id="navigation-links" aria-label="Seitennavigation">
|
<nav class="site-navigation__links" id="navigation-links" aria-label="Seitennavigation">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#termin">Termin</a></li>
|
|
||||||
<li><a href="#formular">Deine Angaben</a></li>
|
|
||||||
<li><a href="../index.html">Zurück zur Übersicht</a></li>
|
<li><a href="../index.html">Zurück zur Übersicht</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
@ -176,8 +174,8 @@
|
||||||
<div class="tb-review-actions">
|
<div class="tb-review-actions">
|
||||||
<button type="button" class="tb-review-button tb-review-button--secondary"
|
<button type="button" class="tb-review-button tb-review-button--secondary"
|
||||||
id="anfrage-bearbeiten">Zurück und bearbeiten</button>
|
id="anfrage-bearbeiten">Zurück und bearbeiten</button>
|
||||||
<a class="tb-review-button tb-review-button--primary" id="anfrage-senden" href="#">Anfrage
|
<button type="button" class="tb-review-button tb-review-button--primary" id="anfrage-senden" disabled>Anfrage
|
||||||
senden</a>
|
senden</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -215,7 +213,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer-legal">
|
<div class="footer-legal">
|
||||||
<span>© 2026 Tri-hub GmbH</span>
|
<span>© 2026 TRI-HUB GmbH</span>
|
||||||
<span>
|
<span>
|
||||||
<a href="https://tri-hub.de/impressum">Impressum</a> ·
|
<a href="https://tri-hub.de/impressum">Impressum</a> ·
|
||||||
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
|
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
|
||||||
|
|
@ -228,13 +226,12 @@
|
||||||
|
|
||||||
<dialog class="tb-confirm-dialog" id="anfrage-bestaetigung" aria-labelledby="bestaetigung-titel"
|
<dialog class="tb-confirm-dialog" id="anfrage-bestaetigung" aria-labelledby="bestaetigung-titel"
|
||||||
aria-describedby="bestaetigung-text">
|
aria-describedby="bestaetigung-text">
|
||||||
<span class="kicker">Anfrage vorbereitet</span>
|
<span class="kicker">Termin gebucht</span>
|
||||||
<h2 id="bestaetigung-titel">Terminanfrage</h2>
|
<h2 id="bestaetigung-titel">Terminbestätigung</h2>
|
||||||
<p id="bestaetigung-text">Deine Terminanfrage wurde an dein E-Mail-Programm übergeben. Sende sie dort
|
<p id="bestaetigung-text">Dein Termin wurde erfolgreich gebucht. Eine Bestätigung wurde an die angegebene E-Mail-Adresse gesendet.</p>
|
||||||
bitte ab. Wir prüfen deinen Wunschtermin und schicken dir anschließend eine Bestätigung per E-Mail.</p>
|
|
||||||
<a class="tb-review-button tb-review-button--primary" href="../index.html">OK, zur Übersicht</a>
|
<a class="tb-review-button tb-review-button--primary" href="../index.html">OK, zur Übersicht</a>
|
||||||
</dialog>
|
</dialog>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer-legal">
|
<div class="footer-legal">
|
||||||
<span>© 2026 Tri-hub GmbH</span>
|
<span>© 2026 TRI-HUB GmbH</span>
|
||||||
<span>
|
<span>
|
||||||
<a href="https://tri-hub.de/impressum">Impressum</a> ·
|
<a href="https://tri-hub.de/impressum">Impressum</a> ·
|
||||||
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
|
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Online-Terminbuchung – Buchungsseite (html/terminbuchung.html).
|
// Online-Terminbuchung – Buchungsseite (html/terminbuchung.html).
|
||||||
// Liest den gewählten Termin aus der URL (berater, datum, zeit), füllt die
|
// Liest den gewählten Termin aus der URL (berater, datum, zeit), füllt die
|
||||||
// Zusammenfassung, verwaltet die Anamnese-Tags und prüft das Formular.
|
// Zusammenfassung, verwaltet die Anamnese-Tags und prüft das Formular.
|
||||||
// Zum Absenden wird wie auf der Kontaktseite das E-Mail-Programm geöffnet.
|
// Die Bestätigung wird nur auf der Seite angezeigt; es wird keine E-Mail versendet.
|
||||||
|
|
||||||
// ----- Scroll-Reveal -----
|
// ----- Scroll-Reveal -----
|
||||||
(function () {
|
(function () {
|
||||||
|
|
@ -34,66 +34,6 @@
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// ----- 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) -----
|
// ----- Anamnese-Tags (Allergien & Unverträglichkeiten) -----
|
||||||
(function () {
|
(function () {
|
||||||
const input = document.getElementById("tag-input");
|
const input = document.getElementById("tag-input");
|
||||||
|
|
@ -175,7 +115,6 @@
|
||||||
const form = document.getElementById("terminbuchung-formular");
|
const form = document.getElementById("terminbuchung-formular");
|
||||||
if (!form) return;
|
if (!form) return;
|
||||||
|
|
||||||
const EMAIL_TARGET = "info@trihub.de";
|
|
||||||
const bestaetigungDialog = document.getElementById("anfrage-bestaetigung");
|
const bestaetigungDialog = document.getElementById("anfrage-bestaetigung");
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
|
|
@ -193,7 +132,7 @@
|
||||||
const pruefBereich = document.getElementById("anfrage-pruefen");
|
const pruefBereich = document.getElementById("anfrage-pruefen");
|
||||||
const pruefDetails = document.getElementById("anfrage-pruefen-details");
|
const pruefDetails = document.getElementById("anfrage-pruefen-details");
|
||||||
const bearbeitenButton = document.getElementById("anfrage-bearbeiten");
|
const bearbeitenButton = document.getElementById("anfrage-bearbeiten");
|
||||||
const sendenLink = document.getElementById("anfrage-senden");
|
const sendenButton = document.getElementById("anfrage-senden");
|
||||||
|
|
||||||
function wrap(el) {
|
function wrap(el) {
|
||||||
return el.closest(".field");
|
return el.closest(".field");
|
||||||
|
|
@ -234,6 +173,7 @@
|
||||||
|
|
||||||
function allValid() {
|
function allValid() {
|
||||||
return (
|
return (
|
||||||
|
terminIstGueltig() &&
|
||||||
fields.art.value !== "" &&
|
fields.art.value !== "" &&
|
||||||
fields.format.value !== "" &&
|
fields.format.value !== "" &&
|
||||||
fields.gespraech.value !== "" &&
|
fields.gespraech.value !== "" &&
|
||||||
|
|
@ -266,25 +206,75 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Termin-Angaben aus der URL für das E-Mail übernehmen.
|
// Nur ein eingetragener, noch zukünftiger Termin darf angefragt werden.
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const datumRoh = params.get("datum") || "";
|
const beraterId = params.get("berater");
|
||||||
const zeitRoh = params.get("zeit") || "";
|
const datum = params.get("datum");
|
||||||
let terminText = "–";
|
const zeit = params.get("zeit");
|
||||||
if (datumRoh && zeitRoh) {
|
let termin = null;
|
||||||
const [jahr, monat, tag] = datumRoh.split("-").map(Number);
|
|
||||||
if (jahr && monat && tag) {
|
function terminIstGueltig() {
|
||||||
const formatiert = new Intl.DateTimeFormat("de-DE", {
|
return termin !== null && termin.zeitpunkt > Date.now();
|
||||||
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 terminFehlerAnzeigen(text) {
|
||||||
|
termin = null;
|
||||||
|
const zusammenfassung = document.getElementById("termin-zusammenfassung");
|
||||||
|
zusammenfassung.classList.add("tb-summary--fehlend");
|
||||||
|
zusammenfassung.innerHTML = `
|
||||||
|
<div class="tb-summary__item">
|
||||||
|
<span class="tb-summary__label">Bitte einen gültigen Termin wählen</span>
|
||||||
|
<p class="tb-summary__value" style="font-size: 0.95rem; font-weight: 400;">
|
||||||
|
Wähle auf der <a href="../index.html#berater">Startseite</a> einen Berater,
|
||||||
|
einen verfügbaren Tag und eine Uhrzeit.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
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) {
|
function labelVon(key) {
|
||||||
const select = fields[key];
|
const select = fields[key];
|
||||||
|
|
@ -308,15 +298,22 @@
|
||||||
formularBereich.scrollIntoView({ behavior: "smooth", block: "start" });
|
formularBereich.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
});
|
});
|
||||||
|
|
||||||
sendenLink.addEventListener("click", function (event) {
|
sendenButton.addEventListener("click", function () {
|
||||||
event.preventDefault();
|
if (!terminIstGueltig()) {
|
||||||
|
terminFehlerAnzeigen("Der Termin ist nicht mehr gültig. Bitte wähle einen neuen Termin.");
|
||||||
|
formularBereich.scrollIntoView({ block: "start" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
bestaetigungDialog.showModal();
|
bestaetigungDialog.showModal();
|
||||||
window.location.href = sendenLink.href;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
form.addEventListener("submit", function (event) {
|
form.addEventListener("submit", function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
form.dataset.touched = "true";
|
form.dataset.touched = "true";
|
||||||
|
if (!terminIstGueltig()) {
|
||||||
|
terminFehlerAnzeigen("Bitte wähle zuerst einen verfügbaren, zukünftigen Termin.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const okArt = validateSelect("art");
|
const okArt = validateSelect("art");
|
||||||
const okFormat = validateSelect("format");
|
const okFormat = validateSelect("format");
|
||||||
|
|
@ -330,7 +327,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagListe = window.TerminbuchungTags ? window.TerminbuchungTags.holen() : [];
|
const tagListe = window.TerminbuchungTags ? window.TerminbuchungTags.holen() : [];
|
||||||
const beraterText = elBerater ? elBerater.textContent.trim() : "–";
|
const beraterText = termin.berater;
|
||||||
|
const terminText = termin.text;
|
||||||
const anfrageDaten = [
|
const anfrageDaten = [
|
||||||
["Berater/in", beraterText],
|
["Berater/in", beraterText],
|
||||||
["Termin", terminText],
|
["Termin", terminText],
|
||||||
|
|
@ -344,28 +342,8 @@
|
||||||
["Telefon", fields.phone.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);
|
werteAnzeigen(anfrageDaten);
|
||||||
sendenLink.href =
|
sendenButton.disabled = false;
|
||||||
"mailto:" + EMAIL_TARGET +
|
|
||||||
"?subject=" + encodeURIComponent("Terminanfrage: " + terminText) +
|
|
||||||
"&body=" + encodeURIComponent(body);
|
|
||||||
formularBereich.hidden = true;
|
formularBereich.hidden = true;
|
||||||
pruefBereich.hidden = false;
|
pruefBereich.hidden = false;
|
||||||
pruefBereich.scrollIntoView({ behavior: "smooth", block: "start" });
|
pruefBereich.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
|
@ -379,4 +357,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSubmitState();
|
updateSubmitState();
|
||||||
|
terminLaden();
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -77,18 +77,17 @@
|
||||||
const tabelle = terminDaten[String(beraterId)];
|
const tabelle = terminDaten[String(beraterId)];
|
||||||
if (!tabelle || typeof tabelle !== "object") return [];
|
if (!tabelle || typeof tabelle !== "object") return [];
|
||||||
const zeiten = tabelle[schluessel];
|
const zeiten = tabelle[schluessel];
|
||||||
return Array.isArray(zeiten) ? zeiten : [];
|
// Auch bereits vergangene Uhrzeiten am heutigen Tag ausblenden.
|
||||||
|
return Array.isArray(zeiten)
|
||||||
|
? zeiten.filter((zeit) => new Date(`${schluessel}T${zeit}:00`).getTime() > Date.now())
|
||||||
|
: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function hatFreieTermine(beraterId) {
|
function hatFreieTermine(beraterId) {
|
||||||
const tabelle = terminDaten[String(beraterId)];
|
const tabelle = terminDaten[String(beraterId)];
|
||||||
if (!tabelle || typeof tabelle !== "object") return false;
|
if (!tabelle || typeof tabelle !== "object") return false;
|
||||||
|
|
||||||
return Object.entries(tabelle).some(([schluessel, zeiten]) => {
|
return Object.keys(tabelle).some((schluessel) => zeitenFuer(beraterId, schluessel).length > 0);
|
||||||
const datum = /^(\d{4})-(\d{2})-(\d{2})$/.exec(schluessel);
|
|
||||||
if (!datum || !Array.isArray(zeiten) || zeiten.length === 0) return false;
|
|
||||||
return !istVergangen(Number(datum[1]), Number(datum[2]) - 1, Number(datum[3]));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function istVerfuegbar(jahr, monat, tag) {
|
function istVerfuegbar(jahr, monat, tag) {
|
||||||
|
|
@ -121,7 +120,7 @@
|
||||||
<div class="tb-dialog" role="dialog" aria-modal="true" aria-labelledby="tb-title">
|
<div class="tb-dialog" role="dialog" aria-modal="true" aria-labelledby="tb-title">
|
||||||
<button type="button" class="tb-close" aria-label="Terminbuchung schließen">×</button>
|
<button type="button" class="tb-close" aria-label="Terminbuchung schließen">×</button>
|
||||||
<span class="tb-kicker">Online-Terminbuchung</span>
|
<span class="tb-kicker">Online-Terminbuchung</span>
|
||||||
<h2 class="tb-title" id="tb-title">Trainingsplan & Beratung – Termin wählen</h2>
|
<h2 class="tb-title" id="tb-title"> Termin wählen</h2>
|
||||||
<p class="tb-subtitle">Wähle deinen Berater, einen verfügbaren Tag und eine Uhrzeit.</p>
|
<p class="tb-subtitle">Wähle deinen Berater, einen verfügbaren Tag und eine Uhrzeit.</p>
|
||||||
|
|
||||||
<span class="tb-trainer-label">Berater/in wählen:</span>
|
<span class="tb-trainer-label">Berater/in wählen:</span>
|
||||||
|
|
@ -181,7 +180,32 @@
|
||||||
if (event.target === overlay) schliessen();
|
if (event.target === overlay) schliessen();
|
||||||
});
|
});
|
||||||
document.addEventListener("keydown", (event) => {
|
document.addEventListener("keydown", (event) => {
|
||||||
if (istGeoeffnet && event.key === "Escape") schliessen();
|
if (!istGeoeffnet) return;
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
event.preventDefault();
|
||||||
|
schliessen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key !== "Tab") return;
|
||||||
|
|
||||||
|
// Tab und Shift+Tab bleiben zwischen den sichtbaren Dialog-Buttons.
|
||||||
|
const buttons = [...overlay.querySelectorAll("button:not(:disabled)")]
|
||||||
|
.filter((button) => button.getClientRects().length > 0);
|
||||||
|
const erster = buttons[0];
|
||||||
|
const letzter = buttons[buttons.length - 1];
|
||||||
|
const fokus = document.activeElement;
|
||||||
|
if (!overlay.contains(fokus) || (event.shiftKey && fokus === erster)) {
|
||||||
|
event.preventDefault();
|
||||||
|
(event.shiftKey ? letzter : erster).focus();
|
||||||
|
} else if (!event.shiftKey && fokus === letzter) {
|
||||||
|
event.preventDefault();
|
||||||
|
erster.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener("focusin", (event) => {
|
||||||
|
if (istGeoeffnet && !overlay.contains(event.target)) {
|
||||||
|
overlay.querySelector(".tb-close").focus();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Buchen
|
// Buchen
|
||||||
|
|
@ -273,9 +297,14 @@
|
||||||
kalenderZeichnen();
|
kalenderZeichnen();
|
||||||
zeitenZeichnen();
|
zeitenZeichnen();
|
||||||
buchButtonAktualisieren();
|
buchButtonAktualisieren();
|
||||||
|
overlay.querySelector(".tb-day--ausgewaehlt")?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function zeitKlicken(zeit) {
|
function zeitKlicken(zeit) {
|
||||||
|
if (!zeitenFuer(zustand.beraterId, zustand.datum).includes(zeit)) {
|
||||||
|
auswahlAktualisieren();
|
||||||
|
return;
|
||||||
|
}
|
||||||
zustand.zeit = zeit;
|
zustand.zeit = zeit;
|
||||||
overlay.querySelectorAll(".tb-time-chip").forEach((chip) => {
|
overlay.querySelectorAll(".tb-time-chip").forEach((chip) => {
|
||||||
chip.classList.toggle("is-active", chip.dataset.zeit === zeit);
|
chip.classList.toggle("is-active", chip.dataset.zeit === zeit);
|
||||||
|
|
@ -285,6 +314,11 @@
|
||||||
|
|
||||||
function buchen() {
|
function buchen() {
|
||||||
if (!zustand.datum || !zustand.zeit || zustand.beraterId == null) return;
|
if (!zustand.datum || !zustand.zeit || zustand.beraterId == null) return;
|
||||||
|
// Die Seite kann seit der Auswahl offen geblieben sein.
|
||||||
|
if (!zeitenFuer(zustand.beraterId, zustand.datum).includes(zustand.zeit)) {
|
||||||
|
auswahlAktualisieren();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const ziel =
|
const ziel =
|
||||||
ZIELSEITE +
|
ZIELSEITE +
|
||||||
"?berater=" + encodeURIComponent(zustand.beraterId) +
|
"?berater=" + encodeURIComponent(zustand.beraterId) +
|
||||||
|
|
@ -293,6 +327,15 @@
|
||||||
window.location.href = ziel;
|
window.location.href = ziel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function auswahlAktualisieren() {
|
||||||
|
zustand.zeit = null;
|
||||||
|
kalenderZeichnen();
|
||||||
|
zeitenZeichnen();
|
||||||
|
legendeAktualisieren();
|
||||||
|
buchButtonAktualisieren();
|
||||||
|
overlay.querySelector(".tb-close").focus();
|
||||||
|
}
|
||||||
|
|
||||||
// ----- Zeichnen -----
|
// ----- Zeichnen -----
|
||||||
|
|
||||||
function kalenderZeichnen() {
|
function kalenderZeichnen() {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
"url": "https://instagram.com"
|
"url": "https://instagram.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Trainingsplan buchen",
|
"text": "Termin buchen",
|
||||||
"url": "https://tri-hub.de/booking",
|
"url": "https://tri-hub.de/booking",
|
||||||
"aktion": "terminbuchung"
|
"aktion": "terminbuchung"
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
"url": "https://instagram.com"
|
"url": "https://instagram.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Trainingsplan buchen",
|
"text": "Termin buchen",
|
||||||
"url": "https://tri-hub.de/booking",
|
"url": "https://tri-hub.de/booking",
|
||||||
"aktion": "terminbuchung"
|
"aktion": "terminbuchung"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue