Shop G1: Verdrahtung #webshop, Webshop-Kachel, Bestellseite mit key und optionaler Zusammenfassung
Dashboard-Abschnitt view-webshop, shop.css und Platzhalter shop.js, app.js (Hash, Refresher, Label, Elternansicht), Kachel in funktionen.js nach shop.verfuegbar, GET /api/funktionen liefert shop.verfuegbar, funktion-bestellen.js nimmt key/name/zusammenfassung (Standard: Terminplaner unverändert). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0ebd2e6663
commit
809da80c9b
11 files changed
+333
-19
No files matched your search
@@ -4,12 +4,15 @@
|
||||
Es gibt keine Texte des Registers im Client: Knopf, Haken, Belehrung, Preis und Unterlagen kommen als `bestellung` aus
|
||||
GET /api/termine/einstellungen (Server: bestellungAnsicht), und der Knopf zeigt genau bestellung.consent.knopf, denselben Text, den der
|
||||
Server im Nachweis speichert. Fremdtext kommt nur über textContent in die Seite (h aus termine-dom.js), nie über innerHTML.
|
||||
Zusammenfassung: zusammenfassung() aus termine-view.js. Checkout: POST /api/funktionen/termine/checkout (Server: routes/funktionen.js). */
|
||||
Zusammenfassung: zusammenfassung() aus termine-view.js (Standard für key 'termine'; andere Funktionen übergeben eine eigene Funktion oder
|
||||
zeigen keine). Checkout: POST /api/funktionen/<key>/checkout (Server: routes/funktionen.js), key ist standardmäßig 'termine'. */
|
||||
|
||||
import { fehlerKarte, h, request } from './termine-dom.js'
|
||||
import { FRAGEBOGEN_SCHRITTE, zusammenfassung } from './termine-view.js'
|
||||
|
||||
const CHECKOUT = '/api/funktionen/termine/checkout'
|
||||
const STANDARD_KEY = 'termine'
|
||||
// Checkout-Pfad der Funktion; der Schlüssel ist ein Registerschlüssel (Kleinbuchstaben), alles andere fällt auf den Terminplaner zurück
|
||||
const checkoutPfad = (key) => `/api/funktionen/${typeof key === 'string' && /^[a-z][a-z0-9-]{0,31}$/.test(key) ? key : STANDARD_KEY}/checkout`
|
||||
const RECHTLICHES = '/rechtliches/'
|
||||
const KUNDENANSICHT = 'In der Kundenansicht kann nicht bestellt werden.'
|
||||
const ZAHLUNGSSEITE = 'Die Zahlungsseite konnte nicht geöffnet werden. Bitte versuch es noch einmal oder schreib uns im Chat.'
|
||||
@@ -113,12 +116,16 @@ function ladeBeiRueckkehrNeu(element) {
|
||||
* Der Knopf ist gesperrt, solange ein Haken fehlt, offene Punkte bleiben oder die Kundenansicht läuft. Ein Klick sperrt ihn, sendet
|
||||
* bestellKoerper und geht zur Zahlungsseite. Bei 409 lädt die Seite die Daten neu und setzt die Haken zurück (bestellung_offen zeigt die
|
||||
* Punkte aus der Antwort, fassung_veraltet die neue Fassung aus der Antwort, ein Abo-Code den Text der Antwort).
|
||||
* Weitere Optionen: key = Schlüssel der Funktion im Register (Standard 'termine', bestimmt den Checkout-Pfad), zusammenfassung =
|
||||
* Funktion (einstellung, fragebogen, branchen) => [{ schritt, titel, zeilen }]; ohne sie zeigt nur der Terminplaner seine Zusammenfassung,
|
||||
* andere Funktionen lassen den Block weg.
|
||||
*/
|
||||
export function renderBestellen(root, { ctx, daten, zumSchritt } = {}) {
|
||||
return baueSeite(root, { ctx, daten, zumSchritt }, {})
|
||||
export function renderBestellen(root, { ctx, daten, zumSchritt, key = STANDARD_KEY, zusammenfassung: gruppenFunktion } = {}) {
|
||||
const gruppenFn = typeof gruppenFunktion === 'function' ? gruppenFunktion : key === STANDARD_KEY ? zusammenfassung : null
|
||||
return baueSeite(root, { ctx, daten, zumSchritt, key, gruppenFn }, {})
|
||||
}
|
||||
|
||||
function baueSeite(root, { ctx, daten, zumSchritt }, uebernahme) {
|
||||
function baueSeite(root, { ctx, daten, zumSchritt, key, gruppenFn }, uebernahme) {
|
||||
const roh = daten?.bestellung
|
||||
if (!roh || !istConsent(roh.consent)) {
|
||||
const karte = fehlerKarte('Die Bestellseite konnte gerade nicht geladen werden. Bitte lade die Seite neu.')
|
||||
@@ -140,7 +147,7 @@ function baueSeite(root, { ctx, daten, zumSchritt }, uebernahme) {
|
||||
const gehe = (schritt) => { if (springt) zumSchritt(schritt) }
|
||||
|
||||
// 1 Zusammenfassung: je Gruppe die Angaben als Text und „Ändern“
|
||||
const gruppen = zusammenfassung(daten.einstellung, daten.fragebogen, daten.branchen).map((gruppe) => h('div', { class: 'tm-bestellen-gruppe', 'data-schritt': gruppe.schritt },
|
||||
const gruppen = (gruppenFn ? gruppenFn(daten.einstellung, daten.fragebogen, daten.branchen) : []).map((gruppe) => h('div', { class: 'tm-bestellen-gruppe', 'data-schritt': gruppe.schritt },
|
||||
h('div', { class: 'tm-bestellen-gruppe-kopf' },
|
||||
h('h4', null, gruppe.titel),
|
||||
springt
|
||||
@@ -200,8 +207,8 @@ function baueSeite(root, { ctx, daten, zumSchritt }, uebernahme) {
|
||||
|
||||
const seite = h('section', { class: 'kb-panel tm-bestellen' },
|
||||
h('h3', null, 'Prüfen und bestellen'),
|
||||
h('p', { class: 'tm-abschnitt-text tm-hinweis' }, 'Lies alles in Ruhe durch. Mit „Ändern“ kommst du zu dem Schritt zurück, in dem du die Angabe gemacht hast.'),
|
||||
h('div', { class: 'tm-bestellen-zusammenfassung' }, gruppen),
|
||||
gruppenFn ? h('p', { class: 'tm-abschnitt-text tm-hinweis' }, 'Lies alles in Ruhe durch. Mit „Ändern“ kommst du zu dem Schritt zurück, in dem du die Angabe gemacht hast.') : null,
|
||||
gruppenFn ? h('div', { class: 'tm-bestellen-zusammenfassung' }, gruppen) : null,
|
||||
offenBlock,
|
||||
preisBlock,
|
||||
belehrungBlock,
|
||||
@@ -246,7 +253,7 @@ function baueSeite(root, { ctx, daten, zumSchritt }, uebernahme) {
|
||||
karte.focus({ preventScroll: true })
|
||||
return
|
||||
}
|
||||
const neueSeite = baueSeite(root, { ctx, daten: neu, zumSchritt }, {
|
||||
const neueSeite = baueSeite(root, { ctx, daten: neu, zumSchritt, key, gruppenFn }, {
|
||||
offen: err.code === 'bestellung_offen' ? err.body?.offen : undefined,
|
||||
consent: err.code === 'fassung_veraltet' ? err.body?.consent : undefined,
|
||||
})
|
||||
@@ -264,7 +271,7 @@ function baueSeite(root, { ctx, daten, zumSchritt }, uebernahme) {
|
||||
aktualisiere()
|
||||
zeigeMeldung('')
|
||||
try {
|
||||
const antwort = await request(CHECKOUT, { method: 'POST', body: bestellKoerper({ projectId: ctx?.projectId, bestellung, gewaehlt }) })
|
||||
const antwort = await request(checkoutPfad(key), { method: 'POST', body: bestellKoerper({ projectId: ctx?.projectId, bestellung, gewaehlt }) })
|
||||
if (geheZu(antwort?.url)) return // die Seite wird verlassen, der Knopf bleibt gesperrt
|
||||
zeigeMeldung(ZAHLUNGSSEITE)
|
||||
} catch (err) {
|
||||
@@ -288,13 +295,15 @@ function baueSeite(root, { ctx, daten, zumSchritt }, uebernahme) {
|
||||
* projekt = Eintrag des Projekts aus GET /api/funktionen (projectId), bestellung = projekt.features.termine (state, orderedAt, ticketWoid,
|
||||
* invoiceUrl), einrichtung = Text der Zeiteinschätzung (Register: setupEstimate), danke = Rückkehr von Stripe, kundenansicht = Sitzung in der
|
||||
* Kundenansicht, meldung = Text, der am Knopf „Zahlung fortsetzen“ steht (z. B. der Fehler eines Versuchs vor dem Neuzeichnen).
|
||||
* key = Schlüssel der Funktion (Standard 'termine', Checkout-Pfad), name = Name der Funktion in
|
||||
* den Texten (Standard 'Terminplaner'), hinweisEinrichtung = Satz unter den Fakten im Zustand setup (Standard: Einstellungen gesperrt).
|
||||
* weiter(fall) wird gerufen, wenn „Zahlung fortsetzen“ scheitert (Ruling R110): fall = { bestellen, meldung }, bestellen ist wahr bei 409
|
||||
* zustimmung_fehlt (keine offene Sitzung mit Nachweis: die Ansicht zeigt wieder das Bestellformular), meldung der Fehlertext der Antwort.
|
||||
* weiter() lädt neu und zeichnet die Ansicht nach dem Zustand von neuem, damit keine veraltete Karte stehen bleibt (z. B. wenn die Bestellung
|
||||
* inzwischen bezahlt ist und der Server 400 antwortet); wirft es, bleibt die Karte stehen und der Fehler steht am Knopf. Fehlt weiter,
|
||||
* steht die Meldung des Servers am Knopf.
|
||||
*/
|
||||
export function statusKarte({ projekt, bestellung, einrichtung, danke, kundenansicht, meldung } = {}, { weiter } = {}) {
|
||||
export function statusKarte({ projekt, bestellung, einrichtung, danke, kundenansicht, meldung, key = STANDARD_KEY, name = 'Terminplaner', hinweisEinrichtung } = {}, { weiter } = {}) {
|
||||
const bestellt = bestellung?.state === 'ordered'
|
||||
const datum = datumTag(bestellung?.orderedAt)
|
||||
const ticket = bestellung?.ticketWoid
|
||||
@@ -317,7 +326,7 @@ export function statusKarte({ projekt, bestellung, einrichtung, danke, kundenans
|
||||
fortsetzenKnopf.disabled = true
|
||||
meldungEl.textContent = ''
|
||||
try {
|
||||
const antwort = await request(CHECKOUT, { method: 'POST', body: fortsetzenKoerper(projekt?.projectId) })
|
||||
const antwort = await request(checkoutPfad(key), { method: 'POST', body: fortsetzenKoerper(projekt?.projectId) })
|
||||
if (geheZu(antwort?.url)) return // die Seite wird verlassen
|
||||
meldungEl.textContent = ZAHLUNGSSEITE
|
||||
} catch (err) {
|
||||
@@ -341,12 +350,12 @@ export function statusKarte({ projekt, bestellung, einrichtung, danke, kundenans
|
||||
const karte = h('section', { class: 'kb-panel tm-statuskarte', 'data-state': bestellt ? 'ordered' : 'setup' },
|
||||
danke ? h('p', { class: 'tm-danke', role: 'status', 'data-danke': true }, DANKE) : null,
|
||||
h('span', { class: 'tm-pille tm-pille-wait' }, bestellt ? 'Zahlung offen' : 'In Einrichtung'),
|
||||
h('h3', null, bestellt ? 'Deine Bestellung wartet auf die Zahlung' : 'Wir richten deinen Terminplaner ein'),
|
||||
h('p', null, bestellt ? 'Sobald deine Zahlung bei uns ankommt, legen wir mit der Einrichtung los.' : 'Sobald die Einrichtung fertig ist, schalten wir deinen Terminplaner für dich frei.'),
|
||||
h('h3', null, bestellt ? 'Deine Bestellung wartet auf die Zahlung' : `Wir richten deinen ${text(name) || 'Terminplaner'} ein`),
|
||||
h('p', null, bestellt ? 'Sobald deine Zahlung bei uns ankommt, legen wir mit der Einrichtung los.' : `Sobald die Einrichtung fertig ist, schalten wir deinen ${text(name) || 'Terminplaner'} für dich frei.`),
|
||||
fakten.length > 0 ? h('dl', { class: 'tm-fakten' }, fakten) : null,
|
||||
bestellt
|
||||
? h('p', { class: 'tm-hinweis' }, 'Zahlst du per SEPA-Lastschrift, kann es einige Tage dauern, bis die Zahlung bei uns ankommt.')
|
||||
: h('p', { class: 'tm-hinweis' }, 'Deine Einstellungen sind jetzt gesperrt, Änderungen laufen über den Chat.'),
|
||||
: h('p', { class: 'tm-hinweis' }, text(hinweisEinrichtung) || 'Deine Einstellungen sind jetzt gesperrt, Änderungen laufen über den Chat.'),
|
||||
meldungEl,
|
||||
bestellt && kundenansicht === true ? h('p', { class: 'tm-hinweis' }, KUNDENANSICHT) : null,
|
||||
h('div', { class: 'tm-aktionen' },
|
||||
|
||||
Reference in new issue
Block a user