Files
dogfather-universe/server/rechte.js
T
DogFatherGitandClaude Opus 5 150555bc33 Der Notizblock -- und eine Pruefung, die zwoelf Kacheln nie angesehen hat
Filipe: „fuer die modis, rechte und linke hand und dogfather eine
kachel hinzufuegst, sie soll: Notizen, heissen. ich will dass du das
auch wie ein notizblock erstellst. ich will dass es uebelst geil und
einzigartig ist."

=================================================================
TEIL 1: DIE FARBE -- UND WAS DABEI AUFFIEL
=================================================================

Fuer die neue Kachel braucht es einen Ton. Beim Suchen fiel auf, dass
tools/kachelton-entzerren.mjs zwoelf der 45 Farben fuer FREI hielt.

Sie sind es nicht. bereicheFuer() gibt fuer die fuenf Rollen des
Agenturhauses null zurueck -- das heisst „nimm die Liste aus dem
Browser", und die steht in workspace/assets/js/bereiche.js. Dort
stehen die Toene 1 bis 22 und 44: Dashboard, Zahlen, Team-Lage,
Scout-Pipeline. Kacheln, die jeden Tag jemand ansieht.

pruef-kachelfarben hatte denselben blinden Fleck. Sie meldete seit
Wochen „33 benutzte Toene" und war gruen. Was sie dadurch NICHT sah:

  * Der Farblauf von gestern Nacht hat zwoelf dieser Kacheln
    verschoben und drei davon unter die Buntheitsgrenze gedrueckt.
    Gruen geblieben.
  * Ton 7, 16 und 22 lagen SEIT JEHER unter der Grenze (0,112 / 0,116
    / 0,068). Nie gemeldet.

Das ist die Sorte gruener Haken, vor der die Hausregel warnt: Er sagt
nur, dass die Bedingung erfuellt war -- nicht, dass sie das Richtige
angesehen hat.

BEHOBEN, und zwar an der Wurzel: Die REGELN (Kontrast, Buntheit)
gelten jetzt fuer jeden Ton, der irgendwo an einer Kachel steht --
gelesen aus denselben fuenf Dateien, die auch pruef-kachel-universum
liest. Dazu die Namen der Kacheln, damit „Ton 1 traegt Dashboard"
ueberhaupt pruefbar ist.

