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:
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
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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; }
|
||||
Reference in new issue
Block a user