Terminplaner TP2: Reiter-Exporte und wochenplanFeld fuer den Fragebogen
- termine-tab-einstellungen.js: die acht Abschnitte (betrieb, darstellung, regeln,
gastFelder, erinnerung, storno, rechtliches, zahlung) sind Exporte; betrieb nimmt
{ nur, titel, hinweis } (nur die gewaehlten Felder, lese schreibt nur diese)
- termine-tab-team.js: wochenplanFeld(r, pfad) => { el, lese() => { plan } | { fehler } },
der Editor einer Person benutzt es selbst
- termine-speichern.js: fehlt kommt aus der Antwort des Servers
- termine.js: baueCtx aus zeigeReiter herausgeloest (Verhalten gleich)
- test/support/fakeDom.js: Minimal-DOM und Fake-fetch fuer die Client-Tests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0090332c70
commit
747be432c5
7 files changed
+1032
-59
No files matched your search
@@ -112,8 +112,9 @@ export function neuerSpeicher(ctx, wurzel, neuZeichnen) {
|
||||
try {
|
||||
const antwort = await ctx.api.send('PATCH', '/einstellungen', { version: ctx.daten.version, einstellung: entwurf })
|
||||
if (!ctx.aktuell()) return false
|
||||
// Der Server hat streng geprueft: Telefon und Anschrift fehlen nach einem Erfolg nie mehr
|
||||
Object.assign(ctx.daten, { einstellung: antwort.einstellung, version: antwort.version, fehlt: [] })
|
||||
// fehlt (Pflichtangaben fuer Gaeste) nennt der Server in der Antwort: in aktiver Funktion ist es nach einem Erfolg leer, im Entwurf
|
||||
// des Fragebogens koennen Telefon und Anschrift noch fehlen
|
||||
Object.assign(ctx.daten, { einstellung: antwort.einstellung, version: antwort.version, fehlt: antwort.fehlt ?? [] })
|
||||
ctx.hinweise()
|
||||
try {
|
||||
await ctx.neuLaden() // neue Personen/Plaetze bekommen vom Server ihre Kalender-Links
|
||||
|
||||
@@ -69,48 +69,79 @@ export function render(panel, ctx) {
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- Betrieb --- */
|
||||
function betrieb(ctx, e) {
|
||||
const textFeld = (wert, max, extra) => h('input', { type: 'text', maxlength: max, autocomplete: 'off', value: wert ?? '', ...extra })
|
||||
const urlFeld = (wert) => textFeld(wert, MAX.url, { inputmode: 'url', placeholder: 'https://' })
|
||||
|
||||
/** Die Angaben zum Betrieb in der Reihenfolge der Seite: Name, Mailadresse des Betriebs, Telefon, Anschrift, Kleinunternehmer, vier Links. */
|
||||
const BETRIEB_FELDER = {
|
||||
name: { eingabe: (b) => textFeld(b.name, MAX.betriebName), zeige: (i) => feld('betrieb.name', 'Name des Betriebs', i) },
|
||||
email: {
|
||||
eingabe: (b) => h('input', { type: 'email', maxlength: '254', autocomplete: 'off', value: b.email ?? '' }),
|
||||
zeige: (i) => feld('betrieb.email', 'E-Mail des Betriebs', i, 'Hierhin schicken wir dir Nachrichten zu Terminen. Gäste können auf ihre Mails hier antworten.'),
|
||||
},
|
||||
telefon: {
|
||||
eingabe: (b) => textFeld(b.telefon, MAX.telefon),
|
||||
zeige: (i) => feld('betrieb.telefon', 'Telefon (Pflicht)', i, 'Gäste sehen die Nummer als Ausweichweg und in ihren Mails.'),
|
||||
},
|
||||
adresse: {
|
||||
eingabe: (b) => textFeld(b.adresse, MAX.adresse),
|
||||
zeige: (i) => feld('betrieb.adresse', 'Anschrift', i, 'Pflicht, wenn Privatpersonen bei dir buchen oder anfragen: Die Anschrift steht in der Widerrufsbelehrung.'),
|
||||
},
|
||||
kleinunternehmer: {
|
||||
eingabe: (b) => h('input', { type: 'checkbox', checked: b.kleinunternehmer === true }),
|
||||
zeige: (i) => hakenFeld('betrieb.kleinunternehmer', i, 'Ich bin Kleinunternehmer nach § 19 UStG', 'Dann steht bei den Preisen „kein Ausweis der USt nach § 19 UStG“ statt „inkl. USt“.'),
|
||||
},
|
||||
impressumUrl: { eingabe: (b) => urlFeld(b.impressumUrl), zeige: (i) => feld('betrieb.impressumUrl', 'Link zum Impressum', i, 'Vollständige Adresse mit https://') },
|
||||
datenschutzUrl: { eingabe: (b) => urlFeld(b.datenschutzUrl), zeige: (i) => feld('betrieb.datenschutzUrl', 'Link zur Datenschutzerklärung', i, 'Vollständige Adresse mit https://') },
|
||||
agbUrl: { eingabe: (b) => urlFeld(b.agbUrl), zeige: (i) => feld('betrieb.agbUrl', 'Link zu deinen AGB (optional)', i, 'Vollständige Adresse mit https://') },
|
||||
websiteUrl: { eingabe: (b) => urlFeld(b.websiteUrl), zeige: (i) => feld('betrieb.websiteUrl', 'Link zu deiner Website', i, 'Vollständige Adresse mit https://') },
|
||||
}
|
||||
/** Wie die Felder auf der Seite zusammenstehen: Zeilen im Raster, das Ankreuzfeld steht allein. */
|
||||
const BETRIEB_ZEILEN = [
|
||||
{ felder: ['name', 'email'], raster: true },
|
||||
{ felder: ['telefon', 'adresse'], raster: true },
|
||||
{ felder: ['kleinunternehmer'], raster: false },
|
||||
{ felder: ['impressumUrl', 'datenschutzUrl', 'agbUrl', 'websiteUrl'], raster: true },
|
||||
]
|
||||
const BETRIEB_TITEL = 'Dein Betrieb'
|
||||
const BETRIEB_HINWEIS = 'Diese Angaben sehen deine Gäste beim Buchen und in ihren E-Mails. Die meisten haben wir aus deinem Kundenkonto übernommen.'
|
||||
|
||||
/**
|
||||
* Abschnitt „Dein Betrieb“. Ohne Optionen alle neun Felder wie bisher. Der Fragebogen zeigt in einem Schritt nur einen Teil:
|
||||
* nur = Teilmenge der Felder (name, email, telefon, adresse, kleinunternehmer, impressumUrl, datenschutzUrl, agbUrl, websiteUrl); es entstehen nur
|
||||
* diese Felder (in der Reihenfolge der Seite) und lese schreibt nur diese in den Entwurf. titel und hinweis ersetzen Ueberschrift und Erklaersatz
|
||||
* (hinweis: null laesst ihn weg). lese(entwurf) schreibt in entwurf.betrieb und gibt die gelesenen Felder zurueck (nur die gezeigten).
|
||||
* Der Hinweis auf fehlende Pflichtangaben nennt mit nur nur Angaben, die dabei zu sehen sind.
|
||||
*/
|
||||
export function betrieb(ctx, e, { nur, titel = BETRIEB_TITEL, hinweis = BETRIEB_HINWEIS } = {}) {
|
||||
const b = e.betrieb || {}
|
||||
const text = (wert, max, extra) => h('input', { type: 'text', maxlength: max, autocomplete: 'off', value: wert ?? '', ...extra })
|
||||
const url = (wert) => text(wert, MAX.url, { inputmode: 'url', placeholder: 'https://' })
|
||||
const f = {
|
||||
name: text(b.name, MAX.betriebName), email: h('input', { type: 'email', maxlength: '254', autocomplete: 'off', value: b.email ?? '' }), telefon: text(b.telefon, MAX.telefon),
|
||||
adresse: text(b.adresse, MAX.adresse), impressumUrl: url(b.impressumUrl), datenschutzUrl: url(b.datenschutzUrl), agbUrl: url(b.agbUrl), websiteUrl: url(b.websiteUrl),
|
||||
kleinunternehmer: h('input', { type: 'checkbox', checked: b.kleinunternehmer === true }),
|
||||
}
|
||||
const fehlt = ctx.daten.fehlt || []
|
||||
for (const name of nur || []) if (!Object.hasOwn(BETRIEB_FELDER, name)) throw new Error(`Unbekanntes Feld im Betrieb: ${name}`)
|
||||
const gezeigt = Object.keys(BETRIEB_FELDER).filter((name) => !nur || nur.includes(name))
|
||||
const f = Object.fromEntries(gezeigt.map((name) => [name, BETRIEB_FELDER[name].eingabe(b)]))
|
||||
const fehlt = (ctx.daten.fehlt || []).filter((pfad) => !nur || gezeigt.includes(pfad.replace(/^betrieb\./, '')))
|
||||
const warnung = fehlt.length
|
||||
? h('div', { class: 'tm-box tm-box-warn', role: 'note' }, `Ohne Telefon/Anschrift ist der Terminplaner auf deiner Website nicht buchbar. Es fehlt noch: ${fehltText(fehlt) || 'eine Angabe zu deinem Betrieb'}.`)
|
||||
: null
|
||||
const el = abschnitt('Dein Betrieb',
|
||||
'Diese Angaben sehen deine Gäste beim Buchen und in ihren E-Mails. Die meisten haben wir aus deinem Kundenkonto übernommen.',
|
||||
warnung,
|
||||
h('div', { class: 'tm-raster' },
|
||||
feld('betrieb.name', 'Name des Betriebs', f.name),
|
||||
feld('betrieb.email', 'E-Mail des Betriebs', f.email, 'Hierhin schicken wir dir Nachrichten zu Terminen. Gäste können auf ihre Mails hier antworten.')),
|
||||
h('div', { class: 'tm-raster' },
|
||||
feld('betrieb.telefon', 'Telefon (Pflicht)', f.telefon, 'Gäste sehen die Nummer als Ausweichweg und in ihren Mails.'),
|
||||
feld('betrieb.adresse', 'Anschrift', f.adresse, 'Pflicht, wenn Privatpersonen bei dir buchen oder anfragen: Die Anschrift steht in der Widerrufsbelehrung.')),
|
||||
hakenFeld('betrieb.kleinunternehmer', f.kleinunternehmer, 'Ich bin Kleinunternehmer nach § 19 UStG', 'Dann steht bei den Preisen „kein Ausweis der USt nach § 19 UStG“ statt „inkl. USt“.'),
|
||||
h('div', { class: 'tm-raster' },
|
||||
feld('betrieb.impressumUrl', 'Link zum Impressum', f.impressumUrl, 'Vollständige Adresse mit https://'),
|
||||
feld('betrieb.datenschutzUrl', 'Link zur Datenschutzerklärung', f.datenschutzUrl, 'Vollständige Adresse mit https://'),
|
||||
feld('betrieb.agbUrl', 'Link zu deinen AGB (optional)', f.agbUrl, 'Vollständige Adresse mit https://'),
|
||||
feld('betrieb.websiteUrl', 'Link zu deiner Website', f.websiteUrl, 'Vollständige Adresse mit https://')))
|
||||
const zeilen = BETRIEB_ZEILEN
|
||||
.map(({ felder, raster }) => {
|
||||
const els = felder.filter((name) => f[name]).map((name) => BETRIEB_FELDER[name].zeige(f[name]))
|
||||
if (!els.length) return null
|
||||
return raster ? h('div', { class: 'tm-raster' }, els) : els
|
||||
})
|
||||
.filter(Boolean)
|
||||
const el = abschnitt(titel, hinweis, warnung, ...zeilen)
|
||||
return {
|
||||
el,
|
||||
lese(entwurf) {
|
||||
entwurf.betrieb = {
|
||||
...entwurf.betrieb,
|
||||
name: f.name.value, email: f.email.value, telefon: f.telefon.value, adresse: f.adresse.value, kleinunternehmer: f.kleinunternehmer.checked,
|
||||
impressumUrl: f.impressumUrl.value, datenschutzUrl: f.datenschutzUrl.value, agbUrl: f.agbUrl.value, websiteUrl: f.websiteUrl.value,
|
||||
}
|
||||
lese(entwurf = {}) {
|
||||
const gelesen = Object.fromEntries(gezeigt.map((name) => [name, name === 'kleinunternehmer' ? f[name].checked : f[name].value]))
|
||||
entwurf.betrieb = { ...entwurf.betrieb, ...gelesen }
|
||||
return gelesen
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------- Anrede und Farbe --- */
|
||||
function darstellung(e) {
|
||||
export function darstellung(e) {
|
||||
const anrede = h('select', null, wahl(ANREDE_WAHL, e.anrede ?? 'sie'))
|
||||
const farbe = h('input', { type: 'color', value: /^#[0-9a-f]{6}$/.test(e.farbe) ? e.farbe : '#7c3aed' })
|
||||
const el = abschnitt('Anrede und Farbe', 'So treten die Buchungsseite und die E-Mails an deine Gäste auf.',
|
||||
@@ -121,7 +152,7 @@ function darstellung(e) {
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ Regeln --- */
|
||||
function regeln(e) {
|
||||
export function regeln(e) {
|
||||
const r = e.regeln || {}
|
||||
const raster = h('select', null, wahl(RASTER_MIN.map((n) => [n, `${n} Minuten`]), r.raster ?? 15))
|
||||
const vorlauf = zahlenFeld('regeln.vorlaufMin', 'Mindestvorlauf (Minuten)', r.vorlaufMin, { min: 0, max: 20160, hinweis: 'Wie kurzfristig darf gebucht werden? 120 sind 2 Stunden, höchstens 20160 (14 Tage).' })
|
||||
@@ -146,7 +177,7 @@ function regeln(e) {
|
||||
}
|
||||
|
||||
/* ------------------------------------------------- Angaben von Gaesten --- */
|
||||
function gastFelder(ctx, e) {
|
||||
export function gastFelder(ctx, e) {
|
||||
const g = e.gastFelder || {}
|
||||
const telefon = h('select', null, wahl(GAST_FELD_WAHL, g.telefon ?? 'optional'))
|
||||
const nachricht = h('select', null, wahl(GAST_FELD_WAHL, g.nachricht ?? 'aus'))
|
||||
@@ -210,7 +241,7 @@ function gastFelder(ctx, e) {
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- Erinnerung --- */
|
||||
function erinnerung(e) {
|
||||
export function erinnerung(e) {
|
||||
const r = e.erinnerung || {}
|
||||
const anbieten = h('input', { type: 'checkbox', checked: r.anbieten !== false })
|
||||
const stunden = h('select', null, wahl(ERINNERUNG_STUNDEN.map((n) => [n, `${n} Stunden vorher`]), r.stundenVorher ?? 24))
|
||||
@@ -221,7 +252,7 @@ function erinnerung(e) {
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------- Stornobedingungen --- */
|
||||
function storno(ctx, e, fristFeld) {
|
||||
export function storno(ctx, e, fristFeld) {
|
||||
const s = e.storno || {}
|
||||
const textfeld = h('textarea', { rows: '6', maxlength: String(MAX.storno) }, s.text ?? '')
|
||||
// das Muster setzt die Frist ein, die im Feld „Selbst absagen bis“ steht (auch ungespeichert)
|
||||
@@ -243,7 +274,7 @@ function storno(ctx, e, fristFeld) {
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- Rechtliches --- */
|
||||
function rechtliches(e) {
|
||||
export function rechtliches(e) {
|
||||
const r = e.recht || {}
|
||||
const gaeste = h('select', null, wahl([['verbraucher', 'Privatpersonen'], ['unternehmer', 'Unternehmen'], ['gemischt', 'Beides']], r.gaesteSind ?? 'verbraucher'))
|
||||
const freizeit = h('input', { type: 'checkbox', checked: r.freizeit === true })
|
||||
@@ -255,7 +286,7 @@ function rechtliches(e) {
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- Bezahlung --- */
|
||||
function zahlung(e) {
|
||||
export function zahlung(e) {
|
||||
const z = e.zahlung || {}
|
||||
const arten = ZAHLARTEN.map(([key, name]) => ({ key, name, input: h('input', { type: 'checkbox', checked: (z.vorOrtArten || []).includes(key) }) }))
|
||||
const el = abschnitt('Bezahlung', 'Bezahlt wird bei dir vor Ort. Welche Zahlarten nimmst du an? Gäste sehen das vor der Buchung.',
|
||||
|
||||
+32
-12
@@ -60,6 +60,34 @@ function personen(t) {
|
||||
: h('p', { class: 'tm-leer' }, 'Noch keine Person und kein Platz. Ohne sie kann niemand buchen.'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Wochenplan einer Person/eines Platzes: je Wochentag ein Textfeld mit den Zeitfenstern (Text wie „09:00–12:30, 13:30–18:00“, leer = geschlossen),
|
||||
* vorbelegt aus r.plan (r darf fehlen). pfad ist der Pfad der Person in der Einstellung ('ressourcen.1'); die Felder heissen `${pfad}.plan.${nr}`,
|
||||
* so finden die Fehler des Servers (ressourcen.1.plan.3) ihr Feld. Der Editor einer Person und der Fragebogen (Schritt „Zeiten“) bauen ihn hiermit.
|
||||
* => { el, lese }: el ist die Gruppe (Beschriftung, Hinweis, sieben Felder), lese() liefert { plan } (geschlossene Tage fehlen im Plan) oder, bei
|
||||
* einem Fenster, das nicht zu lesen ist, { fehler } (Feldname ⇒ Text; dann gibt es keinen Teilplan). Pruefen im Einzelnen bleibt Sache des Servers.
|
||||
*/
|
||||
export function wochenplanFeld(r, pfad) {
|
||||
const texte = planTexte(r?.plan)
|
||||
const tage = WOCHENTAGE_PLAN.map(([nr, tag]) => ({
|
||||
nr, name: `${pfad}.plan.${nr}`,
|
||||
input: h('input', { type: 'text', autocomplete: 'off', placeholder: 'geschlossen', value: texte[nr] }),
|
||||
tag,
|
||||
}))
|
||||
const el = h('fieldset', { class: 'tm-gruppe-felder tm-wochenplan' },
|
||||
h('legend', { class: 'tm-label' }, 'Wochenplan'),
|
||||
h('p', { class: 'tm-hinweis' }, 'Wann kann gebucht werden? Mehrere Zeitfenster trennst du mit Komma, zum Beispiel 09:00–12:30, 13:30–18:00. Leer lassen: an diesem Tag geschlossen. Uhrzeiten in 5-Minuten-Schritten, das Ende bis 24:00.'),
|
||||
h('div', { class: 'tm-raster' }, tage.map(({ name, input, tag }) => feld(name, tag, input))))
|
||||
return {
|
||||
el,
|
||||
lese() {
|
||||
const gelesen = planAusTexten(Object.fromEntries(tage.map(({ nr, input }) => [nr, input.value])))
|
||||
if (gelesen.fehler) return { fehler: Object.fromEntries(Object.entries(gelesen.fehler).map(([nr, text]) => [`${pfad}.plan.${nr}`, text])) }
|
||||
return { plan: gelesen.plan }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Editor einer neuen (r null) oder bestehenden Person/eines Platzes: Formular, darunter (nur bestehend) Kalender-Link und die Rueckfrage zum
|
||||
* Entfernen. Die Rueckfragen stehen neben dem Formular, nicht darin: ein submit der Rueckfrage steigt sonst ins aeussere Formular auf.
|
||||
@@ -78,12 +106,7 @@ function formular(t, { r, schliessen }) {
|
||||
const name = h('input', { type: 'text', maxlength: MAX.ressourcenName, autocomplete: 'off', value: alt.name })
|
||||
const typ = h('select', null, TYPEN.map(([wert, text]) => h('option', { value: wert, selected: alt.typ === wert }, text)))
|
||||
const aktiv = h('input', { type: 'checkbox', checked: alt.aktiv })
|
||||
const texte = planTexte(alt.plan)
|
||||
const tage = WOCHENTAGE_PLAN.map(([nr, tag]) => ({
|
||||
nr, name: `${p}.plan.${nr}`,
|
||||
input: h('input', { type: 'text', autocomplete: 'off', placeholder: 'geschlossen', value: texte[nr] }),
|
||||
tag,
|
||||
}))
|
||||
const wochenplan = wochenplanFeld(alt, p)
|
||||
const zuordnung = leistungen.map((l, k) => ({ id: l.id, input: h('input', { type: 'checkbox', checked: (l.ressourcen || []).includes(alt.id) }), name: `leistungen.${k}.ressourcen`, label: `${l.name}${l.aktiv ? '' : ' (inaktiv)'}` }))
|
||||
|
||||
const speichern = h('button', { type: 'submit', class: 'kb-btn-primary' }, neu ? 'Hinzufügen' : 'Speichern')
|
||||
@@ -105,10 +128,7 @@ function formular(t, { r, schliessen }) {
|
||||
feld(`${p}.name`, 'Name', name, 'So steht es beim Buchen, wenn Gäste eine Person wählen dürfen.'),
|
||||
feld(`${p}.typ`, 'Art', typ)),
|
||||
hakenFeld(`${p}.aktiv`, aktiv, 'Aktiv', 'Nur aktive Personen und Plätze können gebucht werden. Termine, die schon gebucht sind, bleiben bei „Inaktiv“ bestehen.'),
|
||||
h('fieldset', { class: 'tm-gruppe-felder tm-wochenplan' },
|
||||
h('legend', { class: 'tm-label' }, 'Wochenplan'),
|
||||
h('p', { class: 'tm-hinweis' }, 'Wann kann gebucht werden? Mehrere Zeitfenster trennst du mit Komma, zum Beispiel 09:00–12:30, 13:30–18:00. Leer lassen: an diesem Tag geschlossen. Uhrzeiten in 5-Minuten-Schritten, das Ende bis 24:00.'),
|
||||
h('div', { class: 'tm-raster' }, tage.map(({ name: feldName, input, tag }) => feld(feldName, tag, input)))),
|
||||
wochenplan.el,
|
||||
h('fieldset', { class: 'tm-gruppe-felder', tabindex: '-1' },
|
||||
h('legend', { class: 'tm-label' }, 'Diese Leistungen bietet sie an'),
|
||||
zuordnung.length
|
||||
@@ -124,9 +144,9 @@ function formular(t, { r, schliessen }) {
|
||||
form.addEventListener('submit', async (ev) => {
|
||||
ev.preventDefault()
|
||||
const ok = await sp.speichere((entwurf) => {
|
||||
const gelesen = planAusTexten(Object.fromEntries(tage.map(({ nr, input }) => [nr, input.value])))
|
||||
const gelesen = wochenplan.lese()
|
||||
if (gelesen.fehler) {
|
||||
zeigeFehler(form, { fehler: Object.fromEntries(Object.entries(gelesen.fehler).map(([nr, text]) => [`${p}.plan.${nr}`, text])) })
|
||||
zeigeFehler(form, { fehler: gelesen.fehler })
|
||||
return false
|
||||
}
|
||||
const ressource = { ...alt, name: name.value, typ: typ.value, aktiv: aktiv.checked, plan: gelesen.plan }
|
||||
|
||||
+19
-8
@@ -2,7 +2,7 @@
|
||||
initTermine(rootEl, { projectId }): Projektauswahl, Vorlagenauswahl beim ersten Oeffnen, Reiter-Geruest und die Meldung ueber den Reitern.
|
||||
Die Reiter stehen in REITER: jeder ist ein Modul mit render(panel, ctx) => void | Promise (termine-tab-termine.js, termine-tab-leistungen.js,
|
||||
termine-tab-team.js, termine-tab-einstellungen.js; die drei letzten speichern ueber termine-speichern.js).
|
||||
ctx = { s, projectId, daten, nurLesen, auswahl, aktuell(), api, meldung, hinweise, zumReiter, neuLaden }: Zustand und Pruefung "bin ich noch
|
||||
ctx = { s, projectId, daten, nurLesen, auswahl, aktuell(), api, meldung, hinweise, zumReiter, neuLaden } (baueCtx): Zustand und Pruefung "bin ich noch
|
||||
aktuell?" bekommen die Reiter nur ueber ctx, nie ueber Modulvariablen. daten ist die Antwort von GET /api/termine/einstellungen
|
||||
(die Reiter, die speichern, halten daten.einstellung und daten.version aktuell), nurLesen gilt fuer abgeschaltete Funktionen,
|
||||
hinweise() zeichnet die Hinweisleiste ueber den Reitern neu (z. B. wenn Telefon und Anschrift nach dem Speichern nicht mehr fehlen). Server: server/routes/termine.js. Tage sind Berliner Kalendertage 'JJJJ-MM-TT';
|
||||
@@ -164,15 +164,18 @@ function zeigeMeldung(s, text, art = 'ok') {
|
||||
el.scrollIntoView({ block: 'nearest' })
|
||||
}
|
||||
|
||||
function zeigeReiter(s, { projekt, aktuell }) {
|
||||
/**
|
||||
* Kontext fuer die Module, die in der Huelle laufen (die Reiter, der Fragebogen): Zustand und die Pruefung "bin ich noch aktuell?" kommen nur hierher.
|
||||
* hinweise() zeichnet die Hinweisleiste ueber den Reitern neu (der Fragebogen hat keine), zumReiter(key) wechselt den Reiter; beide duerfen fehlen.
|
||||
* nurLesen und daten sind die der Huelle im Moment des Aufrufs; neuLaden() holt die Daten neu und ersetzt sie in s und im Kontext.
|
||||
*/
|
||||
export function baueCtx(s, { projekt, aktuell, hinweise = () => {}, zumReiter = () => {} }) {
|
||||
const projectId = projekt.projectId
|
||||
const nurLesen = s.nurLesen
|
||||
s.meldungEl = h('div', { class: 'tm-meldung', role: 'status', 'aria-live': 'polite', tabindex: '-1' })
|
||||
const ctx = {
|
||||
s,
|
||||
projectId,
|
||||
daten: s.daten,
|
||||
nurLesen,
|
||||
nurLesen: s.nurLesen,
|
||||
auswahl,
|
||||
aktuell,
|
||||
api: {
|
||||
@@ -183,18 +186,26 @@ function zeigeReiter(s, { projekt, aktuell }) {
|
||||
},
|
||||
// eine Meldung, die erst nach dem Wechsel der Website oder der Ansicht eintrifft, gehoert nicht mehr auf diese Seite
|
||||
meldung: (text, art) => { if (aktuell()) zeigeMeldung(s, text, art) },
|
||||
hinweise: () => { if (aktuell()) zeichneHinweise() },
|
||||
zumReiter: (key) => waehleReiter(key),
|
||||
hinweise: () => { if (aktuell()) hinweise() },
|
||||
zumReiter: (key) => zumReiter(key),
|
||||
neuLaden: async () => {
|
||||
const daten = await ladeDaten(projectId)
|
||||
if (aktuell()) {
|
||||
s.daten = daten
|
||||
ctx.daten = daten
|
||||
zeichneHinweise()
|
||||
hinweise()
|
||||
}
|
||||
return daten
|
||||
},
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
function zeigeReiter(s, { projekt, aktuell }) {
|
||||
const projectId = projekt.projectId
|
||||
const nurLesen = s.nurLesen
|
||||
s.meldungEl = h('div', { class: 'tm-meldung', role: 'status', 'aria-live': 'polite', tabindex: '-1' })
|
||||
const ctx = baueCtx(s, { projekt, aktuell, hinweise: zeichneHinweise, zumReiter: waehleReiter })
|
||||
|
||||
// Hinweisleiste: abgeschaltet, oder Pflichtangaben fuer Gaeste fehlen (GET /einstellungen: fehlt). Sie folgt s.daten.
|
||||
const hinweisBox = h('div', { class: 'tm-hinweise' })
|
||||
|
||||
Reference in new issue
Block a user