Buchungskalender: Abschluss-Fixwelle B zusammenführen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
1c6b6bf8d6
10 files changed
+257
-71
No files matched your search
@@ -8,6 +8,19 @@ const CODES = {
|
||||
stripe_nicht_aktiv: 'Online-Zahlung geht erst, wenn dein Stripe-Konto freigeschaltet ist.',
|
||||
erstattung_fehlgeschlagen: 'Die Stornierung wurde durchgeführt, aber die Erstattung ist fehlgeschlagen. Bitte erstatte den Betrag im Stripe-Dashboard oder versuch es später noch einmal.',
|
||||
frist_abgelaufen: 'Die Frist für diese Anfrage ist abgelaufen.',
|
||||
preise_unvollstaendig: 'Die Preise sind noch nicht vollständig. Trage im Reiter „Unterkünfte“ unter „Preise“ alle Pflichtpreise ein und speichere sie.',
|
||||
einstellungen_unvollstaendig: 'Deine Einstellungen sind noch nicht vollständig. Ergänze im Reiter „Einstellungen“ Rechnungsangaben und Zustimmung und speichere sie.',
|
||||
status_ungueltig: 'Das geht beim aktuellen Stand der Anfrage nicht mehr. Lade die Seite neu und prüfe den Status.',
|
||||
limit_erreicht: 'Du hast die Höchstzahl erreicht. Entferne zuerst einen vorhandenen Eintrag, dann kannst du einen neuen anlegen.',
|
||||
kein_konto: 'Es ist noch kein Stripe-Konto verbunden. Richte zuerst die Online-Zahlung im Reiter „Shop“ ein.',
|
||||
nicht_aktiv: 'Der Buchungskalender ist bei dir gerade nicht aktiv. Schalte ihn unter „Funktionen“ ein oder wende dich an uns.',
|
||||
praefix_gesperrt: 'Das Rechnungspräfix kann nach der ersten Rechnung nicht mehr geändert werden. Lass das Feld unverändert.',
|
||||
sperre_aus_ical: 'Diese Sperre stammt aus einem Kalenderimport und lässt sich hier nicht ändern. Ändere sie im Ursprungskalender oder entferne den Import.',
|
||||
online_aus: 'Online-Zahlung ist bei dir ausgeschaltet. Schalte sie in den Einstellungen ein oder wähle „Vor Ort bezahlen“.',
|
||||
zahlung_fehlgeschlagen: 'Die Zahlung konnte nicht gestartet werden. Versuche es in ein paar Minuten noch einmal.',
|
||||
hat_anfragen: 'Zu dieser Unterkunft gibt es schon Anfragen, deshalb kannst du sie nicht löschen. Schalte sie stattdessen auf inaktiv.',
|
||||
konto_vorhanden: 'Es ist schon ein anderes Stripe-Konto hinterlegt. Wende dich an uns, wenn du es wechseln möchtest.',
|
||||
zu_viele_personen: 'Das sind mehr Personen, als die Unterkunft aufnimmt. Prüfe die Personenzahl.',
|
||||
}
|
||||
|
||||
/** => { message, fehler: { feld: text } } (Feldfehler aus body.felder, sonst ein Text unter „_“). */
|
||||
|
||||
@@ -110,6 +110,15 @@ export async function zeichnePreise(ctx, unterkunft) {
|
||||
allgemeinerFehler(),
|
||||
h('div', { class: 'bk-aktionen' }, ctx.nurLesen ? null : h('button', { type: 'button', class: 'kb-btn', 'data-aktion': 'preise-speichern', onclick: speichern }, 'Preise speichern')))
|
||||
if (ctx.nurLesen) for (const el of form.querySelectorAll('input, select, textarea, button')) el.disabled = true
|
||||
// Ein Feldfehler (z. B. Platzhalter in den Stornobedingungen) verschwindet, sobald im Feld getippt wird
|
||||
for (const el of form.querySelectorAll('[data-feld]')) {
|
||||
el.addEventListener('input', () => {
|
||||
const name = el.getAttribute('data-feld')
|
||||
const ziel = form.querySelector(`[data-fehler="${CSS.escape(name)}"]`)
|
||||
if (ziel) ziel.textContent = ''
|
||||
el.removeAttribute('aria-invalid')
|
||||
})
|
||||
}
|
||||
|
||||
async function speichern(ev) {
|
||||
const knopf = ev.currentTarget
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
}
|
||||
.bk-kalenderkopf { flex-wrap: wrap; }
|
||||
.bk-monat { min-width: 9em; text-align: center; }
|
||||
.bk-raster { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; max-width: 560px; margin: 10px 0; }
|
||||
.bk-raster { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; width: 100%; margin: 10px 0; }
|
||||
.bk-wochentag { text-align: center; font-size: 12px; color: var(--kb-muted); }
|
||||
.bk-tag { aspect-ratio: 1; min-height: 40px; border-radius: 8px; border: 1px solid var(--kb-border); background: rgba(255, 255, 255, 0.03); color: inherit; font: inherit; cursor: pointer; }
|
||||
.bk-tag { aspect-ratio: 1.5; min-height: 40px; border-radius: 8px; border: 1px solid var(--kb-border); background: rgba(255, 255, 255, 0.03); color: inherit; font: inherit; cursor: pointer; }
|
||||
.bk-tag.bk-ausserhalb { opacity: 0.45; }
|
||||
.bk-tag.bk-heute { border-color: var(--kb-purple-line); font-weight: 700; }
|
||||
.bk-tag.bk-start { outline: 2px solid #d4b4ff; }
|
||||
@@ -74,4 +74,4 @@
|
||||
}
|
||||
#view-buchungen .bk-posten select option, #view-buchungen .bk-filter select option { color: #111; }
|
||||
#view-buchungen .bk-posten input, #view-buchungen .bk-posten select { width: 100%; }
|
||||
#view-buchungen input[readonly] { box-sizing: border-box; width: 100%; padding: 9px 10px; border-radius: 10px; font: inherit; font-size: 14px; border: 1px solid var(--kb-border-strong); background: rgba(255, 255, 255, 0.05); color: inherit; }
|
||||
#view-buchungen input[readonly] { box-sizing: border-box; width: 100%; padding: 9px 10px; border-radius: 10px; font: inherit; font-size: 15px; border: 1px solid var(--kb-border-strong); background: rgba(255, 255, 255, 0.05); color: inherit; }
|
||||
+71
-53
@@ -79,63 +79,63 @@
|
||||
|
||||
/* ------------------------------------------------------------------- Gestaltung --- */
|
||||
var CSS = [
|
||||
'@layer wku{',
|
||||
'.wku-root{--wku-i-akzent:var(--wku-akzent,var(--wku-vorgabe-akzent,#0f766e));--wku-i-akzent-text:var(--wku-akzent-text,var(--wku-vorgabe-akzent-text,#fff));',
|
||||
'--wku-i-bg:var(--wku-hintergrund,#fff);--wku-i-text:var(--wku-text,#1f2937);--wku-i-rand:var(--wku-rand,#b6bcc6);--wku-i-radius:var(--wku-radius,12px);',
|
||||
'--wku-i-weich:#f3f4f6;--wku-i-bereich:#e5eceb;--wku-i-fehler:#b91c1c;--wku-i-schrift:var(--wku-schrift,inherit);--wku-i-titel:var(--wku-schrift-titel,var(--wku-schrift,inherit))}',
|
||||
'@supports (color:color-mix(in srgb,red,blue)){.wku-root{--wku-i-weich:color-mix(in srgb,var(--wku-i-text) 6%,var(--wku-i-bg));--wku-i-bereich:color-mix(in srgb,var(--wku-i-akzent) 18%,var(--wku-i-bg))}}',
|
||||
'.wku-root{container:wku/inline-size;display:block;position:relative;box-sizing:border-box;color:var(--wku-i-text);font-family:var(--wku-i-schrift);font-size:16px;line-height:1.5;text-align:left}',
|
||||
'.wku-root{container:wku/inline-size;display:block;position:relative;box-sizing:border-box;color:var(--wku-i-text);font-family:var(--wku-i-schrift);font-size:16px;line-height:1.5;text-align:left;width:100%;max-width:100%}',
|
||||
'.wku-root :is(button,input,select,textarea,label,h1,h2,h3,h4,h5,h6,p,a,fieldset,legend){all:revert;box-sizing:border-box}',
|
||||
'.wku-root [hidden]{display:none}',
|
||||
'.wku-root *,.wku-root *::before,.wku-root *::after{box-sizing:border-box}',
|
||||
'.wku-vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}',
|
||||
'.wku-haupt{display:grid;grid-template-columns:minmax(0,1fr);gap:24px}',
|
||||
'.wku-karte{background:var(--wku-i-bg);border:1px solid var(--wku-i-rand);border-radius:var(--wku-i-radius);padding:20px}',
|
||||
'.wku-titel{font-family:var(--wku-i-titel);font-size:1.2rem;line-height:1.25;font-weight:700;margin:0 0 .75rem}',
|
||||
'.wku-ausweich{margin:0}',
|
||||
'.wku-monate{display:grid;grid-template-columns:minmax(0,1fr);gap:20px}',
|
||||
'.wku-zwei{display:none}',
|
||||
'.wku-monat-kopf{display:flex;align-items:center;justify-content:space-between;gap:.5rem;min-height:44px;margin-bottom:.35rem}',
|
||||
'.wku-monat-name{font-family:var(--wku-i-titel);font-size:1.05rem;font-weight:700;margin:0;flex:1;text-align:center}',
|
||||
'.wku-pfeil{font:inherit;font-size:1.4rem;line-height:1;min-width:44px;min-height:44px;border-radius:50%;border:1px solid var(--wku-i-rand);background:var(--wku-i-bg);color:inherit;cursor:pointer;padding:0}',
|
||||
'.wku-pfeil:hover{background:var(--wku-i-weich)}.wku-pfeil[disabled]{opacity:.3;cursor:default}.wku-pfeil-leer{display:inline-block;width:44px;height:44px}',
|
||||
'.wku-raster{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));row-gap:2px}',
|
||||
'.wku-wt{font-size:.78rem;font-weight:600;text-align:center;padding:.25rem 0;opacity:.7}',
|
||||
'.wku-leer{min-height:44px}',
|
||||
'.wku-tag{font:inherit;position:relative;min-height:44px;padding:0;margin:0;border:0;border-radius:0;background:transparent;color:inherit;text-align:center;cursor:pointer;overflow:hidden}',
|
||||
'.wku-tag>span{position:relative;display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:50%;border:1px solid transparent;z-index:1}',
|
||||
'.wku-tag:hover>span{background:var(--wku-i-weich)}',
|
||||
'.wku-heute>span{border-color:var(--wku-i-text)}',
|
||||
'.wku-vergangen,.wku-zu{opacity:.35;cursor:default}.wku-vergangen:hover>span,.wku-zu:hover>span{background:transparent}',
|
||||
'.wku-belegt{color:#6b7280;cursor:default;background:linear-gradient(to top right,transparent calc(50% - 1px),#9ca3af calc(50% - 1px),#9ca3af calc(50% + 1px),transparent calc(50% + 1px)) center/60% 60% no-repeat}',
|
||||
'.wku-belegt:hover>span{background:transparent}',
|
||||
'.wku-bereich{background:var(--wku-i-bereich)}',
|
||||
'.wku-an{background:linear-gradient(to right,transparent 50%,var(--wku-i-bereich) 50%)}',
|
||||
'.wku-ab{background:linear-gradient(to left,transparent 50%,var(--wku-i-bereich) 50%)}',
|
||||
'.wku-an.wku-ab{background:transparent}',
|
||||
'.wku-an>span,.wku-ab>span,.wku-an:hover>span,.wku-ab:hover>span{background:var(--wku-i-akzent);color:var(--wku-i-akzent-text);border-color:var(--wku-i-akzent)}',
|
||||
'.wku-root .wku-vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}',
|
||||
'.wku-root .wku-haupt{display:grid;grid-template-columns:minmax(0,1fr);gap:24px}',
|
||||
'.wku-root .wku-karte{background:var(--wku-i-bg);border:1px solid var(--wku-i-rand);border-radius:var(--wku-i-radius);padding:20px}',
|
||||
'.wku-root .wku-titel{font-family:var(--wku-i-titel);font-size:1.2rem;line-height:1.25;font-weight:700;margin:0 0 .75rem}',
|
||||
'.wku-root.wku-ausweich{margin:0}',
|
||||
'.wku-root .wku-monate{display:grid;grid-template-columns:minmax(0,1fr);gap:20px}',
|
||||
'.wku-root .wku-zwei{display:none}',
|
||||
'.wku-root .wku-monat-kopf{display:flex;align-items:center;justify-content:space-between;gap:.5rem;min-height:44px;margin-bottom:.35rem}',
|
||||
'.wku-root .wku-monat-name{font-family:var(--wku-i-titel);font-size:1.05rem;font-weight:700;margin:0;flex:1;text-align:center}',
|
||||
'.wku-root .wku-pfeil{font:inherit;font-size:1.4rem;line-height:1;min-width:44px;min-height:44px;border-radius:50%;border:1px solid var(--wku-i-rand);background:var(--wku-i-bg);color:inherit;cursor:pointer;padding:0}',
|
||||
'.wku-root .wku-pfeil:hover{background:var(--wku-i-weich)}.wku-root .wku-pfeil[disabled]{opacity:.3;cursor:default}.wku-root .wku-pfeil-leer{display:inline-block;width:44px;height:44px}',
|
||||
'.wku-root .wku-raster{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));row-gap:2px}',
|
||||
'.wku-root .wku-wt{font-size:.78rem;font-weight:600;text-align:center;padding:.25rem 0;opacity:.7}',
|
||||
'.wku-root .wku-leer{min-height:44px}',
|
||||
'.wku-root .wku-tag{font:inherit;position:relative;min-height:44px;padding:0;margin:0;border:0;border-radius:0;background:transparent;color:inherit;text-align:center;cursor:pointer;overflow:hidden}',
|
||||
'.wku-root .wku-tag>span{position:relative;display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:50%;border:1px solid transparent;z-index:1}',
|
||||
'.wku-root .wku-tag:hover>span{background:var(--wku-i-weich)}',
|
||||
'.wku-root .wku-heute>span{border-color:var(--wku-i-text)}',
|
||||
'.wku-root .wku-vergangen,.wku-root .wku-zu{opacity:.35;cursor:default}.wku-root .wku-vergangen:hover>span,.wku-root .wku-zu:hover>span{background:transparent}',
|
||||
'.wku-root .wku-belegt{color:#6b7280;cursor:default;background:linear-gradient(to top right,transparent calc(50% - 1px),#9ca3af calc(50% - 1px),#9ca3af calc(50% + 1px),transparent calc(50% + 1px)) center/60% 60% no-repeat}',
|
||||
'.wku-root .wku-belegt:hover>span{background:transparent}',
|
||||
'.wku-root .wku-bereich{background:var(--wku-i-bereich)}',
|
||||
'.wku-root .wku-an{background:linear-gradient(to right,transparent 50%,var(--wku-i-bereich) 50%)}',
|
||||
'.wku-root .wku-ab{background:linear-gradient(to left,transparent 50%,var(--wku-i-bereich) 50%)}',
|
||||
'.wku-root .wku-an.wku-ab{background:transparent}',
|
||||
'.wku-root .wku-an>span,.wku-root .wku-ab>span,.wku-root .wku-an:hover>span,.wku-root .wku-ab:hover>span{background:var(--wku-i-akzent);color:var(--wku-i-akzent-text);border-color:var(--wku-i-akzent)}',
|
||||
'.wku-root :focus-visible{outline:3px solid var(--wku-i-text);outline-offset:2px}',
|
||||
'.wku-legende{display:flex;flex-wrap:wrap;gap:.25rem 1rem;margin:.75rem 0 0;font-size:.8rem;opacity:.8}',
|
||||
'.wku-zus{margin:1rem 0 0;font-weight:700;min-height:1.5rem}',
|
||||
'.wku-hinweis{margin:.25rem 0 0;font-size:.9rem;color:var(--wku-i-fehler);font-weight:600;min-height:1.4rem}',
|
||||
'.wku-form{margin:0}',
|
||||
'.wku-feld{margin:0 0 .9rem}',
|
||||
'.wku-label{display:block;font-weight:600;font-size:.92rem;margin-bottom:.25rem}',
|
||||
'.wku-stern{color:var(--wku-i-fehler)}',
|
||||
'.wku-eingabe{display:block;width:100%;min-height:44px;font:inherit;color:inherit;background:var(--wku-i-bg);border:1px solid var(--wku-i-rand);border-radius:calc(var(--wku-i-radius) * .7);padding:.5rem .7rem}',
|
||||
'textarea.wku-eingabe{min-height:5.5rem;resize:vertical}',
|
||||
'.wku-eingabe[aria-invalid="true"]{border-color:var(--wku-i-fehler);border-width:2px}',
|
||||
'.wku-feldfehler{color:var(--wku-i-fehler);font-weight:600;font-size:.88rem;margin:.25rem 0 0}',
|
||||
'.wku-reihe{display:grid;grid-template-columns:minmax(0,1fr);gap:0 12px}',
|
||||
'.wku-fehler{margin:0 0 .75rem;padding:.6rem .8rem;border-left:4px solid var(--wku-i-fehler);background:var(--wku-i-weich);font-weight:600}',
|
||||
'.wku-datenschutz{font-size:.85rem;margin:0 0 1rem;opacity:.9}.wku-datenschutz a{color:inherit}',
|
||||
'.wku-btn{font:inherit;font-weight:700;min-height:48px;padding:.6rem 1.5rem;border-radius:var(--wku-i-radius);border:2px solid var(--wku-i-akzent);background:var(--wku-i-akzent);color:var(--wku-i-akzent-text);cursor:pointer;line-height:1.25;width:100%}',
|
||||
'.wku-btn:hover{filter:brightness(1.08)}.wku-btn[disabled]{opacity:.6;cursor:default}',
|
||||
'.wku-danke{margin:0;font-size:1.05rem;font-weight:600}',
|
||||
'.wku-hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}',
|
||||
'@container wku (min-width:520px){.wku-reihe{grid-template-columns:1fr 1fr}}',
|
||||
'@container wku (min-width:760px){.wku-haupt{grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);align-items:start}}',
|
||||
'@container wku (min-width:1100px){.wku-zwei{display:block}.wku-monate{grid-template-columns:1fr 1fr}.wku-weiter-ein{display:none}}',
|
||||
'@media (prefers-reduced-motion:no-preference){.wku-btn,.wku-tag>span{transition:background-color .15s}}',
|
||||
'}'
|
||||
'.wku-root .wku-legende{display:flex;flex-wrap:wrap;gap:.25rem 1rem;margin:.75rem 0 0;font-size:.8rem;opacity:.8}',
|
||||
'.wku-root .wku-zus{margin:1rem 0 0;font-weight:700;min-height:1.5rem}',
|
||||
'.wku-root .wku-hinweis{margin:.25rem 0 0;font-size:.9rem;color:var(--wku-i-fehler);font-weight:600;min-height:1.4rem}',
|
||||
'.wku-root .wku-form{margin:0}',
|
||||
'.wku-root .wku-feld{margin:0 0 .9rem}',
|
||||
'.wku-root .wku-label{display:block;font-weight:600;font-size:.92rem;margin-bottom:.25rem}',
|
||||
'.wku-root .wku-stern{color:var(--wku-i-fehler)}',
|
||||
'.wku-root .wku-eingabe{display:block;width:100%;min-height:44px;font:inherit;color:inherit;background:var(--wku-i-bg);border:1px solid var(--wku-i-rand);border-radius:calc(var(--wku-i-radius) * .7);padding:.5rem .7rem}',
|
||||
'.wku-root textarea.wku-eingabe{min-height:5.5rem;resize:vertical}',
|
||||
'.wku-root .wku-eingabe[aria-invalid="true"]{border-color:var(--wku-i-fehler);border-width:2px}',
|
||||
'.wku-root .wku-feldfehler{color:var(--wku-i-fehler);font-weight:600;font-size:.88rem;margin:.25rem 0 0}',
|
||||
'.wku-root .wku-reihe{display:grid;grid-template-columns:minmax(0,1fr);gap:0 12px}',
|
||||
'.wku-root .wku-fehler{margin:0 0 .75rem;padding:.6rem .8rem;border-left:4px solid var(--wku-i-fehler);background:var(--wku-i-weich);font-weight:600}',
|
||||
'.wku-root .wku-datenschutz{font-size:.85rem;margin:0 0 1rem;opacity:.9}.wku-root .wku-datenschutz a{color:inherit}',
|
||||
'.wku-root .wku-btn{font:inherit;font-weight:700;min-height:48px;padding:.6rem 1.5rem;border-radius:var(--wku-i-radius);border:2px solid var(--wku-i-akzent);background:var(--wku-i-akzent);color:var(--wku-i-akzent-text);cursor:pointer;line-height:1.25;width:100%}',
|
||||
'.wku-root .wku-btn:hover{filter:brightness(1.08)}.wku-root .wku-btn[disabled]{opacity:.6;cursor:default}',
|
||||
'.wku-root .wku-danke{margin:0;font-size:1.05rem;font-weight:600}',
|
||||
'.wku-root .wku-hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}',
|
||||
'@container wku (min-width:520px){.wku-root .wku-reihe{grid-template-columns:1fr 1fr}}',
|
||||
'@container wku (min-width:760px){.wku-root .wku-haupt{grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);align-items:start}}',
|
||||
'@container wku (min-width:1100px){.wku-root .wku-zwei{display:block}.wku-root .wku-monate{grid-template-columns:1fr 1fr}.wku-root .wku-weiter-ein{display:none}}',
|
||||
'@media (prefers-reduced-motion:no-preference){.wku-root .wku-btn,.wku-root .wku-tag>span{transition:background-color .15s}}'
|
||||
].join('')
|
||||
|
||||
function ensureStyle() {
|
||||
@@ -411,7 +411,7 @@
|
||||
var jetzt = new Date()
|
||||
var von = jetzt.getUTCFullYear() + '-' + pad(jetzt.getUTCMonth() + 1) + '-' + pad(jetzt.getUTCDate())
|
||||
von = plusTage(von, -1)
|
||||
var bis = plusTage(von, 540)
|
||||
var bis = plusTage(von, 600)
|
||||
return holen(basis + '/belegung?unterkunft=' + encodeURIComponent(unterk.id) + '&von=' + von + '&bis=' + bis, { headers: { Accept: 'application/json' } }).then(function (r) {
|
||||
if (nr !== ladeNr) return
|
||||
if (!r.ok || !r.body.heute || !r.body.bisMax) throw new Error('belegung')
|
||||
@@ -459,8 +459,26 @@
|
||||
}
|
||||
var b = r.body || {}
|
||||
var gezeigt = false
|
||||
if (b.felder) Object.keys(b.felder).forEach(function (k) { if (Object.prototype.hasOwnProperty.call(b.felder, k) && feldFehler(k, String(b.felder[k]))) gezeigt = true })
|
||||
var uebrig = []
|
||||
if (b.felder) Object.keys(b.felder).forEach(function (k) {
|
||||
if (!Object.prototype.hasOwnProperty.call(b.felder, k)) return
|
||||
if (feldFehler(k, String(b.felder[k]))) gezeigt = true
|
||||
else if (k === 'zeitraum' || k === 'personen') uebrig.push(String(b.felder[k]))
|
||||
})
|
||||
if (uebrig.length) { zeigeFehler(uebrig.join(' ')); gezeigt = true }
|
||||
var fehlerFokus = form.querySelector('[aria-invalid="true"]')
|
||||
if (fehlerFokus && fehlerFokus.focus) fehlerFokus.focus()
|
||||
if (gezeigt) return
|
||||
if (b.fehler === 'neu_laden') {
|
||||
zeigeFehler('Das Formular war zu lange geöffnet. Bitte senden Sie Ihre Anfrage noch einmal ab.')
|
||||
holen(basis, { headers: { Accept: 'application/json' } }).then(function (c) { if (c.ok && c.body.formToken) token = c.body.formToken }, function () {})
|
||||
return
|
||||
}
|
||||
if (b.fehler === 'mindestnaechte') {
|
||||
var mn = parseInt((b.felder && b.felder.mindestNaechte) || unterk.mindestNaechte, 10) || 1
|
||||
zeigeFehler('Mindestaufenthalt: ' + (mn === 1 ? '1 Nacht' : mn + ' Nächte') + '. Bitte wählen Sie einen längeren Zeitraum.')
|
||||
return
|
||||
}
|
||||
if (b.fehler === 'belegt' || r.status === 409) { zeigeFehler('Dieser Zeitraum ist leider nicht mehr frei. Bitte wählen Sie einen anderen.'); laden().then(null, function () {}) }
|
||||
else if (r.status === 429) zeigeFehler('Zu viele Anfragen. Bitte versuchen Sie es später noch einmal.')
|
||||
else {
|
||||
|
||||
@@ -14,6 +14,8 @@ body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;b
|
||||
.uk-feld input{width:100%;padding:10px 12px;border:1px solid #cbd5e1;border-radius:8px;font:inherit;background:#fff}
|
||||
.uk-feld input:focus-visible,.uk-knopf:focus-visible,.uk-zahlart input:focus-visible,.uk-link:focus-visible{outline:3px solid var(--uk-akzent);outline-offset:2px}
|
||||
.uk-zeile2{display:grid;grid-template-columns:1fr 2fr;gap:12px}
|
||||
.uk-zahlart-gruppe{border:0;margin:0 0 12px;padding:0;min-width:0}.uk-zahlart-gruppe legend{font-size:1.1rem;font-weight:700;padding:0;margin:0 0 8px}
|
||||
.uk-feld input[aria-invalid="true"]{border-color:#b91c1c;border-width:2px}
|
||||
.uk-zahlart{display:flex;gap:10px;align-items:flex-start;margin:0 0 8px}
|
||||
.uk-zahlart input{margin-top:5px}
|
||||
.uk-pflicht{margin:20px 0 16px;padding:14px 16px;border:1px solid #e5e7eb;border-radius:8px;background:#f9fafb;font-size:.92rem}
|
||||
|
||||
@@ -26,12 +26,16 @@
|
||||
ungueltig: 'Dieser Link ist ungültig.',
|
||||
abgelaufen: 'Die Reservierung ist abgelaufen. Bitte fragen Sie erneut an.',
|
||||
abbruch: 'Zahlung abgebrochen – Sie können jetzt oder vor Ort bezahlen.',
|
||||
widerruf: 'Ein Widerrufsrecht besteht nicht (§ 312g Abs. 2 Nr. 9 BGB).',
|
||||
widerruf: 'Ein Widerrufsrecht besteht nicht (§ 312g Abs. 2 Satz 1 Nr. 9 BGB).',
|
||||
kleinunternehmer: 'Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.',
|
||||
laden: 'Die Buchung konnte nicht geladen werden. Bitte versuchen Sie es später erneut.',
|
||||
senden: 'Die Buchung konnte nicht abgeschlossen werden. Bitte versuchen Sie es erneut.',
|
||||
belegt: 'Der Zeitraum ist leider inzwischen belegt. Bitte fragen Sie einen anderen Zeitraum an.',
|
||||
knopf: 'Zahlungspflichtig buchen',
|
||||
onlineAus: 'Online-Zahlung ist gerade nicht möglich – bitte wählen Sie „Vor Ort bezahlen“.',
|
||||
nichtMoeglich: 'Die Buchung ist gerade nicht möglich. Bitte wenden Sie sich direkt an den Anbieter.',
|
||||
standGeaendert: 'Der Stand Ihrer Buchung hat sich geändert.',
|
||||
zahlungStart: 'Die Zahlung konnte nicht gestartet werden. Bitte versuchen Sie es erneut.',
|
||||
}
|
||||
|
||||
function h(tag, attrs, kinder) {
|
||||
@@ -69,9 +73,13 @@
|
||||
})
|
||||
}
|
||||
|
||||
function httpsUrl(u) { return typeof u === 'string' && /^https:\/\//i.test(u) ? u : '' }
|
||||
var meldungNachLaden = ''
|
||||
function zeige(karte, hinweis) {
|
||||
app.textContent = ''
|
||||
if (hinweis) app.appendChild(p(hinweis, 'uk-hinweis'))
|
||||
var text = [hinweis, meldungNachLaden].filter(Boolean).join(' ')
|
||||
meldungNachLaden = ''
|
||||
if (text) app.appendChild(h('p', { text: text, class: 'uk-hinweis', role: 'status' }))
|
||||
app.appendChild(karte)
|
||||
}
|
||||
function nurText(titel, text) {
|
||||
@@ -140,7 +148,7 @@
|
||||
var input = h('input', { type: 'text', name: name, autocomplete: (extra && extra.ac) || 'off', maxlength: 120 })
|
||||
input.value = wert || ''
|
||||
felder[name] = input
|
||||
var fehler = h('span', { class: 'uk-feldfehler', 'data-fehler': name, hidden: true })
|
||||
var fehler = h('span', { class: 'uk-feldfehler', 'data-fehler': name, id: 'uk-fehler-' + name, hidden: true })
|
||||
return { wurzel: h('label', { class: 'uk-feld' }, [h('span', { class: 'uk-bez', text: bez }), input, fehler]), fehler: fehler }
|
||||
}
|
||||
var fName = feld('name', 'Name', ra.name || g.name, { ac: 'name' })
|
||||
@@ -165,8 +173,7 @@
|
||||
fName.wurzel, fFirma.wurzel, fStrasse.wurzel,
|
||||
h('div', { class: 'uk-zeile2' }, [fPlz.wurzel, fOrt.wurzel]),
|
||||
fLand.wurzel,
|
||||
h('h2', { text: 'Zahlart' }),
|
||||
h('div', { 'data-tag': 'zahlart' }, radios),
|
||||
h('fieldset', { class: 'uk-zahlart-gruppe', 'data-tag': 'zahlart' }, [h('legend', { text: 'Zahlart' })].concat(radios)),
|
||||
pflichtangaben(d, gewaehlt),
|
||||
knopf,
|
||||
meldung,
|
||||
@@ -180,10 +187,20 @@
|
||||
function waehl() { var r = radios.map(function (l) { return l.querySelector('input') }).find(function (i) { return i.checked }); return r ? r.value : gewaehlt }
|
||||
var sendet = false
|
||||
function fehlerAnzeigen(felderFehler, text) {
|
||||
Object.keys(alle).forEach(function (k) { alle[k].fehler.hidden = true; alle[k].fehler.textContent = '' })
|
||||
Object.keys(felderFehler || {}).forEach(function (k) { if (alle[k]) { alle[k].fehler.textContent = String(felderFehler[k]); alle[k].fehler.hidden = false } })
|
||||
Object.keys(alle).forEach(function (k) { markiere(k, '') })
|
||||
var erstes = null
|
||||
Object.keys(alle).forEach(function (k) {
|
||||
if (felderFehler && Object.prototype.hasOwnProperty.call(felderFehler, k)) { markiere(k, String(felderFehler[k])); if (!erstes) erstes = felder[k] }
|
||||
})
|
||||
meldung.textContent = text || ''
|
||||
meldung.hidden = !text
|
||||
if (erstes && erstes.focus) erstes.focus()
|
||||
}
|
||||
function markiere(k, text) {
|
||||
alle[k].fehler.textContent = text
|
||||
alle[k].fehler.hidden = !text
|
||||
if (text) { felder[k].setAttribute('aria-invalid', 'true'); felder[k].setAttribute('aria-describedby', 'uk-fehler-' + k) }
|
||||
else { felder[k].removeAttribute('aria-invalid'); felder[k].removeAttribute('aria-describedby') }
|
||||
}
|
||||
form.addEventListener('submit', function (ev) {
|
||||
ev.preventDefault()
|
||||
@@ -204,14 +221,24 @@
|
||||
var zahlart = waehl()
|
||||
api('', 'POST', { rechnungsadresse: val, zahlart: zahlart }).then(function (r) {
|
||||
if (r.ok && r.body.status === 'gebucht') {
|
||||
if (zahlart === 'online' && r.body.zahlungUrl) { window.location.assign(r.body.zahlungUrl); return }
|
||||
if (zahlart === 'online' && httpsUrl(r.body.zahlungUrl)) { window.location.assign(r.body.zahlungUrl); return }
|
||||
bestaetigt(Object.assign({}, d, { status: 'gebucht', zahlart: zahlart, bezahlt: false, rechnungNummer: r.body.rechnungNummer }))
|
||||
return
|
||||
}
|
||||
if (r.status === 404) return nurText('Buchung', TXT.ungueltig)
|
||||
if (r.status === 410 || r.body.fehler === 'frist_abgelaufen' || r.body.fehler === 'abgelaufen') return nurText('Buchung', TXT.abgelaufen)
|
||||
if (r.body.fehler === 'zeitraum_belegt') return nurText('Buchung', TXT.belegt)
|
||||
if (r.status === 409 || r.ok) { laden(); return }
|
||||
var code = r.body.fehler
|
||||
if (r.status === 409 && (code === 'online_aus' || code === 'einstellungen_unvollstaendig' || code === 'zustimmung_fehlt')) {
|
||||
sendet = false
|
||||
knopf.removeAttribute('disabled')
|
||||
if (code === 'online_aus') {
|
||||
var vor = radios.map(function (l) { return l.querySelector('input') }).find(function (i) { return i.value === 'vorort' })
|
||||
if (vor) vor.checked = true
|
||||
}
|
||||
return fehlerAnzeigen({}, code === 'online_aus' ? TXT.onlineAus : TXT.nichtMoeglich)
|
||||
}
|
||||
if (r.status === 409 || r.ok) { meldungNachLaden = TXT.standGeaendert; laden(); return }
|
||||
sendet = false
|
||||
knopf.removeAttribute('disabled')
|
||||
fehlerAnzeigen(r.body.felder, r.status === 400 && r.body.felder ? 'Bitte prüfen Sie Ihre Angaben.' : TXT.senden)
|
||||
@@ -224,7 +251,7 @@
|
||||
// Fehler verschwinden, sobald der Gast das Feld aendert; die allgemeine Meldung, wenn kein Feldfehler mehr steht
|
||||
Object.keys(alle).forEach(function (k) {
|
||||
felder[k].addEventListener('input', function () {
|
||||
alle[k].fehler.hidden = true; alle[k].fehler.textContent = ''
|
||||
markiere(k, '')
|
||||
if (!Object.keys(alle).some(function (n) { return !alle[n].fehler.hidden })) { meldung.textContent = ''; meldung.hidden = true }
|
||||
})
|
||||
})
|
||||
@@ -245,9 +272,11 @@
|
||||
knopf.addEventListener('click', function () {
|
||||
knopf.setAttribute('disabled', '')
|
||||
api('/zahlung', 'POST', {}).then(function (r) {
|
||||
if (r.ok && r.body.url) { window.location.assign(r.body.url); return }
|
||||
if (r.ok && httpsUrl(r.body.url)) { window.location.assign(r.body.url); return }
|
||||
if (r.status === 409 && r.body.fehler === 'status_ungueltig') { meldungNachLaden = TXT.standGeaendert; laden(); return }
|
||||
knopf.removeAttribute('disabled')
|
||||
meldung.textContent = 'Die Zahlung konnte nicht gestartet werden. Bitte versuchen Sie es erneut.'; meldung.hidden = false
|
||||
meldung.textContent = r.status === 409 && r.body.fehler === 'online_aus' ? TXT.onlineAus : r.status === 409 && (r.body.fehler === 'einstellungen_unvollstaendig' || r.body.fehler === 'zustimmung_fehlt') ? TXT.nichtMoeglich : TXT.zahlungStart
|
||||
meldung.hidden = false
|
||||
}, function () {
|
||||
knopf.removeAttribute('disabled')
|
||||
meldung.textContent = 'Die Zahlung konnte nicht gestartet werden. Bitte versuchen Sie es erneut.'; meldung.hidden = false
|
||||
|
||||
@@ -843,3 +843,28 @@ test('Posten-Editor: die Summenanzeige enthaelt bei Regelbesteuerung kein Text-"
|
||||
assert.doesNotMatch(fx.text(e.el), /null/)
|
||||
} finally { fx.restore() }
|
||||
})
|
||||
|
||||
// Abschluss F3: jeder Servercode hat einen deutschen Du-Satz mit Handlungshinweis
|
||||
test('Fehlertexte: alle Codes des Portals sind übersetzt (Du-Form, kein Standardtext)', async () => {
|
||||
const { fehlerFuerFormular } = await import('../public/buchungen-fehler.js')
|
||||
const standard = fehlerFuerFormular({ fehler: 'gibt_es_nicht' }).message
|
||||
for (const code of ['preise_unvollstaendig', 'einstellungen_unvollstaendig', 'status_ungueltig', 'limit_erreicht', 'kein_konto', 'nicht_aktiv', 'praefix_gesperrt', 'sperre_aus_ical', 'online_aus', 'zahlung_fehlgeschlagen']) {
|
||||
const m = fehlerFuerFormular({ fehler: code }).message
|
||||
assert.notEqual(m, standard, code)
|
||||
assert.ok(m.length > 30 && m.endsWith('.'), code)
|
||||
assert.doesNotMatch(m, /\bSie\b|\bIhre?\b/, code)
|
||||
}
|
||||
})
|
||||
|
||||
test('Preis-Editor: Platzhalter-Feldfehler verschwindet beim Tippen', mit(ukServer(), async (fx) => {
|
||||
const { panel } = await zeigeTab(fx, ukTab)
|
||||
await fx.klick(aktion(fx, panel, 'bearbeiten'))
|
||||
await fx.ruhe()
|
||||
const storno = fx.finde(panel, '#bk-p-storno')
|
||||
await fx.setze(storno, 'Bis zu [X] Tage vor Anreise kostenfrei stornierbar, danach 100 %.')
|
||||
await fx.klick(aktion(fx, panel, 'preise-speichern'))
|
||||
await fx.ruhe()
|
||||
assert.match(fehlerText(fx, panel, 'stornobedingungen'), /Platzhalter/)
|
||||
await fx.setze(storno, 'Bis zu 14 Tage vor Anreise kostenfrei stornierbar, danach 100 %.')
|
||||
assert.equal(fehlerText(fx, panel, 'stornobedingungen'), '')
|
||||
}))
|
||||
@@ -17,9 +17,8 @@ test('kein innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, new F
|
||||
assert.doesNotMatch(OHNE_KOMMENTARE, /innerHTML|outerHTML|insertAdjacentHTML|document\.write|\beval\s*\(|new Function/)
|
||||
})
|
||||
|
||||
test('API-Pfad, Praefix .wku-, @layer wku, Style-Id, kein Storage, keine Cookies, keine Fremdadressen', () => {
|
||||
test('API-Pfad, Praefix .wku-, Style-Id, kein Storage, keine Cookies, keine Fremdadressen', () => {
|
||||
assert.ok(OHNE_KOMMENTARE.includes('/api/public/unterkunft/v1/'))
|
||||
assert.ok(OHNE_KOMMENTARE.includes('@layer wku'))
|
||||
assert.ok(OHNE_KOMMENTARE.includes('wku-style'))
|
||||
assert.match(OHNE_KOMMENTARE, /\.wku-root/)
|
||||
assert.doesNotMatch(OHNE_KOMMENTARE, /localStorage|sessionStorage|document\.cookie|https?:\/\//)
|
||||
@@ -29,3 +28,18 @@ test('Pflichttexte und Honeypot', () => {
|
||||
for (const t of ['Anfragen gerne per Telefon oder E-Mail.', 'Bitte wählen Sie Anreise und Abreise im Kalender.', 'Anfrage senden', 'kontakt_url', 'formToken']) assert.ok(QUELLE.includes(t), t)
|
||||
assert.match(QUELLE, /Danke! Ihre Anfrage/)
|
||||
})
|
||||
|
||||
// F1 (Abschluss): kein @layer (Seitenregeln wuerden gewinnen), jede Regel unter .wku-root, Reset der Formularelemente.
|
||||
test('CSS-Isolation: kein @layer, jeder Selektor beginnt mit .wku-root, all:revert fuer Elemente, Fenster +600 Tage', () => {
|
||||
assert.doesNotMatch(OHNE_KOMMENTARE, /@layer/)
|
||||
const m = QUELLE.match(/var CSS = \[([\s\S]*?)\]\.join\(''\)/)
|
||||
assert.ok(m)
|
||||
const css = [...m[1].matchAll(/'((?:[^'\\]|\\.)*)'/g)].map((x) => x[1]).join('')
|
||||
const flach = css.replace(/@(container|media|supports)[^{]*\{/g, '')
|
||||
const regeln = [...flach.matchAll(/([^{}]+)\{[^{}]*\}/g)].map((x) => x[1])
|
||||
assert.ok(regeln.length > 40)
|
||||
for (const sel of regeln) for (const teil of sel.split(/,(?![^(]*\))/)) assert.match(teil.trim(), /^\.wku-root(?![\w-])/, teil)
|
||||
assert.match(css, /\.wku-root :is\(button,input,select,textarea[^)]*\)\{all:revert/)
|
||||
assert.match(css, /\.wku-root\{[^}]*width:100%/)
|
||||
assert.ok(OHNE_KOMMENTARE.includes('plusTage(von, 600)'))
|
||||
})
|
||||
@@ -106,7 +106,7 @@ test('freigegeben: Pflichtangaben in Reihenfolge direkt ueber dem Knopf, Abruf m
|
||||
const block = fx.finde(el, '[data-tag="pflichtangaben"]')
|
||||
assert.equal(kinder[kinder.indexOf(block) + 1], knopf, 'Block steht direkt ueber dem Knopf')
|
||||
const t = fx.text(block)
|
||||
const marken = ['Anbieter: Haus Test GmbH', 'Weg 1', 'Unterkunft: Ferienwohnung, 10.11.2026 bis 14.11.2026', 'Check-in ab 15:00 Uhr', 'Check-out bis 11:00 Uhr', 'Personen: 2', 'Übernachtung, 4 Nächte', 'Endreinigung', 'Gesamtpreis: 450,00', '7 % USt', 'Zahlarten:', 'Stornobedingungen: Bis 14 Tage vor Anreise kostenfrei.', 'Ein Widerrufsrecht besteht nicht (§ 312g Abs. 2 Nr. 9 BGB).', 'Datenschutz:', 'Reserviert bis 10.10.2026 14:30 Uhr']
|
||||
const marken = ['Anbieter: Haus Test GmbH', 'Weg 1', 'Unterkunft: Ferienwohnung, 10.11.2026 bis 14.11.2026', 'Check-in ab 15:00 Uhr', 'Check-out bis 11:00 Uhr', 'Personen: 2', 'Übernachtung, 4 Nächte', 'Endreinigung', 'Gesamtpreis: 450,00', '7 % USt', 'Zahlarten:', 'Stornobedingungen: Bis 14 Tage vor Anreise kostenfrei.', 'Ein Widerrufsrecht besteht nicht (§ 312g Abs. 2 Satz 1 Nr. 9 BGB).', 'Datenschutz:', 'Reserviert bis 10.10.2026 14:30 Uhr']
|
||||
let pos = -1
|
||||
for (const m of marken) {
|
||||
const i = t.indexOf(m, pos + 1)
|
||||
@@ -289,3 +289,61 @@ test('Fehler der Rechnungsadresse verschwinden, sobald der Gast die Felder ausfu
|
||||
assert.ok(meldung.hidden)
|
||||
})
|
||||
})
|
||||
|
||||
/* ----------------------------------------------------- Abschluss: 409-Codes, a11y --- */
|
||||
test('409-Codes beim Buchen: eigene Banner-Texte statt stillem Neuladen', async () => {
|
||||
const faelle = [
|
||||
['online_aus', /Online-Zahlung ist gerade nicht möglich – bitte wählen Sie „Vor Ort bezahlen“\./, false],
|
||||
['einstellungen_unvollstaendig', /Die Buchung ist gerade nicht möglich\. Bitte wenden Sie sich direkt an den Anbieter\./, false],
|
||||
['zustimmung_fehlt', /Die Buchung ist gerade nicht möglich\. Bitte wenden Sie sich direkt an den Anbieter\./, false],
|
||||
['status_ungueltig', /Der Stand Ihrer Buchung hat sich geändert\./, true],
|
||||
]
|
||||
for (const [code, muster, neuLaden] of faelle) {
|
||||
const antworten = [{ status: 200, body: DATEN }, { status: 409, body: { fehler: code } }]
|
||||
if (neuLaden) antworten.push({ status: 200, body: { ...DATEN, status: 'gebucht', zahlart: 'vorort' } })
|
||||
await mitFx(antworten, undefined, async (fx, el) => {
|
||||
await fuelle(fx, el)
|
||||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||||
assert.match(fx.text(el), muster, code)
|
||||
assert.equal(fx.fetchLog.length, neuLaden ? 3 : 2, code)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('409 bei /zahlung: status_ungueltig laedt neu mit Hinweis; online_aus zeigt Text', async () => {
|
||||
const d = { ...DATEN, status: 'gebucht', zahlart: 'online', bezahlt: false, rechnungNummer: 'R-1' }
|
||||
await mitFx([{ status: 200, body: d }, { status: 409, body: { fehler: 'status_ungueltig' } }, { status: 200, body: { ...d, bezahlt: true } }], undefined, async (fx, el) => {
|
||||
await fx.klick(fx.finde(el, '[data-tag="online-zahlen"]'))
|
||||
assert.match(fx.text(el), /Der Stand Ihrer Buchung hat sich geändert\./)
|
||||
assert.match(fx.text(el), /Bezahlt – vielen Dank\./)
|
||||
})
|
||||
await mitFx([{ status: 200, body: d }, { status: 409, body: { fehler: 'online_aus' } }], undefined, async (fx, el) => {
|
||||
await fx.klick(fx.finde(el, '[data-tag="online-zahlen"]'))
|
||||
assert.match(fx.text(el), /Online-Zahlung ist gerade nicht möglich/)
|
||||
})
|
||||
})
|
||||
|
||||
test('zahlungUrl nur bei https; Widerrufs-Zitat vollstaendig', async () => {
|
||||
await mitFx([{ status: 200, body: DATEN }, { status: 200, body: { status: 'gebucht', rechnungNummer: 'R-1', zahlungUrl: 'javascript:alert(1)' } }], undefined, async (fx, el) => {
|
||||
assert.match(fx.text(el), /§ 312g Abs\. 2 Satz 1 Nr\. 9 BGB/)
|
||||
await fuelle(fx, el)
|
||||
await fx.klick(fx.finde(el, 'input[value="online"]'))
|
||||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||||
assert.deepEqual(fx.assigned, [])
|
||||
})
|
||||
})
|
||||
|
||||
test('Feldfehler: aria-invalid, aria-describedby, Fokus auf erstes Fehlerfeld; Zahlart als fieldset/legend', async () => {
|
||||
await mitFx([{ status: 200, body: DATEN }], undefined, async (fx, el) => {
|
||||
const gruppe = fx.finde(el, 'fieldset[data-tag="zahlart"]')
|
||||
assert.ok(gruppe)
|
||||
assert.equal(fx.text(fx.finde(gruppe, 'legend')), 'Zahlart')
|
||||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||||
const strasse = fx.finde(el, '[name="strasse"]')
|
||||
assert.equal(strasse.getAttribute('aria-invalid'), 'true')
|
||||
assert.equal(strasse.getAttribute('aria-describedby'), 'uk-fehler-strasse')
|
||||
assert.equal(fx.document.activeElement, strasse)
|
||||
await fx.setze(strasse, 'Weg 1')
|
||||
assert.equal(strasse.getAttribute('aria-invalid'), null)
|
||||
})
|
||||
})
|
||||
@@ -90,3 +90,21 @@ test('Monatsnavigation: erster Monat hat aktiven Weiter-Knopf, Fokus bleibt, Tab
|
||||
assert.doesNotMatch(tag(fx, el, '2026-11-22').getAttribute('aria-label'), /Abreisetag möglich/)
|
||||
} finally { fx.restore() }
|
||||
})
|
||||
|
||||
test('Serverfehler: felder.zeitraum, neu_laden und mindestnaechte erscheinen mit eigenem Text', async () => {
|
||||
const faelle = [
|
||||
[{ status: 400, body: { fehler: 'ungueltig', felder: { zeitraum: 'Ungültiger Zeitraum.' } } }, /Ungültiger Zeitraum\./],
|
||||
[{ status: 400, body: { fehler: 'neu_laden' } }, /zu lange geöffnet/],
|
||||
[{ status: 400, body: { fehler: 'mindestnaechte', felder: { mindestNaechte: 3 } } }, /Mindestaufenthalt: 3 Nächte\./],
|
||||
]
|
||||
for (const [antwort, muster] of faelle) {
|
||||
const { fx, el } = start([{ status: 200, body: CFG }, { status: 200, body: BEL }, antwort, { status: 200, body: CFG }])
|
||||
try {
|
||||
await fx.ruhe(); await fx.ruhe()
|
||||
await fx.setze(fx.finde(el, '[name="name"]'), 'Max'); await fx.setze(fx.finde(el, '[name="email"]'), 'max@example.com')
|
||||
await fx.klick(tag(fx, el, '2026-11-10')); await fx.klick(tag(fx, el, '2026-11-14'))
|
||||
await fx.klick(fx.finde(el, 'button[type="submit"]'))
|
||||
assert.match(fx.text(el), muster)
|
||||
} finally { fx.restore() }
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user