Files
dogfather-universe/workspace/crew-index.html
T
DogFatherGitandClaude Opus 5 670ce4a5c1 Personen & Zugaenge auf der Team-Seite -- und eine Rolle laesst sich endlich aendern
Filipe wollte VanVan die Rolle "Rechte Hand" geben. Auf die Frage nach
ihrem Code: "die kategorie personen & zugaenge fehlt also muss das
hinzugefuegt werden und bitte nur basiert auf diese seite."

BEIM NACHSEHEN KAMEN ZWEI DINGE HERAUS, und das zweite war das
eigentliche:

  Die Kachel fehlte, weil ich sie mit den Agenturkacheln entfernt hatte
  -- ausgerechnet die, mit der man jemandem eine Rolle gibt. Die
  Team-Adresse war damit eine Seite, auf der man das Team nicht
  verwalten kann.

  UND ES GAB DIE FUNKTION GAR NICHT. Im ganzen Server aendert keine
  einzige Stelle `personen.rolle`. Anlegen ja, sperren ja, loeschen ja
  -- aendern nirgends, seit dem ersten Tag. Wer jemandem eine andere
  Aufgabe geben wollte, musste ihn loeschen und neu anlegen, und daran
  haengen seine Aufgaben, seine Nachrichten, seine Eintraege, sein
  ganzer Verlauf. Kapitel 4 des Pflichtenhefts verlangt ausdruecklich
  das Gegenteil.

  (Nebenbefund aus derselben Messung, ihm gemeldet: Auf dem Server gibt
  es KEINE Rolle 'hand'. VanVan ist ein zweiter DogFather-Zugang. Die
  Rueckmeldungen mit "nur an DogFather" wuerde sie deshalb heute
  mitlesen -- die Regel fragt "ist das DogFather?", und ihre Rolle
  antwortet ja.)

DIE KACHEL traegt Namen, Zeichen und Farbton der Agenturseite. Es ist
dieselbe Seite mit demselben Zweck; ein zweiter Name dafuer waere ein
zweites Ding, das es nicht gibt.

SIE STEHT NUR DORT, WO SIE AUCH FUNKTIONIERT. Die Personenseite haengt
serverseitig an `nurAdmin`. In der Kachelliste der rechten Hand haette
sie auf eine 404 gefuehrt -- ein Knopf, der eine Absage bringt, ist
schlimmer als kein Knopf. Wenn sie das duerfen soll, ist das eine
eigene Entscheidung und gehoert an dieselbe Stelle wie nurAdmin.

"NUR BASIERT AUF DIESE SEITE" steht nicht in der Kachel, sondern im
Server: Auf crew. liefert die Liste nur Team Dogi, und angelegt werden
koennen nur Team-Rollen. Beides kommt aus Funktionen, die es schon gab
(hausBedingung, darfAnlegen) -- und `darfAnlegen` baut auch die Knoepfe
in der Oberflaeche, weshalb die anderen Rollen dort von selbst
verschwinden statt eine Absage zu bringen.

DER ROLLENWECHSEL HAT FUENF SICHERUNGEN, und jede hat ihren Grund:

  NUR DOGFATHER -- wer Rollen vergeben kann, kann sich selbst zum
  DogFather machen.

  NIE DIE EIGENE. Wer sich selbst herabstuft, sperrt sich aus; die
  Funktion zum Zurueckdrehen haengt an der Rolle, die er gerade
  abgegeben hat. Das ist keine Warnung wert, das ist eine Tuer, die
  zubleibt.

  NIE DEN LETZTEN AKTIVEN DOGFATHER. Gezaehlt werden die AKTIVEN: Ein
  gesperrter kann niemanden hereinlassen, ihn mitzuzaehlen waere eine
  Sicherung, die sich selbst beluegt.

  ALLE SITZUNGEN DIESER PERSON ENDEN. Eine Sitzung gehoert seit dem
  10.09.2026 zu einer ADRESSE, und welche das ist, entscheidet die
  Rolle. Wer eben noch DogFather war und jetzt rechte Hand ist, saesse
  sonst mit einer Sitzung da, die auf der Agenturadresse laeuft und
  dort nicht mehr hingehoert -- ein halb gueltiger Zustand, der erst
  beim naechsten Klick auffaellt.

  DER CODE BLEIBT. Er haengt am Menschen, nicht an der Rolle. Ihn
  mitzutauschen waere bequem und falsch: Dann muesste jede
  Rollenaenderung von einem Gespraech begleitet sein, und wer das
  vergisst, sperrt jemanden aus, ohne es zu merken.

Und es steht im Protokoll, mit beiden Rollen im Klartext.

