Terminplaner: Portal-Ansicht, Kachel, Termine
Kachel in Funktionen (Kommt bald / Aus / Aktiv mit Anfragen-Zahl / Abgeschaltet), Ansicht #termine als Unterseite von Funktionen: Projektauswahl, Vorlagenauswahl beim ersten Oeffnen, Reiter-Geruest (Platzhalter fuer Leistungen, Team & Plaetze, Einstellungen) und der Reiter Termine mit Tag/Woche/Monat, Filtern, Aktionen mit Rueckfragen in der Seite, Termin eintragen und Zeit sperren. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
70940e02c8
commit
43e4826ae3
10 files changed
+2552
-8
No files matched your search
+60
-2
@@ -136,6 +136,58 @@ function analyseTile(projects) {
|
||||
</article>`
|
||||
}
|
||||
|
||||
/* Terminplaner: Kachel nur, wenn der Schalter es erlaubt (termine.verfuegbar), sonst "Kommt bald". Die Ansicht selbst
|
||||
(termine.js) wird von app.js geladen; hier steht nur die Kachel, damit funktionen.js keinen weiteren Import braucht. */
|
||||
const TERMINE_TEXT = 'Deine Kundschaft bucht Termine direkt über deine Website.'
|
||||
const termineOf = (project) => project.features?.termine || { state: 'off' }
|
||||
const anfragenText = (n) => (n === 1 ? '1 Anfrage wartet' : `${n} Anfragen warten`)
|
||||
|
||||
function pickTermineProject(projects) {
|
||||
return projects.find((p) => termineOf(p).state === 'active') || projects.find((p) => termineOf(p).state === 'disabled') || projects[0]
|
||||
}
|
||||
|
||||
function termineTile(projects) {
|
||||
const states = projects.map((p) => termineOf(p).state)
|
||||
const active = states.filter((s) => s === 'active').length
|
||||
const target = pickTermineProject(projects)
|
||||
const disabled = !active && states.length > 0 && states.every((s) => s === 'disabled')
|
||||
const pill = active ? ['Aktiv', 'on'] : disabled ? ['Abgeschaltet', 'off'] : ['Aus', 'off']
|
||||
let extra
|
||||
let action
|
||||
if (active) {
|
||||
extra = '<p class="fn-count" data-termine-count hidden></p>'
|
||||
action = `<button type="button" class="kb-btn-primary fn-tile-cta" data-view="termine" data-project="${esc(target.projectId)}">Termine öffnen</button>`
|
||||
} else if (disabled) {
|
||||
extra = '<p>Der Terminplaner ist für diese Website abgeschaltet. Deine Termine kannst du noch ansehen und exportieren.</p>'
|
||||
action = `<button type="button" class="kb-btn-primary fn-tile-cta" data-view="termine" data-project="${esc(target.projectId)}">Termine ansehen</button>`
|
||||
} else {
|
||||
extra = '<p>Wir schalten den Terminplaner für dich frei.</p>'
|
||||
action = '<button type="button" class="kb-btn-primary fn-tile-cta" data-view="chat">Chat öffnen</button>'
|
||||
}
|
||||
const count = projects.length > 1 && active ? `<p class="fn-count">aktiv für ${active} von ${projects.length} Websites</p>` : ''
|
||||
return `
|
||||
<article class="fn-tile" data-fn-tile="termine">
|
||||
<div class="fn-tile-head">${svg('calendar')}<span class="fn-pill fn-pill-${pill[1]}">${pill[0]}</span></div>
|
||||
<h3>Terminplaner</h3>
|
||||
<p>${TERMINE_TEXT}</p>
|
||||
${extra}
|
||||
${count}
|
||||
${action}
|
||||
</article>`
|
||||
}
|
||||
|
||||
// "{n} Anfragen warten" aus anfragenOffen der aktiven Websites; ein Fehler laesst die Kachel einfach ohne Zahl stehen
|
||||
async function showTermineCount(root, projects) {
|
||||
const el = root.querySelector('[data-termine-count]')
|
||||
const active = projects.filter((p) => termineOf(p).state === 'active')
|
||||
if (!el || !active.length) return
|
||||
const counts = await Promise.all(active.map((p) => request(`/api/termine/einstellungen?projectId=${encodeURIComponent(p.projectId)}`).then((d) => Number(d.anfragenOffen) || 0, () => 0)))
|
||||
const open = counts.reduce((sum, n) => sum + n, 0)
|
||||
if (open < 1) return
|
||||
el.textContent = anfragenText(open)
|
||||
el.hidden = false
|
||||
}
|
||||
|
||||
const soonTile = (icon, title, text) => `
|
||||
<article class="fn-tile fn-tile-soon">
|
||||
<div class="fn-tile-head">${svg(icon)}<span class="fn-pill fn-pill-soon">Kommt bald</span></div>
|
||||
@@ -145,15 +197,21 @@ const soonTile = (icon, title, text) => `
|
||||
|
||||
export async function initFunktionen(root) {
|
||||
if (!root) return
|
||||
const { projects = [] } = await request('/api/funktionen')
|
||||
const { projects = [], termine } = await request('/api/funktionen')
|
||||
root.innerHTML = `
|
||||
<div class="fn-tiles">
|
||||
${newsTile(projects)}
|
||||
${soonTile('calendar', 'Terminplaner', 'Deine Kundschaft bucht Termine direkt über deine Website.')}
|
||||
${termine?.verfuegbar ? termineTile(projects) : soonTile('calendar', 'Terminplaner', TERMINE_TEXT)}
|
||||
${analyseTile(projects)}
|
||||
</div>`
|
||||
root.querySelector('[data-fn-tile="news"] .fn-tile-cta')?.addEventListener('click', (ev) => { selection.projectId = ev.currentTarget.dataset.project })
|
||||
root.querySelector('[data-fn-tile="analyse"] .fn-tile-cta[data-project]')?.addEventListener('click', (ev) => { analyseSel.projectId = ev.currentTarget.dataset.project })
|
||||
// Die Terminplaner-Ansicht liest das gewaehlte Projekt aus ihrem Behaelter (termine.js, initTermine)
|
||||
root.querySelector('[data-fn-tile="termine"] .fn-tile-cta[data-project]')?.addEventListener('click', (ev) => {
|
||||
const body = document.getElementById('termine-body')
|
||||
if (body) body.dataset.wunschProjekt = ev.currentTarget.dataset.project
|
||||
})
|
||||
if (termine?.verfuegbar) await showTermineCount(root, projects)
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- News --- */
|
||||
|
||||
Reference in new issue
Block a user