Hustler-Pixelwelt T3: Seite hält den Feed, Bildschirme Hochhaus/Pult/Keller, Vollhöhen-Rahmen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-22 08:02:44 +00:00
1 parent 8bd365b1fc
commit d8912ee363
5 files changed
+527 -23

No files matched your search

@@ -0,0 +1,33 @@
// Bildschirm „Hochhaus“ (Spielwelt, Kopfzeile, Detailansicht). Gerüst von T3: Zustände wie bisher im WorkersBoard; T5–T8 füllen die Welt.
// Reine Darstellung ohne Auth-Bezug: isAdmin, der Feed (feed = { state, error, loaded, refresh }), die Hustler-Tabelle (workers), die Regie-Anbindung
// (regieRef, die Seite ruft regieRef.current.push(events) bei neuen Ereignissen) und der Bildschirmwechsel (onOpen(tab)) kommen von der Seite.
// Kein CSS-Import und keine Bilder: die Stile hängt WorkersPage ein (Regeln dieser Datei in styles/hustler/hustler-ui.css).
export default function Hochhaus({ api, isAdmin, feed, workers, regieRef, onOpen }) {
const ready = feed.loaded && Boolean(feed.state)
// Dienst noch nie erreicht: Pixel-Leerzustand mit Fehlertext und „Neu laden“
if (!ready && feed.error) {
return (
<div className="hk-hochhaus">
<div className="hk-leer">
<strong className="hk-leer-titel">Der Hustler-Dienst ist noch nicht erreichbar</strong>
<p className="hk-leer-text">{feed.error}</p>
<button type="button" className="hk-knopf" onClick={() => feed.refresh()}>Neu laden</button>
</div>
</div>
)
}
if (!ready) {
return (
<div className="hk-hochhaus">
<div className="hk-laden" role="status">Wird geladen …</div>
</div>
)
}
// T5 füllt diesen Rahmen mit der Welt (Kopfzeile T6, Detailansicht T7, Regie T8)
return (
<div className="hk-hochhaus">
<div className="hk-welt-rahmen" />
</div>
)
}
+63 -14
View File
@@ -1,15 +1,22 @@
import { useEffect } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import { PageHeader, Tabs } from '../components/ui'
import BotLeadList from '../components/workers/BotLeadList'
import Hochhaus from '../components/workers/hochhaus/Hochhaus'
import PultList from '../components/workers/PultList'
import WorkersBoard from '../components/workers/WorkersBoard'
import { useAuth } from '../context/AuthContext'
import { useWorkersFeed } from '../hooks/useWorkersFeed'
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/pult.css'
import '../styles/hustler/hustler-ui.css'
// Reiter der Seite (Hochhaus, Freigabe von P3, Bot-Leads von P6); die Reiterleiste erscheint erst ab zwei Reitern.
// 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.
const WORKERS_TABS = [
{ id: 'hochhaus', label: 'Hochhaus' },
{ id: 'freigabe', label: 'Freigabe' },
@@ -18,14 +25,18 @@ const WORKERS_TABS = [
const api = workersApi
// Seite „Hustler“ (Dateiname bleibt WorkersPage): Titel, Reiterwahl über ?tab= und der Inhalt des gewählten Reiters.
// ?tab=botleads&bot=<Id> (Link aus dem Ereignisstrom) zeigt einen einzelnen Bot-Lead; ein Reiterwechsel lässt bot weg.
// Auth und Dienst-API kommen von hier; das Board selbst kennt weder Anmeldung noch Appwrite.
// 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.
export default function WorkersPage() {
const { isAdmin, user } = useAuth()
const [params, setParams] = useSearchParams()
const active = pickTab(WORKERS_TABS, params.get('tab'))
const bot = params.get('bot') ?? ''
const [workers, setWorkers] = useState({})
const regieRef = useRef(null)
// Tab-Titel nur solange die Seite offen ist; beim Verlassen kommt der vorige Titel zurück
useEffect(() => {
@@ -34,15 +45,53 @@ export default function WorkersPage() {
return () => { document.title = previous }
}, [])
// Neue Ereignisse: Tabelle fortschreiben und der Regie vorspielen (die Regie kennt den Rückstau selbst)
const onEvents = useCallback((events) => {
setWorkers((w) => reduceEvents(w, events))
if (regieRef.current) regieRef.current.push(events)
}, [])
const { state, error, loaded, refresh } = useWorkersFeed(api, { onEvents })
useEffect(() => {
if (state) setWorkers(indexWorkers(state))
}, [state])
return (
<div className="page wk-page">
<PageHeader title="Hustler" subtitle="Die Nachtschicht deiner Claude-Armee" />
{WORKERS_TABS.length > 1 && (
<Tabs tabs={WORKERS_TABS} active={active.id} onChange={(id) => setParams({ tab: id })} />
<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}
onOpen={(tab) => setParams({ tab })}
/>
)}
{/* Pult und Keller stecken bis T9/T10 noch in der alten hellen Hülle (.page.wk-page); hustler-ui.css gibt ihnen dafür normale Höhe */}
{active.id === 'freigabe' && (
<div className="page wk-page">
<PultList
api={api}
me={{ id: user?.$id, name: user?.name }}
kpis={state?.kpis}
workers={workers}
onBack={() => setParams({ tab: 'hochhaus' })}
/>
</div>
)}
{active.id === 'botleads' && (
<div className="page wk-page">
<BotLeadList
api={api}
bot={bot}
onClearBot={() => setParams({ tab: 'botleads' })}
kpis={state?.kpis}
workers={workers}
onBack={() => setParams({ tab: 'hochhaus' })}
/>
</div>
)}
{active.id === 'hochhaus' && <WorkersBoard api={api} isAdmin={isAdmin} />}
{active.id === 'freigabe' && <PultList api={api} me={{ id: user?.$id, name: user?.name }} />}
{active.id === 'botleads' && <BotLeadList api={api} bot={bot} onClearBot={() => setParams({ tab: 'botleads' })} />}
</div>
)
}
+27
View File
@@ -0,0 +1,27 @@
/* Handgeschriebene Regeln der Hustler-Pixelwelt (WorkersPage, Hochhaus, später Pult und Keller). Nicht erzeugt: die drei Bildschirm-Dateien
hochhaus.css, keller.css und pult.css kommen von scripts/hustler-import.mjs und werden nie von Hand geändert. Alle Klassen beginnen mit hk-
und stehen unter einer der Wurzeln .hk-seite, .hk-hochhaus, .hk-keller oder .hk-pult. */
/* ---------- Seite: füllt den Inhaltsbereich (main) neben der WOMS-Seitenleiste in voller Höhe, kein Seiten-Scrollen ---------- */
.hk-seite { position: relative; height: 100vh; height: 100dvh; overflow: hidden; background: #0b1030; }
@media (max-width: 860px) {
/* Mobilleiste aus global.css (.mobile-bar, 52 px) abziehen */
.hk-seite { height: calc(100vh - 52px); height: calc(100dvh - 52px); }
}
.hk-seite > .hk-hochhaus { position: absolute; inset: 0; }
/* Übergang bis T9/T10: Pult und Keller stehen noch in der alten hellen Hülle (.page.wk-page), brauchen normale Höhe, Scrollen und hellen Grund.
Entfällt, sobald PultList und BotLeadList in ihre Pixelszenen umgebaut sind (.hk-pult / .hk-keller füllen dann selbst die Seite). */
.hk-seite[data-tab="freigabe"], .hk-seite[data-tab="botleads"] { height: auto; overflow: visible; background: none; }
/* ---------- Hochhaus: Laden und Leerzustand im Pixel-Stil ---------- */
.hk-hochhaus .hk-laden,
.hk-hochhaus .hk-leer {
position: absolute; inset: 0;
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px;
padding: 24px; box-sizing: border-box; text-align: center;
background: #0b1030; color: #e6ebff;
font-family: 'Pixelify Sans', ui-monospace, monospace; font-size: 18px; line-height: 1.4;
}
.hk-hochhaus .hk-leer-titel { display: block; margin: 0; color: #ffe9a0; font-size: 22px; font-weight: 400; }
.hk-hochhaus .hk-leer-text { margin: 0; max-width: 46ch; color: #b8c2f0; overflow-wrap: anywhere; }