DIE PALETTE WURDE NEU GERECHNET, mit dem dritten Verfahren an einem
Tag -- die ersten zwei stehen als Fehler im Kopf des Werkzeugs:

  1. Alle 45 neu verteilt. Lief ueber zwei ausdrueckliche Wuensche
     hinweg (#ff1a1a, #a8d8ff).
  2. Nur die Kollisionen, aber immer nur EINEN der beiden bewegt.
     Ergebnis: Ton 1 sprang vom Tuerkis ins Altrosa, 0,197 weit.
  3. BEIDE duerfen sich bewegen, und zwar beide nur ein bisschen.
     Zwei Toene, die 0,02 auseinanderstehen und 0,09 brauchen, teilen
     sich das -- jeder rueckt 0,045, und beide bleiben, was sie waren.

  kleinster Abstand   0,0154  ->  0,0905
  zu blass                 4  ->  0
  sichtbar veraendert           7 Kacheln (ueber 0,05)
  kaum zu sehen                28 (13 zwischen 0,02 und 0,05, 15 darunter)

EINE AUSNAHME MIT ZAHL, keine mit Achselzucken: Ton 1 (Dashboard)
bleibt acht Tausendstel unter der Buntheitsgrenze. Gemessen ueber ALLE
16,7 Millionen sRGB-Farben ist die naechste, die alle Regeln haelt,
#7a75c8 -- ein Blauviolett, 0,113 entfernt. Tuerkis erreicht in sRGB
schlicht keine hoehere Buntheit, und das schmale Band teilen sich
schon sieben Toene. Eine Kachel, die ihre Farbfamilie behaelt, ist die
bessere Antwort auf „richtig geil und speziell" als eine, die acht
Tausendstel bunter ist und niemand wiedererkennt. blassBis sagt jetzt
bei jeder Ausnahme, WIE WEIT sie reicht -- vorher hiess blassErlaubt
schlicht „hier wird weggesehen".

DIE NEUE FARBE IST GRUEN UND WOLLTE BERNSTEIN SEIN. Gesucht war die
Farbe von Papier. Gemessen gibt es sie nicht mehr: Der beste Bernstein
im ganzen Farbraum haelt 0,0799 Abstand -- unter der Hausgrenze. Und
Platz schaffen hilft nicht: Setzt man ihn fest, muss ein warmer Ton
das Band verlassen (Ton 45 waere 0,212 weit ins Magenta gewandert).
Die groesste wirklich freie Luecke liegt im Gruen, bei 0,0917. Ein
linierter Block in Gruen ist Papier, seit es Papier gibt.

=================================================================
TEIL 2: DER BLOCK
=================================================================

DREI ENTSCHEIDUNGEN, AUS DENEN DER REST FOLGT:

1. ES GIBT KEINEN SPEICHERN-KNOPF. Nirgends. Wer einen Block
   aufschlaegt und lostippt, drueckt hinterher nicht auf „sichern";
   er klappt ihn zu. Gesichert wird 800 ms nach dem letzten
   Tastendruck. Geht das nicht, bleibt der Text im Browser liegen und
   wird nachgereicht -- und der Fuss sagt ehrlich „noch nicht
   gesichert", statt einen Verlust zu melden, den man gerade nicht
   verhindern kann.

2. DAS BLATT IST EIN SCHREIBFELD MIT EINER DECKSCHICHT. Das Feld
   traegt Text und Cursor und ist unsichtbar; darueber zeichnet eine
   zweite Schicht denselben Text noch einmal -- mit Kaestchen,
   Ueberschriften, Strichen und Links.

   DARAUS FOLGT EINE EISERNE REGEL, und sie steht dreimal im Code:
   KEINE Auszeichnung darf die BREITE eines Zeichens aendern. Kein
   Fettdruck, keine andere Groesse, keine Sperrung. Erlaubt sind nur
   Farbe, Flaeche, Rahmen und Durchstreichen. Ein einziges
   font-weight: 700 verschoebe den Umbruch, und ab der zweiten Zeile
   stuende der sichtbare Text neben dem Cursor.

   pruef-notizen misst das am echten Umbruch: eine Probe mit allen
   Auszeichnungen und einer Zeile, die umbrechen MUSS. Sind beide
   Schichten verschieden hoch, sitzt der Umbruch woanders. Mit
   Gegenprobe -- Fettdruck auf der Deckschicht bricht die Deckung um
   genau eine Zeile (30 px), und die Zeile wird rot.

3. DIE TASTATUR FUEHRT DIE LISTE FORT, NICHT EINE LEISTE. Ein
   Spiegelstrich und Enter macht den naechsten Punkt, ein leeres
   Kaestchen und Enter das naechste Kaestchen -- und ein GESETZTER
   Haken wird dabei nicht mitgenommen, die naechste Aufgabe ist ja
   noch nicht erledigt. Zweimal Enter beendet die Liste. Ein Klick
   aufs Kaestchen hakt ab. Es gibt keine Werkzeugleiste, weil man beim
   Schreiben nie den Stift wechselt.

UND: NIEMAND SIEHT DIE NOTIZEN EINES ANDEREN. Auch DogFather nicht.
Es gibt in workspace-notizen.js keinen siehtAlles()-Zweig, keinen
Umschalter, keine fremde Liste -- jede Abfrage hat person_id = ? fest
eingebaut. Die Kachel steht deshalb in „Fuer dich" und nicht in
„Taeglich": Die Gruppe sagt die wichtigste Eigenschaft, bevor man sie
anfasst.

NUR AUF DER TEAM-ADRESSE. Auf der Agenturadresse ist die Seite ein
404 -- auch fuer DogFather. Das ist dieselbe Trennung wie bei Chat,
Kalender und Aufgaben, und sie steht an zwei Stellen: in
GEHOERT_ZU_ADRESSE (die Datei) und in der Schnittstelle (die Daten).
Die Seite ist nur HTML; die Daten sind die Sache.

Dazu: sechs Papierfarben, Anheften, Suche mit Hervorhebung im Text,
Papierkorb mit dreissig Tagen und einem Eintrag in der
Aufbewahrungsliste (ohne den waere die Frist ein Satz in einem
Kommentar -- genau das ist dem Support am 24.09. passiert).

=================================================================
EIN FUND BEIM BAUEN, DER ALLEN GEHOERT
=================================================================

DELETE /workspace/api/notizen/:id gab es schon -- in
workspace-aufgaben.js, fuer die Notizen AN einer Aufgabe. Weil jener
Router frueher eingehaengt ist, hat er jeden Loeschversuch des Blocks
abgefangen und mit 404 beantwortet.

Von aussen sah das aus wie ein Fehler im neuen Modul: Anlegen ging,
Aendern ging, Loeschen nicht. Man sucht dann im eigenen Code, und dort
ist nichts. Express meldet so etwas nicht -- es nimmt die erste Route,
die passt, und schweigt ueber die zweite.

Der Block liegt jetzt unter /workspace/api/notizblock. Und
pruef-notizen geht seitdem den Routenbaum von express durch und meldet
jedes Paar aus Methode und Pfad, das zweimal vergeben ist. Gemessen:
307 Wege, keine Dublette. Die Pruefung gilt fuers ganze Haus, auch
wenn sie in dieser Datei steht -- hier ist sie gefunden worden.

Nebenbei berichtigt: pruef-crew-adresse verlangte von jedem Eintrag
der Haustafel HTTP 200 mit Inhalt. Das stimmte, solange dort nur
oeffentliche Dateien standen. notizen.html ist die erste Seite hinter
der Anmeldung -- sie antwortet mit 302, und das ist richtig. Gefragt
wird jetzt, was die Tafel wirklich meint: GIBT es das hier.

GEMESSEN, alles nach den Aenderungen:

  pruef-notizen             76 Punkte, 0 Fehler (neu)
  pruef-kachelfarben        31 Punkte, 0 Fehler (vorher 26)
  pruef-kachel-universum    13 Punkte, 0 Fehler
  pruef-crew-adresse       157 Punkte, 0 Fehler
  pruef-buehne             230 Punkte, 0 Fehler -- notizen.html neu in
                           der Liste, schlechtester Kontrast 6,73:1,
                           also 50 % ueber der Grenze
  pruef-rechtetafel, -haus-seiten, -struktur, -css-klassen,
  -jeder-hat-eine-seite, -workspace-seiten, -kachelraster,
  -rueckmeldung            alle 0 Fehler

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-26 12:42:59 +02:00

817 lines
39 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =====================================================================
DIE RECHTETAFEL — was jede Rolle sehen darf, an einer Stelle
=====================================================================
Filipe, 11.09.2026:
"egal welche rolle oder person hinzugefuegt wird soll immer nur
das sehen wass ich erlaube. mehr nicht. soll nichts so sein dass
wenn mann eine rolle oder jemanden hinzufuegt dass er dan alles
sieht, soll jede rolle immer nur das sehen was sie sehen sollen."
---------------------------------------------------------------------
DAS IST EINE UMKEHRUNG DER BAUWEISE, KEINE EINSTELLUNG
Vorher galt: erlauben, ausser es steht etwas dagegen. Die Tabelle
der geschuetzten Seiten hiess sogar so -- "GESCHUETZT ist die
Ausnahme, nicht die Regel" -- und sie kannte zwei Wege, auf denen
etwas durchfiel:
1. `null` als Wert hiess "jede angemeldete Rolle". NEUN von zwanzig
Seiten standen darauf: Start, Uebersicht, Aufgaben, Chat,
Kalender, Dateien, Calls, Start-Check, Wissen. Eine neue Rolle
erbte sie alle, ohne dass jemand etwas erlaubt haette.
2. Eine Seite, die GAR NICHT in der Tabelle stand, fiel durch
dieselbe Bedingung (`if (erlaubt && ...)`) und war damit
ebenfalls fuer jeden offen. Das betraf am 11.09.2026 keine
einzige Seite -- gemessen, nicht gehofft --, aber jede neue
waere so entstanden.
Ab hier gilt das Gegenteil: WAS NICHT IN DIESER TAFEL STEHT, IST
VERBOTEN. Kein `null`, kein `undefined`, kein Zweig, der etwas
durchlaesst. `darfSeite()` kennt genau zwei Antworten.
---------------------------------------------------------------------
WARUM DAS NICHT "DIE LISTE EINMAL DURCHGEHEN" IST
Das waere die naheliegende Antwort und die falsche. Die Rolle wird
im Haus an 74 Stellen in 20 Modulen abgefragt (gezaehlt, nicht
geschaetzt). Drei Gruende, und alle drei sind hier schon eingetreten:
* Eine Liste ALTERT. Am 09.09. war `content.html` fuer eine neue
Rolle offen -- nicht aus Nachlaessigkeit, sondern weil die Seite
spaeter dazukam als die Ueberlegung.
* Eine vergessene Erlaubnis SCHWEIGT. Sie faellt nur dem auf, der
hindurchgeht, und der merkt nichts: Die Seite laedt ja. In der
anderen Richtung ist es schlimmer -- eine Seite, die zu viel
zeigt, sieht aus wie eine Seite, die funktioniert.
* Bei 74 Stellen ist "ich gehe sie alle durch" kein Verfahren,
sondern ein Vorsatz.
Deshalb steht die Antwort nicht in einem Kommentar, sondern in
`pruef-rechtetafel.mjs`: Eine Rolle ohne Eintrag macht den Prueflauf
rot. Eine Seite ohne Eintrag macht den Prueflauf rot. Man KANN es
nicht mehr vergessen.
---------------------------------------------------------------------
DIESE DATEI HAENGT VON NICHTS AB
Kein Import aus workspace.js -- sonst gaebe es einen Ring, und die
Tafel muesste warten, bis das grosse Modul geladen ist. Sie ist
damit auch ohne laufenden Server pruefbar, genau wie crew-adresse.js.
===================================================================== */
/** Alle Rollen, die es gibt. Muss zu ROLLEN in workspace.js passen --
* pruef-rechtetafel vergleicht die beiden und wird rot, wenn eine
* Rolle nur in einer der beiden Listen steht. */
export const ALLE_ROLLEN = [
"spicy", "admin", "manager", "scout", "creator", "hand", "modi",
/* DIE LINKE HAND (21.09.2026). Dieselbe Liste wie ROLLEN in
workspace.js -- pruef-rechtetafel haelt beide gegeneinander und
wird rot, sobald eine Rolle nur in einer steht. Genau das ist
beim Bauen passiert und hat den Eintrag hier gefunden. */
"linke",
/* DIE COMMUNITY (11.09.2026). Zuschauer mit einem Zugang, die den
Treff sehen -- und sonst nichts. Sie steht hier NICHT in `ALLE`
(siehe unten): Wer neu dazukommt, bekommt genau die zwei Seiten,
die weiter unten ausdruecklich seinen Namen tragen. */
"gast",
];
/* Kurzform fuer die Tafel unten. `ALLE` heisst hier ausdruecklich
"alle Rollen, die es GIBT" -- nicht "alle, die es geben wird".
Genau darin liegt der Unterschied zum alten `null`. */
/* ACHTUNG: `ALLE` ist NICHT ALLE_ROLLEN.
Es ist die Abkuerzung fuer "alle Rollen, die diese Seiten am
11.09.2026 schon hatten" -- die sieben des Hauses. Die Community
gehoert ausdruecklich nicht dazu; sie bekommt ihre Seiten einzeln.
Wuerde hier ALLE_ROLLEN stehen, haette jede kuenftige Rolle wieder
automatisch neun Seiten geerbt -- genau der Fehler, gegen den diese
Datei gebaut ist, nur mit einem anderen Namen. */
const ALLE = ["spicy", "admin", "manager", "scout", "creator", "hand", "modi"];
/* Schutz der angemeldeten Seiten. Serverseitig, nicht nur im Browser --
sonst könnte man die Seite einfach direkt aufrufen. */
/* Pfad -> erlaubte Rollen. `null` heisst: jede angemeldete Rolle.
Die Rolle wird hier mitgeprueft und nicht nur in der Schnittstelle.
Sonst bekommt z. B. ein Creator die Verwaltungsseite zwar ausgeliefert
(HTTP 200) und sieht ihr Geruest, auch wenn sie danach leer bleibt und
das Skript ihn wegschickt. Sichtbar sein soll sie gar nicht. */
export const SEITEN = {
/* AUS `null` WURDE EINE LISTE (11.09.2026).
Filipe: "egal welche rolle oder person hinzugefuegt wird soll
immer nur das sehen wass ich erlaube."
Diese neun Seiten standen auf `null` -- das hiess "jede
angemeldete Rolle", also auch jede Rolle, die es noch gar nicht
gibt. Jetzt stehen die sieben, die es wirklich gibt.
FUER DIE VORHANDENEN ROLLEN AENDERT SICH DAMIT NICHTS. Das ist
Absicht: Eine Umkehrung, die nebenbei Rechte entzieht, waere
zwei Aenderungen in einer -- und man wuesste hinterher nicht,
welche der beiden etwas kaputtgemacht hat. Wer hier enger
stellen will, macht das als eigenen Schritt. */
/* Die Community sieht hier ihre sieben Kacheln und sonst nichts --
welche das sind, entscheidet bereicheFuer() in workspace.js. */
"/workspace/start.html": [...ALLE, "gast"],
"/workspace/uebersicht.html": [...ALLE],
/* AUSDRUECKLICH OHNE 'modi' (10.09.2026). Vorher stand hier `null`
("jede angemeldete Rolle") -- und mit der neuen Rolle hiess das
auch: sie. Die Seite laedt dann zwar, ihre Schnittstelle antwortet
ihm aber mit 404, weil `content` nicht zu seinen Bereichen gehoert.
Eine Seite, die man oeffnen kann und die dann leer bleibt, sieht
aus wie ein Fehler -- und ist einer.
Das ist die Kehrseite von "geschuetzt ist die Regel, nicht die
Ausnahme": Eine NEUE ROLLE erbt jedes `null` automatisch. Wer eine
Rolle hinzufuegt, muss diese Liste durchgehen -- der Kachel-Durchgang
in pruef-rollen findet die Faelle, die dabei auffallen. */
"/workspace/content.html": ["spicy", "admin", "manager", "scout", "creator"],
"/workspace/aufgaben.html": [...ALLE],
/* Chat (06.09.2026): jede Rolle. WEN man erreicht, entscheidet
schreibbareIds() -- nicht der Zugang zur Seite. Ein Creator, der
die Seite nicht öffnen dürfte, könnte auch nicht antworten, und
genau das Antworten ist der Zweck.
SEIT DEM 19.09.2026 AUCH DIE COMMUNITY. Sie bekommt genau EINEN
Raum -- den Treff -- und sonst keinen: `schreibbareIds` liefert
ihr weiterhin eine leere Liste (ohneAussen), ein Zweier-Gespraech
entsteht also nicht. Was sie sieht, ist der Kanal, in dem sie
Mitglied ist.
Und sie telefoniert nicht: Der Riegel dafuer sitzt am
Anruf-Router, nicht hier (Filipe: "die sollen nicht anrufen
koennen. nur schreiben"). */
"/workspace/chat.html": [...ALLE, "gast"],
/* NUR DogFather (07.09.2026). Wunsch Filipe: "nimm die kategorie
zahlen bei jedem weg, nur die rolle dogfather soll die sehen."
Bis heute stand hier `null` -- jede Rolle durfte die Seite oeffnen,
und WESSEN Zahlen jemand sah, entschied sichtbareCreatorIds().
Die Begruendung dafuer war gut (es sind SEINE Zahlen), sie ist
jetzt aber nicht mehr meine Entscheidung.
WICHTIG: Die Kachel wegzunehmen haette NICHT gereicht. Eine Kachel
ist ein Weg, keine Schranke -- wer die Adresse kennt oder ein altes
Lesezeichen hat, waere weiterhin hineingekommen. Beides gehoert
zusammen: bereiche.js zeigt sie nur noch DogFather, diese Zeile
laesst nur ihn hinein. */
/* JETZT WIEDER FUER ALLE (11.09.2026). Filipe, mit der Kachel im
Bild: "jetzt soll jede rolle diese kachel sehen. spicy, dogfather,
manager, scout und creator. aber die creator kriegen dan quasi nur
ihre daten zu sehen und der manager scout dogfather oder spicy
koennen eintragen."
Die Zeile stand seit dem 07.09.2026 auf ["admin"] -- erst "nimm
die kategorie zahlen bei jedem weg", dann die Praezisierung, dass
auch Spicy Media sie nicht sieht. Beides ist damit aufgehoben.
AUSDRUECKLICH ALS LISTE, NICHT ALS `null`. `null` hiesse "jede
angemeldete Rolle" -- und damit automatisch auch jede Rolle, die
es noch gar nicht gibt. Genau das ist am 10.09. bei content.html
schiefgegangen. Hier stehen die fuenf, die Filipe genannt hat.
WER WAS DARF, steht NICHT hier. Diese Zeile oeffnet nur die Tuer.
Dahinter gilt weiterhin: sichtbareCreatorIds() entscheidet, WESSEN
Zahlen jemand sieht (ein Creator sieht genau sich selbst), und
darfEintragen() entscheidet, wer schreiben darf (Leitung + Scout,
also NICHT der Creator). Beides war schon vorher so gebaut und
bleibt unangetastet -- es war nur niemand da, der es benutzen
konnte. */
"/workspace/leistung.html": ["spicy", "admin", "manager", "scout", "creator"],
/* NUR DogFather (02.09.2026). Die Rollenauswahl verspricht das seit
jeher ("Manager -- dieselben Rechte, ausser der
Personenverwaltung"), die Schranke hielt sich nur nicht daran.
Wunsch: "die manager sollen diese kategorien garnicht sehen". */
/* PERSONEN & ZUGAENGE: jetzt auch fuer Manager und Spicy Media
(07.09.2026).
Filipe: "die manager sehen immer noch nicht die kachel personen &
zugangscode, wo sie dan die neuen creator hinzufuegen koennen ...
und die sollen in der kategorie wo die jetzt sehen werden auch noch
das mit den hinzufuegen sehen, alles andere auf der seite sollen die
weiterhin nicht sehen."
DIE SEITE OEFFNET SICH, DIE SCHNITTSTELLEN NICHT. Das ist der ganze
Trick und der Grund, warum das hier ungefaehrlich ist: Alles unter
/workspace/api/verwaltung haengt weiterhin an `nurAdmin` -- Rollen
aendern, Codes neu setzen, sperren, loeschen, Protokoll lesen
bleiben zu und antworten mit 404. Wer die Seite oeffnet, bekommt
also genau die Teile zu sehen, fuer die es auch einen Weg gibt.
Eine Seite ist kein Schutz, sie ist ein Weg. Der Schutz steht in den
Schnittstellen, und der ist unveraendert. */
/* 'hand' kam am 10.09.2026 dazu. Filipe: "ich will dass die rechte
Hand auch alle sieht."
ES WAR DIE DRITTE SCHICHT, DIE ZUSTIMMEN MUSSTE. Die Kachel stand
da, die Schnittstelle liess sie lesen -- und die Seite selbst warf
sie auf die Startseite zurueck. Gefunden hat das nicht das Auge,
sondern pruef-rollen: Sie geht jede Kachel jeder Rolle ab und
schaut, wo man landet ("Rechte Hand Kachel personen.html LANDET
AUF start.html").
Dass die Seite aufgeht, gibt ihr nichts, was die Schnittstellen ihr
nicht ohnehin geben: Alles unter /workspace/api/verwaltung ausser
der Liste antwortet ihr weiterhin mit 404. Eine Seite ist kein
Schutz, sie ist ein Weg. */
"/workspace/personen.html": ["spicy", "admin", "manager", "hand"],
"/workspace/profil.html": ["spicy", "admin", "manager", "scout", "creator"],
/* Der eigene Steckbrief -- jede Rolle hat einen. Ein Creator wird
dort nicht hingeschickt (er sieht seinen auf profil.html), darf die
Seite aber aufrufen: Sie zeigt ihm dasselbe, nur ohne die Akte. */
/* 'modi' kam am 10.09.2026 dazu. Der Steckbrief ist die EIGENE Seite
("Dein Bild und deine Kanäle"); profil.html daneben ist der
Entwicklungsplan eines Creators und fuer ihn sinnlos. Der erste
Anlauf hatte genau das verwechselt -- die Kachel hiess "Mein
Profil" und fuehrte auf eine Seite, deren Schnittstelle mit 404
antwortet, weil sie ausdruecklich nur Creator kennt. Gefunden vom
neuen Kachel-Durchgang in pruef-rollen. */
/* JEDER MIT EINEM ZUGANG HAT EINEN STECKBRIEF (19.09.2026).
Filipe: "jeder soll ein steckbrief haben, jeder der ein account
hat, rechte hand, modis und community, jeder soll genau wie ich
foto und so hinzufuegen koennen."
Vorher fehlte hier "gast" -- und damit ausgerechnet die groesste
Gruppe. Die Seite selbst konnte es die ganze Zeit: `/mein` fragt
nach `req.person.id` und kennt gar keine Rolle. Es fehlte nur die
Tuer.
WAS EIN MITGLIED DORT SIEHT, ENTSCHEIDET NICHT DIESE ZEILE,
sondern `sichtbareIds` in workspace-steckbrief.js: Der eigene
Eintrag, DogFather, und was die Sichtbarkeitsregeln sonst
hergeben. Eine Seite ist ein Weg, keine Schranke. */
"/workspace/steckbrief.html": ["spicy", "admin", "manager", "scout", "creator", "hand", "modi", "gast"],
"/workspace/kalender.html": [...ALLE],
"/workspace/dateien.html": [...ALLE],
/* 'modi' kam am 10.09.2026 dazu -- und es war ein echter Fund: Ohne
diese Zeile leiteten VIER seiner Kacheln (Live-Ablauf, Community,
Technik, Ideen-Board) wortlos auf die Startseite zurueck. Der
Rollen-Rundgang meldete sie trotzdem als "ok": Eine Umleitung ist
kein Fehler, die Seite laedt ja -- nur eben eine andere. Seither
prueft pruef-rollen fuer JEDE Rolle, dass jede Kachel, die sie
bekommt, auch wirklich dort ankommt.
WELCHE Bereiche ein Modi oeffnen darf, entscheidet die Liste
seiner Kacheln (MODI_BEREICHE_ERLAUBT) -- sonst kaeme er ueber die
Adresszeile auch in die Agentur-Ablage. */
/* Die sieben Bretter des Treffs laufen ueber dieselbe Seite wie alle
anderen Bereiche. WELCHE ein Gast oeffnen darf, entscheidet die
Bereichsliste in workspace-bereiche.js -- diese Zeile oeffnet nur
die Tuer. Eine Seite ist ein Weg, keine Schranke. */
"/workspace/bereich.html": ["spicy", "admin", "manager", "scout", "creator", "hand", "modi", "gast"],
"/workspace/report.html": ["spicy", "admin", "manager", "scout", "creator"],
"/workspace/scouting.html": ["spicy", "admin", "manager", "scout"],
/* DIE ARBEITSLAGE DES TEAMS (09.09.2026).
Filipe: "so eine kategorie wie ueber die creator will ich dass nur
fuer die spicy und dogfather rolle auch ueber manager und scouts
gibt."
Nur diese beiden Rollen. Ein Manager sieht seine eigene Zeile hier
ausdruecklich NICHT -- das ist kein Versehen: Eine Uebersicht ueber
die Arbeit der Kollegen gehoert der Leitung, nicht der Reihe. Der
Schutz steht ohnehin in der Schnittstelle (workspace-team.js);
diese Zeile sorgt nur dafuer, dass niemand vor einer leeren Seite
steht. */
"/workspace/team.html": ["spicy", "admin"],
"/workspace/calls.html": [...ALLE],
"/workspace/startcheck.html": [...ALLE],
"/workspace/wissen.html": [...ALLE],
/* Ebenfalls nur DogFather: Was von allein passiert, bestimmt, was
allen anderen zugeschoben wird. */
/* AUTOMATIONEN BLEIBEN BEI DogFather (07.09.2026). Ausdruecklich:
"bei automationen sollen die nicht sehen." Dahinter liegen
Sicherungen, die KI und der Zustand des Servers -- das ist Betrieb,
nicht Betreuung. */
"/workspace/automation.html": ["admin"],
/* Die gemeinsame Lage des Teams -- nur die DogFather-Rolle, also
Filipe und VanVan (10.09.2026). Fuer alle anderen leitet die
Schranke auf die Startseite; die Schnittstelle dahinter antwortet
zusaetzlich mit 404. Zwei Schloesser, absichtlich: Faellt eines
weg, haelt das andere. */
/* Der Eingang: DogFather und seine rechte Hand. Blueprint 3.1 --
"Kann im Alltag vertretend Entscheidungen treffen." Genau das
passiert auf dieser Seite. */
"/workspace/teamlage.html": ["admin", "hand"],
/* DIE REGELN DES TREFFS (11.09.2026). Fuer die Community und fuer
die, die sie durchsetzen — ein Modi, der die Regeln nicht sehen
kann, moderiert nach Gefuehl.
AUSDRUECKLICH NICHT fuer Manager, Scouts, Creator und Spicy: Sie
haben mit dem Treff nichts zu tun, und die Seite nennt Fristen,
Stufen und die Bedingungen des Ausschlusses. */
/* WILLKOMMEN (21.09.2026). Die Orientierungsseite. Sie erklaert nur,
was die Person ohnehin hat -- deshalb ist sie fuer JEDE gueltige
Rolle offen, genau wie die Startseite. Wer sie sperrt, sperrt die
Erklaerung, nicht den Zugang. */
"/workspace/willkommen.html": [...ALLE, "gast"],
/* MATERIAL (22.09.2026). Dieselben Rollen wie der Treff: Das Team
stellt ein, die Community holt sich etwas. WER was darf, steht
nicht hier, sondern in workspace-material.js -- diese Tafel
entscheidet nur, wer die Seite oeffnen darf. */
"/workspace/material.html": ["gast", "modi", "hand", "linke", "admin"],
"/workspace/treff-regeln.html": ["gast", "modi", "hand", "admin"],
/* DIE ANRUF-PROBE (19.09.2026). Sie sagt einem Menschen in seinem
eigenen Browser, warum es nicht klingelt -- die Frage, die sich
von aussen nicht beantworten laesst. Sie ruft niemanden an und
zeigt nichts ueber andere.
ERWEITERT AM 19.09.2026, nachmittags, nach einer Messung: Von
elf Zugaengen hatten SIEBEN kein einziges Geraet angemeldet -- bei
ihnen klingelt nichts und es kommt keine Erinnerung an, solange
die Seite zu ist. Fuenf der sieben sind Creator, Scout oder Spicy
Media und durften diese Seite gar nicht oeffnen.
Das war die falsche Einschraenkung: Die Seite prueft nicht nur
Anrufe, sondern die ganze Kette bis zur angemeldeten Geraete-Kennung
-- und die traegt auch jede Aufgaben- und Terminerinnerung. Wer
Erinnerungen bekommen kann, muss nachsehen koennen, warum sie
ausbleiben.
OHNE "gast". Die Community bekommt keine Erinnerungen, und eine
Seite mit sieben Prueftpunkten ueber etwas, das es fuer sie nicht
gibt, waere nur Verwirrung. */
"/workspace/anruf-probe.html": [...ALLE],
/* DIE ANFRAGE FUER MODI (17.09.2026).
NUR "gast" -- und das ist keine Schlamperei, sondern die Aussage.
Wer schon im Team ist, hat hier nichts zu beantworten; und ein
Formular, das auch Modis sehen, waere die Einladung, es als
Beurteilungsbogen zu benutzen.
Der Server sagt dasselbe noch einmal (SCHICKEN_ROLLEN). Zwei
Schloesser an derselben Tuer, weil die Tafel umstellbar ist:
Wer sie versehentlich oeffnet, bekommt trotzdem keine Anfrage
von einem Modi in den Eingang. */
"/workspace/bewerben.html": ["gast"],
/* DIE ENTWICKLUNGSKARTE (11.09.2026).
DogFather und die rechte Hand fuehren sie ueber das Team. Ein Modi
kommt ebenfalls auf die Seite -- aber er sieht dort AUSSCHLIESSLICH
Block 5, die sechs Fragen ueber sich selbst. Das entscheidet nicht
diese Tafel, sondern workspace-entwicklung.js: Hier steht nur, wer
die Tuer oeffnen darf, nicht was dahinter liegt.
ZWEI SCHLOESSER, ABSICHTLICH. Faellt die Rechtepruefung im Modul
weg, steht immer noch diese Zeile; faellt diese Zeile weg, steht
immer noch das Modul. */
/* WIE GEHT'S DIR? -- dieselben Rollen wie die Entwicklungsseite, und
das ist kein Zufall: Es sind die Menschen, ueber die dort etwas
steht. Wer beobachtet wird, darf auch ueber sich selbst sprechen.
Die Agentur hat diese Seite nicht -- sie hat auch keine
Entwicklungskarte. */
/* DAS ZIEL DES TEILEN-MENÜS (17.09.2026).
DIESELBEN ROLLEN WIE DIE VIDEO-ROUTE, nicht mehr: Wer ein Video
nicht eintragen darf, soll auch nicht auf einer Seite landen, die
ihm einen Knopf dafür zeigt. Ein Knopf, der mit 403 antwortet, ist
schlimmer als kein Knopf.
Die Seite selbst legt nichts an -- das tut die Route, und die
prüft ihrerseits. Das hier ist der Riegel davor, nicht statt. */
"/workspace/teilen.html": ["admin", "hand", "modi"],
"/workspace/befinden.html": ["admin", "hand", "modi"],
"/workspace/entwicklung.html": ["admin", "hand", "modi"],
/* DIE AUSWERTUNG (19.09.2026) -- Kachel "Entwicklung".
MIT "modi", obwohl die Liste aller Karten nur die Leitung sieht.
Die Seite hat zwei Gesichter: Wer das Team fuehrt, sieht die
Karten; wer dazugehoert, sieht ausschliesslich die Bewegung in der
EIGENEN -- ohne Namen, ohne Anlass, ohne wer-hat-was.
Dass die Person ihren eigenen Fortschritt sieht, ist kein
Zugestaendnis, sondern der Punkt: Sichtbarer Fortschritt ist nach
der Forschung der staerkste einzelne Treiber guter Arbeit. Die
Schranke dahinter sitzt in der Route (`/werdegang/liste` und
`/werdegang/person/:id` antworten einem Modi mit 404), nicht hier.
Das hier ist der Riegel davor, nicht statt. */
"/workspace/werdegang.html": ["admin", "hand", "modi"],
/* TALENTE. Hier stehen Namen von Menschen, die nichts davon wissen --
ausdruecklich OHNE "modi": Wer beobachtet wird, ohne es zu wissen,
hat ein Recht darauf, dass der Kreis klein bleibt. */
"/workspace/talente.html": ["admin", "hand"],
/* DER EINGANG FUER DIE ANFRAGEN -- dieselben zwei wie bei den
Talenten, und aus demselben Grund: Hier stehen Saetze, die
jemand ueber sich selbst geschrieben hat, im Vertrauen darauf,
dass zwei Menschen sie lesen.
Filipes Entscheidung vom 17.09.2026 auf die Rueckfrage, wer
entscheiden darf: "Du und die rechte Hand." */
"/workspace/bewerbungen.html": ["admin", "hand"],
/* MELDUNGEN & MASSNAHMEN. Nur wer moderiert -- ausdruecklich OHNE
"gast": Dort stehen die Namen derer, die gemeldet haben. Eine
Meldung, die der Gemeldete lesen kann, ist keine Meldung mehr,
sondern eine Einladung zur Vergeltung. */
"/workspace/treff-moderation.html": ["modi", "hand", "admin"],
/* UNSERE SEITEN (19.09.2026). Die Website und VanVans Shop, mit dem
Rabattcode fuer die Community.
GENAU DIE VIER ROLLEN DES TREFFS -- also MIT "gast", anders als die
Moderationsseite eine Zeile darueber. Sie ist ausdruecklich FUER die
Community gebaut; ohne "gast" waere die Kachel bei jedem Mitglied
sichtbar und beim Antippen zu. Genau diese Sorte toter Weg hat die
Sichtpruefung am 18.09. zehnmal gefunden.
Die Agentur steht bewusst nicht dabei (siehe TREFF_ROLLEN in
workspace.js): Creator, Scouts und Spicy Media haben mit dem Treff
nichts zu tun. */
"/workspace/unsere-seiten.html": ["gast", "modi", "hand", "admin"],
/* DER VERTRAULICHE MELDEWEG (19.09.2026).
Filipe: "es soll auch eine kategorie also eine hauptkachel geben wo
die community leute sich anonym melden koennen wenn sie probleme
haben ... rechte hand und dogfather sollen zugriff auf diese
anonyme nachrichten haben."
DREI ROLLEN, UND MODIS AUSDRUECKLICH NICHT. Sehr oft geht es in
diesen Meldungen um eine Moderationsentscheidung -- wer beteiligt
ist, darf die Beschwerde ueber sich nicht lesen. Das ist keine
Geringschaetzung der Modis, sondern dieselbe Regel, die auch
anderswo gilt: Niemand prueft sich selbst.
WAS AUF DER SEITE ZU SEHEN IST, entscheidet nicht diese Zeile,
sondern `fallFuer` in workspace-hilfe.js: Ein Mitglied sieht seine
eigenen Faelle, die Leitung alle. */
"/workspace/hilfe.html": ["gast", "hand", "admin"],
/* DER SUPPORT: WIRKLICH JEDER (24.09.2026).
Filipe: „da fehlt auch eine support kachel. die jeder sieht.
jeder benutzen kann."
Das ist der Unterschied zum vertraulichen Meldeweg eine Zeile
darueber: Der gilt nur fuer gast, hand und admin -- Modis,
Creator, Scouts, Manager und die linke Hand hatten gar keinen
Weg, einen kaputten Knopf zu melden.
ALLE ROLLEN AUSGESCHRIEBEN und nicht „* " oder ein Weglassen:
Diese Tafel ist die Stelle, an der man nachsieht, wer wohin darf.
Eine Abkuerzung hier hiesse, dass der Support in der Uebersicht
„Wer sieht was" als Luecke erscheint. */
"/workspace/support.html": ["gast", "modi", "creator", "scout", "manager",
"linke", "hand", "spicy", "admin"],
/* UNTERSTÜTZEN (24.09.2026).
Filipe: „jeder soll diese kachel sehen aber nur dogfather soll sie
verändern können oder vieles mehr sehen. alle anderen, rechte und
linke hand, modis und community sollen supporten können."
Die Seite steht damit JEDER Rolle offen — was jemand darauf tun
darf, entscheidet workspace-unterstuetzung.js und nicht diese
Tafel. Sie beantwortet nur, wer die Tür aufbekommt.
ALLE ROLLEN AUSGESCHRIEBEN, dieselbe Überlegung wie beim Support
eine Zeile darüber: Diese Tafel ist die Stelle, an der man
nachsieht, wer wohin darf. Eine Abkürzung hier hieße, dass die
Seite in „Wer sieht was" als Lücke erscheint. */
"/workspace/unterstuetzen.html": ["gast", "modi", "creator", "scout", "manager",
"linke", "hand", "spicy", "admin"],
/* WER SIEHT WAS. DogFather und die rechte Hand (Filipe, 11.09.2026:
"ich will die kategorie auch auf der team dogi seite fuer die
dogfather und rechte hand rollen").
ANSEHEN BEIDE, UMSTELLEN NUR ER. Sie fuehren das Team zusammen;
wer mitentscheidet, muss nachsehen koennen. Wer aber Rechte
vergeben kann, kann sich Rechte vergeben -- und diese eine Tuer
bleibt bei ihm. Durchgesetzt wird das in workspace-rechte.js, nicht
hier: Diese Tafel beantwortet, wer die SEITE oeffnen darf, nicht
was er darauf tun kann.
Weiterhin ausdruecklich NICHT fuer die Agentur und nicht fuer die
Modis: Die Seite ist eine vollstaendige Karte dieses Hauses -- jede
Seite, jede Rolle. Wer sie hat, weiss, wo er es versuchen muesste. */
/* DER NOTIZBLOCK (26.09.2026).
Filipe: „fuer die modis, rechte und linke hand und dogfather
eine kachel hinzufuegst, sie soll: Notizen, heissen."
GENAU DIESE VIER, und die linke Hand steht ausdruecklich dabei
statt sich aus der Schleife weiter unten zu ergeben: Sie ist
hier namentlich genannt, und eine namentliche Nennung gehoert
in die Tafel, die man liest, wenn man wissen will, wer wohin
darf.
WAS AUF DER SEITE ZU SEHEN IST, entscheidet nicht diese Zeile:
In workspace-notizen.js hat jede Abfrage `person_id = ?` fest
eingebaut. Es gibt keine Rolle, die mehr sieht -- auch
DogFather nicht. Diese Tafel beantwortet nur, wer die Tuer
aufbekommt.
DIE AGENTUR STEHT NICHT DABEI, und die Adresse auch nicht: Die
Seite gehoert zum Haus von Team Dogi und ist auf der
Agenturadresse ein 404 (GEHOERT_ZU_ADRESSE in crew-adresse.js). */
"/workspace/notizen.html": ["admin", "hand", "linke", "modi"],
"/workspace/rechte.html": ["admin", "hand"],
};
/* =====================================================================
DIE LINKE HAND BEKOMMT DIE SEITEN DER RECHTEN -- ABGELEITET
===================================================================== (21.09.2026)
Filipe: *"Die linke Hand erhaelt grundsaetzlich dieselben Rechte wie
die rechte Hand in allen Bereichen, die die Modis, Aufgaben,
Aufgabenzuweisungen, Uebersichten, Kommunikation und Auswertungen
betreffen."*
WARUM ABGELEITET UND NICHT SIEBZEHNMAL "linke" HINGESCHRIEBEN: Die
naechste Seite, die jemand der rechten Hand gibt, bekaeme die linke
sonst nicht -- und das faellt niemandem auf, weil eine fehlende
Seite aussieht wie eine Seite, die es nicht gibt. Diese Schleife
kann das nicht vergessen.
DIE AUSNAHMEN STEHEN EINZELN DA, MIT GRUND. Eine Ausnahme ohne
Begruendung ist beim naechsten Umbau eine, die jemand fuer ein
Versehen haelt und wegnimmt. */
const LINKE_NICHT = {
/* Personen anlegen -- ausdruecklich im Auftrag ausgeschlossen. Die
LISTE der Leute braucht sie trotzdem, um Aufgaben zu verteilen;
die kommt aus /api/personen/liste und ist dort eigens geoeffnet. */
"/workspace/personen.html": "legt Personen an",
/* Der Weg, auf dem neue Leute ueberhaupt hereinkommen. */
"/workspace/bewerbungen.html": "fuehrt zu neuen Personen",
"/workspace/talente.html": "fuehrt zu neuen Personen",
/* Der vertrauliche Meldeweg. Das Empfindlichste im Haus -- Meldungen
ueber Menschen. Im Zweifel das kleinere Recht: Ein Wort von Filipe
oeffnet es, eine versehentlich gelesene Meldung laesst sich nicht
zurueckholen. Ein Modi darf ihn uebrigens auch nicht. */
"/workspace/hilfe.html": "vertraulicher Meldeweg",
/* Wer die Rechtetafel bedient, verwaltet Konten -- nicht Aufgaben. */
"/workspace/rechte.html": "Rechteverwaltung",
};
for (const [seite, rollen] of Object.entries(SEITEN)) {
if (!Array.isArray(rollen) || !rollen.includes("hand")) continue;
if (LINKE_NICHT[seite]) continue;
if (rollen.includes("linke")) continue;
/* EINE KOPIE, KEINE AENDERUNG AM ORIGINAL. Mehrere Seiten teilen
sich dieselbe Liste (`ALLE`). Wer die an Ort und Stelle aendert,
gibt die Rolle allen Seiten, die dieselbe Liste benutzen -- auch
denen, die gerade eine Ausnahme sind. Das faellt nicht auf,
solange keine Ausnahme `ALLE` benutzt, und knallt an dem Tag, an
dem eine es tut.
Direkt hinter die rechte Hand -- die Reihenfolge in diesen Listen
ist an einigen Stellen die Anzeigereihenfolge. */
const kopie = rollen.slice();
kopie.splice(kopie.indexOf("hand") + 1, 0, "linke");
SEITEN[seite] = kopie;
}
/** Welche Seiten der rechten Hand die linke NICHT bekommt -- und warum.
* Exportiert, damit die Pruefung die Begruendung mitlesen kann statt
* eine eigene Liste zu fuehren. */
export const LINKE_AUSNAHMEN = LINKE_NICHT;
/* Die Zugangswaende. Sie haben keine Rolle, weil sich dort noch
niemand angemeldet hat -- sie sind der Ort, an dem das passiert.
Sie stehen hier nur, damit die Pruefung "jede Datei hat einen
Eintrag" sie nicht als Luecke meldet. */
export const OHNE_ANMELDUNG = [
"/workspace/index.html",
"/workspace/crew-index.html",
];
/* =====================================================================
DIE EINE FRAGE, DIE ALLES BEANTWORTET
===================================================================== */
/**
* Darf diese Person diese Seite oeffnen?
*
* Zwei Antworten, keine dritte:
* - Die Seite steht in der Tafel UND die Rolle steht darin -> ja.
* - Alles andere -> nein. Auch eine Seite, die es in der Tafel gar
* nicht gibt. Das ist die eigentliche Aenderung.
*
* @param {{rolle?: string}|null} person
* @param {string} pfad z. B. "/workspace/start.html"
*/
/* =====================================================================
DIE TAFEL IST SEIT DEM 11.09.2026 VERAENDERBAR — aber nur auf EINE Art
=====================================================================
Filipe: *"so dass ich die da auch manuel wechseln und speichern kann."*
WAS OBEN STEHT, IST AB JETZT DER GRUNDSTAND, nicht mehr die ganze
Wahrheit. Was DogFather auf der Seite "Wer sieht was" umstellt, liegt
in der Datenbank und wird hier darübergelegt.
DREI ENTSCHEIDUNGEN, DIE MAN DEM CODE NICHT ANSIEHT:
1. GESPEICHERT WIRD NUR DER UNTERSCHIED. In der Datenbank steht eine
Zeile nur dann, wenn sie vom Grundstand ABWEICHT. Damit bedeutet
der Code oben weiterhin etwas -- man sieht, was gedacht war, und
daneben, was jemand daraus gemacht hat. Eine vollstaendige Kopie
in der Datenbank waere nach einer Woche die einzige Wahrheit, und
der Code oben wuerde still veralten.
2. DER GRUNDSTAND IST DER RUECKFALL, wenn die Abweichungen nicht
gelesen werden koennen. Das ist die richtige Richtung: Er ist der
geprüfte Zustand, nicht der zufaellige. Ein Fehlschlag wird laut
protokolliert -- eine Tafel, die still auf etwas anderes
zurueckfaellt, waere die schlimmste Sorte Fehler in dieser Datei.
3. GEAENDERT WIRD NICHT HIER. Diese Datei kennt keine Datenbank; sie
nimmt eine fertige Tafel entgegen. Wer sie setzt und wer das darf,
steht in workspace-rechte.js. Zwei Dateien, zwei Fragen -- und die
Rechtepruefung bleibt ohne laufenden Server pruefbar.
===================================================================== */
/** Die Tafel, die gerade gilt. Am Anfang der Grundstand. */
let TAFEL = SEITEN;
/** Setzt die geltende Tafel. "null" stellt den Grundstand wieder her. */
export function tafelSetzen(neu) {
TAFEL = neu && typeof neu === "object" ? neu : SEITEN;
}
/** Die Tafel, die gerade gilt -- fuer die Anzeige und fuer Pruefungen. */
export function tafelJetzt() {
return TAFEL;
}
/** Weicht diese Seite/Rolle vom Grundstand ab? Fuer die Anzeige: Man
* soll sehen, was jemand von Hand geaendert hat. */
export function weichtAb(pfad, rolle) {
const grund = Array.isArray(SEITEN[pfad]) && SEITEN[pfad].includes(rolle);
const jetzt = Array.isArray(TAFEL[pfad]) && TAFEL[pfad].includes(rolle);
return grund !== jetzt;
}
export function darfSeite(person, pfad) {
const rolle = person?.rolle;
if (!rolle) return false;
const erlaubt = TAFEL[String(pfad || "")];
/* Kein Eintrag heisst NEIN. Vorher hiess es ja -- und genau darin
lag das Loch, das man einer neuen Seite nicht ansieht. */
if (!Array.isArray(erlaubt)) return false;
return erlaubt.includes(rolle);
}
/**
* Die Tafel aus der anderen Richtung: Rolle -> Seiten.
*
* Fuer die Uebersichtsseite ("Wer sieht was") und fuer Pruefungen.
* ABGELEITET, nicht zweitgeschrieben -- eine zweite Liste waere die
* Stelle, an der die beiden auseinanderlaufen.
*/
export function seitenFuer(rolle) {
return Object.entries(TAFEL)
.filter(([, r]) => r.includes(rolle))
.map(([p]) => p)
.sort();
}
/** Alle Seiten der Tafel -- fuer die Pruefung gegen die Platte. */
export function alleSeiten() {
/* AUS DEM GRUNDSTAND, nicht aus der geltenden Tafel.
Die Liste der SEITEN aendert sich durch eine Umstellung nicht --
nur, wer sie oeffnen darf. Wer hier TAFEL naehme, bekaeme bei einem
Fehler in der Zusammenfuehrung eine kuerzere Liste, und
pruef-rechtetafel ("jede Datei hat einen Eintrag") wuerde dann
stillschweigend weniger pruefen. */
return Object.keys(SEITEN).sort();
}
/** Nur DogFather darf die Tafel umstellen -- und selbst er nicht alles.
*
* DIESE DREI SEITEN KANN ER SICH NICHT SELBST WEGNEHMEN. Ohne
* "rechte.html" koennte er die Aenderung nicht zuruecknehmen, ohne
* "personen.html" keinen Zugang mehr vergeben, ohne "start.html"
* nirgends mehr hin. Eine Sperre, aus der man sich aussperren kann,
* ist keine Einstellung, sondern eine Falle -- und sie waere genau
* einen Fehlklick entfernt.
*
* Fuer JEDE andere Rolle darf er jede Seite umstellen. Das ist der
* Sinn der Sache: "soll jede rolle immer nur das sehen was sie sehen
* sollen" -- wer das entscheidet, ist er. */
export const UNANTASTBAR = [
{ pfad: "/workspace/rechte.html", rolle: "admin" },
{ pfad: "/workspace/personen.html", rolle: "admin" },
{ pfad: "/workspace/start.html", rolle: "admin" },
];
/* =====================================================================
WAS DIE RECHTE HAND NICHT ANFASSEN DARF (24.09.2026)
Filipe: „die rechte hand soll das auch sehen. und die selben rechte
da haben wie dogfather. das einzige was sie nicht kann ist die
dogfather rolle oder leute anfassen. also da kann sie nichts
verändern."
SEHEN KONNTE SIE DIE TAFEL SCHON -- `nurLeitung` liess sie herein,
und die Kachel steht in ihrer Liste. Was ihr fehlte, war das
Umstellen: Jeder Knopf antwortete mit „Umstellen kann das nur
DogFather."
ZWEI SPERREN, und beide stehen hier statt in der Route -- damit die
Oberflaeche dieselbe Auskunft bekommt und den Knopf gar nicht erst
als Knopf zeigt:
1. DIE SPALTE „DogFather". Sie entscheidet, was er selbst sieht.
Wer sie aendern kann, kann ihm Seiten wegnehmen.
2. DIE ZEILE „Personen & Zugaenge" -- „leute anfassen". Wer
personen.html fuer eine Rolle freischaltet, hat damit
Zugaenge vergeben, ohne einen einzigen anzulegen.
DRITTENS, UND DAS HAT ER NICHT GESAGT: Sie kann sich auch selbst
nicht aussperren. Dieselbe Ueberlegung wie bei UNANTASTBAR fuer
DogFather -- eine Sperre, aus der man sich aussperren kann, ist
keine Einstellung, sondern eine Falle. Haette ich es weggelassen,
waere der erste Fehlklick auf „rechte.html / Rechte Hand" das Ende
ihres Zugangs zu dieser Seite gewesen.
===================================================================== */
/** Die Spalte, die nur DogFather selbst aendern darf. */
const NUR_DOGFATHER_SPALTE = "admin";
/** Die Zeile, die nur DogFather aendern darf -- fuer JEDE Rolle. */
const NUR_DOGFATHER_ZEILE = "/workspace/personen.html";
/** Und was die rechte Hand sich selbst nicht wegnehmen kann. */
const HAND_BRAUCHT = ["/workspace/rechte.html", "/workspace/start.html"];
/** Darf DIESE PERSON dieses Feld anfassen? -- unabhaengig davon, ob
* sie es an- oder abwaehlen will.
*
* Getrennt von `darfUmstellen`, weil die Oberflaeche genau diese
* Frage stellt, bevor sie ein Feld zeichnet: Ein Knopf, der eine
* Absage bringt, ist schlimmer als keiner.
*
* @returns {string|null} Grund -- oder null, wenn es geht. */
export function darfFeldAnfassen(person, pfad, rolle) {
const wer = person?.rolle;
if (wer === "admin") return null;
if (wer !== "hand") return "Umstellen kann das nur DogFather und die rechte Hand.";
if (rolle === NUR_DOGFATHER_SPALTE) {
return "Was DogFather sieht, stellt nur er selbst um.";
}
if (pfad === NUR_DOGFATHER_ZEILE) {
return "Wer Zugänge vergeben darf, entscheidet nur DogFather.";
}
if (rolle === "hand" && HAND_BRAUCHT.includes(pfad)) {
return "Diese Seite kannst du dir nicht selbst wegnehmen – "
+ "sonst kämst du an die Rechte nicht mehr heran.";
}
return null;
}
/** Alle Felder, die diese Person nicht anfassen darf -- fuer die
* Oberflaeche, damit sie sie als fest zeichnet.
*
* ABGELEITET AUS `darfFeldAnfassen`, nicht danebengeschrieben: Zwei
* Listen fuer dieselbe Regel waeren die, die beim naechsten Umbau
* auseinanderlaufen -- und die in der Oberflaeche waere die, die
* jeder herunterlaedt. */
export function festeFelder(person, pfad) {
const aus = UNANTASTBAR.filter((u) => u.pfad === pfad).map((u) => u.rolle);
for (const r of ALLE_ROLLEN) {
if (aus.includes(r)) continue;
if (darfFeldAnfassen(person, pfad, r)) aus.push(r);
}
return aus;
}
/** @returns {string|null} Grund, warum das nicht geht -- oder null. */
export function darfUmstellen(pfad, rolle, erlaubt, person) {
if (!Object.prototype.hasOwnProperty.call(SEITEN, pfad)) return "Diese Seite gibt es nicht.";
if (!ALLE_ROLLEN.includes(rolle)) return "Diese Rolle gibt es nicht.";
/* ZUERST DIE PERSON, DANN DER INHALT. Wer ein Feld gar nicht
anfassen darf, bekommt denselben Grund -- egal ob er an- oder
abwaehlen wollte. Stuende diese Frage nach `if (erlaubt) return
null`, koennte die rechte Hand DogFather jede Seite ZUSCHALTEN
und nur nicht wegnehmen; das waere dieselbe Tuer, nur andersherum. */
const nein = person ? darfFeldAnfassen(person, pfad, rolle) : null;
if (nein) return nein;
if (erlaubt) return null;
const fest = UNANTASTBAR.find((u) => u.pfad === pfad && u.rolle === rolle);
if (fest) {
return "Diese Seite kannst du dir nicht selbst wegnehmen – sonst käme "
+ "niemand mehr an die Rechte, an die Zugänge oder auf die Startseite.";
}
return null;
}