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:
ClaudeandClaude Sonnet 5.5 committed 2026-10-03 08:17:44 +00:00
1 parent 5fef7e47f0
commit 4465ac7b94
9 files changed
+501 -88

No files matched your search

+182 -79
View File
@@ -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 () {