diff --git a/server/pruef-dabei-optik.mjs b/server/pruef-dabei-optik.mjs index 8c78417f..65bd6653 100644 --- a/server/pruef-dabei-optik.mjs +++ b/server/pruef-dabei-optik.mjs @@ -97,6 +97,21 @@ const idDogi = anlegen("Filipe Fernandes Ribeiro", "admin", "CODE-DOGI-0001"); const idCig = anlegen("Cigdem", "manager", "CODE-MANA-0001"); const idPat = anlegen("Patrick von Hohenstein", "scout", "CODE-SCOU-0001"); const idLuna = anlegen("Luna", "creator", "CODE-CREA-0001"); +/* DIE RECHTE HAND (11.09.2026). Sie stand in dieser Auswahl bis heute + GAR NICHT -- kalender.js zeichnete nach einer Liste aus fuenf + Agentur-Rollen, und wer nicht darin vorkam, fiel lautlos heraus. + DogFather konnte sein eigenes Team zu keinem Termin einladen, und + der Bildschirm sah dabei vollkommen normal aus. + + Sie heisst hier "VanVan" und damit alphabetisch HINTER allen anderen + ausser Patrick -- mit einem Namen wie "Anna" waere die Zeile + "sie steht vorn" gruen, ohne etwas zu messen. */ +anlegen("VanVan", "hand", "CODE-HAND-0001"); + +/* Die Zahl steht hier und nicht in den Pruefzeilen: Wer unten einen + Menschen dazusetzt, muss sonst drei Zahlen nachziehen -- und die + vergessene ist die, die dann still falsch ist. */ +const ZUR_WAHL = 4; // alle aktiven ausser DogFather selbst d.prepare("INSERT INTO betreuung (creator_id, betreuer_id, seit) VALUES (?,?,?)").run(idLuna, idPat, jetzt); d.prepare("INSERT INTO scout_zuteilung (scout_id, manager_id, seit) VALUES (?,?,?)").run(idPat, idCig, jetzt); d.close(); @@ -139,13 +154,33 @@ console.log("\n=== Die Wahl aus Sicht von DogFather ==="); const knoepfe = await seite.$$("#dabei-wahl .k-dabei__knopf"); /* DIE ANZAHL IN DIE BEDINGUNG: Wären es null, würde jede folgende Messung über eine leere Menge laufen und grün melden. */ - ok(knoepfe.length === 3, - `${knoepfe.length} Personen zur Auswahl (erwartet 3 -- alle außer ihm selbst)`); + ok(knoepfe.length === ZUR_WAHL, + `${knoepfe.length} Personen zur Auswahl (erwartet ${ZUR_WAHL} -- alle außer ihm selbst)`); const gruppen = await seite.$$eval(".k-dabei__gruppe", (g) => g.map((x) => x.dataset.rolle)); - ok(gruppen.length === 3 && !gruppen.includes("admin"), + ok(gruppen.length === ZUR_WAHL && !gruppen.includes("admin"), `Gruppen: ${gruppen.join(", ") || "keine"} — er selbst ist die einzige admin-Person und steht nicht drin`); + /* ---- DIE REIHENFOLGE (Filipe, 11.09.2026) -------------------------- + + "rechte hand soll immer als erstes sein. ueberall." + + Zwei getrennte Fragen, und beide muessen gestellt werden: Ist sie + ueberhaupt da? Und steht sie vorn? Die erste allein war jahrelang + mit Nein zu beantworten, ohne dass es jemand sah. */ + ok(gruppen.includes("hand"), + `die rechte Hand steht ueberhaupt zur Wahl (${gruppen.join(", ")})`); + ok(gruppen[0] === "hand", + `und sie steht ganz vorn (erste Gruppe: ${gruppen[0]})`); + + /* GEGENPROBE: Alphabetisch stuende sie NICHT vorn. Ohne diese Zeile + waere "sie steht vorn" auch dann gruen, wenn gar nicht sortiert + wird -- die Namen kaemen zufaellig in der richtigen Folge. */ + const namen = await seite.$$eval("#dabei-wahl .k-dabei__name", (n) => n.map((x) => x.textContent.trim())); + const alphabetisch = [...namen].sort((a, b) => a.localeCompare(b, "de")); + ok(namen[0] === "VanVan" && alphabetisch[0] !== "VanVan", + `gezeichnet: ${namen.join(", ")} — alphabetisch waere ${alphabetisch.join(", ")}`); + /* DER KERN: Kleben Name und Zeichen? Gemessen wird der echte Abstand zwischen den zwei Kästchen, nicht der CSS-Wert. */ const abstand = await seite.evaluate(() => { @@ -168,8 +203,13 @@ console.log("\n=== Die Wahl aus Sicht von DogFather ==="); /* Die Rolle steht stattdessen über der Gruppe. */ const titel = await seite.$$eval(".k-dabei__titel", (t) => t.map((x) => x.textContent.trim())); - ok(titel.length === 3 && titel.some((t) => /Manager/.test(t)), + ok(titel.length === ZUR_WAHL && titel.some((t) => /Manager/.test(t)), `Gruppenüberschriften: ${titel.join(" · ")}`); + /* Die Ueberschrift der Team-Gruppe kommt vom SERVER -- diese Datei im + Browser darf den Rollennamen gar nicht kennen. Steht sie da, hat + der Weg funktioniert. */ + ok(/Team Dogi/.test(titel[0] || ""), + `und die erste heißt „${titel[0]}" — sie kommt vom Server, nicht aus dem Browser`); /* Daumengröße. 40 px ist die Grenze, die dieses Haus überall nutzt. */ const hoehe = await seite.$eval("#dabei-wahl .k-dabei__knopf", diff --git a/server/pruef-nachwuchs.mjs b/server/pruef-nachwuchs.mjs index 075ce039..3c90a845 100644 --- a/server/pruef-nachwuchs.mjs +++ b/server/pruef-nachwuchs.mjs @@ -647,6 +647,78 @@ melde("=== 11. Probe → Zugang ==="); ok(z?.rolle === "modi" && z?.aktiv === 1, "und er steht als aktiver Modi in der Datenbank"); } +/* ======================================================================= + 12. DIE REIHENFOLGE — NACH RANG, NICHT NACH NAMEN + + Filipe, 11.09.2026 mit Bildschirmfoto der Liste "Wen gehst du + durch?" (Diene, Dogi, VanVan — alphabetisch, die rechte Hand + ganz rechts): "rechte hand soll immer als erstes sein. ueberall. + ... ausser bei personen und zugaengen soll sie ueber jedem aber + unter dogfather sein." + + WARUM DIE NAMEN IM PRUEFSTAND SO GEWAEHLT SIND: Rieke (rechte Hand) + steht alphabetisch HINTER Frida, Mara und Nina. Waere die Sortierung + auch nur an einer Stelle wieder die alte, stuende sie dort hinten — + und die Pruefung wuerde es sehen. Mit einem Namen wie "Anna" waere + jede Zeile hier gruen, ohne irgendetwas zu messen. + ======================================================================= */ +melde(""); +melde("=== 12. Die Reihenfolge ==="); +{ + /* Die Quelle selbst: EINE Liste, und der SQL-Ausdruck daraus + abgeleitet. Zwei gepflegte Listen waeren die Stelle, an der es + beim naechsten Mal auseinanderlaeuft. */ + const w = await import("./workspace.js"); + const reihe = w.ROLLEN_REIHE; + ok(reihe.indexOf("hand") === reihe.indexOf("admin") + 1, + `die rechte Hand steht direkt hinter DogFather (${reihe.join(" > ")})`); + ok(reihe.every((r, i) => w.ROLLEN_SORTIERUNG.includes(`WHEN '${r}' THEN ${i}`)), + "der SQL-Ausdruck kennt jede Rolle mit genau ihrer Nummer"); + ok(w.ROLLEN_SORTIERUNG.split("WHEN").length - 1 === reihe.length, + `und keine darueber hinaus (${w.ROLLEN_SORTIERUNG.split("WHEN").length - 1} von ${reihe.length})`); + /* Die Aufrufer stellen "rolle" per .replace() auf "p.rolle" um -- + das trifft nur die erste Fundstelle. Kaeme das Wort zweimal vor, + waere die zweite Haelfte des Ausdrucks still falsch. */ + ok(w.ROLLEN_SORTIERUNG.split("rolle").length - 1 === 1, + "das Wort „rolle“ kommt darin genau einmal vor"); + ok(!w.ROLLEN_REIHE.includes("gast"), + "die Community steht nicht in der Rangfolge — sie faellt ans Ende"); + + /* ---- Die Liste aus dem Bildschirmfoto ---- */ + const lage = json(await roh("/workspace/api/entwicklung/lage", HAUS, { cookie: kDogi })); + const namen = (lage.personen || []).map((p) => p.name); + ok(namen.length >= 3, `${namen.length} Menschen in „Wen gehst du durch?“ (${namen.join(", ")})`); + ok(namen[0] === "Rieke", `die rechte Hand steht ganz links (${namen[0]})`); + /* GEGENPROBE, und ohne sie beweist die Zeile darueber nichts: + Alphabetisch stuende sie HINTEN. Dass beides verschieden ist, ist + der eigentliche Beweis, dass hier nach Rang sortiert wird. */ + const alphabetisch = [...namen].sort((a, b) => a.localeCompare(b, "de")); + ok(alphabetisch[0] !== "Rieke", + `alphabetisch stuende dort ${alphabetisch[0]} — die Reihenfolge ist also keine Namensfolge`); + + /* ---- Personen & Zugaenge: unter DogFather, ueber allen anderen ---- */ + const liste = json(await roh("/workspace/api/verwaltung/personen", HAUS, { cookie: kDogi })); + const alle = (liste.personen || []).map((p) => ({ name: p.name, rolle: p.rolle })); + ok(alle.length >= 4, `${alle.length} Menschen in „Personen & Zugaenge“`); + const platz = (rolle) => alle.findIndex((p) => p.rolle === rolle); + const vonHand = platz("hand"); + ok(vonHand >= 0, "die rechte Hand steht in der Liste"); + ok(platz("admin") >= 0 && platz("admin") < vonHand, + `DogFather steht ueber ihr (Platz ${platz("admin") + 1} vor ${vonHand + 1})`); + const darunter = alle.filter((p, i) => i > vonHand).map((p) => p.rolle); + const darueber = alle.filter((p, i) => i < vonHand).map((p) => p.rolle); + ok(darueber.every((r) => r === "admin" || r === "spicy"), + `ueber ihr steht nur die Leitung (${darueber.join(", ") || "niemand"})`); + ok(darunter.includes("manager") && darunter.includes("modi"), + `unter ihr stehen Manager und Modis (${[...new Set(darunter)].join(", ")})`); + + /* ---- Und dieselbe Regel im Haus nebenan ---- */ + const crew = json(await roh("/workspace/api/entwicklung/lage", CREW, { cookie: kHand })); + const crewNamen = (crew.personen || []).map((p) => p.name); + ok(crewNamen[0] === "Rieke", + `auch auf der Team-Adresse steht sie vorn (${crewNamen.join(", ")})`); +} + /* ------------------------------------------------------------------- */ melde(""); melde(`${geprueft} Pruefungen, ${fehler} Fehler`); diff --git a/server/pruef-rollen.mjs b/server/pruef-rollen.mjs index 5580cc59..17ade5df 100644 --- a/server/pruef-rollen.mjs +++ b/server/pruef-rollen.mjs @@ -49,7 +49,25 @@ await import("./index.js"); richtig fuer den Betrieb, fatal fuer eine Pruefung. Ohne diese Zeile bleibt der Prozess nach einem Fehler ewig stehen, weil der Server ihn am Leben haelt (siehe helfer-notbremse.mjs). */ -notbremse(480_000, "pruef-rollen"); +/* DIE FRIST MUSS ZUR ARBEIT PASSEN -- 900 s seit dem 11.09.2026. + + Diese Pruefung besucht JEDE Kachel JEDER Rolle einzeln und wartet + dabei auf networkidle. Ihre Dauer haengt also an der Zahl der + Kacheln, und die ist heute gewachsen: Die rechte Hand hat vier + dazubekommen (Entwicklung, Talente, Wer sieht was, Wie geht es dir). + + Bei 480 s ist der Lauf danach in die Notbremse gelaufen -- mitten im + Zaehlen, nichts haing. Gemessen am 11.09.2026 auf diesem Rechner: + 423 s fuer 315 Pruefungen, also 12 % Luft. Das ist zu knapp; eine + Notbremse, die bei normaler Arbeit ausloest, ist keine Notbremse, + sondern ein Fehlalarm -- und ein Fehlalarm, der regelmaessig kommt, + wird ueberlesen (siehe die Projektnotiz zur Loeschwarnung). + + 900 s sind gut das Doppelte der Messung. Wer hier wieder anstoesst, + soll NICHT die Zahl erhoehen, sondern zuerst nachsehen, ob der Lauf + noch vorankommt: eine Frist, die man dreimal hochsetzt, misst + irgendwann gar nichts mehr. */ +notbremse(900_000, "pruef-rollen"); await new Promise((r) => setTimeout(r, 700)); const BASIS = "http://127.0.0.1:4193"; setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 540_000).unref?.(); diff --git a/server/workspace-entwicklung.js b/server/workspace-entwicklung.js index e45fb860..9a97aefe 100644 --- a/server/workspace-entwicklung.js +++ b/server/workspace-entwicklung.js @@ -61,6 +61,7 @@ import express from "express"; import { db, sitzungLesen, protokolliere, echteIp, fuehrtTeamDogi, ROLLEN_NAME, + ROLLEN_SORTIERUNG, } from "./workspace.js"; import { ENTWICKLUNG_BLOECKE, ENTWICKLUNG_PUNKTE, ENTWICKLUNG_STUFEN, @@ -211,7 +212,8 @@ entwicklungRouter.get("/workspace/api/entwicklung/lage", (req, res) => { const platz = KARTEN_ROLLEN.map(() => "?").join(", "); const personen = leitung ? db().prepare(`SELECT id, name, rolle, erstellt FROM personen - WHERE rolle IN (${platz}) AND aktiv = 1 ORDER BY name COLLATE NOCASE`) + WHERE rolle IN (${platz}) AND aktiv = 1 + ORDER BY ${ROLLEN_SORTIERUNG}, name COLLATE NOCASE`) .all(...KARTEN_ROLLEN) : []; diff --git a/server/workspace-kalender.js b/server/workspace-kalender.js index 71266de8..274c8fb8 100644 --- a/server/workspace-kalender.js +++ b/server/workspace-kalender.js @@ -15,7 +15,7 @@ import express from "express"; import { db, protokolliere, echteIp, sitzungLesen, istLeitung, istDogFather, siehtAlles, betreutWo, termineSichtbar, heuteLokal, kategorienFuer, - externPruefen, externSql, betreuteIds, einladbareIds, verborgeneIds, ROLLEN_SORTIERUNG, + externPruefen, externSql, betreuteIds, einladbareIds, verborgeneIds, ROLLEN_SORTIERUNG, ROLLEN_GRUPPE, } from "./workspace.js"; import { nachfuellenAlle, serienPruefen, serieAnlegen, zuordnungErzwingen, @@ -265,10 +265,15 @@ kalenderRouter.get("/workspace/api/einladbar", (req, res) => { tritt ein, sobald jemand niemandem zugeordnet ist. */ if (ids !== null && !ids.length) return res.json({ personen: [] }); + /* DIE UEBERSCHRIFT KOMMT MIT (11.09.2026) -- wie beim Chat. Ohne + sie kann der Browser eine Gruppe nicht beschriften, deren + Rollennamen er gar nicht kennen darf, und liess sie deshalb + einfach weg. Team Dogi stand in dieser Auswahl nie. */ res.json({ personen: db().prepare( `SELECT id, name, rolle FROM personen WHERE aktiv = 1${wo} - ORDER BY ` + ROLLEN_SORTIERUNG + ", name").all(...werte), + ORDER BY ` + ROLLEN_SORTIERUNG + ", name").all(...werte) + .map((p) => ({ ...p, gruppe: ROLLEN_GRUPPE[p.rolle] || "Weitere" })), }); } catch (fehler) { console.error("[workspace] Einladbar:", fehler?.message); diff --git a/server/workspace.js b/server/workspace.js index 21098b6b..32b00ecb 100644 --- a/server/workspace.js +++ b/server/workspace.js @@ -69,10 +69,32 @@ const VERSUCHE_MAX = 8; // pro IP const VERSUCHE_FENSTER_MIN = 10; const ROLLEN = new Set(["spicy", "admin", "manager", "scout", "creator", "hand", "modi", "gast"]); -/* Die Reihenfolge, in der Rollen ueberall erscheinen: Spicy Media - zuerst, dann DogFather, Manager, Scout, Creator. Steht hier einmal, - damit keine Liste eine eigene Reihenfolge erfindet. */ -export const ROLLEN_REIHE = ["spicy", "admin", "manager", "scout", "creator", "hand", "modi"]; +/* DIE EINE REIHENFOLGE, IN DER ROLLEN UEBERALL ERSCHEINEN. + + Sie steht hier einmal, damit keine Liste eine eigene erfindet. + + DIE RECHTE HAND STEHT AN ZWEITER STELLE (Filipe, 11.09.2026: + "rechte hand soll immer als erstes sein. ueberall. ... ausser bei + personen und zugaengen soll sie ueber jedem aber unter dogfather + sein. ich will dass auch ueberall immer nach der hirarchie + gearbeitet wird."). + + EINE ZAHL STATT ZWEIER REGELN: Er hat zwei Saetze gesagt, aber es + braucht nur eine Reihenfolge. In den Team-Listen kommt DogFather + gar nicht vor (er beurteilt, er wird nicht beurteilt) -- dort steht + sie damit automatisch ganz vorn. In "Personen & Zugaenge" steht er + drin, also steht sie dort hinter ihm. Zwei Sonderfaelle waeren zwei + Stellen, an denen es auseinanderlaufen kann. + + SPICY MEDIA BLEIBT DAVOR -- seine ausdrueckliche Entscheidung auf + Nachfrage am 11.09.2026. Die Agentur ist keine Stufe in seinem Team, + sondern steht daneben; an Rechten aendert die Reihenfolge ohnehin + nichts, sie sortiert nur Listen. + + 'gast' (Community) steht mit Absicht NICHT in der Liste: Sie faellt + dadurch ans Ende (siehe ELSE unten), und das ist richtig -- die + Community ist keine Stufe im Team. */ +export const ROLLEN_REIHE = ["spicy", "admin", "hand", "manager", "scout", "creator", "modi"]; /* DIE ROLLEN MIT EINER KACHEL AUF DER ANMELDESEITE. @@ -132,10 +154,22 @@ const CREW_KACHEL = new Set(["admin", "hand", "modi", "gast"]); /* Als SQL-Ausdruck fuer ORDER BY. "ORDER BY rolle" waere alphabetisch (admin, creator, manager, scout, spicy) -- also fast genau falsch - herum. */ -export const ROLLEN_SORTIERUNG = - "CASE rolle WHEN 'spicy' THEN 0 WHEN 'admin' THEN 1 WHEN 'manager' THEN 2" - + " WHEN 'scout' THEN 3 WHEN 'creator' THEN 4 WHEN 'hand' THEN 5 ELSE 6 END"; + herum. + + ABGELEITET UND NICHT ABGESCHRIEBEN (11.09.2026). Bis heute stand die + Reihenfolge zweimal da: einmal als Liste, einmal als CASE mit + Zahlen. Beim Hochziehen der rechten Hand haetten beide geaendert + werden muessen -- und eine gepflegte Liste, die mit einer zweiten + uebereinstimmen muss, ist genau die Bauart, an der im Projekt schon + dreimal etwas verlorengegangen ist (zuletzt drei Spalten beim + Tabellenumbau). Eine Liste, die niemand pflegt, kann nicht veralten. + + Das Wort "rolle" kommt hier genau EINMAL vor -- in "CASE rolle". + Darauf verlassen sich die Aufrufer, die es per .replace() auf + "p.rolle" umstellen; kein Rollenname enthaelt die Zeichenfolge. */ +export const ROLLEN_SORTIERUNG = "CASE rolle " + + ROLLEN_REIHE.map((r, i) => `WHEN '${r}' THEN ${i}`).join(" ") + + ` ELSE ${ROLLEN_REIHE.length} END`; /* LEITUNG = DogFather und Manager. Ein Manager darf alles, was DogFather darf -- mit genau zwei Ausnahmen, die in diff --git a/workspace/app.webmanifest b/workspace/app.webmanifest index 19191867..c03beffe 100644 --- a/workspace/app.webmanifest +++ b/workspace/app.webmanifest @@ -13,25 +13,25 @@ "theme_color": "#06090f", "icons": [ { - "src": "/assets/img/app-symbole/workspace-192.png?v=202609112313", + "src": "/assets/img/app-symbole/workspace-192.png?v=202609112358", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-512.png?v=202609112313", + "src": "/assets/img/app-symbole/workspace-512.png?v=202609112358", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609112313", + "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609112358", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609112313", + "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609112358", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/workspace/assets/js/bereiche.js b/workspace/assets/js/bereiche.js index 22d4c30a..27f8a4a0 100644 --- a/workspace/assets/js/bereiche.js +++ b/workspace/assets/js/bereiche.js @@ -998,6 +998,9 @@ /* Die Reihenfolge, in der Rollen überall erscheinen -- gleiche Begründung, dieselbe Falle: kalender.js hatte seine eigene, ohne Spicy Media, und sortierte sie damit ans Ende. */ + /* ROLLENFOLGE sagt, welche Rollen diese Datei NENNEN darf -- nicht + mehr, in welcher Reihenfolge Menschen erscheinen. Die kommt seit + dem 11.09.2026 vom Server (siehe gruppieren weiter unten). */ const ROLLENFOLGE = ['spicy', 'admin', 'manager', 'scout', 'creator']; /* DIE NAMEN DER ROLLEN -- ebenfalls an EINER Stelle (07.09.2026). @@ -1023,8 +1026,50 @@ scout: 'Scouts', creator: 'Creator', }; + /* ===================================================================== + PERSONEN IN GRUPPEN — IN DER REIHENFOLGE, DIE DER SERVER SCHICKT + (11.09.2026) + + Filipe, mit Bildschirmfoto: "rechte hand soll immer als erstes + sein. ueberall. ... ich will dass auch ueberall immer nach der + hirarchie gearbeitet wird." + + WARUM DAS NICHT EINE ZEILE IM SERVER WAR: Der Server sortierte + schon richtig. Drei Auswahllisten im Browser (Chat, Kalender, + Sicht-Umschalter) haben seine Reihenfolge aber wieder verworfen + und nach ROLLENFOLGE neu gezeichnet — einer Liste mit fuenf + Agentur-Rollen. Team Dogi kommt darin nicht vor und DARF es nicht + (diese Datei laedt jeder herunter). Folge: die rechte Hand stand + unten, in einem Nachzuegler-Block, oder — im Kalender — gar nicht. + + DAS IST DERSELBE FEHLER ZUM VIERTEN MAL. Chat (10.09.), Personen- + liste (10.09.), Sicht-Umschalter (11.09.) und jetzt der Kalender: + jedes Mal eine zweite Reihenfolge neben der des Servers, jedes Mal + verschwindet jemand lautlos. Deshalb hier keine vierte Reparatur, + sondern eine Stelle: Wer Menschen gruppiert, nimmt diese Funktion. + + Eine Map behaelt die Einfuegereihenfolge — die Gruppen erscheinen + also in der Reihenfolge ihres ranghoechsten Mitglieds, ohne dass + diese Datei einen einzigen Rang kennen muss. + + @param {Array} personen wie vom Server geliefert, bereits sortiert + @returns {Array<{titel: string, rolle: string, leute: Array}>} */ + function gruppieren(personen) { + const nach = new Map(); + for (const p of personen || []) { + /* Der Server schickt die Ueberschrift mit. Der Rueckfall auf + ROLLEN_GRUPPE gilt den Wegen, die das noch nicht tun; "Weitere" + ist die letzte Reissleine — lieber eine blasse Ueberschrift als + ein Mensch, der nicht gezeichnet wird. */ + const titel = p.gruppe || ROLLEN_GRUPPE[p.rolle] || 'Weitere'; + if (!nach.has(titel)) nach.set(titel, []); + nach.get(titel).push(p); + } + return [...nach].map(([titel, leute]) => ({ titel, rolle: leute[0].rolle, leute })); + } + window.Bereiche = { ZEICHEN, GRUPPEN, zuSeite, zeichenBauen, merkzeichenBauen, - LEITUNG, ROLLENFOLGE, ROLLEN_NAME, ROLLEN_GRUPPE, + LEITUNG, ROLLENFOLGE, ROLLEN_NAME, ROLLEN_GRUPPE, gruppieren, }; })(); diff --git a/workspace/assets/js/chat.js b/workspace/assets/js/chat.js index c533c5a7..f8adcb0c 100644 --- a/workspace/assets/js/chat.js +++ b/workspace/assets/js/chat.js @@ -1108,38 +1108,20 @@ ziel.append(gruppe); } - const gezeichnet = new Set(); - for (const rolle of window.Bereiche.ROLLENFOLGE) { - const leute = personen.filter((p) => p.rolle === rolle); - gezeichnet.add(rolle); - if (!leute.length) continue; - gruppeBauen(rolle, window.Bereiche.ROLLEN_GRUPPE[rolle], leute); - } + /* EINE SCHLEIFE, NICHT ZWEI (11.09.2026). - /* UND DANN ALLE UEBRIGEN (10.09.2026). + Bis heute standen hier zwei: erst die fünf Rollen aus + ROLLENFOLGE, dann „alle übrigen". Das hielt zwar niemanden mehr + verborgen — aber es sortierte Team Dogi zwangsläufig ans Ende, + egal was der Server sagte. Die rechte Hand stand damit hinter + jedem Creator. - Genau die Lücke, vor der der Kommentar oben warnt -- sie war - offen. `ROLLENFOLGE` hat fünf Einträge; wer eine sechste Rolle - hat, wurde von der Schleife darüber nie erfasst und verschwand - lautlos aus der Auswahl. Der Server schickte ihn mit, der - Browser zeichnete ihn nicht, und niemand konnte sehen, dass - etwas fehlt. - - Aufgefallen ist es beim HINSEHEN, nicht beim Lesen: Ein frisch - angelegter Kanal hatte hinterher zwei Leute statt vier. - - Die Überschrift kommt vom Server -- so kann hier auch eine Rolle - stehen, deren Namen diese Datei gar nicht kennen darf. Nach - Überschrift gruppiert und nicht nach Rolle: Wer einen Menschen - sucht, sucht ihn in einer Liste, nicht in einer Rangordnung. */ - const uebrig = personen.filter((p) => !gezeichnet.has(p.rolle)); - const nachTitel = new Map(); - for (const p of uebrig) { - const t = p.gruppe || 'Weitere'; - if (!nachTitel.has(t)) nachTitel.set(t, []); - nachTitel.get(t).push(p); - } - for (const [titel, leute] of nachTitel) gruppeBauen(leute[0].rolle, titel, leute); + Jetzt zeichnet der Browser in der Reihenfolge, die er bekommt. + Wer die Rangfolge ändern will, ändert sie an EINER Stelle + (ROLLEN_REIHE im Server) — und nicht in drei Auswahllisten. */ + const gruppen = window.Bereiche?.gruppieren?.(personen) + || [{ titel: 'Alle', rolle: '', leute: personen }]; + for (const g of gruppen) gruppeBauen(g.rolle, g.titel, g.leute); wahlGeaendert(); } diff --git a/workspace/assets/js/kalender.js b/workspace/assets/js/kalender.js index afc1d8da..bb56f04b 100644 --- a/workspace/assets/js/kalender.js +++ b/workspace/assets/js/kalender.js @@ -117,15 +117,16 @@ let alleLeute = []; const ROLLENKURZ = window.Bereiche.ROLLEN_NAME; - /* Mehrzahl für die Gruppenüberschriften. "Scout" über vier Namen - liest sich falsch -- über der Gruppe steht, was die Leute SIND. */ - const ROLLENGRUPPE = window.Bereiche.ROLLEN_GRUPPE; - /* Auch diese Liste steht in bereiche.js -- sie hatte denselben - Mangel wie die Leitungsliste: kein 'spicy'. Eine unbekannte Rolle - landet bei indexOf() auf -1 und damit VOR allen anderen; Spicy - Media stand dadurch in der Teilnehmerwahl ganz oben statt an - ihrem Platz. Kein Absturz, nur still falsch. */ - const ROLLENFOLGE = window.Bereiche.ROLLENFOLGE; + /* DIE GRUPPENUEBERSCHRIFT UND IHRE REIHENFOLGE STANDEN HIER (bis + 11.09.2026). Beides macht jetzt window.Bereiche.gruppieren(): Die + Ueberschrift kommt vom Server mit, die Reihenfolge auch. + + Der alte Kommentar an dieser Stelle ist trotzdem lehrreich und + deshalb aufgehoben: Eine unbekannte Rolle landete bei indexOf() + auf -1 und damit VOR allen anderen -- Spicy Media stand in der + Teilnehmerwahl ganz oben statt an ihrem Platz. Kein Absturz, nur + still falsch. Genau diese Sorte Fehler kann eine Reihenfolge, die + niemand im Browser pflegt, nicht mehr machen. */ /** Ein bis zwei Buchstaben, die als Zeichen im Kreis stehen. * "Filipe Fernandes Ribeiro" -> "FR", "Luna" -> "L". @@ -161,9 +162,21 @@ return; } - for (const rolle of ROLLENFOLGE) { - const leute = alleLeute.filter((p) => p.rolle === rolle); - if (!leute.length) continue; // leere Gruppe gar nicht erst zeigen + /* HIER FEHLTE TEAM DOGI GANZ (11.09.2026). + + Diese Schleife lief über ROLLENFOLGE — fünf Agentur-Rollen. Wer + eine sechste hat, wurde nicht gezeichnet: kein Fehler, keine + Lücke, kein Hinweis. Anders als im Chat gab es hier nicht einmal + einen Nachzügler-Block. DogFather konnte seine rechte Hand und + seine Modis also zu keinem Termin einladen, und auf dem + Bildschirm sah das vollkommen normal aus. + + Gefunden beim Aufräumen der Reihenfolge, nicht beim Suchen. */ + const gruppen = window.Bereiche?.gruppieren?.(alleLeute) + || [{ titel: 'Alle', rolle: '', leute: alleLeute }]; + for (const g of gruppen) { + const rolle = g.rolle; + const leute = g.leute; const gruppe = document.createElement('div'); gruppe.className = 'k-dabei__gruppe'; @@ -174,7 +187,7 @@ const punkt = document.createElement('span'); punkt.className = 'k-dabei__punkt'; punkt.setAttribute('aria-hidden', 'true'); - titel.append(punkt, document.createTextNode(ROLLENGRUPPE[rolle] || rolle)); + titel.append(punkt, document.createTextNode(g.titel)); const liste = document.createElement('div'); liste.className = 'k-dabei__liste'; @@ -182,7 +195,7 @@ soll die Überschrift dazu ansagen, sonst hört man neun Namen ohne zu wissen, wovon. */ liste.setAttribute('role', 'group'); - liste.setAttribute('aria-label', ROLLENGRUPPE[rolle] || rolle); + liste.setAttribute('aria-label', g.titel); for (const p of leute) { const b = document.createElement('button'); diff --git a/workspace/assets/js/kopf.js b/workspace/assets/js/kopf.js index 7f881fc6..30a3d847 100644 --- a/workspace/assets/js/kopf.js +++ b/workspace/assets/js/kopf.js @@ -1233,18 +1233,11 @@ stand "UNDEFINED" ueber einem Namen) und die Personenliste. Jedes Mal wurde die zentrale Fassung gepflegt und die Kopie vergessen. - Deshalb steht hier keine Liste mehr, sondern eine Ableitung: - `ROLLENFOLGE` gibt die Reihenfolge, `ROLLEN_GRUPPE` die - Ueberschrift. Kommt morgen eine sechste Rolle dazu, ist sie hier - ohne eine Zeile Arbeit dabei. - - ERST BEIM BAUEN GELESEN, nicht beim Laden dieser Datei. bereiche.js - steht auf allen achtzehn Seiten davor und beide tragen `defer`, die - Reihenfolge stimmt also -- aber eine Liste, die beim Laden - stillschweigend leer bliebe, waere ein Ausfall, den niemand sieht. - Zum Zeitpunkt des Bauens ist die Seite fertig geladen. */ - const rollenGruppen = () => (window.Bereiche?.ROLLENFOLGE || []) - .map((r) => [r, window.Bereiche?.ROLLEN_GRUPPE?.[r] || r]); + Seit dem 11.09.2026 steht hier gar keine Liste mehr -- auch keine + Ableitung. Die Reihenfolge und die Ueberschriften kommen vom + Server; `window.Bereiche.gruppieren()` setzt sie nur noch um. + Das ist die Fassung, die auch dann stimmt, wenn eine Rolle + dazukommt, deren Namen diese Datei gar nicht kennen darf. */ /* EIN WETTLAUF, den es wirklich gab: Auf profil.html rufen zwei Dateien werZeigen() auf (profil.js und steckbrief.js). Beide kamen an der @@ -1305,43 +1298,23 @@ nichts und machte den Knopf so breit, dass am Handy nichts anderes mehr danebenpasste. */ s.append(new Option('Meine Sicht', '')); - const gezeichnet = new Set(); - for (const [rolle, name] of rollenGruppen()) { - gezeichnet.add(rolle); - const drin = personen.filter((p) => p.rolle === rolle && p.id !== ich.id && p.aktiv !== false); - if (!drin.length) continue; + /* IN DER REIHENFOLGE DES SERVERS (11.09.2026). + + Vorher zwei Durchgaenge: erst die fuenf Rollen aus bereiche.js, + dann "alle uebrigen". Niemand fehlte mehr -- aber Team Dogi stand + zwangslaeufig ganz unten, hinter jedem Creator. Filipe wollte die + rechte Hand ueberall vorn haben, und "ueberall" heisst auch hier. + + Die Ueberschrift kommt vom Server, so kann in dieser Auswahl auch + eine Rolle stehen, deren Namen diese Datei gar nicht kennen + darf. */ + const sichtbar = personen.filter((p) => p.id !== ich.id && p.aktiv !== false); + const gruppen = window.Bereiche?.gruppieren?.(sichtbar) + || [{ titel: 'Alle', rolle: '', leute: sichtbar }]; + for (const gruppe of gruppen) { const g = document.createElement('optgroup'); - g.label = name; - for (const p of drin) g.append(new Option(p.name, String(p.id))); - s.append(g); - } - - /* UND ALLE UEBRIGEN (11.09.2026). - - Filipe: "oben bei meine sicht soll ich auch die sicht von allen - jeden moment sehen koennen." - - Er konnte es nicht: `rollenGruppen()` kommt aus bereiche.js und - kennt fuenf Rollen. Wer eine sechste hat, stand nicht in der - Auswahl -- kein Fehler, keine Luecke, kein Hinweis. Das ist - DIESELBE Lücke wie in der Chat-Auswahl und in der Personenliste, - zum dritten Mal an einer dritten Stelle. - - Die Ueberschrift kommt deshalb vom Server (`p.gruppe`), und was - hier ankommt, wird gezeichnet -- auch eine Rolle, deren Namen - diese Datei gar nicht kennen darf. */ - const uebrig = personen.filter( - (p) => !gezeichnet.has(p.rolle) && p.id !== ich.id && p.aktiv !== false); - const nachTitel = new Map(); - for (const p of uebrig) { - const titel = p.gruppe || 'Weitere'; - if (!nachTitel.has(titel)) nachTitel.set(titel, []); - nachTitel.get(titel).push(p); - } - for (const [titel, leute] of nachTitel) { - const g = document.createElement('optgroup'); - g.label = titel; - for (const p of leute) g.append(new Option(p.name, String(p.id))); + g.label = gruppe.titel; + for (const p of gruppe.leute) g.append(new Option(p.name, String(p.id))); s.append(g); } if (aktiv) s.value = String(aktiv); diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 2a176a0b..c5a93202 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -12,19 +12,19 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - + + + + + + - +
@@ -252,10 +252,10 @@ - - - - - + + + + +