Terminplaner TP2: Kachel mit Bestellweg, Abo-Felder in der Funktionen-Liste

GET /api/funktionen liefert bei termine.verfuegbar: true zusaetzlich bestellbar
(true | false | null bei Lesefehler im Abo), grund (kein_abo | gekuendigt |
nicht_aktiv | null), preis und einrichtung aus dem Register; nicht verfuegbar
bleibt exakt { verfuegbar: false }. Die Kachel zeigt je Zustand Pille und Knopf:
Aus mit Abo "Terminplaner einrichten · 149 €" bzw. "Fragebogen fortsetzen",
Aus ohne Abo keinen Knopf, sondern den Hinweis zum Abo, Zahlung offen und In
Einrichtung "Status ansehen", Aktiv "Termine oeffnen", Abgeschaltet "Termine
ansehen". Zielprojekt: aktiv, ordered/setup, abgeschaltet, Entwurf, erstes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-04 22:34:50 +00:00
1 parent 8d628ef906
commit 6ae0f67457
5 files changed
+364 -43

No files matched your search

+64 -5
View File
@@ -72,6 +72,8 @@ function portalHilfe(p, json) {
function neueFixture() {
const fx = {
verfuegbar: true,
bestellbar: true, // Abo des Kunden (GET /api/funktionen, termine.bestellbar / termine.grund): true | false | null
aboGrund: null,
projekte: [{ projectId: 'p1', projectName: 'Salon Müller', liveUrl: 'https://salon-mueller.example', features: { news: { state: 'off' }, termine: { state: 'active' } } }],
einstellung: srv.einstellung(),
vorlagen: [
@@ -128,7 +130,12 @@ async function fixtureAntwort(fx, route, req, url, json) {
const p = url.pathname
const q = url.searchParams
const m = req.method()
if (p === '/api/funktionen') return json({ projects: fx.projekte, termine: { verfuegbar: fx.verfuegbar } })
if (p === '/api/funktionen') {
const termine = fx.verfuegbar
? { verfuegbar: true, bestellbar: fx.bestellbar, grund: fx.aboGrund, preis: '149 €', einrichtung: 'meist innerhalb von 1–2 Wochen' }
: { verfuegbar: false }
return json({ projects: fx.projekte, termine })
}
if (p === '/api/termine/einstellungen' && m === 'GET') {
const projekt = q.get('projectId')
if (fx.verzoegerung?.[projekt]) await new Promise((fertig) => { setTimeout(fertig, fx.verzoegerung[projekt]) })
@@ -298,15 +305,67 @@ async function pruefungen() {
await check('fx_kachel_aus_aktiv_abgeschaltet', async (page) => {
const fx = page.fx
// Aus
// Aus mit Abo: Weg in die Bestellung
fx.projekte[0].features.termine = { state: 'off' }
await oeffne(page, '#funktionen')
let kachel = page.locator('[data-fn-tile="termine"]')
await kachel.waitFor()
let t = await textVon(kachel)
assert(t.includes('Aus') && t.includes('Wir schalten den Terminplaner für dich frei.'), `Aus: ${t}`)
await knopf(kachel, 'Chat öffnen').waitFor()
eq(await kachel.locator('[data-view="termine"]').count(), 0, 'Aus: kein Weg in die Ansicht')
assert(t.includes('Aus') && t.includes('Terminplaner einrichten · 149 €'), `Aus: ${t}`)
assert(!t.includes('Wir schalten den Terminplaner für dich frei.'), 'der alte Text ist weg')
await knopf(kachel, 'Terminplaner einrichten · 149 €').waitFor()
eq(await kachel.locator('[data-view="termine"]').getAttribute('data-project'), 'p1', 'Aus: Knopf fuehrt in die Ansicht')
eq(await kachel.locator('[data-view="chat"]').count(), 0, 'Aus: kein Chat-Knopf')
await shot(page, 'kachel-aus')
// Entwurf vorhanden (Funktionsdokument mit siteKey)
fx.projekte[0].features.termine = { state: 'off', siteKey: 'abcdefghijklmnop' }
await page.reload()
kachel = page.locator('[data-fn-tile="termine"]')
await knopf(kachel, 'Fragebogen fortsetzen').waitFor()
// Aus ohne Abo: kein Knopf, Hinweis; die Hinweise der uebrigen Gruende
const faelle = [
['kein_abo', 'Im Wartungs-Abo enthalten.'],
['gekuendigt', 'Dein Wartungs-Abo ist gekündigt.'],
['nicht_aktiv', 'Dein Wartungs-Abo ist gerade nicht aktiv'],
]
for (const [grund, text] of faelle) {
fx.bestellbar = false
fx.aboGrund = grund
await page.reload()
kachel = page.locator('[data-fn-tile="termine"]')
await kachel.waitFor()
t = await textVon(kachel)
assert(t.includes('Aus') && t.includes(text), `Aus ohne Abo (${grund}): ${t}`)
eq(await kachel.locator('.fn-tile-cta').count(), 0, `${grund}: kein Knopf`)
eq(await kachel.locator('[data-view="termine"]').count(), 0, `${grund}: kein Weg in die Ansicht`)
}
eq(await kachel.locator('[data-view="vertraege"]').count(), 0, 'nur kein_abo verweist auf die Vertraege')
fx.aboGrund = 'kein_abo'
await page.reload()
kachel = page.locator('[data-fn-tile="termine"]')
await knopf(kachel, 'Zu Verträge').waitFor()
await shot(page, 'kachel-ohne-abo')
fx.bestellbar = null
fx.aboGrund = null
await page.reload()
kachel = page.locator('[data-fn-tile="termine"]')
await kachel.getByText('Das Abo konnte gerade nicht geprüft werden.').waitFor()
eq(await kachel.locator('.fn-tile-cta').count(), 0, 'nicht lesbar: kein Knopf')
// bestellt: Zahlung offen und in Einrichtung (das Abo spielt dann keine Rolle mehr)
fx.bestellbar = false
fx.aboGrund = 'gekuendigt'
for (const [state, pille, text] of [['ordered', 'Zahlung offen', 'Status ansehen'], ['setup', 'In Einrichtung', 'Status ansehen']]) {
fx.projekte[0].features.termine = { state }
await page.reload()
kachel = page.locator('[data-fn-tile="termine"]')
await kachel.waitFor()
t = await textVon(kachel)
assert(t.includes(pille) && t.includes(text), `${state}: ${t}`)
eq(await kachel.locator('[data-view="termine"]').getAttribute('data-project'), 'p1', `${state}: Knopf fuehrt in die Ansicht`)
}
await shot(page, 'kachel-in-einrichtung')
fx.bestellbar = true
fx.aboGrund = null
// Aktiv mit zwei Anfragen
fx.projekte[0].features.termine = { state: 'active' }
fx.buchungen = alleStatus().map((b) => (b.id === 'bkunft' ? { ...b, status: 'angefragt' } : b))