Terminplaner: Einbau-Skript – Nachbesserungen
429 ohne Schleife (Meldung und Erneut-versuchen-Knopf, Formulare bleiben stehen), Schriftfarbe auf der Betriebsfarbe mit mindestens 4,5 : 1, AGB-Link aus der oeffentlichen Konfiguration (betrieb.agbUrl), Haken starten bei geaenderter Auswahl ohne Haken, Fehler ohne Feld nicht verschluckt, Konfiguration fail closed, ladeNeu fuer Neuladen und neu_laden, Tastatur und Fokus (Modifikatoren, Monatspfeile, Belehrungstext), Aufraeumen. UI-Testserver: Timer, offene Verbindungen, Hinweis auf sessionCookie. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5fef7e47f0
commit
4465ac7b94
9 files changed
+501
-88
No files matched your search
+182
-79
@@ -89,7 +89,8 @@
|
||||
'.wkt-belehrung-text{margin-top:.5rem;max-height:18rem;overflow:auto;font-size:.9rem}',
|
||||
'.wkt-belehrung-text p{white-space:pre-line}',
|
||||
'.wkt-datenschutz{font-size:.875rem}',
|
||||
'.wkt-datenschutz a,.wkt-fuss a,.wkt-ausfall a{color:inherit}',
|
||||
'.wkt-datenschutz a,.wkt-fuss a,.wkt-ausfall a,.wkt-box a{color:inherit}',
|
||||
'.wkt-box .wkt-agb{margin:.5rem 0 0}.wkt-agb a{display:inline-block;padding:.2rem 0;min-height:24px}',
|
||||
'.wkt-fuss{margin-top:1.5rem;padding-top:.75rem;border-top:1px solid #c9ced6;font-size:.875rem}',
|
||||
'.wkt-fuss:empty{display:none}',
|
||||
'.wkt-fuss-links{display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem 1.25rem;margin:0 0 .35rem}',
|
||||
@@ -170,18 +171,39 @@
|
||||
return ''
|
||||
}
|
||||
|
||||
// Vorgabe-Akzent aus der Betriebsfarbe (#rrggbb), wenn die Seite --wkt-akzent nicht selbst setzt; Textfarbe nach Helligkeit
|
||||
function setzeBetriebsfarbe (box, farbe) {
|
||||
if (!/^#[0-9a-fA-F]{6}$/.test(farbe || '')) return
|
||||
var cs = getComputedStyle(box)
|
||||
if (cs.getPropertyValue('--wkt-akzent').trim()) return
|
||||
// @farben-anfang (reine Funktionen, test/termineEmbedStatic.test.js fuehrt sie einzeln aus)
|
||||
// Relative Leuchtdichte nach WCAG 2.1 fuer '#rrggbb'.
|
||||
function luminanz (farbe) {
|
||||
var rgb = [1, 3, 5].map(function (i) {
|
||||
var c = parseInt(farbe.slice(i, i + 2), 16) / 255
|
||||
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
|
||||
})
|
||||
var l = 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]
|
||||
return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]
|
||||
}
|
||||
|
||||
// Kontrastverhaeltnis zweier Leuchtdichten (1 bis 21).
|
||||
function kontrast (a, b) {
|
||||
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05)
|
||||
}
|
||||
|
||||
// Schrift auf der Betriebsfarbe: Weiss oder das dunkle Grau, je nachdem was mehr Kontrast gibt; erreicht keines 4,5 : 1
|
||||
// (mittlere Toene), nimmt sie Schwarz (mindestens 4,58 : 1 fuer jede Farbe).
|
||||
function textFarbeAuf (farbe) {
|
||||
var l = luminanz(farbe)
|
||||
var weiss = kontrast(l, 1)
|
||||
var dunkel = kontrast(l, luminanz('#111827'))
|
||||
var beste = Math.max(weiss, dunkel)
|
||||
if (beste < 4.5 && kontrast(l, 0) > beste) return '#000000'
|
||||
return weiss >= dunkel ? '#ffffff' : '#111827'
|
||||
}
|
||||
// @farben-ende
|
||||
|
||||
// Vorgabe-Akzent aus der Betriebsfarbe (#rrggbb), wenn die Seite --wkt-akzent nicht selbst setzt
|
||||
function setzeBetriebsfarbe (box, farbe) {
|
||||
if (!/^#[0-9a-fA-F]{6}$/.test(farbe || '')) return
|
||||
if (getComputedStyle(box).getPropertyValue('--wkt-akzent').trim()) return
|
||||
box.style.setProperty('--wkt-vorgabe-akzent', farbe)
|
||||
box.style.setProperty('--wkt-vorgabe-akzent-text', l > 0.4 ? '#111827' : '#ffffff')
|
||||
box.style.setProperty('--wkt-vorgabe-akzent-text', textFarbeAuf(farbe))
|
||||
}
|
||||
|
||||
function init (root) {
|
||||
@@ -271,12 +293,28 @@
|
||||
|
||||
// ------------------------------------------------------------ Konfiguration
|
||||
|
||||
function nichtLeer (v) { return istText(v) && v !== '' }
|
||||
|
||||
// Fail closed: fehlt etwas, das die Strecke zwingend braucht (Knopftext, Texte der Haken, Belehrung), gilt die Funktion als nicht aktiv
|
||||
function konfigGueltig (d) {
|
||||
if (!d || d.aktiv !== true || !Array.isArray(d.leistungen) || d.leistungen.length === 0 || !Array.isArray(d.ressourcen)) return false
|
||||
if (!d.betrieb || typeof d.betrieb !== 'object' || !TAGESZEILE.test(d.heute || '') || !nichtLeer(d.formToken)) return false
|
||||
if (!d.texte || typeof d.texte !== 'object' || !d.regeln || typeof d.regeln !== 'object' || !d.gastFelder || typeof d.gastFelder !== 'object') return false
|
||||
var leistungenOk = d.leistungen.every(function (l) {
|
||||
return l && typeof l === 'object' && nichtLeer(l.id) && istText(l.name) && nichtLeer(l.knopf) && Array.isArray(l.ressourcen)
|
||||
})
|
||||
var ressourcenOk = d.ressourcen.every(function (r) { return r && typeof r === 'object' && nichtLeer(r.id) && istText(r.name) })
|
||||
if (!leistungenOk || !ressourcenOk) return false
|
||||
if (d.storno && !nichtLeer(d.texte.storno)) return false
|
||||
if (!nichtLeer(d.texte.vorzeitig)) return false
|
||||
if (d.erinnerung && d.erinnerung.anbieten === true && !nichtLeer(d.texte.erinnerung)) return false
|
||||
if (d.widerruf === true && !nichtLeer(d.texte.widerrufsbelehrung)) return false
|
||||
return true
|
||||
}
|
||||
|
||||
function lade (neu) {
|
||||
return request('GET', '/config', undefined, neu).then(function (d) {
|
||||
var ok = d && d.aktiv === true && Array.isArray(d.leistungen) && d.leistungen.length > 0 && Array.isArray(d.ressourcen) &&
|
||||
d.betrieb && typeof d.betrieb === 'object' && TAGESZEILE.test(d.heute || '') && d.texte && typeof d.texte === 'object' &&
|
||||
d.regeln && typeof d.regeln === 'object' && d.gastFelder && typeof d.gastFelder === 'object'
|
||||
if (!ok) throw { inaktiv: true }
|
||||
if (!konfigGueltig(d)) throw { inaktiv: true }
|
||||
S.cfg = d
|
||||
S.cfgAt = performance.now()
|
||||
return d
|
||||
@@ -285,19 +323,27 @@
|
||||
|
||||
function configFrisch () { return !!S.cfg && performance.now() - S.cfgAt <= CONFIG_ALTER_MAX_MS }
|
||||
|
||||
// Alte Konfiguration (Formular-Token laeuft nach 2 h ab) vor jedem Schritt erneuern; false = Auswahl galt nicht mehr, Zustand zurueckgesetzt
|
||||
function sichereConfig () {
|
||||
if (configFrisch()) return Promise.resolve(true)
|
||||
// Konfiguration frisch vom Server holen (neues Formular-Token) und pruefen, dass Leistung und Person noch gelten.
|
||||
// true = weiter; false = die Auswahl galt nicht mehr, die Strecke beginnt von vorn. Fehler beim Laden werden weitergereicht.
|
||||
function ladeNeu () {
|
||||
return lade(true).then(function () {
|
||||
if (S.leistungId && !leistungMitId(S.leistungId)) {
|
||||
var m = T('Das Angebot hat sich geändert. Bitte wählen Sie neu.', 'Das Angebot hat sich geändert. Bitte wähle neu.')
|
||||
starte(false, m)
|
||||
var l = S.leistungId ? leistungMitId(S.leistungId) : null
|
||||
var ressourceOk = !l || !S.ressource || S.ressource === 'egal' || kandidaten(l).some(function (r) { return r.id === S.ressource })
|
||||
if (S.leistungId && (!l || !ressourceOk)) {
|
||||
starte(false, T('Das Angebot hat sich geändert. Bitte wählen Sie neu.', 'Das Angebot hat sich geändert. Bitte wähle neu.'))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, function (err) {
|
||||
})
|
||||
}
|
||||
|
||||
// Alte Konfiguration (Formular-Token laeuft nach 2 h ab) vor jedem Schritt erneuern; false = abbrechen (Auswahl galt nicht mehr
|
||||
// oder Funktion nicht mehr aktiv). Ist der Server nicht erreichbar, geht es mit der alten Konfiguration weiter: der naechste Aufruf meldet es.
|
||||
function sichereConfig () {
|
||||
if (configFrisch()) return Promise.resolve(true)
|
||||
return ladeNeu().catch(function (err) {
|
||||
if (err && err.inaktiv) { zeigeAusweich(); return false }
|
||||
return true // nicht erreichbar: weiter mit der alten Konfiguration, der naechste Aufruf meldet es
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -433,15 +479,16 @@
|
||||
ui.fuss.appendChild(el('p', 'wkt-powered', 'Terminbuchung über WEBklar'))
|
||||
}
|
||||
|
||||
// Netz weg oder Server kaputt mitten in der Strecke
|
||||
function ausfall (wiederhole) {
|
||||
// Netz weg, Server kaputt oder Grenze erreicht (meldung) mitten in der Strecke: Meldung und "Erneut versuchen". Es folgt nie ein
|
||||
// selbststaendiger neuer Abruf, nur der Klick auf den Knopf versucht es noch einmal.
|
||||
function ausfall (wiederhole, meldung) {
|
||||
S.sendet = false
|
||||
ui.body.textContent = ''
|
||||
ui.titel = null
|
||||
var tel = S.cfg && istText(S.cfg.betrieb.telefon) ? S.cfg.betrieb.telefon : ''
|
||||
var p = el('p', 'wkt-fehlertext wkt-ausfall', T('Gerade nicht erreichbar. Bitte rufen Sie an: ', 'Gerade nicht erreichbar. Bitte ruf an: '))
|
||||
var p = el('p', 'wkt-fehlertext wkt-ausfall', meldung || T('Gerade nicht erreichbar. Bitte rufen Sie an: ', 'Gerade nicht erreichbar. Bitte ruf an: '))
|
||||
p.tabIndex = -1
|
||||
if (tel) {
|
||||
if (tel && !meldung) {
|
||||
var a = el('a', '', tel)
|
||||
a.href = 'tel:' + tel.replace(/[^0-9+]/g, '')
|
||||
p.appendChild(a)
|
||||
@@ -458,7 +505,7 @@
|
||||
if (modus === 'inline') {
|
||||
root.classList.remove('wkt-root')
|
||||
root.textContent = ''
|
||||
if (ausweich) root.appendChild(ausweich)
|
||||
if (ausweich) root.appendChild(ausweich.cloneNode(true)) // Kopie: ein zweiter Aufruf darf den Text nicht verlieren
|
||||
return
|
||||
}
|
||||
if (!ui.body) return
|
||||
@@ -483,6 +530,14 @@
|
||||
else gehe('leistung', { fokus: fokus, meldung: meldung })
|
||||
}
|
||||
|
||||
// Zustimmungen gelten nur fuer die Buchung, bei der sie gegeben wurden: bei neuer Leistung, neuem Tag oder neuer Uhrzeit beginnt die
|
||||
// Zusammenfassung wieder ohne Haken (R45)
|
||||
function hakenZurueck () {
|
||||
S.daten.storno = false
|
||||
S.daten.vorzeitig = false
|
||||
S.daten.erinnerung = false
|
||||
}
|
||||
|
||||
function waehleLeistung (lid, opt) {
|
||||
var l = leistungMitId(lid)
|
||||
if (S.leistungId !== lid) {
|
||||
@@ -490,6 +545,7 @@
|
||||
S.monat = ''
|
||||
S.tag = ''
|
||||
S.zeit = ''
|
||||
hakenZurueck()
|
||||
}
|
||||
S.leistungId = lid
|
||||
opt = opt || {}
|
||||
@@ -505,6 +561,7 @@
|
||||
function zeichneLeistung () {
|
||||
var c = S.cfg
|
||||
kopf(T('Welche Leistung möchten Sie buchen?', 'Welche Leistung möchtest du buchen?'))
|
||||
zeigeFehlerListe()
|
||||
var ul = el('ul', 'wkt-liste')
|
||||
c.leistungen.forEach(function (l) {
|
||||
var li = el('li')
|
||||
@@ -525,6 +582,7 @@
|
||||
function zeichnePerson () {
|
||||
var l = aktuelleLeistung()
|
||||
kopf(T('Bei wem möchten Sie den Termin?', 'Bei wem möchtest du den Termin?'))
|
||||
zeigeFehlerListe()
|
||||
var ul = el('ul', 'wkt-liste')
|
||||
var optionen = [{ id: 'egal', name: 'Egal', text: T('Wir weisen Ihnen eine freie Person zu.', 'Wir weisen dir eine freie Person zu.') }]
|
||||
kandidaten(l).forEach(function (r) { optionen.push({ id: r.id, name: r.name, text: '' }) })
|
||||
@@ -546,12 +604,11 @@
|
||||
zurueckZeile(S.vorLeistung ? null : 'leistung')
|
||||
}
|
||||
|
||||
function zurueckZeile (zielSchritt, weiter) {
|
||||
function zurueckZeile (zielSchritt) {
|
||||
if (!zielSchritt) return
|
||||
var nav = el('div', 'wkt-nav')
|
||||
if (zielSchritt) nav.appendChild(knopf('wkt-btn wkt-btn-sek', T('Zurück', 'Zurück'), function () { gehe(zielSchritt) }))
|
||||
if (weiter) nav.appendChild(weiter)
|
||||
if (nav.childNodes.length) add(nav)
|
||||
return nav
|
||||
nav.appendChild(knopf('wkt-btn wkt-btn-sek', 'Zurück', function () { gehe(zielSchritt) }))
|
||||
add(nav)
|
||||
}
|
||||
|
||||
function vorigerSchritt (von) {
|
||||
@@ -566,7 +623,7 @@
|
||||
var c = S.cfg
|
||||
var l = aktuelleLeistung()
|
||||
kopf(T('Wann passt es Ihnen?', 'Wann passt es dir?'))
|
||||
if (S.fehler.zeit || S.fehler.tag) add(el('p', 'wkt-fehlertext', S.fehler.zeit || S.fehler.tag))
|
||||
zeigeFehlerListe()
|
||||
add(el('p', 'wkt-hinweis', l.name + (dauerText(l.dauer) ? ' · ' + dauerText(l.dauer) : '')))
|
||||
|
||||
S.tageCache = {} // Tage immer frisch beim Betreten des Schritts (Leistung und Person koennen sich geaendert haben)
|
||||
@@ -643,6 +700,7 @@
|
||||
S.monat = m.monat
|
||||
monLabel.textContent = m.info.label
|
||||
var nr = monatsNummer(m.monat)
|
||||
var hatteFokus = document.activeElement
|
||||
zurueck.disabled = nr <= minNr
|
||||
vor.disabled = nr >= maxNr
|
||||
tbody.textContent = ''
|
||||
@@ -679,6 +737,12 @@
|
||||
spalte++
|
||||
}
|
||||
setzeFokusZelle(S.tag && S.tag.slice(0, 7) === m.monat ? S.tag : (erstFrei || m.monat + '-01'), false)
|
||||
// Ein Pfeilknopf, der gerade gesperrt wurde, darf den Fokus nicht verlieren: er geht an den anderen Pfeil, sonst ins Raster
|
||||
if ((hatteFokus === zurueck && zurueck.disabled) || (hatteFokus === vor && vor.disabled)) {
|
||||
var anderer = hatteFokus === zurueck ? vor : zurueck
|
||||
if (!anderer.disabled) anderer.focus()
|
||||
else tabelle.querySelector('td[tabindex="0"]').focus()
|
||||
}
|
||||
}
|
||||
|
||||
function zeigeMonat (monat, opt) {
|
||||
@@ -695,23 +759,25 @@
|
||||
return m
|
||||
}, function (err) {
|
||||
if (seq !== S.seq) return null
|
||||
behandleFehler(err, function () { zeichne(true) })
|
||||
fehlerAllgemein(err, function () { zeichne(true) })
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
function wechsleMonat (monat, tagNr, fokussieren) {
|
||||
// Zu einem anderen Monat (innerhalb der Grenzen). tag: Tagesnummer oder Funktion (Kalenderdaten des Zielmonats) => Tagesnummer;
|
||||
// sie wird auf 1 bis Monatslaenge begrenzt. Die Laenge des Nachbarmonats kennt erst die Antwort des Servers.
|
||||
function wechsleMonat (monat, tag, fokussieren) {
|
||||
var nr = monatsNummer(monat)
|
||||
if (nr < minNr || nr > maxNr) return
|
||||
var seq = S.seq
|
||||
holeTage(monat).then(function (m) {
|
||||
if (seq !== S.seq) return
|
||||
baueRaster(m)
|
||||
var t = Math.min(tagNr, m.info.anzahlTage)
|
||||
setzeFokusZelle(m.monat + '-' + pad2(t), fokussieren)
|
||||
var t = typeof tag === 'function' ? tag(m.info) : tag
|
||||
setzeFokusZelle(m.monat + '-' + pad2(Math.max(1, Math.min(m.info.anzahlTage, t))), fokussieren)
|
||||
ansage(m.info.label)
|
||||
}, function (err) {
|
||||
if (seq === S.seq) behandleFehler(err, function () { zeichne(true) })
|
||||
if (seq === S.seq) fehlerAllgemein(err, function () { zeichne(true) })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -719,8 +785,11 @@
|
||||
vor.addEventListener('click', function () { if (aktuell) wechsleMonat(aktuell.info.nachher, 1, false) })
|
||||
|
||||
function waehleTag (tag) {
|
||||
if (S.tag !== tag) {
|
||||
S.zeit = ''
|
||||
hakenZurueck()
|
||||
}
|
||||
S.tag = tag
|
||||
S.zeit = ''
|
||||
var zellen = tabelle.querySelectorAll('td[data-tag]')
|
||||
for (var i = 0; i < zellen.length; i++) zellen[i].setAttribute('aria-selected', zellen[i].getAttribute('data-tag') === tag ? 'true' : 'false')
|
||||
setzeFokusZelle(tag, false)
|
||||
@@ -751,6 +820,7 @@
|
||||
d.zeiten.forEach(function (z) {
|
||||
if (!istText(z)) return
|
||||
var b = knopf('wkt-zeit', z, function () {
|
||||
if (S.zeit !== z) hakenZurueck()
|
||||
S.zeit = z
|
||||
S.datumLang = d.datumLang
|
||||
S.vorzeitig = d.vorzeitigNoetig === true
|
||||
@@ -762,11 +832,12 @@
|
||||
zeitenBox.appendChild(liste)
|
||||
ansage(d.zeiten.length + (d.zeiten.length === 1 ? ' freie Zeit am ' : ' freie Zeiten am ') + d.datumLang)
|
||||
}).catch(function (err) {
|
||||
if (nr === S.zeitSeq) behandleFehler(err, function () { zeichne(true) })
|
||||
if (nr === S.zeitSeq) fehlerAllgemein(err, function () { zeichne(true) })
|
||||
})
|
||||
}
|
||||
|
||||
function tastatur (e) {
|
||||
if (e.altKey || e.ctrlKey || e.metaKey) return // Browser- und Systemkuerzel (Alt+Pfeil = zurueck u. a.) nicht abfangen
|
||||
var td = e.target && e.target.closest ? e.target.closest('td[data-tag]') : null
|
||||
if (!td || !aktuell) return
|
||||
var info = aktuell.info
|
||||
@@ -800,20 +871,9 @@
|
||||
if (ziel >= 1 && ziel <= info.anzahlTage) {
|
||||
setzeFokusZelle(aktuell.monat + '-' + pad2(ziel), true)
|
||||
} else if (ziel < 1) {
|
||||
// in den Vormonat: dessen Laenge kennt erst die Antwort des Servers
|
||||
var vm = info.vorher
|
||||
if (monatsNummer(vm) < minNr) return
|
||||
var seq = S.seq
|
||||
holeTage(vm).then(function (m) {
|
||||
if (seq !== S.seq) return
|
||||
baueRaster(m)
|
||||
setzeFokusZelle(m.monat + '-' + pad2(Math.max(1, m.info.anzahlTage + ziel)), true)
|
||||
ansage(m.info.label)
|
||||
}, function (err) { if (seq === S.seq) behandleFehler(err, function () { zeichne(true) }) })
|
||||
wechsleMonat(info.vorher, function (vi) { return vi.anzahlTage + ziel }, true)
|
||||
} else {
|
||||
var nm = info.nachher
|
||||
if (monatsNummer(nm) > maxNr) return
|
||||
wechsleMonat(nm, ziel - info.anzahlTage, true)
|
||||
wechsleMonat(info.nachher, ziel - info.anzahlTage, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -962,6 +1022,19 @@
|
||||
return uebrig
|
||||
}
|
||||
|
||||
// Meldungen ohne eigenes Feld (z. B. "_" oder ein Schluessel, den der Schritt nicht zeigt) in den Fehlerkasten: nie verschlucken
|
||||
function zeigeRest (box, texte) {
|
||||
texte.forEach(function (t) { box.appendChild(el('p', 'wkt-fehlertext', t)) })
|
||||
if (texte.length) ansage(texte.join(' '))
|
||||
}
|
||||
|
||||
// Meldungen des Servers an Schritten ohne eigene Felder (Leistung, Person, Termin)
|
||||
function zeigeFehlerListe () {
|
||||
var box = el('div', 'wkt-fehlerbox')
|
||||
zeigeRest(box, Object.keys(S.fehler).map(function (k) { return S.fehler[k] }))
|
||||
if (box.childNodes.length) add(box)
|
||||
}
|
||||
|
||||
// ---- Daten
|
||||
|
||||
function zeichneDaten () {
|
||||
@@ -1001,18 +1074,15 @@
|
||||
form.insertBefore(fehlerBox, form.firstChild)
|
||||
add(form)
|
||||
|
||||
var vorhanden = {}
|
||||
Object.keys(S.fehler).forEach(function (k) { if (refs[k]) vorhanden[k] = S.fehler[k] })
|
||||
if (Object.keys(vorhanden).length) zeigeFeldfehler(refs, vorhanden)
|
||||
if (Object.keys(S.fehler).length) zeigeRest(fehlerBox, zeigeFeldfehler(refs, S.fehler))
|
||||
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault()
|
||||
var f = pruefeDaten(g)
|
||||
if (Object.keys(f).length) {
|
||||
S.fehler = f
|
||||
var rest = zeigeFeldfehler(refs, f)
|
||||
fehlerBox.textContent = ''
|
||||
rest.forEach(function (t) { fehlerBox.appendChild(el('p', 'wkt-fehlertext', t)) })
|
||||
zeigeRest(fehlerBox, zeigeFeldfehler(refs, f))
|
||||
return
|
||||
}
|
||||
S.fehler = {}
|
||||
@@ -1067,6 +1137,7 @@
|
||||
|
||||
var dl = el('dl', 'wkt-zusammen')
|
||||
zeile(dl, 'Leistung', l.name + (dauerText(l.dauer) ? ' (' + dauerText(l.dauer) + ')' : ''))
|
||||
zeile(dl, 'Beschreibung', l.beschreibung)
|
||||
var kand = kandidaten(l)
|
||||
if (personSchritt(l) || kand.length === 1) {
|
||||
var istPlatz = kand.every(function (r) { return r.typ === 'platz' })
|
||||
@@ -1082,17 +1153,34 @@
|
||||
zeile(dl, 'E-Mail', dd.email.trim())
|
||||
add(dl)
|
||||
|
||||
if (c.storno && istText(c.storno.text)) {
|
||||
// Stornoregel im Klartext, daneben der Link auf die AGB des Betriebs (nur https, wenn hinterlegt)
|
||||
var agbUrl = httpsUrl(c.betrieb.agbUrl)
|
||||
if ((c.storno && istText(c.storno.text)) || agbUrl) {
|
||||
var sb = el('div', 'wkt-box')
|
||||
sb.appendChild(el('h3', '', T('Stornobedingungen', 'Stornobedingungen')))
|
||||
sb.appendChild(el('p', '', c.storno.text))
|
||||
if (c.storno && istText(c.storno.text)) {
|
||||
sb.appendChild(el('h3', '', 'Stornobedingungen'))
|
||||
sb.appendChild(el('p', '', c.storno.text))
|
||||
}
|
||||
if (agbUrl) {
|
||||
var pa = el('p', 'wkt-agb')
|
||||
var agb = el('a', '', 'AGB des Betriebs')
|
||||
agb.href = agbUrl
|
||||
agb.target = '_blank'
|
||||
agb.rel = 'noopener noreferrer'
|
||||
pa.appendChild(agb)
|
||||
sb.appendChild(pa)
|
||||
}
|
||||
add(sb)
|
||||
}
|
||||
|
||||
if (c.widerruf === true && istText(c.texte.widerrufsbelehrung)) {
|
||||
var det = el('details', 'wkt-belehrung')
|
||||
det.appendChild(el('summary', '', T('Widerrufsbelehrung und Muster-Widerrufsformular anzeigen', 'Widerrufsbelehrung und Muster-Widerrufsformular anzeigen')))
|
||||
det.appendChild(absaetze(c.texte.widerrufsbelehrung, 'wkt-belehrung-text'))
|
||||
det.appendChild(el('summary', '', 'Widerrufsbelehrung und Muster-Widerrufsformular anzeigen'))
|
||||
var text = absaetze(c.texte.widerrufsbelehrung, 'wkt-belehrung-text')
|
||||
text.tabIndex = 0 // der Text scrollt: auch mit der Tastatur erreichbar
|
||||
text.setAttribute('role', 'region')
|
||||
text.setAttribute('aria-label', 'Widerrufsbelehrung und Muster-Widerrufsformular')
|
||||
det.appendChild(text)
|
||||
add(det)
|
||||
}
|
||||
|
||||
@@ -1125,10 +1213,7 @@
|
||||
form.appendChild(nav)
|
||||
add(form)
|
||||
|
||||
var vorhanden = {}
|
||||
Object.keys(S.fehler).forEach(function (k) { if (refs[k]) vorhanden[k] = S.fehler[k] })
|
||||
if (Object.keys(vorhanden).length) zeigeFeldfehler(refs, vorhanden)
|
||||
if (S.fehler._) fehlerBox.appendChild(el('p', 'wkt-fehlertext', S.fehler._))
|
||||
if (Object.keys(S.fehler).length) zeigeRest(fehlerBox, zeigeFeldfehler(refs, S.fehler))
|
||||
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault()
|
||||
@@ -1185,11 +1270,12 @@
|
||||
function nachSendeFehler (err, senden) {
|
||||
if (err && err.code === 'neu_laden') {
|
||||
// Formular-Token abgelaufen oder ungueltig (R38): Konfiguration neu holen, dem Gast Bescheid geben
|
||||
lade(true).then(function () {
|
||||
gehe('pruefen', { fokus: false, meldung: T('Das Formular wurde erneuert. Bitte versuchen Sie es noch einmal.', 'Das Formular wurde erneuert. Bitte versuche es noch einmal.') })
|
||||
ladeNeu().then(function (weiter) {
|
||||
if (weiter) gehe('pruefen', { fokus: false, meldung: T('Das Formular wurde erneuert. Bitte versuchen Sie es noch einmal.', 'Das Formular wurde erneuert. Bitte versuche es noch einmal.') })
|
||||
}, function (e2) { fehlerAllgemein(e2, function () { zeichne(true) }) })
|
||||
return
|
||||
}
|
||||
mitMeldung429(err)
|
||||
if (err && !err.netz && err.fehler && Object.keys(err.fehler).length) {
|
||||
zeigeServerFehler(err.fehler)
|
||||
return
|
||||
@@ -1217,16 +1303,23 @@
|
||||
gehe(step, { fehler: fehler, fokus: true })
|
||||
}
|
||||
|
||||
function meldung429 (err) {
|
||||
return (err.fehler && err.fehler._) || T('Zu viele Anfragen. Bitte versuchen Sie es später noch einmal.', 'Zu viele Anfragen. Bitte versuche es später noch einmal.')
|
||||
}
|
||||
|
||||
// Fehler eines lesenden Abrufs (Konfiguration, Tage, Zeiten) oder Netzfehler: keine Wiederholung von selbst (429 wuerde sonst
|
||||
// in einer Schleife weiterzaehlen), nur Meldung und "Erneut versuchen".
|
||||
function fehlerAllgemein (err, wiederhole) {
|
||||
if (err && err.inaktiv) { zeigeAusweich(); return }
|
||||
if (err && err.status === 429) {
|
||||
gehe(S.step === 'ergebnis' ? 'pruefen' : S.step, { meldung: (err.fehler && err.fehler._) || T('Zu viele Anfragen. Bitte versuchen Sie es später noch einmal.', 'Zu viele Anfragen. Bitte versuche es später noch einmal.'), fokus: false })
|
||||
return
|
||||
}
|
||||
if (err && err.status === 429) { ausfall(wiederhole, meldung429(err)); return }
|
||||
ausfall(wiederhole)
|
||||
}
|
||||
|
||||
function behandleFehler (err, wiederhole) { fehlerAllgemein(err, wiederhole) }
|
||||
// 429 beim Absenden: die Meldung steht im Formular, die Ansicht bleibt wie sie ist
|
||||
function mitMeldung429 (err) {
|
||||
if (err && err.status === 429 && !(err.fehler && Object.keys(err.fehler).length)) err.fehler = { _: meldung429(err) }
|
||||
return err
|
||||
}
|
||||
|
||||
function zeichneErgebnis () {
|
||||
var e = S.ergebnis
|
||||
@@ -1267,7 +1360,7 @@
|
||||
var senden = el('button', 'wkt-btn', 'Widerruf bestätigen')
|
||||
senden.type = 'submit'
|
||||
var nav = el('div', 'wkt-nav')
|
||||
nav.appendChild(knopf('wkt-btn wkt-btn-sek', T('Zurück zur Terminbuchung', 'Zurück zur Terminbuchung'), function () { S.ansicht = 'buchung'; zeichne(true) }))
|
||||
nav.appendChild(zurueckKnopf())
|
||||
nav.appendChild(senden)
|
||||
form.appendChild(nav)
|
||||
add(form)
|
||||
@@ -1291,9 +1384,9 @@
|
||||
zeichne(true)
|
||||
}).catch(function (err) {
|
||||
S.sendet = false
|
||||
mitMeldung429(err)
|
||||
if (err && !err.netz && err.fehler && Object.keys(err.fehler).length) {
|
||||
var uebrig = zeigeFeldfehler(refs, err.fehler)
|
||||
uebrig.forEach(function (t) { fehlerBox.appendChild(el('p', 'wkt-fehlertext', t)) })
|
||||
zeigeRest(fehlerBox, zeigeFeldfehler(refs, err.fehler))
|
||||
senden.disabled = false
|
||||
return
|
||||
}
|
||||
@@ -1302,9 +1395,14 @@
|
||||
})
|
||||
}
|
||||
|
||||
// Aus der Widerrufsansicht zurueck zur Buchung (der Stand der Buchung bleibt)
|
||||
function zurueckKnopf () {
|
||||
return knopf('wkt-btn wkt-btn-sek', 'Zurück zur Terminbuchung', function () { S.ansicht = 'buchung'; zeichne(true) })
|
||||
}
|
||||
|
||||
function zurueckAnsicht () {
|
||||
var nav = el('div', 'wkt-nav')
|
||||
nav.appendChild(knopf('wkt-btn wkt-btn-sek', T('Zurück zur Terminbuchung', 'Zurück zur Terminbuchung'), function () { S.ansicht = 'buchung'; zeichne(true) }))
|
||||
nav.appendChild(zurueckKnopf())
|
||||
add(nav)
|
||||
}
|
||||
|
||||
@@ -1315,9 +1413,14 @@
|
||||
injectStyle()
|
||||
ausweich = document.createDocumentFragment()
|
||||
while (root.firstChild) ausweich.appendChild(root.firstChild)
|
||||
bauGeruest(root)
|
||||
starte(false)
|
||||
}).catch(function () { /* Fehler oder inaktiv: der Ausweichtext der Seite bleibt */ })
|
||||
try {
|
||||
bauGeruest(root)
|
||||
starte(false)
|
||||
} catch (e) {
|
||||
// Der Ausweichtext ist schon beiseite: scheitert der Aufbau, steht er wieder da (nie eine leere Stelle auf der Seite)
|
||||
zeigeAusweich()
|
||||
}
|
||||
}, function () { /* nicht erreichbar oder nicht aktiv: der Ausweichtext der Seite bleibt unberuehrt stehen */ })
|
||||
}
|
||||
|
||||
function schliesse () {
|
||||
|
||||
Reference in new issue
Block a user