Öffnungszeiten live: im Abo-Modell Einschalten und kein Behalten-Weg
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5f3d8f3e96
commit
c37faabcd3
9 files changed
+190
-18
No files matched your search
+3
-1
@@ -30,6 +30,8 @@ let S = null // aktuelle Sitzung; eine alte Antwort darf eine neuere nicht ueber
|
||||
const textOr = (x) => (typeof x === 'string' && x.trim() ? x : '')
|
||||
// Abo-Modell (Antwort von GET /api/funktionen, strikt): die Reiter zeichnet die Etage, die Ansicht meldet sie (fn-reiter.js)
|
||||
const istAbo = (s) => s.modell === 'abo'
|
||||
/** Mitarbeiter in der Kundenansicht (window.KbView, von der Hülle gesetzt): Einschalten bleibt gesperrt. */
|
||||
const inKundenansicht = () => typeof window !== 'undefined' && Boolean(window.KbView?.id?.())
|
||||
/** Eine Ansicht ohne Reiter meldet die leere Liste, damit die Etage nicht die Reiter der vorigen stehen laesst. */
|
||||
const keineReiter = (s) => { if (istAbo(s)) meldeReiter(s.root, { eintraege: [] }) }
|
||||
const zustandVon = (projekt) => projekt?.features?.unterkunft?.state || 'off'
|
||||
@@ -142,7 +144,7 @@ function zeigeAnfrage(s, { status, aktuell }) {
|
||||
s.buehne.replaceChildren(wurzel)
|
||||
renderEinschaltSeite(wurzel, {
|
||||
ctx: { projectId, aktuell },
|
||||
daten: { offen: [], kundenansicht: false },
|
||||
daten: { offen: [], kundenansicht: inKundenansicht() },
|
||||
key: 'unterkunft',
|
||||
zusammenfassung: () => (ansicht.anfrage ? [{ schritt: 'anfrage', titel: 'Deine Anfrage', zeilen: anfrageZeilen(ansicht.anfrage).map(({ name, wert }) => `${name}: ${wert}`) }] : []),
|
||||
einschalten: s.einschalten,
|
||||
|
||||
@@ -535,7 +535,7 @@ function renderBookingAbo(stage, project, { einschalten, neuLaden }) {
|
||||
Promise.resolve().then(neuLaden).catch(() => {})
|
||||
}
|
||||
try {
|
||||
Promise.resolve(einschalten(platz, { key: 'news', projectId: project.projectId, viewAs: false, onFertig })).catch(fehlt)
|
||||
Promise.resolve(einschalten(platz, { key: 'news', projectId: project.projectId, viewAs: Boolean(window.KbView?.id?.()), onFertig })).catch(fehlt)
|
||||
} catch {
|
||||
fehlt()
|
||||
}
|
||||
|
||||
+3
-1
@@ -53,6 +53,8 @@ const zustandVon = (projekt) => projekt?.features?.shop?.state || 'off'
|
||||
const text = (wert) => (typeof wert === 'string' ? wert : '')
|
||||
// Abo-Modell (Antwort von GET /api/funktionen, strikt): die Reiter zeichnet die Etage, die Ansicht meldet sie (fn-reiter.js)
|
||||
const istAbo = (s) => s.funktionen?.modell === 'abo'
|
||||
/** Mitarbeiter in der Kundenansicht (window.KbView, von der Hülle gesetzt): Einschalten bleibt gesperrt. */
|
||||
const inKundenansicht = () => typeof window !== 'undefined' && Boolean(window.KbView?.id?.())
|
||||
/** Eine Ansicht ohne Reiter meldet die leere Liste, damit die Etage nicht die Reiter der vorigen stehen laesst. */
|
||||
const keineReiter = (s) => { if (istAbo(s)) meldeReiter(s.root, { eintraege: [] }) }
|
||||
|
||||
@@ -284,7 +286,7 @@ function zeigeBestellseite(s, { status, aktuell }) {
|
||||
const daten = bestelldaten(s, status)
|
||||
if (istAbo(s)) {
|
||||
// Abo-Modell: die Zusage der Anfrage fuehrt zum Einschalten (kein Preis, keine Haken, kein Checkout)
|
||||
renderEinschaltSeite(wurzel, { ctx, daten, key: 'shop', zusammenfassung: zusammenfassungVon(daten.anfrage), einschalten: s.einschalten, nachEinschalten: () => ladeNeu(s) })
|
||||
renderEinschaltSeite(wurzel, { ctx, daten: { ...daten, kundenansicht: inKundenansicht() }, key: 'shop', zusammenfassung: zusammenfassungVon(daten.anfrage), einschalten: s.einschalten, nachEinschalten: () => ladeNeu(s) })
|
||||
return
|
||||
}
|
||||
renderBestellen(wurzel, { ctx, daten, key: 'shop', zusammenfassung: zusammenfassungVon(daten.anfrage) })
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
ctx (baut zeiten.js): { projectId, features, ustHinweis, kundenansicht (Boolean), neuZeichnen({ meldung }), fertig({ danke }), zurueck() }.
|
||||
Bei 409 (inklusive, kein_abo, gekuendigt, nicht_aktiv, fassung_veraltet, schon bestellt …) holt neuZeichnen die Liste neu und zeichnet die Ansicht
|
||||
nach dem neuen Stand mit der Meldung des Servers; fehlt ctx.neuZeichnen oder scheitert es, bleibt die Meldung am Knopf.
|
||||
zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, danke, meldung }, { behalten, weiter }) ist die Karte fuer ordered, setup, active
|
||||
und disabled (Zustand off hat die Seite oben). */
|
||||
zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, danke, meldung, modell }, { behalten, weiter }) ist die Karte fuer ordered, setup, active
|
||||
und disabled (Zustand off hat die Seite oben).
|
||||
Abo-Modell (modell === 'abo' in GET /api/funktionen, strikt): renderZeitenEinschalten zeigt statt der Seiten oben die gemeinsame Einschalt-Seite
|
||||
(funktion-bestellen.js, Ablauf von P3); die Karte kennt kein Behalten und keinen Preis: „Eingeschaltet am“, bei Abo-Ende die Frist (Z2), nach der
|
||||
Abschaltung „Deine Eingaben bleiben … gespeichert“ (Z3). */
|
||||
|
||||
import { bestellKoerper, sicherLink } from './funktion-bestellen.js'
|
||||
import { bestellKoerper, renderEinschaltSeite, sicherLink } from './funktion-bestellen.js'
|
||||
import { fehlerKarte, h, hakenFeld, request } from './termine-dom.js'
|
||||
|
||||
const WEGE = {
|
||||
@@ -260,6 +263,21 @@ export function renderZeitenBestellen(root, { ctx = {}, angebot = {}, projekt =
|
||||
return seite
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------- Einschalten im Abo-Modell --- */
|
||||
/**
|
||||
* Abo-Modell, Zustand off: die gemeinsame Einschalt-Seite (renderEinschaltSeite) mit key 'zeiten'. Den Ablauf (Bestätigung, Knopf, Fehlertexte) zeichnet
|
||||
* ctx.einschalten (Init-Option der Hülle, P3); nach dem Einschalten zeichnet ctx.fertig({ danke }) die Ansicht neu. Kein Preis, kein Haken, kein Behalten.
|
||||
*/
|
||||
export function renderZeitenEinschalten(root, { ctx = {}, projekt = {} } = {}) {
|
||||
return renderEinschaltSeite(root, {
|
||||
ctx: { ...ctx, projectId: text(ctx.projectId) || text(projekt.projectId) },
|
||||
daten: { offen: [], kundenansicht: ctx.kundenansicht === true },
|
||||
key: 'zeiten',
|
||||
einschalten: ctx.einschalten,
|
||||
nachEinschalten: () => (typeof ctx.fertig === 'function' ? ctx.fertig({ danke: 'eingeschaltet' }) : undefined),
|
||||
})
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- Statuskarte --- */
|
||||
const PILLE = { ordered: ['Zahlung offen', 'wait'], setup: ['In Einrichtung', 'wait'], active: ['Aktiv', 'ok'], disabled: ['Abgeschaltet', 'off'] }
|
||||
const DANKE = {
|
||||
@@ -286,7 +304,8 @@ function dankeText(danke, feature) {
|
||||
* weiter({ bestellen, meldung }) wird gerufen, wenn „Zahlung fortsetzen“ scheitert (bestellen: es gibt keine offene Zahlungsseite mehr, die Ansicht
|
||||
* zeigt wieder die Bestellseite).
|
||||
*/
|
||||
export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, danke, meldung } = {}, { behalten, weiter } = {}) {
|
||||
export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, danke, meldung, modell } = {}, { behalten, weiter } = {}) {
|
||||
const abo = modell === 'abo'
|
||||
const state = text(feature?.state) || 'off'
|
||||
const [pilleText, pilleArt] = PILLE[state] || PILLE.setup
|
||||
const preis = text(angebot?.preis) || '30 €'
|
||||
@@ -298,7 +317,7 @@ export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, da
|
||||
const zeile = (name, wert) => h('div', null, h('dt', null, name), h('dd', null, wert))
|
||||
const fakten = [
|
||||
state === 'setup' || state === 'active' ? (hatTicket ? zeile('Ticketnummer', `#${ticket}`) : null) : null,
|
||||
bestelltAm ? zeile('Bestellt am', bestelltAm) : null,
|
||||
bestelltAm ? zeile(abo ? 'Eingeschaltet am' : 'Bestellt am', bestelltAm) : null,
|
||||
].filter(Boolean)
|
||||
|
||||
const aboEnde = feature?.disabledReason === 'abo_ende'
|
||||
@@ -327,7 +346,11 @@ export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, da
|
||||
const behaltenKnopf = (beschriftung) => h('button', { type: 'button', class: 'kb-btn-outline', 'data-aktion': 'behalten', disabled: sperre, onclick: () => { if (typeof behalten === 'function') behalten() } }, beschriftung)
|
||||
const lage = behaltenLage(angebot)
|
||||
let behaltenBox = null
|
||||
if (frist && lage === 'bezahlen') {
|
||||
if (abo) {
|
||||
// Abo-Modell: kein Behalten gegen Geld; Öffnungszeiten live läuft nach dem Abo-Ende noch bis zur Frist (Text Z2)
|
||||
if (frist && lage === 'bezahlen') behaltenBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'abo-ende' }, h('span', null, `Dein Wartungs-Abo ist beendet. Öffnungszeiten live läuft noch bis ${frist} und schaltet sich dann ab.`))
|
||||
else if (frist && lage === 'abo') behaltenBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'abo-laeuft' }, h('span', null, 'Dein Wartungs-Abo läuft wieder – Öffnungszeiten live ist darin enthalten.'))
|
||||
} else if (frist && lage === 'bezahlen') {
|
||||
behaltenBox = h('div', { class: 'tm-box tm-box-warn', role: 'note', 'data-hinweis': 'behalten' },
|
||||
h('span', null, `Dein Wartungs-Abo ist beendet – behalte die Funktion bis ${frist} für einmalig ${preis}.`),
|
||||
behaltenKnopf(`Für ${preis} behalten`))
|
||||
@@ -335,7 +358,11 @@ export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, da
|
||||
behaltenBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'abo-laeuft' }, h('span', null, 'Dein Wartungs-Abo läuft wieder – Öffnungszeiten live ist darin enthalten.'))
|
||||
}
|
||||
let abgeschaltetBox = null
|
||||
if (daten && lage === 'bezahlen') {
|
||||
if (abo && daten && lage === 'bezahlen') {
|
||||
// Text Z3: die Eingaben bleiben gespeichert, ein neues Wartungs-Abo schaltet die Funktion wieder ein
|
||||
abgeschaltetBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'daten-gespeichert' },
|
||||
h('span', null, `Deine Eingaben bleiben${tagText(feature?.loeschungAm) ? ` bis ${tagText(feature.loeschungAm)}` : ''} gespeichert. Mit einem neuen Wartungs-Abo schaltet sich Öffnungszeiten live wieder ein.`))
|
||||
} else if (daten && lage === 'bezahlen') {
|
||||
abgeschaltetBox = h('div', { class: 'tm-box tm-box-warn', role: 'note', 'data-hinweis': 'wieder-einschalten' },
|
||||
h('span', null, `Deine Eingaben bleiben${tagText(feature?.loeschungAm) ? ` bis ${tagText(feature.loeschungAm)}` : ''} gespeichert. Für einmalig ${preis} schaltest du die Funktion wieder ein.`),
|
||||
behaltenKnopf(`Für ${preis} wieder einschalten`))
|
||||
|
||||
+15
-8
@@ -20,7 +20,7 @@
|
||||
Kundenansicht (window.KbView.id() gesetzt): Einschalten, Bestellen und Behalten sind gesperrt, der Server antwortet dort ohnehin mit 403. */
|
||||
|
||||
import { fehlerKarte, h, infoKarte, request } from './termine-dom.js'
|
||||
import { behaltenLage, renderZeitenBestellen, zeitenStatusKarte } from './zeiten-bestellen.js'
|
||||
import { behaltenLage, renderZeitenBestellen, renderZeitenEinschalten, zeitenStatusKarte } from './zeiten-bestellen.js'
|
||||
import { renderEditor } from './zeiten-editor.js'
|
||||
|
||||
// Auswahl bleibt beim Wechsel zwischen Funktionen und Zeiten erhalten
|
||||
@@ -52,14 +52,15 @@ function pickProjekt(projects) {
|
||||
* Darf der Kunde die Funktion jetzt behalten (30 €)? Nach dem erkannten Abo-Ende in Einrichtung/aktiv, abgeschaltet nur mit gespeicherten Eingaben,
|
||||
* und nur ohne Abo (behaltenLage 'bezahlen': inklusive false UND grund kein_abo); laeuft das Abo (nicht_aktiv, gekuendigt, inklusive), gibt es nichts zu bezahlen.
|
||||
*/
|
||||
function behaltenMoeglich(projekt, angebot) {
|
||||
function behaltenMoeglich(projekt, angebot, modell) {
|
||||
if (modell === 'abo') return false // Abo-Modell: kein Behalten gegen Geld (Z2: Öffnungszeiten live läuft noch bis zur Frist)
|
||||
const feature = projekt?.features?.zeiten
|
||||
if (!feature || behaltenLage(angebot) !== 'bezahlen') return false
|
||||
if (feature.state === 'setup' || feature.state === 'active') return Boolean(feature.aboEndeAt)
|
||||
return feature.state === 'disabled' && feature.disabledReason === 'abo_ende' && projekt.zeitenLeer === false
|
||||
}
|
||||
|
||||
export async function initZeiten(root, { projectId } = {}) {
|
||||
export async function initZeiten(root, { projectId, einschalten } = {}) {
|
||||
if (!root) return
|
||||
const rueckkehr = leseRueckkehr()
|
||||
const wunsch = rueckkehr.projekt || projectId || root.dataset.wunschProjekt || '' // die Kachel in Funktionen legt ihr Projekt hier ab
|
||||
@@ -71,7 +72,7 @@ export async function initZeiten(root, { projectId } = {}) {
|
||||
const projects = Array.isArray(antwort.projects) ? antwort.projects : []
|
||||
const s = {
|
||||
root, projects, angebot: antwort.zeiten || null, features: antwort.features || {}, ustHinweis: typeof antwort.ustHinweis === 'string' ? antwort.ustHinweis : '',
|
||||
projekt: null, lauf: 0, buehne: null, danke: '', editorAlt: null,
|
||||
modell: antwort.modell, einschalten, projekt: null, lauf: 0, buehne: null, danke: '', editorAlt: null,
|
||||
}
|
||||
// der Editorplatz der bisherigen Ansicht (mit seinem Editor) wird in den neuen Aufbau mitgenommen, ausser nach der Rueckkehr von Stripe
|
||||
const alterEditor = !rueckkehr.bestellt && !rueckkehr.behalten && S?.root === root ? root.querySelector('.zt-editor') : null
|
||||
@@ -95,7 +96,7 @@ export async function initZeiten(root, { projectId } = {}) {
|
||||
const key = stand(s.projekt, s.angebot)
|
||||
const editorDa = root.dataset.zeitenKey === key && root.querySelector('.zt-editor') && S?.root === root
|
||||
if (editorDa && !rueckkehr.bestellt && !rueckkehr.behalten) {
|
||||
if (wunschWeg === 'behalten' && behaltenMoeglich(S.projekt, S.angebot)) zeigeBehalten(S)
|
||||
if (wunschWeg === 'behalten' && behaltenMoeglich(S.projekt, S.angebot, S.modell)) zeigeBehalten(S)
|
||||
return
|
||||
}
|
||||
S = s
|
||||
@@ -104,7 +105,7 @@ export async function initZeiten(root, { projectId } = {}) {
|
||||
: rueckkehr.behalten && (!rueckkehr.projekt || rueckkehr.projekt === s.projekt.projectId) ? 'behalten' : ''
|
||||
s.buehne = h('div', { id: 'zeiten-buehne' })
|
||||
root.replaceChildren(...(projects.length > 1 ? [projektwahl(s)] : []), s.buehne)
|
||||
zeichneProjekt(s, { weg: wunschWeg })
|
||||
zeichneProjekt(s, { weg: s.modell === 'abo' ? '' : wunschWeg })
|
||||
}
|
||||
|
||||
/** Alles, was die Statuskarte zeigt und der Editorplatz davon abhaengt (auch die Abo-Lage: Behalten gibt es nur ohne Abo); aendert es sich, wird neu aufgebaut. */
|
||||
@@ -134,6 +135,7 @@ async function ladeListe(s) {
|
||||
const projects = Array.isArray(antwort.projects) ? antwort.projects : []
|
||||
s.projects = projects
|
||||
s.angebot = antwort.zeiten || null
|
||||
s.modell = antwort.modell
|
||||
s.features = antwort.features || {}
|
||||
s.ustHinweis = typeof antwort.ustHinweis === 'string' ? antwort.ustHinweis : ''
|
||||
s.projekt = projects.find((p) => p.projectId === s.projekt?.projectId) || s.projekt
|
||||
@@ -164,6 +166,7 @@ function baueCtx(s, projekt) {
|
||||
features: s.features,
|
||||
ustHinweis: s.ustHinweis,
|
||||
kundenansicht: kundenansicht(),
|
||||
einschalten: s.einschalten, // Ablauf des Einschaltens im Abo-Modell (Init-Option der Hülle)
|
||||
neuZeichnen: (optionen) => neuZeichnen(s, optionen),
|
||||
fertig: ({ danke } = {}) => neuZeichnen(s, { danke }),
|
||||
}
|
||||
@@ -216,18 +219,22 @@ function zeichneProjekt(s, { weg = '', meldung = '', bestellformular = false } =
|
||||
}
|
||||
|
||||
const statusKarte = (zusatz = {}) => zeitenStatusKarte(
|
||||
{ projekt, feature, angebot: s.angebot, kundenansicht: ctx.kundenansicht, danke, ...zusatz },
|
||||
{ projekt, feature, angebot: s.angebot, kundenansicht: ctx.kundenansicht, danke, modell: s.modell, ...zusatz },
|
||||
{
|
||||
behalten: () => zeigeBehalten(s),
|
||||
weiter: (fall) => zeichneProjekt(s, { bestellformular: fall?.bestellen === true, meldung: fall?.meldung || '' }),
|
||||
})
|
||||
|
||||
if (zustand === 'off' && s.modell === 'abo') {
|
||||
renderZeitenEinschalten(kopf, { ctx, projekt })
|
||||
return
|
||||
}
|
||||
if (zustand === 'off' || (zustand === 'ordered' && bestellformular)) {
|
||||
renderZeitenBestellen(kopf, { ctx, angebot: s.angebot, projekt })
|
||||
return
|
||||
}
|
||||
kopf.replaceChildren(statusKarte())
|
||||
if (weg === 'behalten' && behaltenMoeglich(projekt, s.angebot)) zeigeBehalten(s)
|
||||
if (weg === 'behalten' && behaltenMoeglich(projekt, s.angebot, s.modell)) zeigeBehalten(s)
|
||||
}
|
||||
|
||||
/** Behalten-Seite (30 €) statt der Statuskarte; der Editor darunter bleibt, nur versteckt, damit nichts verloren geht. „Zurück“ zeigt wieder die Karte. */
|
||||
|
||||
Reference in new issue
Block a user