/* Oeffnungszeiten live im Kundenbereich: Editor, Bereiche 4 „Hinweis-Leiste“ und 5 „Kontakt-Leiste (Handy)“ (Spec 3.4). Jeder Bereich ist eine Funktion
(t) => Element; t = { ctx, sp, zeichne, wurzel } kommt aus zeiten-editor.js (sp = Speicher des Editors, zeichne([…]) zeichnet einzelne Abschnitte neu).
Jede Aenderung speichert die ganzen Daten (zeiten-speichern.js); nach einem Speichern wird nur der eigene Abschnitt neu gezeichnet, ungespeicherte Eingaben
in anderen Formularen bleiben stehen. Abgeschaltete Funktion (ctx.nurLesen): dieselben Felder gesperrt, ohne Knoepfe zum Aendern. Stil: zeiten-ausnahmen.js.
Hinweis-Leiste: Liste der laufenden und geplanten Hinweise (Vergangenes eingeklappt), „Hinweis-Leiste anlegen“ (dieser Text ist fest: die Relaunch-Station
tippt den Knopf an) und je Hinweis Text (<= 140), Art (Info, Urlaub, Aktion, Wichtig mit festen Farben wie auf der Website), optionaler Knopf (Link
https oder Anrufen) und Zeitraum von–bis (Kalendertage, beide optional; „Aktion“ verlangt ein Enddatum). Der Browser prueft frueh (Aktion ohne Ende, Ende vor
Beginn, leerer Text) und zeigt bei Link und Rufnummer nur einen Hinweis (linkOk): entschieden wird auf dem Server, sein Fehler steht am Feld.
Ein anderer Abschnitt kann einen Hinweis anlegen (Urlaub-Frage der Ausnahmen): hinweisListeErneuern(t) zeichnet dann nur die Liste neu und laesst offene Formulare dieses
Abschnitts samt ungespeicherter Eingaben stehen.
Kontakt-Leiste: Ein/Aus, Telefon, Adresse, WhatsApp (Nummer, Starttext, Haken „Datenschutzerklaerung“). Der Server speichert Rufnummern als Ziffern mit
Laendervorwahl; der Editor zeigt die gespeicherte Nummer aus der Antwort (+49 …) im Feld und in der Meldung, damit der Kunde sieht, was gilt.
R12: den Zeitpunkt der WhatsApp-Bestaetigung (kontakt.whatsapp.bestaetigtAt) setzt nur der Kunde selbst. In der Kundenansicht ist der Haken gesperrt und der
gespeicherte Wert geht unveraendert zurueck (ohne gespeicherten kein Speichern mit WhatsApp); beim Kunden geht ein frischer Zeitpunkt nur hinaus, wenn der Haken
neu gesetzt wurde (der Server ersetzt ihn durch seine Zeit), sonst der gespeicherte Wert. */
import { datumKurz, heuteBerlin, istTag } from './termine-view.js'
import { abschnitt, allgemeinerFehler, editorZeile, feld, gesperrt, h, hakenFeld, neuerEintrag, zeigeFehler } from './termine-dom.js'
import { HINWEIS_ARTEN, ZEITEN_LIMITS, ZEITEN_MAX, linkOk, neueZeitenId, preisHinweisNoetig } from './zeiten-view.js'
import { entfernenKnopf, nachSpeichern, stelleVon } from './zeiten-speichern.js'
const istObjekt = (wert) => wert !== null && typeof wert === 'object' && !Array.isArray(wert)
/* ----------------------------------------------------------- Hinweis-Leiste --- */
const ART_NAME = Object.freeze({ info: 'Info', urlaub: 'Urlaub', aktion: 'Aktion', wichtig: 'Wichtig' })
const KNOPF_WAHL = Object.freeze([['', 'Kein Knopf'], ['link', 'Link'], ['anruf', 'Anrufen']])
const PREIS_HINWEIS = 'Bei Preisangaben gelten Endpreise. Bei Waren nennst du dazu den niedrigsten Preis der letzten 30 Tage.'
const LINK_HINWEIS = 'Bitte gib eine vollständige Adresse mit https:// an.'
const ANRUF_HINWEIS = 'Bitte nur Ziffern, Leerzeichen, + / und - (höchstens 30 Zeichen).'
const KNOPF_ARTEN = Object.freeze(['link', 'anruf'])
/** Rufnummer eines Anruf-Knopfs wie auf dem Server: nur Ziffern, Leerzeichen, / und -, ein + nur vorn, mindestens eine Ziffer, hoechstens 30 Zeichen. */
const anrufOk = (ziel) => {
const s = typeof ziel === 'string' ? ziel.trim() : ''
return s !== '' && [...s].length <= ZEITEN_MAX.telefon && /^\+?[0-9 /-]+$/.test(s) && /\d/.test(s)
}
/** 'JJJJ-MM-TT' oder '' (fehlt, null oder kein Kalendertag). */
const tagOder = (wert) => (typeof wert === 'string' && istTag(wert) ? wert : '')
/** „12.10.2026 – 26.10.2026“, „ab …“, „bis …“ oder „immer sichtbar“. */
function zeitraumText(x) {
const von = tagOder(x.von)
const bis = tagOder(x.bis)
if (von && bis) return von === bis ? datumKurz(von) : `${datumKurz(von)} – ${datumKurz(bis)}`
if (von) return `ab ${datumKurz(von)}`
if (bis) return `bis ${datumKurz(bis)}`
return 'immer sichtbar'
}
const nachBeginn = (a, b) => (tagOder(a.von) < tagOder(b.von) ? -1 : tagOder(a.von) > tagOder(b.von) ? 1 : 0)
/** Eine Zeile der Hinweis-Liste (laufend, geplant oder vergangen) mit ihrem Bearbeiten-Formular unter dem Knopf. */
function hinweisReihe(t, x, heute) {
const { ctx } = t
const vorbei = tagOder(x.bis) !== '' && x.bis < heute
const art = HINWEIS_ARTEN.includes(x.art) ? x.art : 'info'
const knopf = istObjekt(x.knopf) && KNOPF_ARTEN.includes(x.knopf.art) ? x.knopf : null
const von = tagOder(x.von)
const zustand = vorbei ? ['vorbei', 'off'] : von && von > heute ? ['geplant', 'wait'] : ['läuft', 'ok']
const text = typeof x.text === 'string' ? x.text : ''
return editorZeile({
attrs: { 'data-id': x.id },
nurLesen: ctx.nurLesen,
label: `Hinweis ${text}`,
haupt: [
h('strong', { class: 'tm-name' }, text),
h('span', { class: 'tm-meta' },
h('span', { class: `zt-art zt-art-${art}` }, ART_NAME[art]),
` · ${zeitraumText(x)}${knopf ? ` · Knopf „${typeof knopf.text === 'string' ? knopf.text : ''}“ (${knopf.art === 'anruf' ? 'Anrufen' : 'Link'})` : ''}`),
],
pille: h('span', { class: `tm-pille tm-pille-${zustand[1]}` }, zustand[0]),
baue: (schliessen) => formular(t, { a: x, schliessen }),
})
}
/**
* Die Liste der Hinweise: [Liste der laufenden und geplanten (oder „Keine Hinweise eingetragen.“), eingeklappte Liste der vergangenen]. vorhanden (Map ID =>
):
* Zeilen, die schon in der Seite stehen, werden wiederverwendet (samt ihrem offenen Formular) statt neu gebaut; nur für den Fall, dass sich an diesen Hinweisen nichts geändert hat.
*/
function hinweisListe(t, vorhanden = new Map()) {
const alle = (Array.isArray(t.ctx.stand.daten.hinweise) ? t.ctx.stand.daten.hinweise : []).filter((x) => istObjekt(x) && typeof x.id === 'string')
const heute = heuteBerlin()
const vorbei = (x) => tagOder(x.bis) !== '' && x.bis < heute
const reihe = (x) => vorhanden.get(x.id) ?? hinweisReihe(t, x, heute)
const kommend = alle.filter((x) => !vorbei(x)).sort(nachBeginn)
const alt = alle.filter(vorbei).sort(nachBeginn).reverse()
return {
voll: alle.length >= ZEITEN_LIMITS.hinweise,
teile: [
kommend.length
? h('ul', { class: 'tm-eintraege', 'aria-label': 'Laufende und geplante Hinweise' }, kommend.map(reihe))
: h('p', { class: 'tm-leer' }, 'Keine Hinweise eingetragen.'),
alt.length
? h('details', { class: 'tm-details' }, h('summary', null, `Vergangene Hinweise (${alt.length})`), h('ul', { class: 'tm-eintraege' }, alt.map(reihe)))
: null,
],
}
}
const GRENZ_TEXT = `Mehr als ${ZEITEN_LIMITS.hinweise} Hinweise sind nicht möglich.`
export function hinweise(t) {
const { ctx } = t
const { voll, teile } = hinweisListe(t)
const neu = neuerEintrag({
nurLesen: ctx.nurLesen, text: 'Hinweis-Leiste anlegen', werkzeug: 'hinweis-neu', primaer: true, voll, grenzText: GRENZ_TEXT,
baue: (schliessen) => formular(t, { a: null, schliessen }),
})
return abschnitt('Hinweis-Leiste',
'Eine Leiste ganz oben auf deiner Website – zum Beispiel für Betriebsurlaub, eine Aktion oder etwas Wichtiges. Die Leiste erscheint nur in dem Zeitraum, den du einstellst. Gelten mehrere gleichzeitig, zeigt die Website nur einen: zuerst „Wichtig“, dann „Urlaub“, „Aktion“ und „Info“.',
h('div', { class: 'zt-werkzeuge' }, neu.leiste),
neu.box,
h('div', { class: 'zt-hinweis-liste' }, teile))
}
/**
* Nur die Liste der Hinweise neu zeichnen, ohne den Abschnitt anzufassen: für den Fall, dass ein anderer Abschnitt einen Hinweis angelegt hat (die Frage „Hinweis-Leiste dazu
* anlegen?“ der Ausnahmen). Offene, ungespeicherte Formulare bleiben stehen: das Formular „Neuer Hinweis“ liegt nicht in der Liste, und die Zeilen der bisherigen Hinweise werden
* mit ihrem offenen Bearbeiten-Formular wiederverwendet. Der Knopf „Hinweis-Leiste anlegen“ folgt der Obergrenze. => false, wenn es den Abschnitt (noch) nicht gibt.
*/
export function hinweisListeErneuern(t) {
const el = t.wurzel.querySelector('[data-abschnitt="hinweise"]')
const platz = el?.querySelector('.zt-hinweis-liste')
if (!platz) return false
const vorhanden = new Map([...platz.querySelectorAll('li[data-id]')].map((li) => [li.getAttribute('data-id'), li]))
const { voll, teile } = hinweisListe(t, vorhanden)
platz.replaceChildren(...teile)
const knopf = el.querySelector('[data-werkzeug="hinweis-neu"]')
if (knopf) {
knopf.disabled = voll
const zeile = knopf.parentNode
const grenze = zeile.querySelector('p')
if (voll && !grenze) zeile.append(h('p', { class: 'tm-hinweis' }, GRENZ_TEXT))
else if (!voll && grenze) grenze.remove()
}
return true
}
/**
* Formular eines neuen (a null) oder bestehenden Hinweises. Die Felder heissen nach dem Pfad der Fehler des Servers (hinweise..text, .art, .knopf.art,
* .knopf.text, .knopf.ziel, .von, .bis); Absenden sucht den Hinweis per ID.
*/
function formular(t, { a, schliessen }) {
const { ctx, sp } = t
const neu = a === null
const alt = a || { id: neueZeitenId('h'), art: 'info', text: '', knopf: null, von: null, bis: null }
const p = `hinweise.${alt.id}`
const knopfAlt = istObjekt(alt.knopf) && KNOPF_ARTEN.includes(alt.knopf.art) ? alt.knopf : null
const text = h('input', { type: 'text', maxlength: ZEITEN_MAX.hinweisText, autocomplete: 'off', placeholder: 'z. B. Heute geschlossen – morgen wieder für dich da', value: typeof alt.text === 'string' ? alt.text : '' })
const art = h('select', null, HINWEIS_ARTEN.map((k) => h('option', { value: k, selected: k === (HINWEIS_ARTEN.includes(alt.art) ? alt.art : 'info') }, ART_NAME[k])))
const knopfArt = h('select', null, KNOPF_WAHL.map(([wert, name]) => h('option', { value: wert, selected: wert === (knopfAlt?.art ?? '') }, name)))
const knopfText = h('input', { type: 'text', maxlength: ZEITEN_MAX.knopfText, autocomplete: 'off', placeholder: 'z. B. Mehr erfahren', value: typeof knopfAlt?.text === 'string' ? knopfAlt.text : '' })
const knopfZiel = h('input', { type: 'url', maxlength: ZEITEN_MAX.link, autocomplete: 'off', value: typeof knopfAlt?.ziel === 'string' ? knopfAlt.ziel : '' })
const von = h('input', { type: 'date', value: tagOder(alt.von) })
const bis = h('input', { type: 'date', value: tagOder(alt.bis) })
const preisHinweis = h('p', { class: 'tm-hinweis', 'data-hinweis': 'preis', hidden: true }, PREIS_HINWEIS)
const zielHinweis = h('p', { class: 'tm-hinweis', 'data-hinweis': 'knopf-ziel', hidden: true }, LINK_HINWEIS)
const zielFeld = feld(`${p}.knopf.ziel`, 'Link', knopfZiel)
zielFeld.insertBefore(zielHinweis, zielFeld.querySelector('[data-fehler]'))
const textFeld = feld(`${p}.text`, 'Text', text, `Höchstens ${ZEITEN_MAX.hinweisText} Zeichen.`)
textFeld.insertBefore(preisHinweis, textFeld.querySelector('[data-fehler]'))
const knopfFelder = h('div', { class: 'tm-raster zt-knopf-felder', 'data-knopf-felder': '' },
feld(`${p}.knopf.text`, 'Beschriftung des Knopfs', knopfText, `Höchstens ${ZEITEN_MAX.knopfText} Zeichen.`),
zielFeld)
// Muster: so sieht die Leiste in der Farbe der Art aus (feste Farben wie auf der Website)
const musterText = h('span', { class: 'zt-muster-text' })
const musterKnopf = h('span', { class: 'zt-muster-knopf' })
const muster = h('div', { class: 'zt-muster', 'aria-hidden': 'true', 'data-muster': '' }, musterText, musterKnopf)
const aktualisiere = () => {
preisHinweis.hidden = !preisHinweisNoetig(text.value)
const wahl = knopfArt.value
knopfFelder.hidden = wahl === ''
knopfZiel.type = wahl === 'anruf' ? 'tel' : 'url'
knopfZiel.placeholder = wahl === 'anruf' ? 'z. B. 06301 123456' : 'https://'
zielFeld.querySelector('label').textContent = wahl === 'anruf' ? 'Telefonnummer' : 'Link'
const ziel = knopfZiel.value.trim()
zielHinweis.textContent = wahl === 'anruf' ? ANRUF_HINWEIS : LINK_HINWEIS
zielHinweis.hidden = wahl === '' || ziel === '' || (wahl === 'anruf' ? anrufOk(ziel) : linkOk(ziel))
muster.className = `zt-muster zt-art-${art.value}`
musterText.textContent = text.value.trim() || 'Dein Hinweistext'
musterKnopf.textContent = knopfText.value.trim()
musterKnopf.hidden = wahl === '' || knopfText.value.trim() === ''
}
for (const steuer of [text, art, knopfArt, knopfText, knopfZiel]) {
steuer.addEventListener('input', aktualisiere)
steuer.addEventListener('change', aktualisiere)
}
aktualisiere()
const entfernen = neu || ctx.nurLesen ? null : entfernenKnopf(sp, {
titel: 'Hinweis entfernen?',
text: 'Die Leiste verschwindet von deiner Website.',
aendere: (entwurf) => { entwurf.hinweise = (Array.isArray(entwurf.hinweise) ? entwurf.hinweise : []).filter((x) => x?.id !== alt.id) },
nachher: () => nachSpeichern(t, 'Hinweis entfernt.', '[data-werkzeug="hinweis-neu"]', () => t.zeichne(['hinweise'])),
})
const speichern = h('button', { type: 'submit', class: 'kb-btn-primary', 'data-aktion': 'hinweis-speichern' }, neu ? 'Hinweis speichern' : 'Speichern')
const form = h('form', { class: 'kb-panel tm-formular zt-hinweis-form', novalidate: true, 'aria-label': neu ? 'Neuer Hinweis' : 'Hinweis bearbeiten' },
h('h3', null, neu ? 'Neuer Hinweis' : 'Hinweis'),
gesperrt(ctx.nurLesen, [
textFeld,
feld(`${p}.art`, 'Art', art, 'Jede Art hat ihre feste Farbe.'),
muster,
feld(`${p}.knopf.art`, 'Knopf (optional)', knopfArt, 'Ein Knopf in der Leiste: ein Link auf eine Seite oder ein Anruf.'),
knopfFelder,
h('div', { class: 'tm-raster' },
feld(`${p}.von`, 'Sichtbar ab (optional)', von),
feld(`${p}.bis`, 'Sichtbar bis (bei einer Aktion nötig)', bis)),
h('p', { class: 'tm-hinweis' }, 'Ohne Datum gilt der Hinweis immer. Bei einer Aktion brauchst du ein Enddatum.'),
]),
allgemeinerFehler(),
h('div', { class: 'tm-frage-knoepfe' },
ctx.nurLesen ? null : speichern,
h('button', { type: 'button', class: 'kb-btn-outline', onclick: schliessen }, ctx.nurLesen ? 'Schließen' : 'Abbrechen'),
entfernen?.knopf))
form.addEventListener('submit', async (ev) => {
ev.preventDefault()
const ok = await sp.speichere((entwurf) => {
const fehler = {}
const vonWert = von.value.trim()
const bisWert = bis.value.trim()
const hinweisText = text.value.trim()
if (hinweisText === '') fehler[`${p}.text`] = 'Bitte gib einen Text ein.'
if (vonWert !== '' && !istTag(vonWert)) fehler[`${p}.von`] = 'Bitte gib ein gültiges Datum an.'
if (bisWert !== '' && !istTag(bisWert)) fehler[`${p}.bis`] = 'Bitte gib ein gültiges Datum an.'
if (!fehler[`${p}.von`] && !fehler[`${p}.bis`]) {
if (vonWert !== '' && bisWert !== '' && bisWert < vonWert) fehler[`${p}.bis`] = 'Das Ende darf nicht vor dem Beginn liegen.'
else if (art.value === 'aktion' && bisWert === '') fehler[`${p}.bis`] = 'Eine Aktion braucht ein Enddatum.'
}
if (Object.keys(fehler).length) {
zeigeFehler(form, { fehler })
return false
}
// ein Knopf ohne Beschriftung und Ziel ist kein Knopf (wie auf dem Server)
const ziel = knopfZiel.value.trim()
const beschriftung = knopfText.value.trim()
const knopf = knopfArt.value !== '' && (ziel !== '' || beschriftung !== '') ? { art: knopfArt.value, ziel, text: beschriftung } : null
const eintrag = { id: alt.id, art: art.value, text: hinweisText, knopf, von: vonWert || null, bis: bisWert || null }
if (neu) {
entwurf.hinweise = [...(Array.isArray(entwurf.hinweise) ? entwurf.hinweise : []), eintrag]
return true
}
const stelle = stelleVon(entwurf.hinweise, alt.id, form, 'Dieser Hinweis')
if (stelle < 0) return false
entwurf.hinweise[stelle] = eintrag
return true
}, { container: form })
if (!ok || !sp.nochHier()) return
nachSpeichern(t, 'Hinweis gespeichert.', neu ? '[data-werkzeug="hinweis-neu"]' : `li[data-id="${CSS.escape(alt.id)}"] [data-aktion="bearbeiten"]`, () => t.zeichne(['hinweise']))
})
return h('div', { class: 'tm-editor' }, form, entfernen?.box)
}
/* ------------------------------------------------------------ Kontakt-Leiste --- */
const WHATSAPP_HAKEN = 'Ich habe den WhatsApp-Hinweis in meine Datenschutzerklärung aufgenommen'
const HAKEN_FEHLT = 'Bitte bestätige, dass du den WhatsApp-Hinweis in deine Datenschutzerklärung aufgenommen hast.'
const KUNDE_BESTAETIGT = 'Der Kunde muss den WhatsApp-Hinweis selbst bestätigen – in der Kundenansicht geht das nicht.'
/** Gespeicherte Rufnummer (Ziffern mit Laendervorwahl, z. B. 491711234567) als „+49 1711234567“; alles andere bleibt, wie es gespeichert ist. */
export function nummerAnzeige(wert) {
const s = typeof wert === 'string' ? wert.trim() : ''
if (!/^\d{6,15}$/.test(s)) return s
return s.startsWith('49') ? `+49 ${s.slice(2)}` : `+${s}`
}
export function kontakt(t) {
const { ctx, sp } = t
const nurLesen = ctx.nurLesen
const kundenansicht = ctx.kundenansicht === true || ctx.stand.kundenansicht === true
const k = istObjekt(ctx.stand.daten.kontakt) ? ctx.stand.daten.kontakt : {}
const wa = istObjekt(k.whatsapp) ? k.whatsapp : null
const gespeichertAt = typeof wa?.bestaetigtAt === 'string' ? wa.bestaetigtAt : ''
const telefonAlt = typeof k.telefon === 'string' ? k.telefon : ''
const nummerAlt = typeof wa?.nummer === 'string' ? wa.nummer : ''
const telefonAnzeige = nummerAnzeige(telefonAlt)
const nummerAnz = nummerAnzeige(nummerAlt)
const an = h('input', { type: 'checkbox', checked: k.an === true })
const telefon = h('input', { type: 'tel', maxlength: ZEITEN_MAX.telefon, autocomplete: 'off', placeholder: 'z. B. 06301 123456', value: telefonAnzeige })
const adresse = h('input', { type: 'text', maxlength: ZEITEN_MAX.adresse, autocomplete: 'off', placeholder: 'z. B. Hauptstraße 1, 67722 Winnweiler', value: typeof k.adresse === 'string' ? k.adresse : '' })
const waAn = h('input', { type: 'checkbox', checked: wa !== null })
const nummer = h('input', { type: 'tel', maxlength: ZEITEN_MAX.telefon, autocomplete: 'off', placeholder: 'z. B. 0171 1234567', value: nummerAnz })
const starttext = h('textarea', { rows: 3, maxlength: ZEITEN_MAX.starttext, placeholder: 'z. B. Hallo, ich habe eine Frage.' })
starttext.value = typeof wa?.starttext === 'string' ? wa.starttext : ''
const bestaetigt = h('input', { type: 'checkbox', checked: gespeichertAt !== '', disabled: kundenansicht })
const waBlock = h('div', { class: 'zt-whatsapp', 'data-whatsapp': '', hidden: wa === null },
feld('kontakt.whatsapp.nummer', 'WhatsApp-Nummer', nummer, 'Mit Vorwahl. Wir speichern sie mit Ländervorwahl, zum Beispiel +49 …'),
feld('kontakt.whatsapp.starttext', 'Vorgeschriebener Text (optional)', starttext, `Dieser Text steht schon im Nachrichtenfeld, wenn jemand auf den WhatsApp-Knopf tippt. Höchstens ${ZEITEN_MAX.starttext} Zeichen.`),
hakenFeld('kontakt.whatsapp.bestaetigtAt', bestaetigt, WHATSAPP_HAKEN, 'Beim Tippen öffnet sich WhatsApp (ein Dienst von Meta). Das gehört in die Datenschutzerklärung deiner Website – frag uns im Chat, wenn du den passenden Absatz brauchst.'),
kundenansicht ? h('p', { class: 'tm-hinweis', 'data-hinweis': 'whatsapp-kundenansicht' }, 'Kundenansicht: Den Haken zur Datenschutzerklärung kann nur der Kunde selbst setzen.') : null)
waAn.addEventListener('change', () => { waBlock.hidden = !waAn.checked })
const speichern = h('button', { type: 'submit', class: 'kb-btn-primary', 'data-aktion': 'kontakt-speichern' }, 'Kontakt-Leiste speichern')
const form = h('form', { novalidate: true, 'aria-label': 'Kontakt-Leiste' },
gesperrt(nurLesen, [
hakenFeld('kontakt.an', an, 'Kontakt-Leiste auf dem Handy anzeigen', 'Eine Leiste unten auf deiner Website, nur auf dem Handy: Anrufen, Route und WhatsApp – je nachdem, was du einträgst.'),
feld('kontakt.telefon', 'Telefon', telefon, 'Für „Anrufen“. Wir speichern die Nummer mit Ländervorwahl (+49 für Deutschland).'),
feld('kontakt.adresse', 'Adresse für „Route“', adresse, `Höchstens ${ZEITEN_MAX.adresse} Zeichen.`),
hakenFeld('kontakt.whatsapp', waAn, 'WhatsApp-Knopf anbieten'),
waBlock,
]),
allgemeinerFehler(),
nurLesen ? null : h('div', { class: 'tm-frage-knoepfe' }, speichern))
form.addEventListener('submit', async (ev) => {
ev.preventDefault()
const ok = await sp.speichere((entwurf) => {
const fehler = {}
let whatsapp = null
if (waAn.checked) {
const nummerWert = nummer.value === nummerAnz ? nummerAlt : nummer.value.trim()
if (nummerWert === '') fehler['kontakt.whatsapp.nummer'] = 'Bitte gib die Nummer an.'
let bestaetigtAt
if (kundenansicht) {
// R12: im Namen des Kunden gibt es nie einen neuen Zeitpunkt; ein gespeicherter geht unveraendert zurueck
if (gespeichertAt !== '') bestaetigtAt = gespeichertAt
else fehler['kontakt.whatsapp.bestaetigtAt'] = KUNDE_BESTAETIGT
} else if (bestaetigt.checked) {
bestaetigtAt = gespeichertAt !== '' ? gespeichertAt : new Date().toISOString() // der Server ersetzt einen neuen Zeitpunkt durch seine Zeit
} else {
fehler['kontakt.whatsapp.bestaetigtAt'] = HAKEN_FEHLT
}
whatsapp = { nummer: nummerWert, starttext: starttext.value.trim(), bestaetigtAt }
}
if (Object.keys(fehler).length) {
zeigeFehler(form, { fehler })
return false
}
entwurf.kontakt = {
...(istObjekt(entwurf.kontakt) ? entwurf.kontakt : {}),
an: an.checked,
telefon: telefon.value === telefonAnzeige ? telefonAlt : telefon.value.trim(),
adresse: adresse.value.trim(),
whatsapp,
}
return true
}, { container: form })
if (!ok || !sp.nochHier()) return
// was der Server gespeichert hat, steht danach im Feld (nur dieser Abschnitt wird neu gezeichnet) und in der Meldung
const neu = istObjekt(ctx.stand.daten.kontakt) ? ctx.stand.daten.kontakt : {}
const nummern = [neu.telefon ? `Telefon ${nummerAnzeige(neu.telefon)}` : '', neu.whatsapp?.nummer ? `WhatsApp ${nummerAnzeige(neu.whatsapp.nummer)}` : ''].filter(Boolean)
nachSpeichern(t, `Kontakt-Leiste gespeichert.${nummern.length ? ` Gespeichert als ${nummern.join(', ')}.` : ''}`, 'form[aria-label="Kontakt-Leiste"] button[type="submit"]', () => t.zeichne(['kontakt']))
})
return abschnitt('Kontakt-Leiste (Handy)',
'Auf dem Handy erscheint unten auf deiner Website eine Leiste mit Knöpfen zum Anrufen, für die Route und für WhatsApp. Auf größeren Bildschirmen erscheint sie nicht.',
form)
}