Files
tickte-system/src/pages/WorkersPage.jsx
T
ClaudeandClaude Sonnet 5.5 6eb4dd3c51 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>
2026-10-22 12:21:59 +00:00

120 lines
5.9 KiB
React

import { useCallback, useEffect, useRef, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import BotLeadList from '../components/workers/BotLeadList'
import Hochhaus from '../components/workers/hochhaus/Hochhaus'
import PultList from '../components/workers/PultList'
import { useAuth } from '../context/AuthContext'
import { useWorkersFeed } from '../hooks/useWorkersFeed'
import { HUSTLER_STUFE, tabErlaubt } from '../lib/hustlerStufe'
import { indexWorkers, reduceEvents } from '../lib/workersEvents'
import { workersApi } from '../lib/workersClient'
import { pickTab } from '../lib/workersView'
import '../styles/workers.css'
import '../styles/hustler/schrift.css'
import '../styles/hustler/hochhaus.css'
import '../styles/hustler/keller.css'
import '../styles/hustler/hustler-keller-ui.css'
import '../styles/hustler/pult.css'
import '../styles/hustler/hustler-pult-ui.css'
import '../styles/hustler/hustler-ui.css'
import '../styles/hustler/hustler-welt.css'
import '../styles/hustler/hustler-kopf.css'
import '../styles/hustler/hustler-detail.css'
import '../styles/hustler/hustler-regie.css'
// Bildschirme der Seite: das Hochhaus, das Pult (Freigabe von P3) und der Keller (Bot-Leads von P6). Eine Reiterleiste gibt es nicht mehr;
// gewechselt wird im Haus (Etage Freigabe, Etage Keller) und über „◂ Hochhaus“ in Pult und Keller.
// Stufen-Sperre (lib/hustlerStufe.js, HUSTLER_STUFE): der Keller ist ab Stufe 3 ein Bildschirm (begehbar), die Bot-Leads ruft er aber erst ab Stufe 6 ab; darunter zeigt
// BotLeadList sein leeres Register und fragt /api/workers/botleads nie an. Unterhalb von Stufe 3 fallen ?tab=botleads (auch mit &bot=) und jeder andere unbekannte Wert
// auf das Hochhaus zurück (pickTab nimmt den ersten Reiter), BotLeadList wird dann nie eingehängt.
const WORKERS_TABS = [
{ id: 'hochhaus', label: 'Hochhaus' },
{ id: 'freigabe', label: 'Freigabe' },
{ id: 'botleads', label: 'Bot-Leads' },
]
const api = workersApi
// Seite „Hustler“ (Dateiname bleibt WorkersPage): Tab-Titel, Bildschirmwahl über ?tab= und der gewählte Bildschirm in voller Höhe.
// ?tab=botleads&bot=<Id> (Link aus dem Ereignisstrom) zeigt einen einzelnen Bot-Lead; ein Wechsel per setParams lässt bot weg.
// Auth und Dienst-API kommen von hier. Der Feed (Zustand alle 15 s, Ereignisse alle 5 s) läuft genau einmal pro Seite, egal welcher
// Bildschirm offen ist; die Hustler-Tabelle (indexWorkers beim Schnappschuss, reduceEvents bei Ereignissen) gehört ebenfalls hierher.
// Ereignisse gehen zusätzlich an die Regie, die nur das Hochhaus über regieRef einhängt.
// Die Tabelle liegt nie einen Render hinter dem Schnappschuss zurück (sonst stünden alle Figuren kurz auf „Leerlauf“): sie merkt sich, aus welchem Schnappschuss sie
// entstand, und wird im Render neu aus `state` gebaut, sobald der Schnappschuss ein anderer ist. Pause (state.paused) geht an alle drei Bildschirme.
// Das Hochhaus bekommt vonTab (der Bildschirm, von dem man zurückkommt) und fokussiert dann die Etage, über die man gegangen war.
// stufe (Standard HUSTLER_STUFE) ist nur für Tests ein Parameter; die App rendert die Seite ohne und liest die Stufe aus dem Bau.
export default function WorkersPage({ stufe = HUSTLER_STUFE }) {
const { isAdmin, user } = useAuth()
const [params, setParams] = useSearchParams()
const active = pickTab(WORKERS_TABS.filter((t) => tabErlaubt(t.id, stufe)), params.get('tab'))
const bot = params.get('bot') ?? ''
const [tabelle, setTabelle] = useState({ von: null, workers: {} }) // Hustler-Tabelle samt dem Schnappschuss, aus dem sie entstand
const [verlauf, setVerlauf] = useState({ aktuell: active.id, vorher: null }) // aktueller und vorheriger Bildschirm
const regieRef = useRef(null)
// Tab-Titel nur solange die Seite offen ist; beim Verlassen kommt der vorige Titel zurück
useEffect(() => {
const previous = document.title
document.title = 'Hustler'
return () => { document.title = previous }
}, [])
// Neue Ereignisse: Tabelle fortschreiben und der Regie vorspielen (die Regie kennt den Rückstau selbst)
const onEvents = useCallback((events) => {
setTabelle((t) => ({ von: t.von, workers: reduceEvents(t.workers, events) }))
if (regieRef.current) regieRef.current.push(events)
}, [])
const { state, error, loaded, refresh } = useWorkersFeed(api, { onEvents })
// Neuer Schnappschuss: Tabelle sofort neu bauen (nicht erst nach einem Effekt) und merken
const veraltet = Boolean(state) && tabelle.von !== state
const workers = veraltet ? indexWorkers(state) : tabelle.workers
if (veraltet) setTabelle({ von: state, workers })
// Bildschirmwechsel: der bisherige wird zum vorherigen
const gewechselt = verlauf.aktuell !== active.id
const vonTab = gewechselt ? verlauf.aktuell : verlauf.vorher
if (gewechselt) setVerlauf({ aktuell: active.id, vorher: vonTab })
const paused = state?.paused === true
return (
<div className="hk-seite" data-tab={active.id}>
{active.id === 'hochhaus' && (
<Hochhaus
api={api}
isAdmin={isAdmin}
feed={{ state, error, loaded, refresh }}
workers={workers}
regieRef={regieRef}
vonTab={vonTab}
stufe={stufe}
onOpen={(tab) => { if (tabErlaubt(tab, stufe)) setParams({ tab }) }}
/>
)}
{active.id === 'freigabe' && (
<PultList
api={api}
me={{ id: user?.$id, name: user?.name }}
kpis={state?.kpis}
workers={workers}
paused={paused}
stufe={stufe}
onBack={() => setParams({ tab: 'hochhaus' })}
/>
)}
{active.id === 'botleads' && (
<BotLeadList
api={api}
stufe={stufe}
bot={bot}
onClearBot={() => setParams({ tab: 'botleads' })}
kpis={state?.kpis}
workers={workers}
paused={paused}
onBack={() => setParams({ tab: 'hochhaus' })}
/>
)}
</div>
)
}