Shop Review B I-5: Bestaetigen nur ohne Platzhalter, Musterkopf und mit Adresse der Widerrufsfunktion

pruefeBestaetigbar listet die Probleme in Du-Form; POST /api/shop/rechtstexte/bestaetigen
antwortet 400 nicht_bestaetigbar mit der Liste, GET und PUT liefern sie als probleme mit.
Der Reiter Rechtliches zeigt die Punkte im Bestaetigungsblock und sperrt den Knopf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 09:53:17 +00:00
1 parent 1f56b86791
commit 2f4f360a21
6 files changed
+169 -15

No files matched your search

+18 -2
View File
@@ -1,9 +1,11 @@
/* Webshop, Reiter „Rechtliches“ (Plan Block G, Task G6): die Rechtstexte des Haendlers. Er ist Verkaeufer und verantwortlich; WEBklar liefert
Muster und keine Rechtsberatung.
Schnittstelle (server/routes/shop.js): GET /api/shop/rechtstexte?projectId= => { entwurf, bestaetigt: { texte, at, von, aktuell } | null,
muster: { agb, widerrufsbelehrung, widerrufsformular }, versandUndZahlung, gewaehrleistung: { bild, text }, hash, bestaetigungText, vollstaendig };
muster: { agb, widerrufsbelehrung, widerrufsformular }, versandUndZahlung, gewaehrleistung: { bild, text }, hash, bestaetigungText, vollstaendig,
probleme: [Text] };
PUT /api/shop/rechtstexte { projectId, rechtstexte } speichert den Entwurf (Antwort wie GET, Feldfehler als err.fehler);
POST /api/shop/rechtstexte/bestaetigen { projectId, hash } bestaetigt den gespeicherten Entwurf (409 texte_geaendert, 400 unvollstaendig,
400 nicht_bestaetigbar mit probleme: Platzhalter in eckigen Klammern, Musterkopfzeile, Widerrufsbelehrung ohne Adresse der Widerrufsfunktion;
403 in der Kundenansicht). Der Hash kommt aus der letzten Antwort von GET oder PUT; bestaetigt wird nur, was gespeichert ist: bei ungespeicherten
Aenderungen heisst der Knopf „Erst speichern“. Ersetzen eines vorhandenen Textes durch das Muster fragt vorher in der Seite (kein confirm()).
render(panel, ctx): ctx = { projectId, nurLesen, zeigeMeldung(text, art), neuLaden(), request } (siehe shop.js).
@@ -108,8 +110,11 @@ export async function render(panel, ctx = {}) {
const haken = h('input', { type: 'checkbox', 'data-feld': 'bestaetigung' })
const bestaetigen = h('button', { type: 'button', class: 'kb-btn-primary', 'data-aktion': 'bestaetigen' }, 'Texte bestätigen')
const fehlerBest = h('p', { class: 'sh-fehler', 'data-fehler': 'bestaetigung', role: 'alert' })
// Punkte, die das Bestaetigen verhindern (Platzhalter, Musterkopf, fehlende Adresse der Widerrufsfunktion), vom Server
const problemPlatz = h('div', { 'data-probleme-platz': '' })
const bestBlock = h('section', { class: 'kb-panel sh-abschnitt', 'data-block': 'bestaetigen' },
h('h3', null, 'Texte bestätigen'),
problemPlatz,
h('label', { class: 'sh-haken' }, haken, h('span', null, text(daten.bestaetigungText))),
hinweisBest,
fehlerBest,
@@ -135,7 +140,13 @@ export async function render(panel, ctx = {}) {
statusPlatz.replaceChildren(...teile)
const schmutzig = geaendert()
const bereitZumBestaetigen = !schmutzig && daten.vollstaendig === true
const probleme = !schmutzig && Array.isArray(daten.probleme) ? daten.probleme.filter((t) => typeof t === 'string' && t !== '') : []
problemPlatz.replaceChildren(...(probleme.length
? [h('div', { class: 'sh-box sh-box-warn', role: 'alert', 'data-probleme': '' },
h('strong', null, 'Das musst du vor dem Bestätigen noch ändern:'),
h('ul', null, probleme.map((t) => h('li', null, t))))]
: []))
const bereitZumBestaetigen = !schmutzig && daten.vollstaendig === true && probleme.length === 0
// schon bestaetigt und nichts geaendert: nichts zu tun
bestBlock.hidden = Boolean(b?.aktuell) && !schmutzig
bestaetigen.textContent = schmutzig ? 'Erst speichern' : 'Texte bestätigen'
@@ -201,6 +212,11 @@ export async function render(panel, ctx = {}) {
}
} catch (err) {
fehlerBest.textContent = err.message
// 400 nicht_bestaetigbar: die Liste des Servers statt des allgemeinen Satzes zeigen
if (err?.code === 'nicht_bestaetigbar' && Array.isArray(err.body?.probleme)) {
daten = { ...daten, probleme: err.body.probleme }
fehlerBest.textContent = ''
}
// 409: die Texte haben sich inzwischen geaendert; der neue Stand wird geholt, ungespeicherte Eingaben gibt es hier nicht (sie sperren den Knopf)
if (err?.status === 409) {
try {