Hustler-Pixelwelt T12: Prüfstand auf die Pixelwelt umgestellt (Szenarien A–M), Handy-Kopfzeile zweireihig

Prüfstand: entry.jsx zeigt nur noch die echte Seite WorkersPage (BrowserRouter, Adresse mit ?tab= lesbar); shot.cjs neu mit den Szenarien A–M
(Hochhaus, Regie, Handy, weniger Bewegung, Notbremse, Ausfall, Detail, Etagen, Pause, Pult P3, Pult P4, Keller, Handy Pult/Keller),
harte Prüfungen und Bildern mit Szenario-Vorsilbe; Befunde an der App stehen getrennt von den Verstößen. Skript-API: Gründe der Bot-Leads
wörtlich wie im Dienst. Kopfzeile am Handy: zwei Reihen (Geld + Pause/Zahnrad, dann die fünf Kennzahlen), schmaler Panel-Rand, 40-px-Knöpfe,
Querformat unter 500 px Höhe in einer Reihe; hustlerKopf.test um die Zusicherungen dazu ergänzt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-22 09:32:33 +00:00
1 parent ebab573803
commit 2470f05acb
6 files changed
+1431 -2053

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@
// outDir muss ein neuer oder leerer Ordner unter dem Temp-Ordner (TMPDIR, sonst /tmp) oder unter .superpowers/ im Repo sein.
// Die Schrift /fonts/* bleibt außen vor: der Server von shot.cjs liefert sie aus public/fonts.
// Drei Module der App hängen an Appwrite (Anmeldung, Mitarbeiterliste, Sitzungs-JWT) und werden nur im Prüfstand ausgetauscht, damit die
// echte Seite WorkersPage (Reiter Hochhaus, Freigabe und Bot-Leads) samt PultList, ProspectPanel und BotLeadList ohne Netz läuft: AuthContext -> stub-auth.js,
// echte Seite WorkersPage (Bildschirme Hochhaus, Pult und Keller samt Brief, Akte, ProspectPanel und Einstellungen) ohne Netz läuft: AuthContext -> stub-auth.js,
// useEmployees -> stub-employees.js, workersClient -> stub-client.js. Am Quelltext der App ändert das nichts.
import esbuild from 'esbuild'
import fs from 'node:fs'
+23 -36
View File
@@ -1,23 +1,23 @@
// Einstiegspunkt des Prüfstands: das Hochhaus-Board der Hustler-Seite mit der Skript-API, ohne Anmeldung und ohne Appwrite.
// Drumherum steht dieselbe Hülle wie in der App (Menüleiste links, Seitenrand), damit die Bühne genauso breit wird wie im echten Betrieb.
// ?halted=1 startet mit gesetzter Bounce-Notbremse (H4). window.__wk gibt es nur hier: die Bühne und die Skript-API für shot.cjs.
// ?fault=none|down|session|hang schaltet zwischen Board und Skript-API den echten API-Client (createWorkersApi) mit einer Fetch-Weiche
// (scripted-fetch.js): window.__wk.control.fault lässt sich zur Laufzeit auf null | 'down' | 'session' | 'hang' setzen (Ausfall-Szenario).
// Ohne den Parameter bekommt das Board die Skript-API direkt (Szenarien A bis E).
// ?page=1 zeigt statt des Boards die echte Seite WorkersPage (Reiter Hochhaus, Freigabe und Bot-Leads; PultList, ProspectPanel, BotLeadList); AuthContext,
// useEmployees und workersClient sind dort durch stub-*.js ersetzt (build.mjs). ?tab=freigabe öffnet den Reiter gleich (Deep-Link), ebenso ?tab=botleads
// mit ?bot=<Id> (Einzelansicht aus dem Ereignisstrom) und ?botleads=leer (die Liste ist leer: Leerzustand ohne Filter). Die Skript-API liefert 60 feste
// Bot-Leads samt drei gekürzten Stubs (scripted-api.js, „Bot-Leads (P6)“).
// Mit ?page=1&fault=none läuft die Seite über den echten API-Client und die Fetch-Weiche (Pult, Aktionen, Interessent).
// ?p4=1 (nur mit ?page=1) nimmt den Datensatz für die Gespräche und die Werkstatt (P4: als Mensch antworten, Vorschau freigeben und bauen,
// Antwort des Kunden einfügen) statt der P3-Einträge.
import { StrictMode, createRef } from 'react'
// Einstiegspunkt des Prüfstands: die echte Seite WorkersPage der Pixelwelt (Hochhaus, Pult, Keller) mit der Skript-API, ohne Anmeldung und ohne Appwrite.
// Drumherum steht dieselbe Hülle wie in der App (Menüleiste links, Seitenrand), damit die Welt genauso groß wird wie im echten Betrieb.
// Der Router ist ein BrowserRouter: ?tab=… und ?bot=… stehen in der echten Adresse des Browsers, ein Wechsel des Bildschirms (Etage Freigabe, „◂ Hochhaus“)
// ändert sie wie in der App; die Adresse lässt sich also lesen und prüfen. Die Parameter des Prüfstands selbst werden nur beim Laden gelesen.
// AuthContext, useEmployees und workersClient sind durch stub-*.js ersetzt (build.mjs); die Seite bekommt ihre API über harness-bridge.js.
// Parameter (alle optional):
// ?tab=hochhaus|freigabe|botleads öffnet den Bildschirm gleich (Deep-Link wie aus dem Ereignisstrom); mit &bot=<Id> den Einzelfall im Keller
// ?halted=1 startet mit gesetzter Bounce-Notbremse (Banner „Versand gestoppt“)
// ?fault=none|down|session|hang die Seite läuft über den echten API-Client (createWorkersApi) und die Fetch-Weiche scripted-fetch.js;
// window.__wk.control.fault lässt sich zur Laufzeit auf null | 'down' | 'session' | 'hang' setzen (Ausfall).
// Ohne den Parameter bekommt die Seite die Skript-API direkt.
// ?p4=1 nimmt für das Pult den Datensatz der Gespräche und der Werkstatt (als Mensch antworten, Vorschau freigeben und
// bauen, Antwort des Kunden einfügen) statt der P3-Einträge
// ?botleads=leer die Bot-Lead-Liste ist leer (Leerzustand ohne Filter)
// window.__wk = { api, control }: die Skript-API (Schalter faults, pultEmpty, pultCalls, prospectCalls, pauseCalls, botleadCalls …) und die Steuerung der
// Fetch-Weiche (fault, calls, jwts) für shot.cjs.
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { MemoryRouter } from 'react-router-dom'
import { BrowserRouter } from 'react-router-dom'
import '../../src/styles/global.css'
import '../../src/styles/workers.css'
import PageHeader from '../../src/components/ui/PageHeader'
import WorkersBoard from '../../src/components/workers/WorkersBoard'
import WorkersPage from '../../src/pages/WorkersPage'
import { createWorkersApi } from '../../src/lib/workersApi.js'
import { createScriptedApi } from './scripted-api.js'
@@ -25,15 +25,13 @@ import { createScriptedFetch } from './scripted-fetch.js'
import { holder } from './harness-bridge.js'
const params = new URLSearchParams(window.location.search)
const pageMode = params.get('page') === '1'
const scripted = createScriptedApi({
now: () => performance.now(),
base: Date.now(),
halted: params.get('halted') === '1',
pult: pageMode ? (params.get('p4') === '1' ? 'p4' : true) : false,
pult: params.get('p4') === '1' ? 'p4' : true,
botleadsEmpty: params.get('botleads') === 'leer',
})
const stage = createRef()
const fault = params.get('fault')
const control = { fault: fault && fault !== 'none' ? fault : null, calls: [], jwts: [] }
// Zeitgrenze im Prüfstand kurz (1,5 s statt 20 s), damit der Ausfall „Antwort kommt nie“ in Sekunden sichtbar wird
@@ -47,31 +45,20 @@ const api = fault === null
fetchImpl: createScriptedFetch(scripted, control),
timeoutMs: 1500,
})
window.__wk = { stage, api: scripted, control }
window.__wk = { api: scripted, control }
holder.api = api
const tab = params.get('tab')
const bot = params.get('bot')
const entries = [pageMode && tab ? `/hustler?tab=${encodeURIComponent(tab)}${bot ? `&bot=${encodeURIComponent(bot)}` : ''}` : '/hustler']
createRoot(document.getElementById('root')).render(
<StrictMode>
<MemoryRouter initialEntries={entries}>
<BrowserRouter>
<div className="shell">
<div className="sidebar" aria-hidden="true" />
<div className="main">
<main id="inhalt">
{pageMode ? (
<WorkersPage />
) : (
<div className="page wk-page">
<PageHeader title="Hustler" subtitle="Die Nachtschicht deiner Claude-Armee" />
<WorkersBoard api={api} isAdmin stageRef={stage} />
</div>
)}
<WorkersPage />
</main>
</div>
</div>
</MemoryRouter>
</BrowserRouter>
</StrictMode>,
)
+16 -7
View File
@@ -494,14 +494,23 @@ export const BOTLEAD_IDS = Object.freeze({
const BOTLEAD_ROOM_LABEL = Object.freeze({ aerzte: 'Ärzte & Praxen', handwerk: 'Handwerk', selbst: 'Selbstständige', laden: 'Läden & Gastro' })
const BOTLEAD_ANCHOR_MS = Date.parse('2026-10-17T20:00:00.000Z')
const BOTLEAD_STEP_MS = 14.5 * 60 * 60 * 1000
// Die 14 festen Grundtexte des Dienstes (workers-dev-p6/src/scoring/business.js, Konstante GRUND; wörtlich wie tests/fixtures/botlead-gruende.json): die Akte im Keller
// erkennt sie am Wortlaut und zeigt Symbol und Erklärung dazu. Die Stelle 2 („Website in gutem Zustand“) bestimmt unten, welche Einträge eine Website haben.
const BOTLEAD_REASONS = Object.freeze([
'Keine veröffentlichte E-Mail-Adresse gefunden (nur ein Kontaktformular).',
'Gehört zu einer Kette oder einem Filialverbund, wird nicht angeschrieben.',
'Website ist in gutem Zustand (modern, mobil nutzbar), kein Bedarf erkennbar.',
'Kein Mail-Platz mehr in dieser Nacht (Tageslimit erreicht).',
'Mail nur über eine Sammeladresse des Verbunds, nicht eindeutig dem Betrieb zuzuordnen.',
'Lesen der Seite fehlgeschlagen (Zeitüberschreitung).',
'Name fehlt in Impressum und Karte.',
'keine veröffentlichte Mail',
'Kette oder Franchise',
'Website in gutem Zustand',
'Punktzahl unter 55',
'Adresse nicht zustellbar',
'veraltet (nicht ausgewählt)',
'schon bekannt (E-Mail oder Telefon)',
'schon bekannt (Name und PLZ)',
'Sperrliste',
'Lesen fehlgeschlagen',
'Impressum passt nicht zum Betrieb',
'Lauf unterbrochen',
'Bewertung ausgefallen',
'Name fehlt',
])
const BOTLEAD_LONG_REASON = 'Impressum nennt nur eine Postfachadresse und ein Kontaktformular; die einzige Mail-Adresse (verwaltung@sehr-langer-domainname-der-nicht-umbricht-handwerksbetrieb-westpfalz-gruppe.example) gehört einem Dritten.'
const BOTLEAD_LONG_NAME = 'Elektroinstallation-und-Haustechnik-Hoffmann-Sonnenschein-Wittgensteiner GmbH'
File diff suppressed because it is too large. Load diff