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:
1 parent
8363b63374
commit
6eb4dd3c51
11 files changed
+121
-82
No files matched your search
@@ -18,7 +18,7 @@ function Ueberschrift() {
|
||||
// (regieRef, die Seite ruft regieRef.current.push(events) bei neuen Ereignissen), der Bildschirmwechsel (onOpen(tab)) und vonTab (der Bildschirm, von dem man
|
||||
// zurückkommt: 'freigabe' | 'botleads' | null) kommen von der Seite. Beim Zurückkommen bekommt die Etage, über die man gegangen war, den Fokus (M2).
|
||||
// Die Kopfzeile steht im DOM vor der Welt, damit die Tab-Reihenfolge oben beginnt (M7); über der Welt liegt sie per z-index.
|
||||
// stufe (Standard HUSTLER_STUFE, hustlerStufe.js) geht an die Welt (Keller-Etage als Link nur ab Stufe 6) und an die Detailansicht (Spur-Links in den Keller nur ab Stufe 6).
|
||||
// stufe (Standard HUSTLER_STUFE, hustlerStufe.js) geht an die Welt (Keller-Etage als Link nur ab Stufe 3) und an die Detailansicht (Spur-Links in den Keller nur ab Stufe 3).
|
||||
// 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, vonTab = null, stufe = HUSTLER_STUFE }) {
|
||||
const wurzelRef = useRef(null)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Himmel, Funkeln, Mond, die 2400 x 1200 große Bühne (per transform verschoben und gezoomt) mit Welt-, Licht-, Haus- und Fensterebenen, Neon, Antenne, Aufzug,
|
||||
// Raumschilder, Bildschirme, Radar, Briefkasten-Zähler, Tresor-/Keller-Text, den Etagen (Knöpfe, Freigabe und Keller sind Links) und den 19 Hustlern; davor der Schnee.
|
||||
// Alles hängt am Dienst-Zustand: Figur und Zusätze nach Status (figurZustand), graue Raum-Ebene samt Papierschild nur für nicht angeschlossene Such-Räume.
|
||||
// Stufen-Sperre (hustlerStufe.js): unterhalb der Keller-Stufe (6) ist die Keller-Etage kein Link auf die Bot-Leads, sondern ein Knopf wie jede andere Etage (heranzoomen).
|
||||
// Stufen-Sperre (hustlerStufe.js): unterhalb der Stufe, ab der der Keller begehbar ist (3), ist die Keller-Etage kein Link auf den Keller, sondern ein Knopf wie jede andere Etage (heranzoomen).
|
||||
// Die Kamera (Rad, Ziehen, Pinch, Tasten) steckt in useKamera. `children` (die Regie-Ebene) liegt in der Bühne über den Figuren, in Weltkoordinaten.
|
||||
// Bedienung: Tastaturfokus (:focus-visible) auf Etage oder Figur außerhalb des Bildes holt sie per Kamera herein (beiFokus, kein Schwenk bei Mausklick); das Sichtfenster ist
|
||||
// per tabindex -1 fokussierbar (ein Klick in die Welt gibt ihm den Fokus, damit + - 0 Esc wirken); die Tippflächen der Figuren wachsen mit dem Zoom (--hk-tipp-*, tippRand);
|
||||
@@ -10,7 +10,7 @@
|
||||
// Kein CSS-Import (die Stile hängt WorkersPage ein: styles/hustler/hochhaus.css, hustler-ui.css, hustler-welt.css), keine Bilder, kein innerHTML.
|
||||
import { forwardRef, useImperativeHandle } from 'react'
|
||||
import { anzeigeName, figurZustand, schilder, tippRand } from '../../../lib/hustlerSicht.js'
|
||||
import { HUSTLER_STUFE, kellerOffen } from '../../../lib/hustlerStufe.js'
|
||||
import { HUSTLER_STUFE, kellerBegehbar } from '../../../lib/hustlerStufe.js'
|
||||
import { AUFZUG, ETAGEN, ETAGEN_SPALTE, HAUS_BILD, HUSTLER, RAEUME, SCHILDER } from '../../../lib/hustlerWelt.js'
|
||||
import { FLOOR_TITLES, ROLES, ROOM_TITLES } from '../../../lib/workersRoster.js'
|
||||
import useKamera from './useKamera.js'
|
||||
@@ -21,9 +21,9 @@ const LINK_ETAGEN = Object.freeze({
|
||||
keller: Object.freeze({ tab: 'botleads', text: 'Keller · Bot-Leads ▸', label: 'Keller · Bot-Leads: Bot-Leads öffnen' }),
|
||||
})
|
||||
|
||||
// Welche Etagen Links sind: der Keller nur, wenn es die Bot-Leads schon gibt; sonst zoomt er heran wie jede andere Etage
|
||||
// Welche Etagen Links sind: der Keller nur, wenn er begehbar ist (ab Stufe 3, auch wenn sein Register noch leer ist); sonst zoomt er heran wie jede andere Etage
|
||||
const OHNE_KELLER = Object.freeze({ freigabe: LINK_ETAGEN.freigabe })
|
||||
const linkEtagen = (stufe) => (kellerOffen(stufe) ? LINK_ETAGEN : OHNE_KELLER)
|
||||
const linkEtagen = (stufe) => (kellerBegehbar(stufe) ? LINK_ETAGEN : OHNE_KELLER)
|
||||
|
||||
const HAUS_RECHTECK = Object.freeze({ left: HAUS_BILD.x, top: HAUS_BILD.y, width: HAUS_BILD.w, height: HAUS_BILD.h })
|
||||
|
||||
@@ -220,7 +220,7 @@ const HochhausWelt = forwardRef(function HochhausWelt({ state, workers, onZeige,
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
{/* Etagen: Knöpfe zoomen heran, Freigabe (und ab der Keller-Stufe der Keller) sind Links auf die anderen Bildschirme */}
|
||||
{/* Etagen: Knöpfe zoomen heran, Freigabe (und ab der Stufe, ab der der Keller begehbar ist, der Keller) sind Links auf die anderen Bildschirme */}
|
||||
{ETAGEN.map((e) => {
|
||||
const stil = { left: ETAGEN_SPALTE.x, top: e.oben, width: ETAGEN_SPALTE.w, height: e.unten - e.oben }
|
||||
const link = links[e.id]
|
||||
|
||||
@@ -27,7 +27,7 @@ const isRecord = (v) => v !== null && typeof v === 'object' && !Array.isArray(v)
|
||||
|
||||
// Einziger Ort des Dialogs, an dem ein Ziel für einen Link entsteht (wie TraceText in WorkerCard): nur was safeLink durchlässt, wird ein Link
|
||||
// (extern in neuem Tab mit noopener, intern über den Router), alles andere bleibt Text. Stufen-Sperre: ein interner Link in den Keller (?tab=botleads) bleibt
|
||||
// unterhalb der Keller-Stufe (hustlerStufe.js) Text.
|
||||
// unterhalb der Stufe, ab der der Keller begehbar ist (hustlerStufe.js, Stufe 3), Text.
|
||||
function SpurText({ link, stufe, children }) {
|
||||
const safe = isRecord(link) ? safeLink(link.href) : null
|
||||
if (!safe || !linkErlaubt(safe.href, stufe)) return <>{children}</>
|
||||
|
||||
+18
-11
@@ -1,14 +1,18 @@
|
||||
// Stufen-Sperre der Hustler-Seite: EINE Quelle der Wahrheit dafür, was die Oberfläche schon zeigen darf. Der Dienst (workers-dienst) wächst Stufe für Stufe
|
||||
// (P1, P3, P4, P6); die Oberfläche ist fertig und würde sonst Knöpfe und Bildschirme zeigen, deren Endpunkte noch 404 liefern.
|
||||
// Stufe < 4: im Pult keine Antwort-Knöpfe (paste „Antwort des Kunden einfügen“, reply „Als Mensch antworten“, approve-preview, start-build).
|
||||
// Stufe < 6: kein Keller mit den Bot-Leads (kein Bildschirm, kein Link, kein Abruf von /api/workers/botleads); die Keller-Etage zoomt wie jede andere.
|
||||
// Stufe < 3: kein Keller (keine Etage als Link, kein Bildschirm, keine Spur-Links in den Keller); die Keller-Etage zoomt wie jede andere.
|
||||
// Stufe >= 3: der Keller ist begehbar (Keller-Etage, ?tab=botleads, Spur-Links): Arne, Regale und Register-Buch stehen da, das Register aber leer, ehrlich beschriftet.
|
||||
// Stufe < 6: der Keller ruft die Bot-Leads nicht ab (kein GET /api/workers/botleads, auch kein JWT); der Endpunkt gibt es im Dienst erst mit P6.
|
||||
// Kein React, kein DOM, kein Appwrite: unter Node testbar (loadModule). Es gibt keine Überschreibung zur Laufzeit (kein localStorage, keine Adresse): die Stufe ist
|
||||
// zur Bauzeit festgelegt. Nur der Prüfstand (scripts/workers-harness) baut mit VITE_HUSTLER_STUFE=6, damit seine P4- und Keller-Szenarien weiter laufen.
|
||||
// zur Bauzeit festgelegt. Nur der Prüfstand (scripts/workers-harness) baut mit VITE_HUSTLER_STUFE=6, damit seine P4- und Keller-Szenarien (mit Live-Daten) weiter laufen.
|
||||
|
||||
/** Ab dieser Stufe erscheinen die Antwort-Knöpfe im Pult (Dienst P4: paste, reply, approve-preview, start-build). */
|
||||
export const STUFE_ANTWORTEN = 4
|
||||
/** Ab dieser Stufe erscheint der Keller mit den Bot-Leads (Dienst P6: GET /botleads). */
|
||||
export const STUFE_KELLER = 6
|
||||
/** Ab dieser Stufe ist der Keller begehbar: Keller-Etage als Link, Bildschirm ?tab=botleads, Spur-Links. Schon mit P3; ohne P6 bleibt das Register leer. */
|
||||
export const KELLER_BEGEHBAR_AB = 3
|
||||
/** Ab dieser Stufe ruft der Keller die Bot-Leads ab (Dienst P6: GET /botleads). Darunter keine Anfrage, auch kein JWT. */
|
||||
export const BOTLEADS_AB = 6
|
||||
|
||||
const KLEINSTE = 1
|
||||
const GROESSTE = 9
|
||||
@@ -33,7 +37,7 @@ function buildWert() {
|
||||
}
|
||||
}
|
||||
|
||||
// 3 = Dienst P3 live; ab 4 (P4) erscheinen die Antwort-Knöpfe im Pult, ab 6 (P6) der Keller mit den Bot-Leads; beim jeweiligen Livegang hier hochsetzen
|
||||
// 3 = Dienst P3 live (der Keller ist begehbar, aber leer); ab 4 (P4) erscheinen die Antwort-Knöpfe im Pult, ab 6 (P6) liefert der Keller die Bot-Leads; beim jeweiligen Livegang hier hochsetzen
|
||||
export const HUSTLER_STUFE = stufeAus(buildWert(), 3)
|
||||
|
||||
// Eine übergebene Stufe (Parameter der Funktionen und Bausteine, vor allem für Tests); fehlt sie oder ist sie unbrauchbar, gilt HUSTLER_STUFE
|
||||
@@ -42,21 +46,24 @@ const stufeOder = (stufe) => stufeAus(stufe, HUSTLER_STUFE)
|
||||
/** Zeigt das Pult die Antwort-Knöpfe (paste, reply, approve-preview, start-build)? Ab Stufe 4. */
|
||||
export const antwortenOffen = (stufe = HUSTLER_STUFE) => stufeOder(stufe) >= STUFE_ANTWORTEN
|
||||
|
||||
/** Gibt es den Keller (Bildschirm Bot-Leads, Abruf von /botleads)? Ab Stufe 6. */
|
||||
export const kellerOffen = (stufe = HUSTLER_STUFE) => stufeOder(stufe) >= STUFE_KELLER
|
||||
/** Ist der Keller begehbar (Keller-Etage als Link, Bildschirm Bot-Leads, Spur-Links)? Ab Stufe 3. Ob er Daten hat, sagt botleadsAbrufbar. */
|
||||
export const kellerBegehbar = (stufe = HUSTLER_STUFE) => stufeOder(stufe) >= KELLER_BEGEHBAR_AB
|
||||
|
||||
/** Darf dieser Bildschirm (Wert von ?tab=) auf der Stufe gezeigt werden? Nur der Keller ('botleads') ist gesperrt, alles andere ist frei. */
|
||||
export const tabErlaubt = (tabId, stufe = HUSTLER_STUFE) => tabId !== 'botleads' || kellerOffen(stufe)
|
||||
/** Darf der Keller die Bot-Leads abrufen (GET /botleads, Dienst P6)? Ab Stufe 6. Darunter zeigt er sein leeres Register und fragt nichts an. */
|
||||
export const botleadsAbrufbar = (stufe = HUSTLER_STUFE) => stufeOder(stufe) >= BOTLEADS_AB
|
||||
|
||||
/** Darf dieser Bildschirm (Wert von ?tab=) auf der Stufe gezeigt werden? Nur der Keller ('botleads') ist an die Stufe gebunden (ab 3), alles andere ist frei. */
|
||||
export const tabErlaubt = (tabId, stufe = HUSTLER_STUFE) => tabId !== 'botleads' || kellerBegehbar(stufe)
|
||||
|
||||
// Adressen, unter denen die Hustler-Seite liegt: /hustler und die alten /workers und /leads (App.jsx leitet sie weiter, die Query bleibt)
|
||||
const SEITEN_PFADE = ['/hustler', '/workers', '/leads']
|
||||
|
||||
/**
|
||||
* Darf ein interner Link (aus der Spur im Dialog) auf der Stufe ein Link sein? Ein Link in den Keller (…/hustler?tab=botleads, auch über /workers und /leads)
|
||||
* ist unterhalb der Keller-Stufe nur Text; alles andere bleibt ein Link. Externe Adressen berührt das nicht.
|
||||
* ist unterhalb der Stufe, ab der der Keller begehbar ist (3), nur Text; alles andere bleibt ein Link. Externe Adressen berührt das nicht.
|
||||
*/
|
||||
export function linkErlaubt(href, stufe = HUSTLER_STUFE) {
|
||||
if (kellerOffen(stufe)) return true
|
||||
if (kellerBegehbar(stufe)) return true
|
||||
if (typeof href !== 'string' || !/^\/(?![/\\])/.test(href)) return true // nur interne Pfade (ein einzelner / am Anfang), nie //host oder eine ganze Adresse
|
||||
let ziel
|
||||
try {
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
// Rein: kein Appwrite-Import, getJwt und fetch werden injiziert (workersClient.js verdrahtet sie), damit die
|
||||
// deutschen Fehlertexte unter Node testbar sind. Fehler sind Error mit deutscher `message` und `status`.
|
||||
|
||||
import { HUSTLER_STUFE, kellerOffen } from './hustlerStufe'
|
||||
import { HUSTLER_STUFE, botleadsAbrufbar } from './hustlerStufe'
|
||||
|
||||
export const DOWN_TEXT = 'Der Hustler-Dienst ist gerade nicht erreichbar.'
|
||||
export const BAD_REPLY_TEXT = 'Der Hustler-Dienst antwortet nicht richtig.'
|
||||
export const SESSION_TEXT = 'Anmeldung abgelaufen. Bitte neu anmelden.'
|
||||
export const FORBIDDEN_TEXT = 'Dafür fehlt dir die Berechtigung.'
|
||||
export const INVALID_ID_TEXT = 'Ungültige Kennung.'
|
||||
// Der Keller (GET /botleads) gehört zu Stufe 6 (hustlerStufe.js); darunter gibt es den Endpunkt im Dienst noch nicht
|
||||
export const KELLER_GESPERRT_TEXT = 'Der Keller ist noch nicht freigeschaltet.'
|
||||
// Die Bot-Leads (GET /botleads) gehören zu Stufe 6 (hustlerStufe.js, BOTLEADS_AB); darunter gibt es den Endpunkt im Dienst noch nicht
|
||||
export const BOTLEADS_GESPERRT_TEXT = 'Die Bot-Leads sind noch nicht freigeschaltet.'
|
||||
|
||||
// Zeitgrenze je Abruf (JWT holen, senden, Antwort lesen): ohne sie bliebe ein Abruf auf toter Verbindung (Rechner im Ruhezustand,
|
||||
// Netzwechsel) minutenlang offen und der Feed (ein Abruf je Schleife) stünde still, ohne Fehlermeldung.
|
||||
@@ -198,9 +198,9 @@ export function createWorkersApi({
|
||||
if (!PULT_ACTIONS.includes(action)) throw statusError(`Unbekannte Aktion: ${action}`, 400)
|
||||
return request(`/pult/${pathId(id)}/${action}`, { method: 'POST', body, limitMs: SLOW_PULT_ACTIONS.includes(action) ? actionTimeoutMs : undefined })
|
||||
},
|
||||
// Unterhalb der Keller-Stufe (6) geht gar keine Anfrage hinaus: der Endpunkt fehlt im Dienst noch (die Oberfläche zeigt den Keller dort auch nicht)
|
||||
// Unterhalb der Stufe 6 (BOTLEADS_AB) geht gar keine Anfrage hinaus: der Endpunkt fehlt im Dienst noch (der Keller ruft ihn dort auch nicht auf, er zeigt sein leeres Register)
|
||||
botleads: async ({ room, q, page, id } = {}) => {
|
||||
if (!kellerOffen(stufe)) throw statusError(KELLER_GESPERRT_TEXT, 404)
|
||||
if (!botleadsAbrufbar(stufe)) throw statusError(BOTLEADS_GESPERRT_TEXT, 404)
|
||||
return request(`/botleads${query([['room', room], ['q', q], ['page', page], ['id', id]])}`)
|
||||
},
|
||||
prospect: async (id) => request(`/prospect/${pathId(id)}`),
|
||||
|
||||
@@ -24,8 +24,9 @@ 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 erst ab Stufe 6 ein Bildschirm. Darunter fallen ?tab=botleads (auch mit &bot=) und jeder
|
||||
// andere unbekannte Wert auf das Hochhaus zurück (pickTab nimmt den ersten Reiter), BotLeadList wird nie eingehängt und /api/workers/botleads nie abgerufen.
|
||||
// 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' },
|
||||
@@ -104,6 +105,7 @@ export default function WorkersPage({ stufe = HUSTLER_STUFE }) {
|
||||
{active.id === 'botleads' && (
|
||||
<BotLeadList
|
||||
api={api}
|
||||
stufe={stufe}
|
||||
bot={bot}
|
||||
onClearBot={() => setParams({ tab: 'botleads' })}
|
||||
kpis={state?.kpis}
|
||||
|
||||
Reference in new issue
Block a user