diff --git a/server/pruef-erwaehnung.mjs b/server/pruef-erwaehnung.mjs
index e71109d0..c4464a1f 100644
--- a/server/pruef-erwaehnung.mjs
+++ b/server/pruef-erwaehnung.mjs
@@ -242,6 +242,36 @@ ok(Number.isInteger(raumId), `eine Gruppe mit drei Leuten steht (Raum ${raumId})
`die Raumliste bringt die Teilnehmer schon mit (${r?.teilnehmer?.length})`);
ok(r?.teilnehmer?.every((t) => t.id && t.name),
" mit Nummer und Namen — mehr braucht die Auswahl nicht");
+
+ /* ---- UND MIT DEM PROFILBILD (20.09.2026) ---------------------------
+
+ Filipe: "da soll man im chat auch die profilfotos von den leuten
+ sehen wenn die schon eins drin haben."
+
+ Es fehlte in GENAU DIESER Abbildung: `teilnehmerVon` liest das Bild
+ aus der Datenbank, und die Zeile in der Raumliste warf es weg. Die
+ Detailansicht nahm es mit. Folge: im offenen Gespraech ein Gesicht,
+ in der Liste daneben ein Buchstabe -- vom selben Menschen.
+
+ Geprueft wird mit einem eingetragenen Dateinamen; ob die Datei
+ existiert, ist eine andere Frage und gehoert zum Steckbrief. Hier
+ geht es darum, dass der WEG ueberhaupt ankommt. */
+ {
+ const d = new DatabaseSync(process.env.WORKSPACE_DB);
+ d.prepare("UPDATE personen SET bild = ? WHERE id = ?").run("probe.webp", idVan);
+ d.close();
+ const l2 = json(await ruf("/workspace/api/chat/raeume", aDogi.keks));
+ const r2 = (l2.raeume || []).find((x) => x.id === raumId);
+ const van = r2?.teilnehmer?.find((t) => t.id === idVan);
+ ok(typeof van?.bild === "string" && van.bild.includes("probe.webp"),
+ `das Profilbild kommt in der Gespraechsliste an (${van?.bild})`);
+ const ohne = r2?.teilnehmer?.find((t) => t.id === idFunny);
+ ok(ohne && !ohne.bild,
+ "Gegenprobe: wer keines hinterlegt hat, bekommt auch keines vorgegaukelt");
+ const d2 = new DatabaseSync(process.env.WORKSPACE_DB);
+ d2.prepare("UPDATE personen SET bild = NULL WHERE id = ?").run(idVan);
+ d2.close();
+ }
}
/* Jetzt die eigentliche Nachricht. */
diff --git a/server/pruef-team-stufen.mjs b/server/pruef-team-stufen.mjs
index 84650311..3596c68f 100644
--- a/server/pruef-team-stufen.mjs
+++ b/server/pruef-team-stufen.mjs
@@ -143,8 +143,33 @@ ok(!!marina && !!kim, "beide sind namentlich wiederzufinden");
ok(marina?.aktiv === true && kim?.aktiv === false,
`Status stimmt (Marina aktiv: ${marina?.aktiv}, Kim aktiv: ${kim?.aktiv})`);
-/* AKTIVE ZUERST -- wer pausiert ist, steht unten und nicht mittendrin. */
-ok(leute[0]?.id === idMarina, "die Aktiven stehen oben");
+/* AKTIVE ZUERST -- wer pausiert ist, steht unten und nicht mittendrin.
+
+ GEPRUEFT WIRD DIE EIGENSCHAFT, NICHT DIE POSITION (berichtigt
+ 20.09.2026). Hier stand `leute[0]?.id === idMarina` -- und das wurde
+ rot, sobald die rechte Hand nach vorn sortiert wurde. Richtig wurde
+ es dadurch nicht: Die Aussage "die Aktiven stehen oben" hat mit
+ Marinas Platz nichts zu tun. Eine Pruefung, die eine Position
+ festnagelt, verbietet jede Umsortierung -- auch die gewollte. */
+const letzteAktive = leute.map((m) => m.aktiv).lastIndexOf(true);
+const ersteInPause = leute.map((m) => m.aktiv).indexOf(false);
+ok(ersteInPause === -1 || letzteAktive < ersteInPause,
+ `die Aktiven stehen oben (letzte aktive Stelle ${letzteAktive}, erste Pause ${ersteInPause})`);
+
+/* ==== UND DIE RECHTE HAND VOR DEN MODIS (20.09.2026) ================
+
+ Filipe: "ich will dass hier wie ueberall die reihenfolge immer
+ rechte hand und dan erst die modis."
+
+ Vorher sortierte die Abfrage nur nach Namen -- die Rolle wurde nicht
+ einmal mitgelesen. Damit stand die Reihenfolge im Alphabet, und das
+ ist keine. */
+const platzHand = leute.findIndex((m) => m.id === idHand);
+const ersterModi = leute.findIndex((m) => m.id !== idHand && m.aktiv);
+ok(platzHand >= 0 && (ersterModi === -1 || platzHand < ersterModi),
+ `die rechte Hand steht vor den Modis (Platz ${platzHand} vor ${ersterModi})`);
+ok(leute.every((m) => typeof m.rolle === "string" && m.rolle),
+ "und jede Karte weiss, welche Rolle sie hat — sonst kann die Seite sie nicht unterscheiden");
ok(typeof marina?.seit === "string" && marina.seit.startsWith(vor40Tagen.slice(0, 10)),
`"dabei seit" kommt mit (${(marina?.seit || "").slice(0, 10)})`);
diff --git a/server/workspace-chat.js b/server/workspace-chat.js
index f920f08b..0f7abf9b 100644
--- a/server/workspace-chat.js
+++ b/server/workspace-chat.js
@@ -160,6 +160,20 @@ function istDrin(raumId, person) {
wurde damit zu Programmtext, und die Datei liess sich nicht mehr
lesen. Ein Kommentar, der die Zeichenkette schliesst, in der er
steht. */
+/** Der Weg zum Profilbild dieser Person -- oder null.
+ *
+ * ALS EIGENE FUNKTION, weil die Zeile
+ * `bild ? "/workspace/api/steckbrief/bild/" + bild : null`
+ * sonst an drei Stellen steht. Die dritte Abschrift ist die, die
+ * den Pfad falsch schreibt, und ein kaputter Bildpfad sieht aus wie
+ * "die Person hat halt kein Bild". */
+function bildWegFuer(personId) {
+ try {
+ const z = db().prepare("SELECT bild FROM personen WHERE id = ?").get(personId);
+ return z?.bild ? `/workspace/api/steckbrief/bild/${z.bild}` : null;
+ } catch { return null; }
+}
+
function teilnehmerVon(raumId) {
return db().prepare(`
SELECT t.person_id AS id, p.name, p.rolle, p.bild, t.leitung, t.gelesen_bis
@@ -545,7 +559,18 @@ chatRouter.get("/workspace/api/chat/raeume", (req, res) => {
Stellen steht; genau daran ist es beim ersten Anlauf
gescheitert. */
darf_aufloesen: darfAufloesen(r, req.person),
- teilnehmer: leute.map((t) => ({ id: t.id, name: t.name, rolle: t.rolle })),
+ /* DAS BILD GEHOERT DAZU (20.09.2026).
+
+ Filipe: "da soll man im chat auch die profilfotos von den
+ leuten sehen wenn die schon eins drin haben."
+
+ Es fehlte genau hier: `teilnehmerVon` liest das Bild aus der
+ Datenbank, und diese Zeile warf es weg. Die Detailansicht
+ weiter unten nimmt es mit -- die LISTE nicht. Deshalb stand
+ im offenen Gespraech ein Gesicht und in der Liste daneben ein
+ Buchstabe, vom selben Menschen. */
+ teilnehmer: leute.map((t) => ({ id: t.id, name: t.name, rolle: t.rolle,
+ bild: t.bild })),
letzte: letzte ? {
/* Ein Anhang ohne Begleittext hätte hier eine leere Zeile
hinterlassen. "Foto"/"PDF" ist die Vorschau, die man in
@@ -1061,6 +1086,13 @@ chatRouter.post("/workspace/api/chat/raeume/:id/nachrichten", gleicheHerkunft,
const nachricht = {
id, raum_id: raumId, von_id: req.person.id, von: req.person.name,
+ /* DAS BILD MUSS MIT (20.09.2026). Der Verlauf beim Laden bringt
+ `von_bild` mit -- diese Nachricht hier nicht, und sie ist die,
+ die per Ereignisstrom bei allen anderen ankommt. Folge: Wer
+ gerade zusieht, bekommt einen Buchstaben; wer die Seite neu
+ laedt, ein Gesicht. Derselbe Mensch, zwei Darstellungen, und
+ der Unterschied haengt nur daran, wann man geschaut hat. */
+ von_bild: bildWegFuer(req.person.id),
rolle: req.person.rolle, text, erstellt: n, zurueckgenommen: false,
antwort: zitat ? {
id: antwortAuf, von: zitat.von || "Gelöscht",
diff --git a/server/workspace-teamlage.js b/server/workspace-teamlage.js
index df99ce9a..b8073658 100644
--- a/server/workspace-teamlage.js
+++ b/server/workspace-teamlage.js
@@ -34,6 +34,7 @@
import express from "express";
import {
db, sitzungLesen, istDogFather, fuehrtTeamDogi, heuteLokal, protokolliere, echteIp,
+ ROLLEN_SORTIERUNG,
} from "./workspace.js";
import { MODI_PUNKTE } from "./workspace-modi-punkte.js";
import { checklistenTabellen } from "./workspace-checkliste.js";
@@ -171,10 +172,24 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
verhindern soll. Der Riegel eine Zeile hoeher (`darfEingang`)
bleibt beim Angemeldeten: Das ist ein Recht, keine Anzeige. */
const zeigt = req.sicht || person;
+ /* ==== DIE RECHTE HAND STEHT ZUERST (20.09.2026) ==============
+
+ Filipe: "ich will dass hier wie ueberall die reihenfolge immer
+ rechte hand und dan erst die modis."
+
+ Hier stand `ORDER BY aktiv DESC, name` -- die Rolle wurde nicht
+ einmal MITGELESEN. Die Seite konnte also gar nicht wissen, wer
+ rechte Hand ist und wer Modi; sie sortierte alphabetisch, und
+ damit stand Diene vor Funny, weil D vor F kommt.
+
+ Sortiert wird jetzt mit ROLLEN_SORTIERUNG -- derselben Regel, die
+ auch die Personenliste, der Chat und die Rechtetafel benutzen.
+ Eine eigene Reihenfolge hier waere die zweite Wahrheit, und die
+ zweite ist die, die beim naechsten Rollenwechsel abweicht. */
const modis = db().prepare(
- `SELECT id, name, bild, letzter_login, aktiv, erstellt, stufe FROM personen
+ `SELECT id, name, rolle, bild, letzter_login, aktiv, erstellt, stufe FROM personen
WHERE rolle IN ('hand','modi') AND id <> ?
- ORDER BY aktiv DESC, name`).all(zeigt.id);
+ ORDER BY aktiv DESC, ` + ROLLEN_SORTIERUNG + `, name`).all(zeigt.id);
if (!modis.length) {
/* `eingang` auch hier -- die Oberflaeche darf nicht raten
@@ -374,6 +389,11 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
return {
id: m.id,
name: m.name,
+ /* DIE ROLLE GEHT MIT (20.09.2026). Ohne sie kann die Seite
+ die rechte Hand nicht von einem Modi unterscheiden -- und
+ genau das soll sie: andere Reihenfolge, anderes Aussehen.
+ Sie stand bisher nicht einmal in der Abfrage. */
+ rolle: m.rolle,
bild: m.bild ? `/workspace/api/steckbrief/bild/${m.bild}` : null,
letzter_login: m.letzter_login,
/* Die drei Felder aus Kapitel 4: Status, "dabei seit", Stufe. */
diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html
index 1a95cc5c..95e1a868 100644
--- a/workspace/anruf-probe.html
+++ b/workspace/anruf-probe.html
@@ -16,15 +16,15 @@
verdeckt. Gefunden hat es pruef-struktur.
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
-
-
+
+
-
+