diff --git a/server/pruef-creator-anlegen.mjs b/server/pruef-creator-anlegen.mjs index b846559a..354ff552 100644 --- a/server/pruef-creator-anlegen.mjs +++ b/server/pruef-creator-anlegen.mjs @@ -97,6 +97,9 @@ let idDogi, idMax, idMia, idScoutMax, idScoutMia, idCreator; idScoutMax = anlegen(d, "SamVonMax", "scout", "CODE-SAM-00001"); idScoutMia = anlegen(d, "TimVonMia", "scout", "CODE-TIM-00001"); idCreator = anlegen(d, "Luna", "creator", "CODE-LUNA-0001"); + /* Seit dem 10.09.2026 gehoert Spicy Media dazu: Sie darf Manager UND + Scouts anlegen, und genau das steht im Abschnitt 6. */ + anlegen(d, "Agentur", "spicy", "CODE-SPIC-0001"); d.prepare("INSERT INTO scout_zuteilung (scout_id, manager_id, seit) VALUES (?,?,?)") .run(idScoutMax, idMax, jetzt); d.prepare("INSERT INTO scout_zuteilung (scout_id, manager_id, seit) VALUES (?,?,?)") @@ -118,6 +121,7 @@ const keksMax = await anmelden("manager", "CODE-MAX-00001"); const keksMia = await anmelden("manager", "CODE-MIA-00001"); const keksScout = await anmelden("scout", "CODE-SAM-00001"); const keksLuna = await anmelden("creator", "CODE-LUNA-0001"); +const keksSpicy = await anmelden("spicy", "CODE-SPIC-0001"); const ruf = async (art, weg, keks, koerper) => { const a = await fetch(BASIS + weg, { method: art, @@ -301,6 +305,145 @@ console.log("\n=== Der Knopf auf dem Dashboard ==="); await browser.close(); } +/* ======================================================================= + 6. SPICY MEDIA LEGT MANAGER UND SCOUTS AN (10.09.2026) + + Filipe, mit Bildschirmfoto: "die spicy rolle soll auch manager und + scouts hinzufuegen koennen." + + Der Fehler davor war kein fehlendes Recht, sondern ZWEI Listen, die + einander widersprachen: Die Oberflaeche erlaubte Spicy Media in einer + Zeile den Manager und nahm ihn drei Zeilen spaeter wieder weg. Fuer + Scouts gab es ueberhaupt keine Tuer. + + Deshalb wird hier BEIDES geprueft — dass es geht, und dass die + Oberflaeche dieselbe Antwort bekommt wie der Server. Eine der beiden + Pruefungen allein haette den Fehler nicht gefunden: Serverseitig war + der Manager ja die ganze Zeit erlaubt. + ======================================================================= */ +console.log("\n=== Spicy Media legt Manager und Scouts an ==="); +{ + const { status, daten } = await ruf("POST", "/workspace/api/manager-anlegen", + keksSpicy, { name: "NeuerManager" }); + ok(status === 201, `ein Manager entsteht (HTTP ${status})`); + ok(daten?.rolle === "manager", `und ist wirklich ein Manager (${daten?.rolle})`); +} +{ + const { status, daten } = await ruf("POST", "/workspace/api/scout-anlegen", + keksSpicy, { name: "NeuerScout" }); + ok(status === 201, `ein Scout entsteht (HTTP ${status})`); + /* DIE ROLLE IST HIER DER GANZE PUNKT. Vor dem Umbau waere die + Oberflaeche mit "scout" beim Creator-Weg gelandet, und der legt + IMMER einen Creator an -- der Knopf haette Erfolg gemeldet und das + Falsche getan. */ + ok(daten?.rolle === "scout", `und ist wirklich ein Scout (${daten?.rolle})`); +} +{ + /* Die Rolle kommt NICHT aus dem Aufruf: Wer sie mitschickt, aendert + nichts. Die Tuer kennt genau eine. */ + const { status, daten } = await ruf("POST", "/workspace/api/scout-anlegen", + keksSpicy, { name: "MoechtegernChef", rolle: "admin" }); + ok(status === 201 && daten?.rolle === "scout", + `ein mitgeschicktes "rolle: admin" bleibt wirkungslos (${daten?.rolle})`); +} +{ + const { status } = await ruf("POST", "/workspace/api/scout-anlegen", + keksMax, { name: "ScoutVonMax" }); + ok(status === 404, `ein Manager kommt durch diese Tuer nicht (HTTP ${status})`); +} +{ + const { status } = await ruf("POST", "/workspace/api/scout-anlegen", + keksScout, { name: "ScoutVomScout" }); + ok(status === 404, `ein Scout auch nicht (HTTP ${status})`); +} +/* DIE AUSNAHME, DIE ES WIRKLICH GIBT — und ihre Grenze. + + Erster Anlauf dieser Pruefung behauptete, Spicy Media komme gar nicht + an /workspace/api/verwaltung. Das war falsch, und die Pruefung wurde + zu Recht rot: `nurAdmin` laesst genau EINEN Fall durch, das LESEN der + Personenliste ("spicy soll genau das sehen koennen"). Ueberblick ist + nicht Verwaltung — und genau diese Trennung gehoert festgenagelt, + sonst weitet sie sich beim naechsten Umbau unbemerkt aus. */ +{ + const { status } = await ruf("GET", "/workspace/api/verwaltung/personen", keksSpicy); + ok(status === 200, `die Personenliste darf Spicy Media LESEN (HTTP ${status})`); +} +{ + const { status } = await ruf("POST", "/workspace/api/verwaltung/personen", + keksSpicy, { name: "AufDemKurzenWeg", rolle: "admin" }); + ok(status === 404, `aber nicht darueber anlegen (HTTP ${status})`); + const z = new DatabaseSync(DB).prepare( + "SELECT rolle FROM personen WHERE name = ?").get("AufDemKurzenWeg"); + ok(!z, "und es entsteht dabei auch niemand"); +} + +/* ---- Und die Oberflaeche fragt dieselbe Stelle ---------------------- */ +{ + const { daten } = await ruf("GET", "/workspace/api/ich", keksSpicy); + const darf = daten?.darf_anlegen || []; + ok(darf.includes("manager") && darf.includes("scout") && darf.includes("creator"), + `/api/ich nennt Spicy Media: ${darf.join(", ")}`); + /* DAS WICHTIGERE: was NICHT drinsteht. Ein Knopf zu viel waere hier + schlimmer als einer zu wenig -- er verspraeche ein Recht, das es + nicht gibt. */ + ok(!darf.includes("admin"), "und keinen DogFather"); +} +{ + const { daten } = await ruf("GET", "/workspace/api/ich", keksMax); + const darf = daten?.darf_anlegen || []; + ok(darf.length === 1 && darf[0] === "creator", + `ein Manager bekommt genau eine Rolle genannt: ${darf.join(", ") || "keine"}`); +} +{ + const { daten } = await ruf("GET", "/workspace/api/ich", keksScout); + const darf = daten?.darf_anlegen || []; + ok(darf.length === 0, `ein Scout bekommt keine genannt (${darf.length})`); +} + +/* ---- Die Knoepfe auf der Zugaenge-Seite ----------------------------- */ +console.log("\n=== Die Rollenknoepfe auf der Zugaenge-Seite ==="); +{ + const pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs"); + const browser = await pw.chromium.launch(); + const knoepfeFuer = async (rolle, code) => { + const ctx = await browser.newContext({ viewport: { width: 1400, height: 900 } }); + const seite = await ctx.newPage(); + await seite.goto(BASIS + "/workspace/", { waitUntil: "networkidle" }); + await seite.click(`.rolle[data-rolle="${rolle}"]`); + await seite.fill("#code", code); + await seite.click("button[type=submit]"); + await seite.waitForURL("**/start.html", { timeout: 25000 }); + await seite.goto(BASIS + "/workspace/personen.html", { waitUntil: "networkidle" }); + await seite.waitForTimeout(1600); + const liste = await seite.$$eval(".rollenwahl__knopf", + (ks) => ks.map((k) => k.dataset.rolle)).catch(() => []); + await ctx.close(); + return liste; + }; + const spicy = await knoepfeFuer("spicy", "CODE-SPIC-0001"); + ok(spicy.includes("manager"), `Spicy Media sieht den Manager-Knopf (${spicy.join(", ")})`); + ok(spicy.includes("scout"), "und den Scout-Knopf"); + ok(spicy.includes("creator"), "und den Creator-Knopf"); + ok(!spicy.includes("admin"), "aber keinen DogFather-Knopf"); + /* GEGENPROBE: Die Zaehlung erkennt einen fehlenden Knopf wirklich. + Beim Manager MUSS genau einer stehen -- stuenden dort drei, waere + die Messung oben wertlos. */ + const managerKnoepfe = await knoepfeFuer("manager", "CODE-MAX-00001"); + ok(managerKnoepfe.length === 1 && managerKnoepfe[0] === "creator", + `ein Manager sieht genau einen: ${managerKnoepfe.join(", ")}`); + /* UND BEI DOGFATHER AENDERT SICH NICHTS. Er bekommt jetzt eine + laengere Liste vom Server (auch die verborgenen Rollen), die + Knopfliste der Seite kennt aber nur die fuenf sichtbaren -- der + Schnitt der beiden ist derselbe wie vorher. Das ist eine Behauptung + ueber MEINE Aenderung, also gehoert sie gemessen und nicht + geglaubt. */ + const dogiKnoepfe = await knoepfeFuer("admin", "CODE-DOGI-0001"); + ok(dogiKnoepfe.length >= 5 && ["spicy", "admin", "manager", "scout", "creator"] + .every((r) => dogiKnoepfe.includes(r)), + `DogFather sieht weiterhin alle fuenf: ${dogiKnoepfe.join(", ")}`); + await browser.close(); +} + kind.kill(); await new Promise((r) => setTimeout(r, 400)); try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Zugabe */ } diff --git a/server/workspace-personen.js b/server/workspace-personen.js index 05c65d82..937b077f 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, istSpicy, verborgeneIds, TEAM_DOGI_ROLLEN, + db, protokolliere, echteIp, sitzungLesen, personAnlegen, codeNeu, sitzungToken, personSperren, betreuungSetzen, scoutZuteilungSetzen, istLeitung, istDogFather, siehtAlles, ROLLEN_SORTIERUNG, ROLLEN_REIHE, istSpicy, verborgeneIds, TEAM_DOGI_ROLLEN, darfAnlegen, } from "./workspace.js"; import { sicherungJetzt } from "./workspace-sicherung.js"; @@ -520,27 +520,61 @@ personenRouter.post("/workspace/api/creator-anlegen", gleicheHerkunft, nurLeitun EINEN MANAGER LEGT MAN NICHT NEBENBEI AN: Er sieht danach jeden Creator, den er betreut, und legt selbst welche an. Deshalb bleibt diese Tuer Spicy Media und DogFather vorbehalten -- ein Manager kann - keinen zweiten Manager schaffen. */ -personenRouter.post("/workspace/api/manager-anlegen", gleicheHerkunft, (req, res) => { - try { - const person = sitzungLesen(req); - if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" }); - if (!siehtAlles(person)) return res.status(404).json({ fehler: "nicht_gefunden" }); + keinen zweiten Manager schaffen. - const name = String(req.body?.name ?? "").trim(); - if (name.length < 2) return res.status(400).json({ fehler: "Name fehlt." }); - if (name.length > NAME_MAX) return res.status(400).json({ fehler: "Name ist zu lang." }); - const vorhanden = db().prepare( - "SELECT 1 FROM personen WHERE lower(name) = lower(?) AND aktiv = 1").get(name); - if (vorhanden) return res.status(409).json({ fehler: "Diesen Namen gibt es schon." }); + NACHTRAG 10.09.2026 — JETZT AUCH SCOUTS, UND TROTZDEM ZWEI TUEREN. - const neu = personAnlegen(name, "manager", { ...person, ip: echteIp(req) }); - res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code }); - } catch (fehler) { - console.error("[workspace] Manager anlegen:", fehler?.message); - res.status(503).json({ fehler: "nicht_verfuegbar" }); - } -}); + Filipe: "die spicy rolle soll auch manager und scouts hinzufuegen + koennen." Fuer Scouts gab es bis heute ueberhaupt keinen Weg ausser + ueber DogFather. + + Der naheliegende Griff waere gewesen, hier ein Feld `rolle` + anzunehmen. Genau davon raet der Absatz oben ab, und der Rat gilt + weiter: Eine Tuer, die NICHTS anderes kann, als eine bestimmte Rolle + anzulegen, ist sicherer als eine, die vorher nachfragt. Eine + vergessene Abfrage waere ein zweiter Zugang mit allen Rechten; eine + vergessene Abfrage an einer Tuer, die nur "scout" kennt, ist ein + Aergernis, aber keine Luecke. + + Also weiterhin zwei Tueren mit je einer festen Rolle — nur nicht mehr + zweimal derselbe Text. `teamTuer(rolle)` baut den Griff, die Rolle + wird beim EINHAENGEN festgelegt und kommt nie aus dem Aufruf. + + Und die Frage "darf diese Person das ueberhaupt" beantwortet nicht + mehr diese Datei, sondern `darfAnlegen` in workspace.js — dieselbe + Auskunft, aus der auch die Oberflaeche ihre Knoepfe baut. Zwei Listen + fuer dieselbe Aussage waren der Grund, warum Spicy Media den + Manager-Knopf nie zu sehen bekam. */ +function teamTuer(rolle) { + return (req, res) => { + try { + const person = sitzungLesen(req); + if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" }); + /* ZWEI SCHLOESSER, ABSICHTLICH. `siehtAlles` sagt, wer diese Tuer + ueberhaupt sieht; `darfAnlegen` sagt, ob sie fuer genau diese + Rolle offen ist. Faellt eines der beiden weg, haelt das andere. */ + if (!siehtAlles(person)) return res.status(404).json({ fehler: "nicht_gefunden" }); + if (!darfAnlegen(person).includes(rolle)) { + return res.status(403).json({ fehler: "Diese Rolle legst du nicht an." }); + } + + const name = String(req.body?.name ?? "").trim(); + if (name.length < 2) return res.status(400).json({ fehler: "Name fehlt." }); + if (name.length > NAME_MAX) return res.status(400).json({ fehler: "Name ist zu lang." }); + const vorhanden = db().prepare( + "SELECT 1 FROM personen WHERE lower(name) = lower(?) AND aktiv = 1").get(name); + if (vorhanden) return res.status(409).json({ fehler: "Diesen Namen gibt es schon." }); + + const neu = personAnlegen(name, rolle, { ...person, ip: echteIp(req) }); + res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code }); + } catch (fehler) { + console.error(`[workspace] ${rolle} anlegen:`, fehler?.message); + res.status(503).json({ fehler: "nicht_verfuegbar" }); + } + }; +} +personenRouter.post("/workspace/api/manager-anlegen", gleicheHerkunft, teamTuer("manager")); +personenRouter.post("/workspace/api/scout-anlegen", gleicheHerkunft, teamTuer("scout")); /* Welche Scouts kann ich einem neuen Creator gleich mitgeben? Dieselbe Regel wie oben, nur lesend -- damit die Oberflaeche genau @@ -594,10 +628,22 @@ personenRouter.post("/workspace/api/verwaltung/personen", gleicheHerkunft, (req, /* ERSTER VORBEHALT: Eine Leitung anlegen darf nur DogFather. Duerfte ein Manager das, koennte er sich einen zweiten Zugang mit vollen Rechten schaffen -- und waere damit nicht mehr begrenzbar. - "Nur DogFather hat alle endgueltigen Rechte" faengt hier an. */ - if (!istDogFather(req.person) && (rolle === "admin" || rolle === "manager")) { + "Nur DogFather hat alle endgueltigen Rechte" faengt hier an. + + SEIT DEM 10.09.2026 STEHT DIE ANTWORT NICHT MEHR HIER, sondern in + `darfAnlegen` (workspace.js) — derselben Auskunft, aus der auch + die beiden Team-Tueren und die Oberflaeche ihre Knoepfe bauen. + Vorher stand die Regel an vier Stellen in drei Dateien, und genau + daran ist sie zerbrochen: Zwei davon widersprachen einander, und + Spicy Media bekam den Manager-Knopf nie zu sehen. + + Am Ergebnis aendert sich nichts — die Liste sagt fuer DogFather + "alles" und fuer einen Manager "nur Creator", also genau das, was + hier vorher ausgeschrieben stand. Es steht nur noch an einer + Stelle. */ + if (!darfAnlegen(req.person).includes(rolle)) { return res.status(403).json({ - fehler: "DogFather und Manager legt nur DogFather selbst an.", + fehler: "Diese Rolle legst du nicht an.", }); } diff --git a/server/workspace.js b/server/workspace.js index e4add7d8..756092fd 100644 --- a/server/workspace.js +++ b/server/workspace.js @@ -115,6 +115,59 @@ const LEITUNG = new Set(["spicy", "admin", "manager"]); export const istLeitung = (person) => !!person && LEITUNG.has(person.rolle); export const istDogFather = (person) => !!person && person.rolle === "admin"; +/* ======================================================================= + WER DARF WEN ANLEGEN — die einzige Liste dazu (10.09.2026) + + Filipe, mit Bildschirmfoto der Zugaenge-Seite: "die spicy rolle soll + auch manager und scouts hinzufuegen koennen." + + Sie konnte es nicht. Der Grund war NICHT ein fehlendes Recht, sondern + ZWEI Listen, die einander widersprachen — in derselben Funktion, drei + Zeilen auseinander (personen.js): + + const darf = ... : ich.rolle === 'spicy' ? ['manager', 'creator'] + ... + if ((r.wert === 'admin' || r.wert === 'manager') && ich.rolle !== 'admin') continue; + + Die erste Zeile erlaubt Spicy Media einen Manager, die zweite nimmt + ihn wieder weg. Uebrig blieb genau ein Knopf: Creator. Serverseitig + war der Weg fuer den Manager die ganze Zeit offen — es gab nur keinen + Knopf dafuer. + + Das ist im Haus die immer gleiche Sorte Fehler: zwei Stellen fuer + dieselbe Aussage, und die spaetere gewinnt still. Deshalb steht die + Antwort ab jetzt EINMAL hier, und alle fragen sie: + + - die drei Anlege-Wege in workspace-personen.js + - die Oberflaeche, ueber `darf_anlegen` in /workspace/api/ich + + Die Oberflaeche hat damit gar keine eigene Liste mehr. Sie kann + deshalb auch nicht mehr von der des Servers abweichen — weder zu + streng (ein Recht, das niemand findet) noch zu grosszuegig (ein Knopf, + der eine Absage bringt). + + WARUM SPICY MEDIA SCOUTS ANLEGEN DARF, ABER KEINE LEITUNG: + Ein Scout und ein Manager arbeiten unter Spicy Media — sie + einzustellen ist genau die Aufgabe dieser Rolle. Einen DogFather + anzulegen ist es nicht: Das waere ein zweiter Zugang mit allen + endgueltigen Rechten, und den vergibt nur DogFather selbst. + ======================================================================= */ +const ANLEGBAR = { + /* DogFather: alles, was es gibt. Er geht ueber /verwaltung/personen. */ + admin: [...ROLLEN], + /* Spicy Media: das ganze Team, aber keine Leitung ueber sich. */ + spicy: ["manager", "scout", "creator"], + /* Ein Manager stellt Creator ein, die er dann auch betreut. */ + manager: ["creator"], +}; + +/** Welche Rollen darf diese Person anlegen? Immer ein Feld, nie null — + * wer nichts darf, bekommt eine leere Liste und keinen Sonderfall. */ +export function darfAnlegen(person) { + if (!person) return []; + return [...(ANLEGBAR[person.rolle] ?? [])]; +} + /* WER FUEHRT TEAM DOGI? (10.09.2026) * * DogFather und seine rechte Hand -- und sonst niemand. Spicy Media und @@ -3262,6 +3315,14 @@ workspaceRouter.get("/workspace/api/ich", (req, res) => { rolle_name: ROLLEN_NAME[person.rolle] ?? person.rolle, sicht, bild, + /* WELCHE ROLLEN ICH ANLEGEN DARF (10.09.2026). + + Damit hat die Oberflaeche keine eigene Liste mehr. Vorher standen + dort zwei, die einander widersprachen, und Spicy Media sah + deshalb nur den Creator-Knopf — obwohl der Weg fuer den Manager + serverseitig offen war. Wer die Antwort nur an einer Stelle hat, + kann sie nicht an zweien verschieden haben. */ + darf_anlegen: darfAnlegen(person), /* DIE KACHELN, WENN SIE NICHT IM BROWSER STEHEN DUERFEN. Fuer die fuenf bekannten Rollen steht hier `null`, und die diff --git a/workspace/app.webmanifest b/workspace/app.webmanifest index c550a4a5..29e0b1cc 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=202609102008", + "src": "/assets/img/app-symbole/workspace-192.png?v=202609102156", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-512.png?v=202609102008", + "src": "/assets/img/app-symbole/workspace-512.png?v=202609102156", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609102008", + "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609102156", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609102008", + "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609102156", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/workspace/assets/js/personen.js b/workspace/assets/js/personen.js index e5605479..1f534de3 100644 --- a/workspace/assets/js/personen.js +++ b/workspace/assets/js/personen.js @@ -60,11 +60,26 @@ Aus derselben Liste wie die Beschreibungen, damit die beiden nicht auseinanderlaufen. */ - const darf = ich?.rolle === 'admin' ? [...window.Bereiche.ROLLENFOLGE] - : ich?.rolle === 'spicy' ? ['manager', 'creator'] + /* KEINE EIGENE LISTE MEHR (10.09.2026). + + Hier standen ZWEI, drei Zeilen auseinander, und sie widersprachen + einander: Die erste erlaubte Spicy Media `['manager','creator']`, + die zweite warf den Manager mit einem `continue` wieder hinaus, + sobald man nicht DogFather war. Uebrig blieb ein einziger Knopf. + + Serverseitig war die Tuer fuer den Manager die ganze Zeit offen — + es gab nur nichts zum Draufdruecken. Genau so sieht dieser Fehler + aus: Nichts ist kaputt, nichts wird rot, es fehlt einfach. + + Die Liste kommt jetzt vom Server (`darf_anlegen` aus /api/ich). + Damit kann sie nicht mehr abweichen — weder zu streng noch zu + grosszuegig. Faellt die Angabe aus (alte Fassung im Zwischen- + speicher), bleibt der Creator uebrig: das Wenigste, nicht das + Meiste. */ + const darf = Array.isArray(ich?.darf_anlegen) && ich.darf_anlegen.length + ? ich.darf_anlegen : ['creator']; for (const r of ROLLEN.filter((x) => darf.includes(x.wert))) { - if ((r.wert === 'admin' || r.wert === 'manager') && ich.rolle !== 'admin') continue; ziel.append(rollenKnopf(ziel, r)); } } @@ -856,10 +871,29 @@ Der Server prueft ohnehin, WER durch welche Tuer darf. Diese Zeilen sorgen nur dafuer, dass niemand gegen eine verschlossene laeuft. */ + /* EINE TUER JE ROLLE, UND ZWAR AUSGESCHRIEBEN (10.09.2026). + + Hier stand `rolle === 'manager' ? manager-anlegen : + creator-anlegen` — ein Zweig mit einem Auffangbecken. Solange + es nur zwei Rollen gab, stimmte das. Mit dem Scout waere es + still falsch geworden: Er waere im `else` gelandet, und + `creator-anlegen` legt IMMER einen Creator an. Der Knopf haette + funktioniert, die Meldung haette Erfolg gemeldet, und in der + Liste stuende ein Creator, wo ein Scout stehen sollte. + + Eine Zuordnung statt einer Kette: Wer keine Tuer hat, bekommt + eine Fehlermeldung — nicht die naechstbeste Tuer. */ const rolle = gewaehlteRolle(); - const weg = ich.rolle === 'admin' ? '/workspace/api/verwaltung/personen' - : rolle === 'manager' ? '/workspace/api/manager-anlegen' - : '/workspace/api/creator-anlegen'; + const TUEREN = { + manager: '/workspace/api/manager-anlegen', + scout: '/workspace/api/scout-anlegen', + creator: '/workspace/api/creator-anlegen', + }; + const weg = ich.rolle === 'admin' ? '/workspace/api/verwaltung/personen' : TUEREN[rolle]; + if (!weg) { + melde(`Für die Rolle „${rolle}“ gibt es hier keinen Weg.`); + return; + } const a = await hole(weg, { method: 'POST', headers: { 'Content-Type': 'application/json' }, diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 816e63b4..c7b7eefb 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -12,12 +12,12 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - + + + + + + @@ -245,10 +245,10 @@ - - - - - + + + + + diff --git a/workspace/automation.html b/workspace/automation.html index ca020d71..b9b01676 100644 --- a/workspace/automation.html +++ b/workspace/automation.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -140,10 +140,10 @@ - - - - - + + + + + diff --git a/workspace/bereich.html b/workspace/bereich.html index b3c12e64..7494e8ca 100644 --- a/workspace/bereich.html +++ b/workspace/bereich.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -157,14 +157,14 @@ - - - - - - - - - + + + + + + + + + diff --git a/workspace/calls.html b/workspace/calls.html index e37b7039..8bdfa4ea 100644 --- a/workspace/calls.html +++ b/workspace/calls.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -82,11 +82,11 @@ - - - - - - + + + + + + diff --git a/workspace/chat.html b/workspace/chat.html index 504f2ba5..25482121 100644 --- a/workspace/chat.html +++ b/workspace/chat.html @@ -12,19 +12,19 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - + + + + - - - + + + @@ -282,10 +282,10 @@ - - - - - + + + + + diff --git a/workspace/content.html b/workspace/content.html index 6cf0b3b0..d9804224 100644 --- a/workspace/content.html +++ b/workspace/content.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -172,12 +172,12 @@ - - - - - - - + + + + + + + diff --git a/workspace/crew-index.html b/workspace/crew-index.html index 612da65c..ddd432ec 100644 --- a/workspace/crew-index.html +++ b/workspace/crew-index.html @@ -30,9 +30,9 @@ - - - + + + @@ -246,6 +246,6 @@ - + diff --git a/workspace/crew.webmanifest b/workspace/crew.webmanifest index 96706371..c2209231 100644 --- a/workspace/crew.webmanifest +++ b/workspace/crew.webmanifest @@ -13,25 +13,25 @@ "theme_color": "#06090f", "icons": [ { - "src": "/assets/img/app-symbole/crew-192.png?v=202609102008", + "src": "/assets/img/app-symbole/crew-192.png?v=202609102156", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/crew-512.png?v=202609102008", + "src": "/assets/img/app-symbole/crew-512.png?v=202609102156", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609102008", + "src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609102156", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609102008", + "src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609102156", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/workspace/dateien.html b/workspace/dateien.html index 09843aa0..de9ef0ff 100644 --- a/workspace/dateien.html +++ b/workspace/dateien.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -101,10 +101,10 @@ - - - - - + + + + + diff --git a/workspace/index.html b/workspace/index.html index 9b6dd74d..c5fa4ca5 100644 --- a/workspace/index.html +++ b/workspace/index.html @@ -30,9 +30,9 @@ - - - + + + @@ -401,6 +401,6 @@ - + diff --git a/workspace/kalender.html b/workspace/kalender.html index 1d61e676..e33da2d7 100644 --- a/workspace/kalender.html +++ b/workspace/kalender.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -370,11 +370,11 @@ - - - - - - + + + + + + diff --git a/workspace/leistung.html b/workspace/leistung.html index d75411d2..97353600 100644 --- a/workspace/leistung.html +++ b/workspace/leistung.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -217,10 +217,10 @@ - - - - - + + + + + diff --git a/workspace/personen.html b/workspace/personen.html index 2321a361..24fd2bb0 100644 --- a/workspace/personen.html +++ b/workspace/personen.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -364,10 +364,10 @@ - - - - - + + + + + diff --git a/workspace/profil.html b/workspace/profil.html index 252354d7..c7df0dd2 100644 --- a/workspace/profil.html +++ b/workspace/profil.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -207,11 +207,11 @@ - - - - - - + + + + + + diff --git a/workspace/report.html b/workspace/report.html index 1b3d94ce..b6163415 100644 --- a/workspace/report.html +++ b/workspace/report.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -99,11 +99,11 @@ - - - - - - + + + + + + diff --git a/workspace/scouting.html b/workspace/scouting.html index 62a86026..7dac9d90 100644 --- a/workspace/scouting.html +++ b/workspace/scouting.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -132,10 +132,10 @@ - - - - - + + + + + diff --git a/workspace/start.html b/workspace/start.html index ff3da086..e7225595 100644 --- a/workspace/start.html +++ b/workspace/start.html @@ -12,14 +12,14 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - + + + + - - + + @@ -667,10 +667,10 @@

- - - - - + + + + + diff --git a/workspace/startcheck.html b/workspace/startcheck.html index 5f79c509..952307ea 100644 --- a/workspace/startcheck.html +++ b/workspace/startcheck.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -78,11 +78,11 @@ - - - - - - + + + + + + diff --git a/workspace/steckbrief.html b/workspace/steckbrief.html index dfe2b832..1ab2b460 100644 --- a/workspace/steckbrief.html +++ b/workspace/steckbrief.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -119,10 +119,10 @@ - - - - - + + + + + diff --git a/workspace/team.html b/workspace/team.html index efbbe911..08210aa8 100644 --- a/workspace/team.html +++ b/workspace/team.html @@ -7,13 +7,13 @@ - - - - - - - + + + + + + + @@ -103,10 +103,10 @@ - - - - - + + + + + diff --git a/workspace/teamlage.html b/workspace/teamlage.html index 89bca640..9f13c038 100644 --- a/workspace/teamlage.html +++ b/workspace/teamlage.html @@ -7,13 +7,13 @@ - - - - - - - + + + + + + + - - - - + + + + diff --git a/workspace/uebersicht.html b/workspace/uebersicht.html index e9fef6c9..a7b55a86 100644 --- a/workspace/uebersicht.html +++ b/workspace/uebersicht.html @@ -12,15 +12,15 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - + + + + - - - + + + @@ -142,11 +142,11 @@ - + - - - - + + + + diff --git a/workspace/wissen.html b/workspace/wissen.html index 21131f30..fc0e858b 100644 --- a/workspace/wissen.html +++ b/workspace/wissen.html @@ -12,13 +12,13 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + @@ -183,10 +183,10 @@ - - - - - + + + + +