diff --git a/server/pruef-css-klassen.mjs b/server/pruef-css-klassen.mjs index cdaf1410..8b013e35 100644 --- a/server/pruef-css-klassen.mjs +++ b/server/pruef-css-klassen.mjs @@ -519,9 +519,34 @@ console.log("\n=== Rollenlisten: eine Quelle, nicht neun"); eigenbau.push(`${n}: [${rollen.join(", ")}]`); } } + /* UND DIESELBE SUCHE FUER ROLLEN-OBJEKTE (07.09.2026). + + Die Liste oben findet Felder und Mengen. Die Namen der Rollen + stehen aber als OBJEKT da (`{ admin: 'DogFather', ... }`), und + genau in dieser Form ist der Fehler ein zweites Mal aufgetreten: + fuenf Skripte, keines mit 'spicy'. Im Chat stand ueber Cigdems + Namen woertlich "UNDEFINED". + + Ein Muster, das nur eine Schreibweise kennt, findet auch nur eine. + Deshalb hier die zweite. */ + for (const n of dateien) { + if (n === "bereiche.js" || n === "gate.js") continue; // Quelle bzw. Anmeldeseite + const roh = readFileSync(join(jsOrdner, n), "utf8"); + const text = roh.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " "); + for (const m of text.matchAll(/\{[^{}]*\badmin\s*:\s*['"][^'"]+['"][^{}]*\}/g)) { + if (!/\bspicy\s*:/.test(m[0])) eigenbau.push(`${n}: ${m[0].slice(0, 44)}…`); + } + } + ok(eigenbau.length === 0, eigenbau.length ? `eigene Rollenliste gefunden: ${eigenbau.slice(0, 4).join(" · ")}` - : "kein Skript baut sich eine eigene Rollenliste"); + : "kein Skript baut sich eine eigene Rollenliste oder ein eigenes Rollen-Objekt"); + + /* GEGENPROBE fuer die Objektform -- sie muss so eines auch finden. */ + const objProbe = "const X = { admin: 'DogFather', manager: 'Manager' };"; + ok([...objProbe.matchAll(/\{[^{}]*\badmin\s*:\s*['"][^'"]+['"][^{}]*\}/g)] + .some((m) => !/\bspicy\s*:/.test(m[0])), + "und ein Rollen-Objekt ohne 'spicy' wird auch wirklich gefunden"); /* GEGENPROBE: Der Sucher muss so eine Liste auch WIRKLICH finden -- sonst wäre die Zeile darüber grün, weil das Muster nie greift. */ diff --git a/server/pruef-spicy.mjs b/server/pruef-spicy.mjs index 6671045b..7876516c 100644 --- a/server/pruef-spicy.mjs +++ b/server/pruef-spicy.mjs @@ -268,8 +268,18 @@ const ruf = async (art, weg, keks, koerper) => { } { /* NICHT: die Personenverwaltung. Anlegen ja, alles andere nein. */ - const { status } = await ruf("GET", "/workspace/api/verwaltung/personen", keksSpicy); - ok(status === 404, `die Personenverwaltung bleibt zu (HTTP ${status})`); + const { status, daten } = await ruf("GET", "/workspace/api/verwaltung/personen", keksSpicy); + /* AM 07.09.2026 GETEILT: Die LISTE ist fuer sie offen, alles andere + nicht. Hier stand vorher ein einziges "bleibt zu" -- das ist seit + Filipes Wunsch "spicy soll genau das sehen koennen" zu grob. Aus + einer Aussage werden drei, und die dritte ist die wichtigste: + Ueberblick ist nicht Verwaltung. */ + ok(status === 200, `die Personenliste ist fuer sie offen (HTTP ${status})`); + const listeRollen = (daten?.personen || []).map((x) => x.rolle); + ok(listeRollen.length > 0 && !listeRollen.includes("admin"), + `und DogFather steht nicht darin (${listeRollen.join(", ") || "leer"})`); + const prot = await ruf("GET", "/workspace/api/verwaltung/protokoll?anzahl=5", keksSpicy); + ok(prot.status === 404, `das Protokoll bleibt zu (HTTP ${prot.status})`); const loeschen = await ruf("DELETE", `/workspace/api/verwaltung/personen/${idLuna}`, keksSpicy); ok(loeschen.status === 404, `loeschen geht nicht (HTTP ${loeschen.status})`); const code = await ruf("POST", `/workspace/api/verwaltung/personen/${idLuna}/code`, keksSpicy); @@ -443,6 +453,24 @@ const ruf = async (art, weg, keks, koerper) => { const dash = await ruf("GET", "/workspace/api/uebersicht", keksSpicy); ok(dash.status === 200, `ihre Uebersicht kommt trotzdem (${dash.status})`); + /* IM CHAT IST SIE FUER JEDEN ERREICHBAR (07.09.2026). + + Filipe: "im chat muss die spicy rolle und die dogfather rolle fuer + jeden zugaenglich sein." Gemessen aus der Sicht eines CREATORS -- + der steht am Ende jeder Kette und ist damit der Prueffall: Wer bei + ihm ankommt, kommt ueberall an. + + Mit Gegenprobe: Seine Liste ist nicht einfach jeder. Waere sie das, + bewiese die Zeile darueber nichts. */ + const keksLuna = await anmelden("creator", "CODE-LUNA-0001"); + const partner = await ruf("GET", "/workspace/api/chat/partner", keksLuna); + const rollenDrin = (partner.daten?.personen || []).map((x) => x.rolle); + ok(rollenDrin.includes("spicy"), + `ein Creator erreicht Spicy Media im Chat (${rollenDrin.join(", ") || "niemanden"})`); + ok(rollenDrin.includes("admin"), "und DogFather ebenfalls"); + ok(!rollenDrin.includes("creator"), + "einen anderen Creator dagegen nicht -- die Liste ist nicht einfach jeder"); + /* Und DogFather bleibt der Einzige mit dem letzten Wort. */ const { status } = await ruf("GET", "/workspace/api/verwaltung/personen", keksDogi); ok(status === 200, `DogFather kommt weiterhin in die Verwaltung (HTTP ${status})`); @@ -469,7 +497,38 @@ console.log("\n=== Die Anmeldeseite ==="); ok(kacheln.includes("Personen & Zugänge"), `sie sieht die Personen-Kachel (${kacheln.length} Kacheln)`); ok(!kacheln.includes("Automationen"), "und die Automationen-Kachel NICHT"); + + /* DIE PERSONENLISTE: Spicy Media sieht alle Abschnitte AUSSER + DogFather (07.09.2026, Wunsch Filipe). Geprueft an den + Ueberschriften, die wirklich auf dem Bildschirm stehen -- nicht an + der Liste im Quelltext, die sie erzeugt. */ + await seite.goto(BASIS + "/workspace/personen.html", { waitUntil: "networkidle" }); + await seite.waitForTimeout(1200); + const abschnitteSpicy = await seite.$$eval(".gruppe__name, .gruppe__kopf", + (k) => k.map((x) => x.textContent.trim()).filter(Boolean)); + const alsText = abschnitteSpicy.join(" | "); + ok(!/DogFather/.test(alsText), + `bei ihr fehlt der Abschnitt DogFather (${alsText.slice(0, 70) || "keine Abschnitte"})`); await ctx.close(); + + /* GEGENPROBE BEI DOGFATHER: Bei ihm steht der Abschnitt "Spicy Media" + sehr wohl -- genau das hat Filipe vermisst. Ohne diese Zeile + bewiese die Zeile darueber nur, dass die Liste leer ist. */ + const ctx2 = await browser.newContext({ viewport: { width: 1440, height: 950 } }); + const seite2 = await ctx2.newPage(); + await seite2.goto(BASIS + "/workspace/", { waitUntil: "networkidle" }); + await seite2.click('.rolle[data-rolle="admin"]'); + await seite2.fill("#code", "CODE-DOGI-0001"); + await seite2.click("button[type=submit]"); + await seite2.waitForURL("**/start.html", { timeout: 25000 }); + await seite2.goto(BASIS + "/workspace/personen.html", { waitUntil: "networkidle" }); + await seite2.waitForTimeout(1400); + const abschnitteDogi = (await seite2.$$eval(".gruppe__name, .gruppe__kopf", + (k) => k.map((x) => x.textContent.trim()).filter(Boolean))).join(" | "); + ok(/Spicy Media/.test(abschnitteDogi), + `bei DogFather steht der Abschnitt "Spicy Media" (${abschnitteDogi.slice(0, 80)})`); + ok(/DogFather/.test(abschnitteDogi), "und seiner ebenfalls"); + await ctx2.close(); await browser.close(); } diff --git a/server/workspace-personen.js b/server/workspace-personen.js index ddfce9ca..80d4b102 100644 --- a/server/workspace-personen.js +++ b/server/workspace-personen.js @@ -15,7 +15,7 @@ import express from "express"; import { - db, protokolliere, echteIp, sitzungLesen, personAnlegen, codeNeu, sitzungToken, personSperren, betreuungSetzen, scoutZuteilungSetzen, istLeitung, istDogFather, siehtAlles, ROLLEN_SORTIERUNG, ROLLEN_REIHE, + db, protokolliere, echteIp, sitzungLesen, personAnlegen, codeNeu, sitzungToken, personSperren, betreuungSetzen, scoutZuteilungSetzen, istLeitung, istDogFather, siehtAlles, ROLLEN_SORTIERUNG, ROLLEN_REIHE, istSpicy, } from "./workspace.js"; import { sicherungJetzt } from "./workspace-sicherung.js"; @@ -48,6 +48,35 @@ const NAME_MAX = 60; function nurAdmin(req, res, next) { const person = sitzungLesen(req); if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" }); + + /* EINE EINZIGE AUSNAHME: die LISTE fuer Spicy Media (07.09.2026). + + Filipe: "spicy soll genau das sehen koennen, aber nur nicht die + rolle dogfather." Bis hierher sah die Rolle auf der Personenseite + nur das Anlegen-Formular. + + Die Ausnahme steht HIER und nicht als eigene Schicht davor. Der + erste Versuch war genau das -- eine Route vor `use(nurAdmin)`, die + mit `next("route")` weiterreicht. Das tut aber das Gegenteil von + dem, wonach es klingt: `next("route")` ueberspringt die restlichen + Handler DIESER Route und geht zur naechsten passenden Schicht -- + also zu nurAdmin. Spicy Media bekam weiter 404, die Oberflaeche + verstand das als "nicht erlaubt" und schickte sie auf die + Startseite zurueck. Gemessen: Auf personen.html standen die + Kategorien der STARTSEITE. + + Nur LESEN, nur diese eine Adresse, nur diese eine Rolle. Codes, + Sperren, Loeschen, Zuteilung und Protokoll bleiben bei DogFather -- + Ueberblick ist nicht Verwaltung. Und DogFathers eigene Zeile faellt + unten aus dem Ergebnis (`req.ohneDogFather`). */ + const nurListe = req.method === "GET" + && req.baseUrl + req.path === "/workspace/api/verwaltung/personen"; + if (istSpicy(person) && nurListe) { + req.person = person; + req.ohneDogFather = true; + return next(); + } + if (!istDogFather(person)) return res.status(404).json({ fehler: "nicht_gefunden" }); req.person = person; next(); @@ -106,6 +135,11 @@ function nurDogFatherBeiLeitung(req, res, next) { Zeichenkette genau diese -- der Server startete danach nicht mehr.) */ personenRouter.get("/workspace/api/verwaltung/personen", (req, res) => { try { + /* Fuer Spicy Media ohne DogFather -- gefiltert am ERGEBNIS und + nicht in der Abfrage: Die Abfrage ist lang und wird von mehreren + Feldern gelesen; eine zusaetzliche Bedingung mittendrin waere die + Stelle, an der beim naechsten Umbau jemand danebengreift. */ + const ohne = req.ohneDogFather === true; res.json({ personen: db().prepare(` SELECT p.id, p.name, p.rolle, p.aktiv, p.erstellt, p.letzter_login, @@ -127,7 +161,8 @@ personenRouter.get("/workspace/api/verwaltung/personen", (req, res) => { FROM personen p LEFT JOIN betreuung b ON b.creator_id = p.id LEFT JOIN scout_zuteilung sz ON sz.scout_id = p.id - ORDER BY ${ROLLEN_SORTIERUNG.replace("rolle", "p.rolle")}, p.aktiv DESC, p.name`).all(), + ORDER BY ${ROLLEN_SORTIERUNG.replace("rolle", "p.rolle")}, p.aktiv DESC, p.name`) + .all().filter((z) => !(ohne && z.rolle === "admin")), /* Wer ueberhaupt als zustaendig eingetragen werden kann. Bis zum 31.08.2026 waren das nur Scouts. In der Auswahl stand diff --git a/server/workspace.js b/server/workspace.js index 3a5ab057..3003f0a6 100644 --- a/server/workspace.js +++ b/server/workspace.js @@ -2273,6 +2273,30 @@ export function schreibbareIds(person) { } catch { /* im Zweifel nur die Betreuungskette */ } } + /* DOGFATHER UND SPICY MEDIA SIND FUER JEDEN ERREICHBAR (07.09.2026). + + Filipe: "im chat muss die spicy rolle und die dogfather rolle fuer + jeden zugaenglich sein." + + DogFather kam bisher ueber einladbareIds() mit hinein; Spicy Media + nicht -- die Rolle steht in keiner Betreuungskette, sie steht + daneben. Genau deshalb war sie fuer einen Creator im Chat gar nicht + vorhanden, obwohl sie fuer alle zustaendig ist. + + Beide werden hier ausdruecklich hinzugefuegt statt sich auf einen + Nebeneffekt zu verlassen: Eine Zustaendigkeit, die nur zufaellig + aus einer anderen Regel herausfaellt, faellt beim naechsten Umbau + genauso zufaellig wieder heraus. + + Die Gegenrichtung stimmt ohne Zutun: Beide Rollen haben + `siehtAlles` und damit ohnehin `null` = jeder. */ + try { + for (const z of db().prepare( + "SELECT id FROM personen WHERE rolle IN ('admin','spicy') AND aktiv = 1").all()) { + basis.add(z.id); + } + } catch { /* im Zweifel bleibt die Betreuungskette */ } + /* Sich selbst nicht -- ein Gespräch mit sich allein ist keines. */ basis.delete(person.id); return [...basis]; diff --git a/workspace/assets/css/personen.css b/workspace/assets/css/personen.css index 038e5c64..3f505af5 100644 --- a/workspace/assets/css/personen.css +++ b/workspace/assets/css/personen.css @@ -333,3 +333,20 @@ body[data-nur-anlegen="ja"] #liste, body[data-nur-anlegen="ja"] .protokoll-block, body[data-nur-anlegen="ja"] #rest-schalter { display: none; } + +/* SPICY MEDIA LIEST DIE LISTE, VERWALTET SIE ABER NICHT (07.09.2026). + + Sie bekommt die Liste (ohne DogFather) -- und deshalb muss die Liste + bei ihr wieder sichtbar sein, obwohl `nur-anlegen` gesetzt ist. Was + sie NICHT bekommt, sind die Knoepfe daran: Code neu erzeugen, + sperren, loeschen, zuteilen. Die haengen am Server an `nurAdmin` und + wuerden bei ihr mit 404 antworten. + + DAS AUSBLENDEN IST KEINE SICHERUNG. Es erspart ihr nur Knoepfe, die + nichts tun -- die Sperre steht im Server. Diese Unterscheidung steht + hier ausdruecklich, weil sie beim naechsten Umbau leicht verrutscht. */ +body[data-nur-lesen="ja"] #liste { display: block; } +body[data-nur-lesen="ja"] .person__knoepfe, +body[data-nur-lesen="ja"] .person__code, +body[data-nur-lesen="ja"] .zuteilung, +body[data-nur-lesen="ja"] select { display: none; } diff --git a/workspace/assets/css/start.css b/workspace/assets/css/start.css index 9feb2f55..b989f4cc 100644 --- a/workspace/assets/css/start.css +++ b/workspace/assets/css/start.css @@ -1040,16 +1040,16 @@ body.start > main { position: relative; z-index: 1; } Drei Tiefenlagen sind gemessen: bei zwei sieht es aus wie ein Druckfehler, ab fünf wie ein Schlagschatten. */ -.kachel__svg .zeichen__tiefe3, -.kachel__svg .zeichen__tiefe2, -.kachel__svg .zeichen__tiefe1 { +:is(.kachel__svg, .dran__svg) .zeichen__tiefe3, +:is(.kachel__svg, .dran__svg) .zeichen__tiefe2, +:is(.kachel__svg, .dran__svg) .zeichen__tiefe1 { stroke: none; } -.kachel__svg .zeichen__tiefe3 { fill: rgba(0, 0, 0, .55); } -.kachel__svg .zeichen__tiefe2 { fill: url(#zeichen-wand); opacity: .85; } -.kachel__svg .zeichen__tiefe1 { fill: url(#zeichen-wand); } +:is(.kachel__svg, .dran__svg) .zeichen__tiefe3 { fill: rgba(0, 0, 0, .55); } +:is(.kachel__svg, .dran__svg) .zeichen__tiefe2 { fill: url(#zeichen-wand); opacity: .85; } +:is(.kachel__svg, .dran__svg) .zeichen__tiefe1 { fill: url(#zeichen-wand); } -.kachel__svg .zeichen__deck { +:is(.kachel__svg, .dran__svg) .zeichen__deck { fill: url(#zeichen-deck); stroke: none; } @@ -1058,7 +1058,7 @@ body.start > main { position: relative; z-index: 1; } hellen Deckfläche liest man beide zusammen als eingravierte Rille, auf der dunklen Plakette verschwindet er und nur die helle Linie bleibt. */ -.kachel__svg .zeichen__saum { +:is(.kachel__svg, .dran__svg) .zeichen__saum { fill: none; stroke: rgba(6, 10, 16, .78); stroke-width: 3.4; @@ -1066,7 +1066,7 @@ body.start > main { position: relative; z-index: 1; } } /* Die kuehle Lichtkante. Duenner als die Kontur und ohne Fuellung -- sie soll die Kante andeuten, nicht eine zweite Zeichnung sein. */ -.kachel__svg .zeichen__rand { +:is(.kachel__svg, .dran__svg) .zeichen__rand { fill: none; /* BREITER als die Kontur darueber (2,7 gegen 1,9) und um ein Drittel Rasterpunkt versetzt -- nur so schaut die Lichtkante oben links @@ -1077,7 +1077,7 @@ body.start > main { position: relative; z-index: 1; } } .kachel__wasserzeichen .zeichen__rand { display: none; } -.kachel__svg .zeichen__linie { +:is(.kachel__svg, .dran__svg) .zeichen__linie { stroke: url(#zeichen-linie); /* 2,05 statt 1,65: Die Zeichen sollen TRAGEN, nicht andeuten. Eine dünne Linie wirkt neben kräftigem Text zaghaft -- und zaghaft war @@ -1123,7 +1123,7 @@ body.start > main { position: relative; z-index: 1; } /* Ohne Farben trägt allein die Linie. Der Schein wäre dort ein grauer Klecks über der Zeichnung. */ .kachel__svg { filter: none; } - .kachel__svg .zeichen__linie { stroke: CanvasText; } + :is(.kachel__svg, .dran__svg) .zeichen__linie { stroke: CanvasText; } } .kachel__text { position: relative; z-index: 1; display: grid; gap: 3px; min-width: 0; } @@ -1328,21 +1328,67 @@ body.start > main { position: relative; z-index: 1; } padding: 11px 15px 11px 17px; color: var(--text); text-decoration: none; font-size: .9rem; } +/* Und das Feld darum ist dasselbe gefasste Schild wie auf den + Kacheln -- abgeschnittene Ecke, Metallring, Farbe innen. Eine + Oberflaeche, eine Sprache. */ .dran__zeichen { + --z-fase: 7px; + position: relative; display: grid; place-items: center; - width: 30px; height: 30px; border-radius: 9px; - border: 1px solid color-mix(in srgb, var(--ton) 30%, transparent); - background: color-mix(in srgb, var(--ton) 13%, transparent); - transition: transform .18s ease, background .18s ease; + width: 34px; height: 34px; + padding: 2px; + border: 0; border-radius: 0; + clip-path: polygon(var(--z-fase) 0, 100% 0, 100% 100%, 0 100%, 0 var(--z-fase)); + background: + conic-gradient(from 210deg, + #b9c8da 0deg, #5a6b80 44deg, #d6e2ef 88deg, #44536a 140deg, + #aebdd0 196deg, #64758c 244deg, #cddaea 296deg, #4e5d73 340deg, + #b9c8da 360deg); + box-shadow: + 0 4px 12px -7px rgba(0, 0, 0, .9), + 0 0 0 1px rgba(6, 10, 18, .5), + 0 8px 26px -12px color-mix(in srgb, var(--ton) 70%, transparent); + transition: transform .18s ease; } +.dran__zeichen::before { + content: ""; position: absolute; inset: 2px; z-index: 0; + clip-path: polygon( + calc(var(--z-fase) - 2px) 0, 100% 0, 100% 100%, 0 100%, + 0 calc(var(--z-fase) - 2px)); + background: + linear-gradient(150deg, rgba(255, 255, 255, .18) 0%, transparent 50%), + radial-gradient(120% 100% at 22% 8%, + color-mix(in srgb, var(--ton) 52%, transparent) 0%, + color-mix(in srgb, var(--ton) 22%, transparent) 50%, + color-mix(in srgb, var(--ton) 8%, transparent) 100%), + linear-gradient(#0a0f18, #0a0f18); +} +.dran__svg { position: relative; z-index: 1; } +/* DIE ZEILENSYMBOLE SIND DIESELBEN SYMBOLE (07.09.2026). + + Filipe: "die symbole sollen auch wie die anderen symbole sein." + + Sie wurden vom selben Bauer erzeugt (`zeichenBauen`), bekamen aber + nie dessen Gestaltung: Alle Lagenregeln waren auf `.kachel__svg` + eingegrenzt, und dieses Zeichen heisst `.dran__svg`. Uebrig blieb + eine flache Kontur in einer Farbe -- daneben auf derselben Seite + dieselben Zeichen mit drei Lichtern, Tiefe und Randlicht. + + Die Regeln gelten jetzt fuer beide Traeger. Wer ein drittes Zeichen + einbaut, traegt es dort ein; die Alternative waere, die Gestaltung + ein zweites Mal zu beschreiben, und zwei Beschreibungen derselben + Sache laufen immer auseinander. + + Groesser ist es auch: 16 -> 20 px im 30er-Feld, damit die drei + Lichter ueberhaupt Platz haben. */ .dran__svg { - width: 16px; height: 16px; - fill: none; stroke: var(--ton); stroke-width: 1.7; + width: 20px; height: 20px; + color: var(--ton); + fill: none; stroke-linecap: round; stroke-linejoin: round; } .dran__link:hover .dran__zeichen { - transform: scale(1.07); - background: color-mix(in srgb, var(--ton) 20%, transparent); + transform: translateY(-1px) scale(1.05); } /* Die Zahl steht vorn und in gleicher Zeichenbreite: Beim Ueberfliegen von sieben Zeilen liest man sie zuerst, und sie sollen untereinander @@ -3283,12 +3329,12 @@ body[data-fremde-sicht="ja"]::after { /* Die Spiegelung liegt auf demselben Körper -- ein schmaler heller Streifen quer über die obere Hälfte, wie Licht auf gewölbtem Glas. */ -.kachel__svg .zeichen__glanz { +:is(.kachel__svg, .dran__svg) .zeichen__glanz { fill: url(#zeichen-glanz); stroke: none; transition: opacity var(--tempo); } -.kachel:hover .kachel__svg .zeichen__glanz { opacity: 1.35; } +.kachel:hover :is(.kachel__svg, .dran__svg) .zeichen__glanz { opacity: 1.35; } /* Im Wasserzeichen NICHT -- dort liegt das Zeichen riesig hinter dem Text der Kachel. Eine Fläche würde ihn hinterlegen. */ @@ -3300,8 +3346,8 @@ body[data-fremde-sicht="ja"]::after { } @media (forced-colors: active) { /* Ohne Farben würde die Fläche zu einem massiven Block. */ - .kachel__svg .zeichen__fuellung, - .kachel__svg .zeichen__glanz { display: none; } + :is(.kachel__svg, .dran__svg) .zeichen__fuellung, + :is(.kachel__svg, .dran__svg) .zeichen__glanz { display: none; } } /* ---------- Material statt Zeichnung (03.09.2026, zweite Stufe) ---------- @@ -3348,7 +3394,7 @@ body[data-fremde-sicht="ja"]::after { } @media (forced-colors: active) { - .kachel__svg .zeichen__eigenschatten, + :is(.kachel__svg, .dran__svg) .zeichen__eigenschatten, .kachel__zeichen::after { display: none; } .kachel__svg { filter: none; } } diff --git a/workspace/assets/js/bereiche.js b/workspace/assets/js/bereiche.js index f576fa83..2f8e85b6 100644 --- a/workspace/assets/js/bereiche.js +++ b/workspace/assets/js/bereiche.js @@ -918,8 +918,31 @@ Spicy Media, und sortierte sie damit ans Ende. */ const ROLLENFOLGE = ['spicy', 'admin', 'manager', 'scout', 'creator']; + /* DIE NAMEN DER ROLLEN -- ebenfalls an EINER Stelle (07.09.2026). + + Sie standen als Objekt in fuenf Skripten: chat.js zweimal, + dateien.js, kalender.js zweimal. In keinem davon 'spicy'. Im Chat + stand ueber Cigdems Namen deshalb woertlich "UNDEFINED" -- Filipe + hat es im Bildschirmfoto geschickt. + + Es ist derselbe Fehler wie bei der Leitungsliste heute Frueh, nur + in anderer Form: damals ein Set, hier ein Objekt. Deshalb sucht + `pruef-css-klassen.mjs` jetzt auch nach Rollen-OBJEKTEN. + + Zwei Schreibweisen, weil beide gebraucht werden: die kurze fuer + Namensschilder ("Scout"), die Gruppenform fuer Ueberschriften + ueber Listen ("Scouts"). */ + const ROLLEN_NAME = { + spicy: 'Spicy Media', admin: 'DogFather', manager: 'Manager', + scout: 'Scout', creator: 'Creator', + }; + const ROLLEN_GRUPPE = { + spicy: 'Spicy Media', admin: 'DogFather', manager: 'Manager', + scout: 'Scouts', creator: 'Creator', + }; + window.Bereiche = { ZEICHEN, GRUPPEN, zuSeite, zeichenBauen, merkzeichenBauen, - LEITUNG, ROLLENFOLGE, + LEITUNG, ROLLENFOLGE, ROLLEN_NAME, ROLLEN_GRUPPE, }; })(); diff --git a/workspace/assets/js/chat.js b/workspace/assets/js/chat.js index 11f18d94..8940d98b 100644 --- a/workspace/assets/js/chat.js +++ b/workspace/assets/js/chat.js @@ -37,7 +37,9 @@ return k; }; - const ROLLENKURZ = { admin: 'DogFather', manager: 'Manager', scout: 'Scout', creator: 'Creator' }; + /* Aus der einen Quelle. Stand hier als eigenes Objekt und hatte + 'spicy' nicht -- siehe bereiche.js. */ + const ROLLENKURZ = window.Bereiche.ROLLEN_NAME; let ich = null; let raeume = []; @@ -509,7 +511,7 @@ const punkt = el('span', 'chat-wahl__punkt'); punkt.setAttribute('aria-hidden', 'true'); titel.append(punkt, document.createTextNode( - { admin: 'DogFather', manager: 'Manager', scout: 'Scouts', creator: 'Creator' }[rolle])); + window.Bereiche.ROLLEN_GRUPPE[rolle])); gruppe.append(titel); const liste = el('div', 'chat-wahl__liste'); diff --git a/workspace/assets/js/dateien.js b/workspace/assets/js/dateien.js index 2f176274..c7a4d3f4 100644 --- a/workspace/assets/js/dateien.js +++ b/workspace/assets/js/dateien.js @@ -91,7 +91,7 @@ : 'Niemand ausgewählt – dann sieht sie nur DogFather.'; } - const ROLLENKURZ = { admin: 'DogFather', manager: 'Manager', scout: 'Scout', creator: 'Creator' }; + const ROLLENKURZ = window.Bereiche.ROLLEN_NAME; /* ---------- Hochladen -------------------------------------------------- */ diff --git a/workspace/assets/js/kalender.js b/workspace/assets/js/kalender.js index 4f4e0920..279ea0b4 100644 --- a/workspace/assets/js/kalender.js +++ b/workspace/assets/js/kalender.js @@ -85,12 +85,10 @@ Fehlklick die ganze Auswahl und ist am Handy kaum zu bedienen. */ let alleLeute = []; - const ROLLENKURZ = { admin: 'DogFather', manager: 'Manager', scout: 'Scout', creator: 'Creator' }; + 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 = { - admin: 'DogFather', manager: 'Manager', scout: 'Scouts', creator: 'Creator', - }; + 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 diff --git a/workspace/assets/js/personen.js b/workspace/assets/js/personen.js index 82092295..986dee08 100644 --- a/workspace/assets/js/personen.js +++ b/workspace/assets/js/personen.js @@ -24,8 +24,7 @@ Liste stand dann schlicht „spicy" statt „Spicy Media". Ein Auffangwert, der halbwegs plausibel aussieht, ist der beste Weg, eine Luecke jahrelang zu behalten. */ - const ROLLENNAME = { spicy: 'Spicy Media', admin: 'DogFather', - manager: 'Manager', scout: 'Scout', creator: 'Creator' }; + const ROLLENNAME = window.Bereiche.ROLLEN_NAME; /* Reihenfolge und Erklaerung an EINER Stelle. Sie gilt fuer die Rollenwahl im Formular und fuer alles, was sonst Rollen auflistet. */ @@ -547,7 +546,16 @@ Eine Rolle ohne Personen wird weggelassen. Eine leere Überschrift ist kein Ordnungsmerkmal, sondern eine Lücke. */ + /* SPICY MEDIA FEHLTE HIER (07.09.2026). Die Rolle gibt es seit heute + Frueh, die Personenliste kannte sie nicht -- ein Spicy-Zugang stand + damit in KEINEM Abschnitt und war in der Liste unsichtbar, obwohl + der Server ihn geliefert hat. Filipe hat es gemeldet: "bei personen + & zugangscode soll auch die kategorie spicy der rolle dogfather + angezeigt werden." + + Die Reihenfolge folgt bereiche.js (Spicy Media ueber DogFather). */ const ROLLEN_ABSCHNITTE = [ + { rolle: 'spicy', name: 'Spicy Media', unter: 'Überblick & Zugänge anlegen' }, { rolle: 'admin', name: 'DogFather', unter: 'Gesamtübersicht & Freigaben' }, { rolle: 'manager', name: 'Manager', unter: 'Betreuung & Freigaben' }, { rolle: 'scout', name: 'Scout', unter: 'Eigene Pipeline & Kontakte' }, @@ -558,7 +566,21 @@ const ziel = $('liste'); ziel.textContent = ''; + /* SPICY MEDIA SIEHT ALLES AUSSER DOGFATHER (07.09.2026). + + Filipe: "spicy soll genau das sehen koennen, aber nur nicht die + rolle dogfather." + + Das ist dieselbe Grenze, die im ganzen Haus gilt: Die Rolle hat + den Ueberblick ueber Manager, Scouts und Creator -- DogFathers + eigene Sachen gehen sie nichts an. Hier faellt deshalb genau + dieser eine Abschnitt weg, nicht mehr und nicht weniger. + + DAS IST KEINE SICHERUNG, sondern Aufraeumen: Der Server liefert + ihr die Liste ohnehin nur, wenn sie sie sehen darf. Eine + ausgeblendete Zeile hat noch nie etwas geschuetzt. */ const vorhanden = ROLLEN_ABSCHNITTE + .filter((a) => !(a.rolle === 'admin' && ich?.rolle === 'spicy')) .map((a) => ({ ...a, drin: alle.filter((p) => p.rolle === a.rolle) })) .filter((a) => a.drin.length); @@ -641,7 +663,17 @@ ziel.textContent = ''; ziel.setAttribute('aria-busy', 'false'); listeZeigen(); - await protokollLaden(); + /* DAS PROTOKOLL BLEIBT BEI DOGFATHER. Fuer Spicy Media antwortet + es mit 404 -- und `hole()` versteht ein 404 unter + `/verwaltung/` als "nicht erlaubt" und schickt zur Startseite + zurueck. Genau so hat der Aufruf sie von der Personenseite + geworfen, kaum dass die Liste da war: Die Seite baute sich auf + und sprang im naechsten Atemzug weg. Gemessen -- auf + personen.html standen die Kategorien der STARTSEITE. + + Eine Abfrage, von der man weiss, dass sie 404 gibt, stellt man + nicht. */ + if (ich.rolle === 'admin') await protokollLaden(); } catch { /* umgeleitet */ } } @@ -764,14 +796,29 @@ 'Hier legst du neue Leute an. Codes, Sperren und das Protokoll bleiben bei DogFather.'; zeile.hidden = false; } - /* UND DIE LISTE HÖRT AUF ZU LADEN. Sie steht mit - aria-busy="true" in der Seite, und `laden()` -- das es wieder - abschaltet -- wird für sie mit Absicht nie aufgerufen. Damit - meldete die Seite einem Screenreader dauerhaft "wird geladen", - und es kam nie etwas. Ein Ladehinweis, der nie endet, ist - schlimmer als keiner. */ + /* SPICY MEDIA BEKOMMT DIE LISTE -- ZUM LESEN (07.09.2026). + + Filipe: "spicy soll genau das sehen koennen, aber nur nicht die + rolle dogfather." Bis hierher sah die Rolle auf dieser Seite + nur das Anlegen-Formular; die Liste wurde bewusst nicht + geladen, weil die Verwaltungsschnittstelle zu war. Sie ist es + weiterhin -- aber die LISTE ist seit heute offen, ohne + DogFathers Zeile (siehe workspace-personen.js). + + Der Manager bekommt sie NICHT: Fuer ihn gilt unveraendert + "anlegen, nicht verwalten". Deshalb steht hier die Rolle und + nicht die Leitung. */ const liste = $('liste'); - if (liste) { + if (ich.rolle === 'spicy') { + document.body.dataset.nurLesen = 'ja'; + laden(); + } else if (liste) { + /* Fuer alle anderen hoert die Liste auf zu laden. Sie steht mit + aria-busy="true" in der Seite, und `laden()` wird fuer sie + nicht aufgerufen -- ohne diese Zeilen meldete die Seite einem + Vorleseprogramm dauerhaft "wird geladen", und es kam nie + etwas. Ein Ladehinweis, der nie endet, ist schlimmer als + keiner. */ liste.setAttribute('aria-busy', 'false'); liste.textContent = ''; } diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index add8f26e..d08a8232 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -9,10 +9,10 @@ - - - - + + + + @@ -212,10 +212,10 @@ - - - - - + + + + + diff --git a/workspace/automation.html b/workspace/automation.html index 83918deb..11889dfa 100644 --- a/workspace/automation.html +++ b/workspace/automation.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -135,10 +135,10 @@ - - - - - + + + + + diff --git a/workspace/bereich.html b/workspace/bereich.html index b10b44ad..9b6a4ed4 100644 --- a/workspace/bereich.html +++ b/workspace/bereich.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -144,14 +144,14 @@ - - - - - - - - - + + + + + + + + + diff --git a/workspace/calls.html b/workspace/calls.html index 2e99a8dc..49401d0d 100644 --- a/workspace/calls.html +++ b/workspace/calls.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -77,11 +77,11 @@ - - - - - - + + + + + + diff --git a/workspace/chat.html b/workspace/chat.html index 255e5e37..f4831cc5 100644 --- a/workspace/chat.html +++ b/workspace/chat.html @@ -9,17 +9,17 @@ - - + + - - - + + + @@ -155,10 +155,10 @@ - - - - - + + + + + diff --git a/workspace/content.html b/workspace/content.html index f37ceb9b..7dd45a3c 100644 --- a/workspace/content.html +++ b/workspace/content.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -167,12 +167,12 @@ - - - - - - - + + + + + + + diff --git a/workspace/dateien.html b/workspace/dateien.html index fd27f35e..01c6d5ff 100644 --- a/workspace/dateien.html +++ b/workspace/dateien.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -96,10 +96,10 @@ - - - - - + + + + + diff --git a/workspace/index.html b/workspace/index.html index 56750c37..7d09f4ee 100644 --- a/workspace/index.html +++ b/workspace/index.html @@ -27,7 +27,7 @@ - + @@ -202,6 +202,6 @@ - + diff --git a/workspace/kalender.html b/workspace/kalender.html index e95eae43..b5bfdfa3 100644 --- a/workspace/kalender.html +++ b/workspace/kalender.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -258,11 +258,11 @@ - - - - - - + + + + + + diff --git a/workspace/leistung.html b/workspace/leistung.html index 3e5c43bc..d2390bcb 100644 --- a/workspace/leistung.html +++ b/workspace/leistung.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -212,10 +212,10 @@ - - - - - + + + + + diff --git a/workspace/personen.html b/workspace/personen.html index c3525be3..3fbbb495 100644 --- a/workspace/personen.html +++ b/workspace/personen.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -137,10 +137,10 @@ - - - - - + + + + + diff --git a/workspace/profil.html b/workspace/profil.html index 57b234f1..4313d6e7 100644 --- a/workspace/profil.html +++ b/workspace/profil.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -202,11 +202,11 @@ - - - - - - + + + + + + diff --git a/workspace/report.html b/workspace/report.html index a9e0d609..cfc7b058 100644 --- a/workspace/report.html +++ b/workspace/report.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -94,11 +94,11 @@ - - - - - - + + + + + + diff --git a/workspace/scouting.html b/workspace/scouting.html index 46ad1f3c..0a6cb789 100644 --- a/workspace/scouting.html +++ b/workspace/scouting.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -127,10 +127,10 @@ - - - - - + + + + + diff --git a/workspace/start.html b/workspace/start.html index eb09c5c5..212d75f9 100644 --- a/workspace/start.html +++ b/workspace/start.html @@ -9,12 +9,12 @@ - - + + - - + + @@ -320,10 +320,10 @@

- - - - - + + + + + diff --git a/workspace/startcheck.html b/workspace/startcheck.html index 98ff7282..cd643b61 100644 --- a/workspace/startcheck.html +++ b/workspace/startcheck.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -73,11 +73,11 @@ - - - - - - + + + + + + diff --git a/workspace/steckbrief.html b/workspace/steckbrief.html index 71046e1a..d98bc20e 100644 --- a/workspace/steckbrief.html +++ b/workspace/steckbrief.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -114,10 +114,10 @@ - - - - - + + + + + diff --git a/workspace/uebersicht.html b/workspace/uebersicht.html index 829c2e9e..eacd4cbc 100644 --- a/workspace/uebersicht.html +++ b/workspace/uebersicht.html @@ -9,13 +9,13 @@ - - + + - - - + + + @@ -137,11 +137,11 @@ - + - - - - + + + + diff --git a/workspace/wissen.html b/workspace/wissen.html index 3387b013..88557531 100644 --- a/workspace/wissen.html +++ b/workspace/wissen.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -178,10 +178,10 @@ - - - - - + + + + +