DIE AUSWAHL IM BROWSER wird nicht noch einmal gebaut, sondern aus dem
Anlege-Formular gelesen. Dort stehen genau die Rollen, die der Server
dieser Person zugesteht -- einschliesslich derer, die in keiner
ausgelieferten Datei stehen duerfen und erst nachtraeglich dazukommen.
Eine zweite Liste waere die, in der eine Rolle fehlt oder eine zu viel
steht, und beides faellt erst auf, wenn jemand sie braucht.

EINE PRUEFUNG WAR WERTLOS UND IST ES NICHT MEHR: "ihre Sitzungen sind
beendet" lief gegen einen leeren Bestand -- ein gruener Haken ueber
einer Null. Jetzt meldet sich die Person vorher an, und die Zahl davor
muss groesser als null sein.

pruef-haus-trennung 32 -> 53 · pruef-rollen 277 · pruef-personen-formular
27 · pruef-css-klassen 30 · pruef-start-ansicht 143 ·
pruef-modi-verborgen 78 · pruef-modi-wortleck 5.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-10 23:47:13 +02:00

258 lines
12 KiB
HTML
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.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Anmeldung · Team Dogi</title>
<meta name="description" content="Privater Arbeitsbereich für DogFather und die Modis." />
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<!-- Die Farbe der Fensterleiste, wenn der Workspace als App laeuft.
Stand auf #0674b9 (kraeftiges Blau) und stach damit als einziger
heller Balken ueber einer durchweg dunklen Seite. Jetzt derselbe
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<!-- Die Bühne ist der erste Eindruck. Ohne diesen Hinweis lädt der Browser sie erst,
nachdem er das CSS gelesen hat - das kostet spürbar Zeit. Quer- und Hochformat
sind getrennt, damit das Handy nicht die große Fassung zieht.
Hier steht bewusst WebP und nicht AVIF, obwohl die AVIF-Fassungen erzeugt
werden und rund ein Drittel kleiner wären: Der ausliefernde Server läuft auf
Express 4, dessen `send` eine alte MIME-Tabelle ohne AVIF mitbringt. Die
Dateien kämen als application/octet-stream an und würden wegen des
nosniff-Headers vom Browser verweigert - die Bühne bliebe leer. Sobald der
Workspace einen eigenen Server bekommt, kann AVIF wieder eingeschaltet
werden (die Dateien liegen bereit). -->
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-1920.webp"
type="image/webp" media="(min-width: 900px)" />
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
type="image/webp" media="(max-width: 899px)" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609102339" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609102339" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609102339" />
<!-- Diese Wand steht NUR auf der Adresse von Team Dogi (ueberall sonst
404, siehe crewWeiche). Sie darf das Haus deshalb direkt nennen
statt ueber den Platzhalter haus.css zu gehen -- und sie MUSS es:
Eine Zugangswand, die sich erst nach dem Umbiegen faerbt, waere
die eine Seite, auf der man das Umfaerben sieht. -->
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609102339" />
</head>
<body class="gate gate--crew">
<!-- ====================================================================
DIE DREI ZEICHEN DIESER WAND (10.09.2026)
Filipe: "3 rollen. dogfather. rechte hand und modis."
Von den fuenf Zeichen der anderen Wand bleibt nur der Husky -- die
uebrigen gehoeren zu Rollen, die es hier nicht gibt, und ihre
Namen haetten im Quelltext nichts verloren.
DER HUSKY BEI DOGFATHER UND BEI DER RECHTEN HAND, die Pfote bei
den Modis (Filipe, 10.09.2026, mit Bildschirmfoto: "bei rechte
hand soll der husky sein und bei modis soll die pfote sein").
DAS DREHT EINE FRUEHERE VORGABE UM, und der Grund gehoert dazu: Am
09.09. hiess es "das modi symbol soll das gleiche sein wie bei
dogfather" -- damals gab es hier zwei Rollen, und der Satz
bedeutete "die Modis gehoeren zu Dogi, nicht zur Agentur". Mit
drei Rollen sagt dieselbe Absicht etwas anderes: Der Husky steht
bei den beiden, die den Ueberblick haben, die Pfote bei denen, die
taeglich unterwegs sind. Zwei gleiche Zeichen und ein anderes --
das liest sich als Gruppe, nicht als Reihe.
Die Pfote traegt bei 22 px, weil es fuenf klar getrennte Flaechen
sind und keine Innenzeichnung -- dasselbe Argument, mit dem auf der
anderen Wand eine Silhouette einem geschliffenen Koerper gewichen
ist.
================================================================= -->
<svg width="0" height="0" style="position:absolute;overflow:hidden" aria-hidden="true"><defs>
<!-- Der Husky ist ein FOTO, kein Pfad. Der Filter zieht ihn auf zwei
Toene zusammen (dunkles Blaugrau -> Silber), damit er neben
gezeichneten Zeichen nicht wie ein Fremdkoerper wirkt. -->
<filter id="rs-husky-duo" color-interpolation-filters="sRGB">
<feComponentTransfer>
<feFuncR type="table" tableValues="0.165 0.910"/>
<feFuncG type="table" tableValues="0.212 0.933"/>
<feFuncB type="table" tableValues="0.267 0.965"/>
</feComponentTransfer>
</filter>
<g id="r-husky">
<image href="assets/img/marke-husky.webp" x="0" y="0" width="24" height="24"
preserveAspectRatio="xMidYMid meet" filter="url(#rs-husky-duo)"/>
<circle fill="#7ec8f2" cx="7.73" cy="12.1" r="1.05"/>
<circle fill="#16232f" cx="7.73" cy="12.1" r="0.52"/>
<circle fill="#ffffff" opacity=".85" cx="7.5" cy="11.85" r="0.2"/>
</g>
<!-- Der Ton der Pfote ist der Modi-Ton (#5f8a9f) nach oben
aufgehellt: dasselbe Blau wie das App-Symbol, nur heller, damit
es bei 22 px gegen den dunklen Grund steht. -->
<linearGradient id="rs-pfote" x1="0" y1="0" x2=".35" y2="1">
<stop offset="0" stop-color="#cfe4ef"/>
<stop offset=".55" stop-color="#8fb6c8"/>
<stop offset="1" stop-color="#5f8a9f"/>
</linearGradient>
<g id="r-pfote">
<ellipse fill="url(#rs-pfote)" cx="12" cy="15.9" rx="5.3" ry="4.4"/>
<ellipse fill="url(#rs-pfote)" cx="6.5" cy="10.1" rx="2" ry="2.6"
transform="rotate(-20 6.5 10.1)"/>
<ellipse fill="url(#rs-pfote)" cx="10.1" cy="7.4" rx="2.05" ry="2.7"/>
<ellipse fill="url(#rs-pfote)" cx="13.9" cy="7.4" rx="2.05" ry="2.7"/>
<ellipse fill="url(#rs-pfote)" cx="17.5" cy="10.1" rx="2" ry="2.6"
transform="rotate(20 17.5 10.1)"/>
<!-- Ein Lichtstreifen oben links auf dem Ballen -- ohne ihn ist er
eine flache Flaeche. -->
<path fill="rgba(255,255,255,.32)"
d="M8.4 13.6c1-1.3 2.6-2 4.2-1.9-1.7.5-3 1.6-3.6 2.9z"/>
</g>
</defs></svg>
<!-- ---------- Bühne: Bild + Schleier ---------------------------------- -->
<div class="buehne" aria-hidden="true">
<picture>
<!-- BIS 2560 PIXEL (07.09.2026). Vorher endete die Reihe bei 1600 --
auf einem 2550 px breiten Schirm rechnete der Browser das Bild
also um das 1,6-fache hoch, und jede Kante wurde weich. Genau
das sah man als "nicht hochwertig", ohne es benennen zu koennen.
Eine hoehere Bildguete haette daran nichts geaendert: Man kann
keine Bildpunkte zurueckholen, die nie ausgeliefert wurden.
Ueber srcset laedt trotzdem jeder nur die Stufe, die er
braucht -- ein Handy weiterhin 32 KB. -->
<source media="(min-width: 900px)" type="image/webp"
srcset="assets/img/buehne/crew-gate-960.webp 960w,
assets/img/buehne/crew-gate-1280.webp 1280w,
assets/img/buehne/crew-gate-1600.webp 1600w,
assets/img/buehne/crew-gate-1920.webp 1920w,
assets/img/buehne/crew-gate-2560.webp 2560w" sizes="100vw" />
<source type="image/webp"
srcset="assets/img/buehne/crew-gate-hoch-440.webp 440w,
assets/img/buehne/crew-gate-hoch-580.webp 580w,
assets/img/buehne/crew-gate-hoch-900.webp 900w" sizes="100vw" />
<img class="buehne__bild" src="assets/img/buehne/crew-gate-1280.webp" alt=""
width="1647" height="955" fetchpriority="high" decoding="async" />
</picture>
<div class="buehne__schleier"></div>
</div>
<!-- ---------- Tafel ----------------------------------------------------
DER ANKER (06.09.2026). Im Motiv steht rechts eine LEERE TAFEL mit
Neonrahmen -- dort gehoert die Anmeldekarte hinein, und zwar genau
hinein, nicht ungefaehr.
Das geht nur, wenn man weiss, wo die gemalte Tafel auf dem
Bildschirm landet. Das Bild liegt mit `object-fit: cover` darunter;
je nach Fensterform schneidet der Browser oben/unten oder
links/rechts etwas ab. Dieser Anker rechnet genau dieselbe
Cover-Geometrie noch einmal nach (siehe gate.css) -- er ist damit
deckungsgleich mit dem Bild, und die Karte kann in Prozent des
BILDES sitzen statt in Prozent des Fensters.
Er faengt keine Klicks ab (pointer-events: none); nur die Karte
darin ist bedienbar. -->
<div class="tafel-anker">
<main class="tafel" id="tafel">
<div class="tafel__kante" aria-hidden="true"></div>
<!-- 10.09.2026, Wunsch Filipe: hier soll alles auf DogFather und sein
Team zugeschnitten sein.
Zwei Namen und ein Zeichen dazwischen -- nebeneinander, nicht
uebereinander. Dogi steht hier nicht ueber den Modis, sie machen
das zusammen.
Das "x" steht als eigenes Element, damit es kleiner und in der
Zwischenfarbe gesetzt werden kann -- so liest man zwei Namen und
ein Zeichen dazwischen, nicht drei gleich laute Wörter. Es trägt
aria-hidden nicht: Vorgelesen wird "DogFather x Team Dogi", und
genau so spricht man es auch aus. -->
<header class="kopf">
<p class="marke marke--paar">
<span class="marke__dogi">DogFather</span>
<span class="marke__x">×</span>
<span class="marke__crew">Team&nbsp;Dogi</span>
</p>
<h1 class="titel">Team&nbsp;Dogi</h1>
<p class="unterzeile">Privater Bereich für DogFather und die Modis.</p>
</header>
<div class="rollen" role="radiogroup" aria-labelledby="rollen-schild">
<p class="feldschild" id="rollen-schild">Womit meldest du dich an?</p>
<button type="button" class="rolle" data-rolle="admin" role="radio" aria-checked="false">
<svg class="rolle__zeichen" viewBox="0 0 24 24" aria-hidden="true"><use href="#r-husky"/></svg>
<span class="rolle__text">
<span class="rolle__name">DogFather</span>
<span class="rolle__zusatz">Überblick &amp; Entscheidungen</span>
</span>
</button>
<!-- Der Untertitel beschreibt eine AUFGABE, kein Recht -- dieselbe
Ueberlegung wie auf der anderen Wand: Wer vor der Auswahl
steht, fragt sich nicht, was er duerfte, sondern was er hier
tut. Und er stellt niemanden ueber jemanden: "vertritt und
koordiniert" ist eine Taetigkeit, kein Rang. -->
<button type="button" class="rolle" data-rolle="hand" role="radio" aria-checked="false">
<svg class="rolle__zeichen" viewBox="0 0 24 24" aria-hidden="true"><use href="#r-husky"/></svg>
<span class="rolle__text">
<span class="rolle__name">Rechte Hand</span>
<span class="rolle__zusatz">Vertritt und koordiniert</span>
</span>
</button>
<button type="button" class="rolle" data-rolle="modi" role="radio" aria-checked="false">
<svg class="rolle__zeichen" viewBox="0 0 24 24" aria-hidden="true"><use href="#r-pfote"/></svg>
<span class="rolle__text">
<span class="rolle__name">Modi</span>
<span class="rolle__zusatz">Chat, Live und eigene Aufgaben</span>
</span>
</button>
</div>
<form class="formular" id="formular" novalidate>
<label class="feldschild" for="code">
Persönlicher Zugangscode <span class="fuer" id="fuer" hidden></span>
</label>
<div class="feld">
<input type="password" id="code" name="code" inputmode="text"
autocomplete="current-password" spellcheck="false"
placeholder="Code eingeben" aria-describedby="hinweis fehler" />
<button type="button" class="feld__auge" id="auge"
aria-label="Code anzeigen" aria-pressed="false">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path class="strich" d="M2.5 12S6 5.8 12 5.8 21.5 12 21.5 12 18 18.2 12 18.2 2.5 12 2.5 12Z"/>
<circle class="strich" cx="12" cy="12" r="2.9"/>
<path class="strich durch" d="m4 20 16-16"/>
</svg>
</button>
</div>
<p class="hinweis" id="hinweis" hidden>Feststelltaste ist aktiv.</p>
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
<button type="submit" class="knopf" id="knopf">
<span class="knopf__text">Anmelden</span>
<span class="knopf__laden" aria-hidden="true"></span>
</button>
</form>
<p class="fuss">
Verschlüsselte Verbindung · Zugriffe werden protokolliert
</p>
</main>
</div>
<script src="assets/js/gate.js?v=202609102339" defer></script>
</body>
</html>