Files
dogfather-universe/workspace/chat.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

299 lines
15 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>Chat · Spicy & Dogi</title>
<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" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609102339" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609102339" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609102339" />
<link rel="stylesheet" href="assets/css/start.css?v=202609102339" />
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
Dialog zu sehen bekam. -->
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609102339" />
<link rel="stylesheet" href="assets/css/chat.css?v=202609102339" />
<link rel="stylesheet" href="assets/css/module.css?v=202609102339" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609102339" />
</head>
<body class="start">
<div class="schleier" aria-hidden="true"></div>
<header class="kopfleiste">
<button type="button" class="zurueck-knopf" id="zurueck" hidden>
<svg class="zurueck-knopf__pfeil" viewBox="0 0 24 24" aria-hidden="true">
<path d="M14.5 5.5 8 12l6.5 6.5" />
</svg>
<span class="zurueck-knopf__text">Zurück</span>
</button>
<p class="marke"><span class="marke__text"><a class="zurueck" href="start.html">Creator&nbsp;Workspace</a> · Chat</span></p>
<div class="kopfleiste__rechts">
<span class="wer" id="wer">…</span>
<button type="button" class="abmelden" id="abmelden">Abmelden</button>
</div>
</header>
<main class="inhalt inhalt--breit chat-seite">
<!-- DIE GANZE SEITE IST EINE FLÄCHE, kein Dokument zum Scrollen.
Ein Chat ist kein Text, den man von oben nach unten liest --
man springt zwischen Gesprächen, und das Eingabefeld muss
immer erreichbar sein, ohne zu suchen. Deshalb: feste Höhe,
und gescrollt wird nur INNERHALB der beiden Spalten.
data-ansicht steuert am Handy, welche Spalte zu sehen ist:
"liste" oder "verlauf". Am Rechner stehen beide nebeneinander
und das Merkmal bewirkt nichts. -->
<div class="chat" id="chat" data-ansicht="liste">
<!-- ---------- Links: die Gespräche ---------- -->
<section class="chat__spalte chat__spalte--liste" aria-labelledby="liste-titel">
<div class="chat__kopf">
<h1 class="chat__titel" id="liste-titel">Gespräche</h1>
<button type="button" class="knopf knopf--klein" id="neu-oeffnen">Neu</button>
</div>
<!-- Suchen erst, wenn es etwas zu suchen gibt (chat.js blendet
es ab acht Gesprächen ein). Ein Suchfeld über drei Einträgen
ist Zierde. -->
<div class="chat__suche" id="suche-zeile" hidden>
<input type="search" id="suche" placeholder="Name oder Gruppe …"
aria-label="Gespräche durchsuchen" autocomplete="off" />
</div>
<ul class="chat__liste" id="raeume" aria-live="polite"></ul>
<!-- ---------- Fundstellen in Nachrichten (09.09.2026, Punkt 15)
Das Suchfeld darueber durchsuchte bisher nur die NAMEN der
Gespraeche. Das ist die kleinere Haelfte: Was man wirklich
sucht, ist ein Satz, den jemand vor drei Wochen geschrieben
hat.
Die Fundstellen stehen UNTER der Gespraechsliste und nicht
an ihrer Stelle -- beides ist eine Antwort auf dieselbe
Eingabe, und wer nach "Patrick" sucht, will vielleicht das
Gespraech und vielleicht die Nachricht. Ein Umschalter
zwischen zwei Ansichten haette ihn zwingen wollen, das
vorher zu wissen. -->
<div class="chat-funde" id="funde" hidden>
<p class="chat-funde__schild" id="funde-schild">In Nachrichten</p>
<ul class="chat-funde__liste" id="funde-liste"></ul>
</div>
<p class="chat__leer" id="liste-leer" hidden>
Noch keine Gespräche. Mit <strong>Neu</strong> schreibst du der
ersten Person.
</p>
</section>
<!-- ---------- Rechts: der Verlauf ---------- -->
<section class="chat__spalte chat__spalte--verlauf" aria-labelledby="verlauf-titel">
<!-- Der Kopf trägt am Handy den Zurück-Weg zur Liste. -->
<div class="chat__kopf chat__kopf--verlauf">
<button type="button" class="chat__zurueck" id="zur-liste" aria-label="Zurück zu den Gesprächen">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 5.5 8 12l6.5 6.5" /></svg>
</button>
<div class="chat__kopf-text">
<h2 class="chat__titel" id="verlauf-titel">Wähle ein Gespräch</h2>
<p class="chat__unter" id="verlauf-unter"></p>
</div>
<!-- GESPRÄCH WEGRÄUMEN (09.09.2026).
Filipe: "man muss die chats auch gelöscht bekommen!!!"
Es verschwindet nur bei DIR -- das Gegenüber behält seinen
Verlauf. Der Knopf steht erst da, wenn auch ein Gespräch
offen ist; sonst wäre er ein Angebot ohne Gegenstand. -->
<!-- WER IST IM KANAL (10.09.2026, Kapitel 7.2).
Steht nur in einem Kanal da und nur bei der Teamleitung --
der Server entscheidet das, nicht diese Datei. -->
<button type="button" class="chat__weg chat__leute" id="kanal-leute" hidden
aria-label="Wer ist in diesem Kanal?"
title="Wer ist in diesem Kanal?">
<svg viewBox="0 0 24 24" aria-hidden="true">
<circle cx="9" cy="8.5" r="3" />
<path d="M3.5 19c0-3 2.5-5 5.5-5s5.5 2 5.5 5" />
<path d="M16 6.2a3 3 0 0 1 0 5.6M17.5 14.4c2 .7 3.3 2.4 3.3 4.6" />
</svg>
</button>
<button type="button" class="chat__weg" id="gespraech-weg" hidden
aria-label="Dieses Gespräch wegräumen"
title="Gespräch wegräumen – nur bei dir">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 7h14M10 7V5h4v2M6.5 7l.8 12h9.4l.8-12M10 10.5v5.5M14 10.5v5.5" />
</svg>
</button>
</div>
<!-- aria-live="polite": Ein Vorleseprogramm sagt neue Nachrichten
an, ohne den Nutzer mitten im Satz zu unterbrechen. -->
<!-- ZUM NEUESTEN (09.09.2026). Vorher sprang der Verlauf bei jeder
neuen Nachricht ans Ende -- wer weiter oben nachlas, wurde
mitten im Satz weggerissen. Jetzt bleibt er stehen, und hier
steht, wie viel unten wartet. -->
<button type="button" class="chat__zum-neuesten" id="zum-neuesten" hidden></button>
<!-- HINEINZIEHEN (09.09.2026). Erscheint nur, solange wirklich
etwas über dem Fenster hängt. Eine dauerhaft sichtbare
Ablagefläche wäre jeden Tag im Weg für den Fall, dass man
sie vielleicht braucht. -->
<div class="chat__ablage" id="ablage" hidden aria-hidden="true">
<span>Loslassen – Foto oder PDF schicken</span>
</div>
<!-- ANGEHEFTETE ANKÜNDIGUNGEN (10.09.2026, Kapitel 7.2).
Sie stehen ÜBER dem Verlauf und rollen nicht mit: Eine
Ankündigung, die man erst suchen muss, ist keine. Leer bleibt
der Kasten verborgen – ein leerer Rahmen wäre jeden Tag im
Weg für den Fall, dass irgendwann etwas darin steht. -->
<div class="chat-pins" id="pins" hidden aria-label="Angeheftet"></div>
<div class="chat__verlauf" id="verlauf" role="log" aria-live="polite" aria-relevant="additions">
<p class="chat__leer" id="verlauf-leer">
Links ein Gespräch auswählen – oder mit <strong>Neu</strong> eines anfangen.
</p>
</div>
<!-- Das Eingabefeld. Ein <form>, damit Enter abschickt, ohne
dass dafür Code nötig wäre. -->
<!-- DIE ANTWORTLEISTE (09.09.2026, Punkt 15).
Sie steht UEBER dem Eingabefeld und innerhalb desselben
<form>: So sieht man beim Tippen, worauf man antwortet, und
kann es mit einem Griff wieder loswerden. Eine Leiste
ausserhalb waere beim Tippen ausserhalb des Blickfelds.
`hidden` als Vorgabe -- sie erscheint erst, wenn wirklich
auf etwas geantwortet wird. Ein leerer Kasten ueber dem
Eingabefeld waere jeden Tag im Weg fuer den Fall, dass man
ihn vielleicht braucht. -->
<form class="chat__eingabe" id="schreiben" hidden>
<div class="antwort-leiste" id="antwort-leiste" hidden>
<span class="antwort-leiste__strich" aria-hidden="true"></span>
<span class="antwort-leiste__inhalt">
<span class="antwort-leiste__schild">Antwort an</span>
<span class="antwort-leiste__von" id="antwort-von"></span>
<span class="antwort-leiste__text" id="antwort-text"></span>
</span>
<button type="button" class="antwort-leiste__weg" id="antwort-weg"
aria-label="Antwort verwerfen">&#10005;</button>
</div>
<label class="chat__eingabe-schild" for="text">Nachricht</label>
<!-- KURZER PLATZHALTER. Hier stand der Hinweis "Enter schickt,
Umschalt+Enter macht eine neue Zeile" -- zwei Fehler auf
einmal: Am Handy brach er auf zwei Zeilen um und schob das
Feld halb unter den Bildschirmrand, UND er stimmte dort
nicht: Auf Geräten ohne Tastatur schickt Enter absichtlich
nicht, sonst käme man nie zu einer zweiten Zeile.
Die Tastenbelegung steht jetzt im title -- sie ist ein
Hinweis für Wiederkehrer, keine Aufforderung. -->
<!-- ANHANG (09.09.2026): "pdfs und fotos".
Das Feld selbst bleibt unsichtbar -- ein Datei-Auswahlfeld
sieht in jedem Browser anders aus und lässt sich nicht
gestalten. Bedient wird es über die Büroklammer daneben. -->
<input type="file" id="anhang-feld" class="chat__anhang-feld"
accept="image/png,image/jpeg,image/webp,image/gif,application/pdf"
tabindex="-1" aria-hidden="true" />
<button type="button" class="chat__anhang" id="anhang-auf"
aria-label="Foto oder PDF anhängen" title="Foto oder PDF anhängen">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M20 11.5 12.3 19.2a4.4 4.4 0 0 1-6.2-6.2l7.9-7.9a2.9 2.9 0 0 1 4.1 4.1l-7.9 7.9a1.5 1.5 0 0 1-2.1-2.1l7.2-7.2" />
</svg>
</button>
<textarea id="text" rows="1" maxlength="4000"
placeholder="Nachricht schreiben …"
title="Enter schickt ab, Umschalt+Enter macht eine neue Zeile"></textarea>
<!-- EMOJI-AUSWAHL (09.09.2026). Die Bildschirmtastatur am Handy
hat sie ohnehin -- am Rechner nicht, und dort schreibt die
Betreuung. Dreissig feste Zeichen statt einer nachgeladenen
Tafel: Das deckt den Alltag ab und laedt nichts. -->
<button type="button" class="chat__emoji" id="emoji-auf"
aria-expanded="false" aria-controls="emoji-tafel"
aria-label="Emoji einfügen" title="Emoji einfügen">☺</button>
<div class="emoji-tafel" id="emoji-tafel" role="group"
aria-label="Emoji auswählen" hidden></div>
<!-- Erscheint erst, wenn es eng wird -- siehe chat.js. -->
<span class="chat__zeichen" id="zeichen" aria-live="polite" hidden></span>
<button type="submit" class="chat__senden" id="senden" aria-label="Abschicken">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M4 12l16-7-7 16-2.5-6.5z" />
</svg>
</button>
</form>
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
</section>
</div>
</main>
<!-- Neues Gespräch. Beides in einem Dialog: Wer eine Person anklickt,
schreibt ihr; wer mehrere anhakt, legt eine Gruppe an. Zwei
getrennte Dialoge hätten die Frage "Einzeln oder Gruppe?" VOR die
eigentliche Absicht gestellt. -->
<dialog class="dialog" id="neu-dialog">
<form method="dialog" id="neu-form" class="neu neu--blank">
<h2 class="dialog__titel" id="neu-titel">Neues Gespräch</h2>
<p class="dialog__unter" id="neu-unter">
Eine Person antippen, um ihr zu schreiben. Mehrere auswählen ergibt
eine Gruppe.
</p>
<!-- ZWEI ARTEN, EIN DIALOG (10.09.2026, Kapitel 7.2).
Ein zweiter Dialog wäre dieselbe Personenwahl ein zweites Mal –
und die zweite Fassung wäre die, in der eine Regel fehlt.
Sichtbar nur, wenn der Server sagt, dass Kanäle erlaubt sind. -->
<div class="neu__art" id="neu-art" role="group" aria-label="Was soll es werden?" hidden>
<button type="button" data-art="gespraech" data-an="ja" aria-pressed="true">Gespräch</button>
<button type="button" data-art="kanal" aria-pressed="false">Kanal</button>
</div>
<div class="neu__zeile" id="kanal-zeile" hidden>
<label class="feld-schild" for="kanal-kategorie">Zuständigkeit</label>
<select id="kanal-kategorie"></select>
<p class="neu__hinweis" id="kanal-alle" hidden>
Für jede Zuständigkeit gibt es schon einen Kanal.
</p>
</div>
<div class="chat-wahl" id="partner"></div>
<!-- Erscheint erst ab zwei Ausgewählten -- vorher wäre ein
Gruppenname eine Frage ohne Anlass. -->
<div class="neu__zeile" id="gruppenname-zeile" hidden>
<label class="feld-schild" for="gruppenname">Name der Gruppe</label>
<input id="gruppenname" maxlength="80" placeholder="z. B. Luna &amp; Betreuung" />
</div>
<p class="fehler" id="neu-fehler" role="alert" aria-live="polite"></p>
<div class="dialog__knoepfe">
<button type="submit" class="knopf knopf--klein" id="neu-los" disabled>Schreiben</button>
<button type="button" class="abmelden" id="neu-zu">Abbrechen</button>
</div>
</form>
</dialog>
<script src="assets/js/bereiche.js?v=202609102339"></script>
<script src="assets/js/wahl.js?v=202609102339" defer></script>
<script src="assets/js/kopf.js?v=202609102339"></script>
<script src="assets/js/glocke.js?v=202609102339"></script>
<script src="assets/js/chat.js?v=202609102339"></script>
</body>
</html>