Hustler-Stufe: Keller begehbar ab Stufe 3, Bot-Leads abrufbar erst ab Stufe 6

Die Stufen-Sperre trennt jetzt zwei Dinge: KELLER_BEGEHBAR_AB = 3 (Keller-Etage als Link,
?tab=botleads gueltig, Spur-Links in den Keller) und BOTLEADS_AB = 6 (Abruf von GET /botleads).
kellerOffen/STUFE_KELLER entfallen, dafuer kellerBegehbar() und botleadsAbrufbar().
workersApi.botleads bleibt unter Stufe 6 hart gesperrt, der Text heisst nun
BOTLEADS_GESPERRT_TEXT. Die Keller-Ansicht selbst (leeres Register ohne Abruf) folgt im
naechsten Commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-22 12:21:59 +00:00
1 parent 8363b63374
commit 6eb4dd3c51
11 files changed
+121 -82

No files matched your search

+36 -17
View File
@@ -269,7 +269,7 @@ const loadSeite = () => (seiteBundle ??= esbuild.build({
// Ein Lauf der Seite. query ist der Teil nach dem ?, feed wird vom Test zwischendurch geändert (setFeed) und die Seite neu gerendert.
// stufe (Stufen-Sperre, hustlerStufe.js): die Zusicherungen dieser Datei gelten für die volle Seite (Stufe 6, Keller offen, Verhalten wie vor der Sperre);
// die Tests „Stufe 3“ weiter unten bauen die Seite mit stufe: 3 (Stand der Produktion).
// die Tests „Stufe 1 und 2“ und „Stufe 3“ weiter unten bauen die Seite mit stufe: 1/2 (Keller zu) und 3 (Stand der Produktion: Keller begehbar, ohne Bot-Leads).
async function seite(query, { admin = false, feed, stufe = 6 } = {}) {
const { __mount, jsx, WorkersPage } = await loadSeite()
const setParamsCalls = []
@@ -330,13 +330,13 @@ test('Seite: ?tab=freigabe zeigt das Pult, ?tab=botleads den Keller, beide direk
} finally { keller.end() }
})
// ---------- Stufen-Sperre (Stufe 3 = Stand der Produktion: Dienst P3, ohne Keller) ----------
// ---------- Stufen-Sperre (Keller begehbar ab Stufe 3 = Stand der Produktion: Dienst P3, Keller ohne Bot-Leads) ----------
// Adressen, die auf Stufe 3 in den Keller führen würden: der Reiter, mit Einzelfall, mit Ballast, andere Schreibweisen und erfundene Parameter
// Adressen, die in den Keller führen würden: der Reiter, mit Einzelfall, mit Ballast, andere Schreibweisen und erfundene Parameter
const KELLER_ADRESSEN = ['tab=botleads', 'tab=botleads&bot=abc123', 'bot=abc123&tab=botleads', 'tab=botleads&bot=', 'tab=BotLeads', 'ansicht=keller', 'tab=keller', 'tab=botleads&ansicht=keller']
test('Stufe 3: jede Adresse in den Keller fällt auf das Hochhaus zurück; der Keller wird nie eingehängt, Hochhaus und Seite bleiben ganz', async () => {
for (const stufe of [1, 3, 5]) {
test('Stufe 1 und 2: jede Adresse in den Keller fällt auf das Hochhaus zurück; der Keller wird nie eingehängt, Hochhaus und Seite bleiben ganz', async () => {
for (const stufe of [1, 2]) {
for (const query of KELLER_ADRESSEN) {
const ui = await seite(query, { stufe, feed: { state: makeState(), error: null, loaded: true, refresh() {} } })
try {
@@ -351,8 +351,8 @@ test('Stufe 3: jede Adresse in den Keller fällt auf das Hochhaus zurück; der K
}
})
test('Stufe 3: der Wechsel der Adresse zu ?tab=botleads zeigt das Hochhaus, Freigabe bleibt erreichbar; der Rückweg aus dem Pult funktioniert wie sonst', async () => {
const ui = await seite('tab=freigabe', { stufe: 3, feed: { state: makeState(), error: null, loaded: true, refresh() {} } })
test('Stufe 2: der Wechsel der Adresse zu ?tab=botleads zeigt das Hochhaus, Freigabe bleibt erreichbar; der Rückweg aus dem Pult funktioniert wie sonst', async () => {
const ui = await seite('tab=freigabe', { stufe: 2, feed: { state: makeState(), error: null, loaded: true, refresh() {} } })
try {
assert.deepEqual(ui.screen(), ['pult'])
ui.go('tab=botleads&bot=x'); assert.deepEqual(ui.screen(), ['hochhaus']); assert.equal(ui.hk.props.hochhaus.vonTab, 'freigabe')
@@ -362,32 +362,51 @@ test('Stufe 3: der Wechsel der Adresse zu ?tab=botleads zeigt das Hochhaus, Frei
} finally { ui.end() }
})
test('Stufe 3: das Hochhaus öffnet nur noch die Freigabe (onOpen(\'botleads\') tut nichts); Hochhaus und Pult bekommen die Stufe', async () => {
const ui = await seite('tab=hochhaus', { stufe: 3, feed: { state: makeState(), error: null, loaded: true, refresh() {} } })
test('Stufe 2: das Hochhaus öffnet nur noch die Freigabe (onOpen(\'botleads\') tut nichts); Hochhaus und Pult bekommen die Stufe', async () => {
const ui = await seite('tab=hochhaus', { stufe: 2, feed: { state: makeState(), error: null, loaded: true, refresh() {} } })
try {
const p = ui.hk.props.hochhaus
p.onOpen('botleads'); p.onOpen('freigabe')
assert.deepEqual(ui.setParamsCalls, [{ tab: 'freigabe' }])
ui.go('tab=freigabe')
assert.equal(ui.hk.props.pult.stufe, 3)
assert.equal(ui.hk.props.pult.stufe, 2)
ui.hk.props.pult.onBack()
assert.deepEqual(ui.setParamsCalls, [{ tab: 'freigabe' }, { tab: 'hochhaus' }])
} finally { ui.end() }
})
test('Stufe 6 und höher: ?tab=botleads zeigt den Keller samt Einzelfall (bot), onOpen(\'botleads\') wechselt; Stufe 4 und 5 haben den Keller noch nicht', async () => {
for (const stufe of [6, 8, 9]) {
// Stufe 3 (Produktion) und höher: der Keller ist ein Bildschirm; die Stufe geht an BotLeadList, die darunter von Stufe 6 entscheidet, ob sie Bot-Leads abruft (botleadList.test.mjs)
test('Stufe 3 bis 9: ?tab=botleads (auch alle Schreibweisen mit tab=botleads) zeigt den Keller samt Einzelfall (bot) und bekommt die Stufe; onOpen(\'botleads\') wechselt; Zurück ins Hochhaus', async () => {
for (const stufe of [3, 4, 5, 6, 8, 9]) {
const ui = await seite('tab=botleads&bot=abc123', { stufe, feed: { state: makeState(), error: null, loaded: true, refresh() {} } })
try {
assert.deepEqual(ui.screen(), ['keller'], `Stufe ${stufe}`); assert.equal(ui.hk.props.keller.bot, 'abc123')
assert.deepEqual(ui.screen(), ['keller'], `Stufe ${stufe}`); assert.equal(ui.hk.props.keller.bot, 'abc123'); assert.equal(ui.hk.props.keller.stufe, stufe)
assert.equal(ui.root().props['data-tab'], 'botleads', `Stufe ${stufe}`)
ui.hk.props.keller.onBack(); ui.hk.props.keller.onClearBot()
assert.deepEqual(ui.setParamsCalls, [{ tab: 'hochhaus' }, { tab: 'botleads' }], `Stufe ${stufe}`)
ui.go('tab=hochhaus'); ui.hk.props.hochhaus.onOpen('botleads')
assert.deepEqual(ui.setParamsCalls, [{ tab: 'botleads' }], `Stufe ${stufe}`)
assert.deepEqual(ui.setParamsCalls, [{ tab: 'hochhaus' }, { tab: 'botleads' }, { tab: 'botleads' }], `Stufe ${stufe}`)
} finally { ui.end() }
}
for (const stufe of [4, 5]) {
const ui = await seite('tab=botleads', { stufe })
try { assert.deepEqual(ui.screen(), ['hochhaus'], `Stufe ${stufe}`) } finally { ui.end() }
for (const query of ['tab=botleads', 'bot=abc123&tab=botleads', 'tab=botleads&bot=']) {
const ui = await seite(query, { stufe: 3, feed: { state: makeState(), error: null, loaded: true, refresh() {} } })
try { assert.deepEqual(ui.screen(), ['keller'], query) } finally { ui.end() }
}
// erfundene Werte bleiben beim Hochhaus, auch auf Stufe 3
for (const query of ['tab=BotLeads', 'ansicht=keller', 'tab=keller']) {
const ui = await seite(query, { stufe: 3, feed: { state: makeState(), error: null, loaded: true, refresh() {} } })
try { assert.deepEqual(ui.screen(), ['hochhaus'], query) } finally { ui.end() }
}
})
test('Stufe 3: Hochhaus und Keller bekommen die Stufe, der Keller zählt als Bildschirm der Seite (data-tab); Pause und Kennzahlen gehen an ihn', async () => {
const state = makeState({ kpis: { pultOpen: 2, dealsMonth: 1, botLeads: 0 } })
const ui = await seite('tab=botleads', { stufe: 3, feed: { state, error: null, loaded: true, refresh() {} } })
try {
assert.equal(ui.hk.props.keller.stufe, 3); assert.equal(ui.hk.props.keller.kpis, state.kpis); assert.equal(ui.hk.props.keller.paused, false)
ui.go('tab=hochhaus')
assert.equal(ui.hk.props.hochhaus.stufe, 3); assert.equal(ui.hk.props.hochhaus.vonTab, 'botleads')
} finally { ui.end() }
})
test('Seite: keine Seitenüberschrift, keine Reiterleiste', async () => {