Terminplaner: Portal-Ansicht Einstellungen – Nachbesserungen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0b75b31009
commit
3ad056f261
10 files changed
+568
-342
No files matched your search
+52
-72
@@ -6,8 +6,8 @@
|
||||
der Server. Abgeschaltete Funktion (ctx.nurLesen): Felder gesperrt, keine Knoepfe zum Aendern, keine Links. */
|
||||
|
||||
import { LIMITS, MAX, WOCHENTAGE_PLAN, eindeutigeId, planAusTexten, planTexte } from './termine-view.js'
|
||||
import { abschnitt, allgemeinerFehler, feld, frage, gesperrt, h, hakenFeld, knopfZeile, zeigeFehler } from './termine-dom.js'
|
||||
import { entfernenFrage, neuerSpeicher } from './termine-speichern.js'
|
||||
import { abschnitt, allgemeinerFehler, editorZeile, feld, frage, gesperrt, h, hakenFeld, knopfZeile, neuerEintrag, zeigeFehler } from './termine-dom.js'
|
||||
import { entfernenKnopf, nachSpeichern, neuerSpeicher, stelleVon } from './termine-speichern.js'
|
||||
import { abwesenheiten, feiertage, sonderoeffnungen } from './termine-team-ausnahmen.js'
|
||||
|
||||
const TYPEN = [['person', 'Person'], ['platz', 'Platz']]
|
||||
@@ -29,49 +29,32 @@ function personen(t) {
|
||||
const { ctx } = t
|
||||
const e = ctx.daten.einstellung
|
||||
const liste = e.ressourcen || []
|
||||
const voll = liste.length >= LIMITS.ressourcen
|
||||
const neuBox = h('div', { class: 'tm-formular-box' })
|
||||
const neuKnopf = ctx.nurLesen ? null : h('button', {
|
||||
type: 'button', class: 'kb-btn-primary', 'data-werkzeug': 'ressource-neu', 'aria-expanded': 'false', disabled: voll,
|
||||
onclick: () => {
|
||||
if (neuBox.firstChild) { neuBox.replaceChildren(); neuKnopf.setAttribute('aria-expanded', 'false'); return }
|
||||
neuKnopf.setAttribute('aria-expanded', 'true')
|
||||
const editor = formular(t, { index: liste.length, r: null, schliessen: () => { neuBox.replaceChildren(); neuKnopf.setAttribute('aria-expanded', 'false'); neuKnopf.focus() } })
|
||||
neuBox.replaceChildren(editor)
|
||||
editor.querySelector('input')?.focus()
|
||||
},
|
||||
}, '+ Person oder Platz hinzufügen')
|
||||
const neu = neuerEintrag({
|
||||
nurLesen: ctx.nurLesen, text: '+ Person oder Platz hinzufügen', werkzeug: 'ressource-neu', primaer: true,
|
||||
voll: liste.length >= LIMITS.ressourcen, grenzText: `Mehr als ${LIMITS.ressourcen} Personen oder Plätze sind nicht möglich.`,
|
||||
baue: (schliessen) => formular(t, { r: null, schliessen }),
|
||||
})
|
||||
|
||||
const reihe = (r, index) => {
|
||||
const li = h('li', { class: 'tm-eintrag', 'data-id': r.id })
|
||||
const editor = h('div', { class: 'tm-editor-box' })
|
||||
const reihe = (r) => {
|
||||
const leistungen = (e.leistungen || []).filter((l) => (l.ressourcen || []).includes(r.id)).length
|
||||
const tage = Object.keys(r.plan || {}).length
|
||||
const oeffnen = h('button', {
|
||||
type: 'button', class: 'kb-btn-outline tm-knopf', 'data-aktion': 'bearbeiten', 'aria-expanded': 'false', 'aria-label': `${ctx.nurLesen ? 'Ansehen' : 'Bearbeiten'}: ${r.name}`,
|
||||
onclick: () => {
|
||||
if (editor.firstChild) { editor.replaceChildren(); oeffnen.setAttribute('aria-expanded', 'false'); return }
|
||||
oeffnen.setAttribute('aria-expanded', 'true')
|
||||
const neuerEditor = formular(t, { index, r, schliessen: () => { editor.replaceChildren(); oeffnen.setAttribute('aria-expanded', 'false'); oeffnen.focus() } })
|
||||
editor.replaceChildren(neuerEditor)
|
||||
neuerEditor.querySelector('input')?.focus()
|
||||
},
|
||||
}, ctx.nurLesen ? 'Ansehen' : 'Bearbeiten')
|
||||
li.append(
|
||||
h('div', { class: 'tm-kopf' },
|
||||
h('div', { class: 'tm-haupt' },
|
||||
h('strong', { class: 'tm-name' }, r.name),
|
||||
h('span', { class: 'tm-meta' }, [r.typ === 'platz' ? 'Platz' : 'Person', tage ? `Zeiten an ${zaehle(tage, 'Tag', 'Tagen')}` : 'keine Zeiten', leistungen ? zaehle(leistungen, 'Leistung', 'Leistungen') : 'keine Leistung'].join(' · '))),
|
||||
h('span', { class: `tm-pille tm-pille-${r.aktiv ? 'ok' : 'off'}` }, r.aktiv ? 'Aktiv' : 'Inaktiv')),
|
||||
h('div', { class: 'tm-aktionen' }, oeffnen),
|
||||
editor)
|
||||
return li
|
||||
return editorZeile({
|
||||
attrs: { 'data-id': r.id },
|
||||
nurLesen: ctx.nurLesen,
|
||||
label: r.name,
|
||||
haupt: [
|
||||
h('strong', { class: 'tm-name' }, r.name),
|
||||
h('span', { class: 'tm-meta' }, [r.typ === 'platz' ? 'Platz' : 'Person', tage ? `Zeiten an ${zaehle(tage, 'Tag', 'Tagen')}` : 'keine Zeiten', leistungen ? zaehle(leistungen, 'Leistung', 'Leistungen') : 'keine Leistung'].join(' · ')),
|
||||
],
|
||||
pille: h('span', { class: `tm-pille tm-pille-${r.aktiv ? 'ok' : 'off'}` }, r.aktiv ? 'Aktiv' : 'Inaktiv'),
|
||||
baue: (schliessen) => formular(t, { r, schliessen }),
|
||||
})
|
||||
}
|
||||
|
||||
return abschnitt('Personen und Plätze',
|
||||
'Wer oder was kann gebucht werden? Jede Person und jeder Platz hat eigene Zeiten und bietet bestimmte Leistungen an. Die Leistungen selbst legst du im Reiter „Leistungen“ an.',
|
||||
ctx.nurLesen ? null : h('div', { class: 'tm-werkzeug-zeile' }, neuKnopf, voll ? h('p', { class: 'tm-hinweis' }, `Mehr als ${LIMITS.ressourcen} Personen oder Plätze sind nicht möglich.`) : null),
|
||||
neuBox,
|
||||
neu.leiste,
|
||||
neu.box,
|
||||
liste.length
|
||||
? h('ul', { class: 'tm-eintraege', 'aria-label': 'Personen und Plätze' }, liste.map(reihe))
|
||||
: h('p', { class: 'tm-leer' }, 'Noch keine Person und kein Platz. Ohne sie kann niemand buchen.'))
|
||||
@@ -80,12 +63,14 @@ function personen(t) {
|
||||
/**
|
||||
* 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.
|
||||
* index = Stelle in ressourcen (so heissen die Pfade der Fehler des Servers).
|
||||
* Die Pfade der Fehler des Servers nennen die Stelle in der gespeicherten Liste (ressourcen.1.plan.3); die Felder heissen danach. Beim
|
||||
* Absenden wird die Person/der Platz per ID gesucht, nicht per Stelle.
|
||||
*/
|
||||
function formular(t, { index, r, schliessen }) {
|
||||
const { ctx, sp, zeichne } = t
|
||||
function formular(t, { r, schliessen }) {
|
||||
const { ctx, sp } = t
|
||||
const e = ctx.daten.einstellung
|
||||
const neu = r === null
|
||||
const index = neu ? (e.ressourcen || []).length : (e.ressourcen || []).findIndex((x) => x.id === r.id)
|
||||
const alt = r || { id: eindeutigeId('r', (e.ressourcen || []).map((x) => x.id)), typ: 'person', name: '', aktiv: true, plan: {} }
|
||||
const p = `ressourcen.${index}`
|
||||
const leistungen = e.leistungen || []
|
||||
@@ -102,8 +87,17 @@ function formular(t, { index, r, schliessen }) {
|
||||
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')
|
||||
const frageBox = h('div', { class: 'tm-frage' })
|
||||
const entfernen = neu || ctx.nurLesen ? null : h('button', { type: 'button', class: 'kb-btn-outline tm-gefahr', onclick: () => fragen() }, 'Entfernen')
|
||||
const entfernen = neu || ctx.nurLesen ? null : entfernenKnopf(sp, {
|
||||
titel: `„${alt.name}“ entfernen?`,
|
||||
text: 'Die Zeiten, die Zuordnung zu den Leistungen und die Abwesenheiten gehen mit verloren. Gibt es noch künftige Termine, geht das Entfernen nicht. Dann setze sie auf „Inaktiv“.',
|
||||
fehlerPfad: `ressourcen.${alt.id}`,
|
||||
aendere: (entwurf) => {
|
||||
entwurf.ressourcen = entwurf.ressourcen.filter((x) => x.id !== alt.id)
|
||||
for (const l of entwurf.leistungen) l.ressourcen = (l.ressourcen || []).filter((id) => id !== alt.id)
|
||||
entwurf.abwesenheiten = (entwurf.abwesenheiten || []).filter((a) => a.ressourceId !== alt.id)
|
||||
},
|
||||
nachher: () => nachSpeichern(t, `„${alt.name}“ entfernt.`, '[data-werkzeug="ressource-neu"]'),
|
||||
})
|
||||
const form = h('form', { class: 'kb-panel tm-formular', novalidate: true, 'aria-label': neu ? 'Person oder Platz hinzufügen' : `${ctx.nurLesen ? 'Person oder Platz' : 'Bearbeiten'}: ${alt.name}` },
|
||||
h('h3', null, neu ? 'Neue Person oder neuer Platz' : alt.name),
|
||||
gesperrt(ctx.nurLesen, [
|
||||
@@ -125,7 +119,7 @@ function formular(t, { index, r, schliessen }) {
|
||||
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))
|
||||
entfernen?.knopf))
|
||||
|
||||
form.addEventListener('submit', async (ev) => {
|
||||
ev.preventDefault()
|
||||
@@ -136,8 +130,13 @@ function formular(t, { index, r, schliessen }) {
|
||||
return false
|
||||
}
|
||||
const ressource = { ...alt, name: name.value, typ: typ.value, aktiv: aktiv.checked, plan: gelesen.plan }
|
||||
if (neu) entwurf.ressourcen.push(ressource)
|
||||
else entwurf.ressourcen[index] = ressource
|
||||
if (neu) {
|
||||
entwurf.ressourcen.push(ressource)
|
||||
} else {
|
||||
const stelle = stelleVon(entwurf.ressourcen, alt.id, form, 'Diese Person oder dieser Platz')
|
||||
if (stelle < 0) return false
|
||||
entwurf.ressourcen[stelle] = ressource
|
||||
}
|
||||
// die Zuordnung steht bei den Leistungen: genau diese Person/diesen Platz bei jeder Leistung ein- oder austragen
|
||||
for (const { id, input } of zuordnung) {
|
||||
const leistung = entwurf.leistungen.find((l) => l.id === id)
|
||||
@@ -146,30 +145,12 @@ function formular(t, { index, r, schliessen }) {
|
||||
leistung.ressourcen = input.checked ? [...ids, alt.id] : ids
|
||||
}
|
||||
return true
|
||||
}, { container: form, knopf: speichern })
|
||||
}, { container: form })
|
||||
if (!ok || !sp.nochHier()) return
|
||||
zeichne()
|
||||
ctx.meldung(neu ? `„${name.value.trim()}“ ist hinzugefügt.` : 'Gespeichert.')
|
||||
t.wurzel.querySelector(`li[data-id="${alt.id}"] [data-aktion="bearbeiten"]`)?.focus()
|
||||
nachSpeichern(t, neu ? `„${name.value.trim()}“ ist hinzugefügt.` : 'Gespeichert.', `li[data-id="${alt.id}"] [data-aktion="bearbeiten"]`)
|
||||
})
|
||||
|
||||
function fragen() {
|
||||
const box = entfernenFrage(sp, {
|
||||
titel: `„${alt.name}“ entfernen?`,
|
||||
text: 'Die Zeiten, die Zuordnung zu den Leistungen und die Abwesenheiten gehen mit verloren. Gibt es noch künftige Termine, geht das Entfernen nicht. Dann setze sie auf „Inaktiv“.',
|
||||
fehlerPfad: `ressourcen.${alt.id}`,
|
||||
aendere: (entwurf) => {
|
||||
entwurf.ressourcen = entwurf.ressourcen.filter((x) => x.id !== alt.id)
|
||||
for (const l of entwurf.leistungen) l.ressourcen = (l.ressourcen || []).filter((id) => id !== alt.id)
|
||||
entwurf.abwesenheiten = (entwurf.abwesenheiten || []).filter((a) => a.ressourceId !== alt.id)
|
||||
},
|
||||
nachher: () => { zeichne(); ctx.meldung(`„${alt.name}“ entfernt.`) },
|
||||
zurueck: () => { frageBox.replaceChildren(); entfernen.focus() },
|
||||
})
|
||||
frageBox.replaceChildren(box)
|
||||
box.focus()
|
||||
}
|
||||
return h('div', { class: 'tm-editor' }, form, neu || ctx.nurLesen ? null : kalenderLink(t, alt), frageBox)
|
||||
return h('div', { class: 'tm-editor' }, form, neu || ctx.nurLesen ? null : kalenderLink(t, alt), entfernen?.box)
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------- Kalender-Abo --- */
|
||||
@@ -201,13 +182,13 @@ function kalenderLink(t, r) {
|
||||
}
|
||||
|
||||
function fragen(ausloeser) {
|
||||
const { ok, zeile } = knopfZeile('Neuen Link erzeugen', () => { frageBox.replaceChildren(); ausloeser.focus() })
|
||||
const { zeile } = knopfZeile('Neuen Link erzeugen', () => { frageBox.replaceChildren(); ausloeser.focus() })
|
||||
const box = frage('Neuen Kalender-Link erzeugen?',
|
||||
h('p', null, 'Der bisherige Link funktioniert danach nicht mehr. Kalender, die ihn abonniert haben, zeigen keine neuen Termine mehr, bis du dort den neuen Link einträgst. Tu das, wenn der Link in falsche Hände geraten ist.'),
|
||||
allgemeinerFehler(),
|
||||
zeile)
|
||||
box.addEventListener('submit', async () => {
|
||||
ok.disabled = true
|
||||
// exklusiv wie ein Speichern: das Erzeugen aendert die Version, ein gleichzeitiges Speichern bekaeme sonst die alte
|
||||
box.addEventListener('submit', () => t.sp.exklusiv(async () => {
|
||||
try {
|
||||
const antwort = await ctx.api.send('POST', `/ressourcen/${encodeURIComponent(r.id)}/feed`)
|
||||
if (!ctx.aktuell() || !eingabe.isConnected) return
|
||||
@@ -220,10 +201,9 @@ function kalenderLink(t, r) {
|
||||
ausloeser.focus()
|
||||
} catch (err) {
|
||||
if (!ctx.aktuell() || !eingabe.isConnected) return
|
||||
ok.disabled = false
|
||||
zeigeFehler(box, err)
|
||||
}
|
||||
})
|
||||
}))
|
||||
frageBox.replaceChildren(box)
|
||||
box.focus()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user