Manager legen Creator an -- eine neue Tuer statt eines Schluessels fuer die alte
Filipe: "wieso greift das in die rechte rein? ist doch ok, die sollen einfach paar leute selber hinzufuegen koennen." Er hat recht, und ich hatte zwei Dinge in einen Topf geworfen: Das hier braucht KEINE Datenbankaenderung -- nur die Rolle "Spicy Media" braucht eine. WARUM EIN EIGENER WEG UND NICHT DIE ALTE TUER Alles unter /workspace/api/verwaltung haengt an EINER Schranke (nurAdmin). Dahinter liegen acht Wege: Rollen aendern, Codes neu setzen, sperren, loeschen, Protokoll lesen. Einen Manager dort hineinzulassen und danach in jedem der acht einzeln zu pruefen, was er darf, ist genau die Bauweise, durch die am 31.08.2026 ein Loch entstanden ist -- zwei von drei Stellen abgesichert, die dritte vergessen. Deshalb bleibt die Tuer zu, und daneben steht eine neue mit genau einem Zweck: POST /workspace/api/creator-anlegen. Sie kann nichts anderes, als einen Creator anzulegen -- nicht weil eine Abfrage es verbietet, sondern weil es hier keinen anderen Weg gibt. Unterschied zwischen "darf nicht" und "kann nicht". DIE ROLLE STEHT NICHT IM AUFRUF, sie wird im Server gesetzt. Ein Feld `rolle` im Koerper waere die naheliegende Loesung und die falsche: Dann muesste eine Abfrage sie pruefen, und eine vergessene Abfrage ist ein zweiter Zugang mit vollen Rechten. Geprueft wird deshalb nicht, dass ein mitgeschicktes `rolle: "admin"` abgelehnt wird, sondern dass es WIRKUNGSLOS ist. DIE ZUTEILUNG PASSIERT SOFORT. Ein Creator ohne Betreuung ist fuer alle ausser DogFather unsichtbar -- der Manager haette ihn angelegt und danach nicht mehr gesehen. Wer anlegt, betreut; ein eigener Scout kann mitgegeben werden. Eine FREMDE Scout-Nummer wird abgelehnt (403) und nicht stillschweigend auf den Anleger zurueckgesetzt: Sonst glaubte der Manager, er haette zugeteilt. Der Knopf sitzt auf dem Dashboard, nicht in der Personenverwaltung -- dort kommt ein Manager gar nicht hinein, und hier sieht er seine Creator ohnehin. Der Zugangscode steht einmal im Dialog und wird nie nachgeladen; deshalb schliesst sich das Fenster NICHT von selbst. pruef-creator-anlegen.mjs, 29 Pruefungen, alle gruen -- und die Haelfte davon prueft, was NICHT geht: Personenverwaltung 404 fuer Manager, Scout und Creator kommen gar nicht erst durch, fremder Scout 403 (und der Creator wird dabei gar nicht erst angelegt), erfundene Nummer 403. Zwei Manager mit je einem eigenen Scout, weil sich "nur die eigenen" mit nur einem Manager gar nicht pruefen laesst. Nebenbei: .feld-hinweis ist von bereich.css nach aufgaben.css gewandert (zu den uebrigen Formularstilen) -- ein Formularbaustein in der Bereichsdatei ist nur so lange richtig, wie ihn keine zweite Seite braucht. Gruen: creator-anlegen (29), personen-liste (33), css-klassen (15), struktur (32), formulare (19). Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
/* =====================================================================
|
||||
EIN MANAGER LEGT EINEN CREATOR AN — und sonst nichts.
|
||||
|
||||
Wunsch Filipe (07.09.2026): "ich will auch dass manager ab jetzt
|
||||
creator und auch wirklich nur creator hinzufuegen koennen."
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WARUM DIESE DATEI MEHR PRUEFT, ALS DER WUNSCH VERLANGT
|
||||
|
||||
Der neue Weg ist der erste, der einem Manager erlaubt, ein KONTO
|
||||
anzulegen. Ein Konto ist ein Zugang; ein Fehler hier ist kein
|
||||
Schoenheitsfehler, sondern ein zweiter Schluessel zum Haus.
|
||||
|
||||
Deshalb wird nicht nur geprueft, dass es geht, sondern vor allem, was
|
||||
NICHT geht. Zu jeder erlaubten Handlung steht die verbotene daneben:
|
||||
|
||||
- Ein Manager legt einen Creator an -> 201
|
||||
- ... und der Creator gehoert danach IHM -> Betreuung
|
||||
- Ein Manager kann die Rolle NICHT bestimmen -> immer creator
|
||||
- Ein Manager kommt NICHT in die Personenverwaltung -> 404
|
||||
- Ein Scout kann diesen Weg nicht benutzen -> 404
|
||||
- Ein Creator kann diesen Weg nicht benutzen -> 404
|
||||
- Ein Manager teilt keinen FREMDEN Scout zu -> 403
|
||||
- Ein Manager teilt seinen EIGENEN Scout zu -> 201
|
||||
|
||||
Der vorletzte Punkt ist der, auf den es ankommt. Er ist der einzige,
|
||||
bei dem eine Zahl im Aufruf ueber eine Zuordnung entscheidet -- und
|
||||
genau so ist am 02.09.2026 schon einmal eine Schranke umgangen
|
||||
worden ("es reichte, eine Zahl in die Adresse zu schreiben").
|
||||
|
||||
Aufruf: node server/pruef-creator-anlegen.mjs
|
||||
===================================================================== */
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { spawn } from "node:child_process";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { scryptSync, randomBytes } from "node:crypto";
|
||||
import { notbremse } from "./helfer-notbremse.mjs";
|
||||
|
||||
notbremse(240_000, "die Creator-Anlegen-Pruefung");
|
||||
|
||||
const HIER = dirname(fileURLToPath(import.meta.url));
|
||||
const { portMussFreiSein } = await import("./helfer-port.mjs");
|
||||
const PORT = 4347;
|
||||
await portMussFreiSein(PORT, "die Creator-Anlegen-Pruefung");
|
||||
const BASIS = `http://127.0.0.1:${PORT}`;
|
||||
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-canlegen-"));
|
||||
const DB = join(ordner, "workspace.db");
|
||||
|
||||
let fehler = 0, gemacht = 0;
|
||||
const ok = (b, t) => { gemacht++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||
|
||||
let kind = null;
|
||||
async function starten() {
|
||||
kind = spawn(process.execPath, [join(HIER, "index.js")], {
|
||||
env: { ...process.env, WORKSPACE_DB: DB, PORT: String(PORT),
|
||||
SITE_ACCESS_SECRET: "lokaler-test",
|
||||
SITE_PUBLIC_LAUNCH_AT: "2020-01-01T00:00:00+01:00" },
|
||||
stdio: ["ignore", "pipe", "pipe"] });
|
||||
let aus = "";
|
||||
kind.stdout.on("data", (d) => { aus += d; });
|
||||
kind.stderr.on("data", (d) => { aus += d; });
|
||||
for (let i = 0; i < 80; i++) {
|
||||
await new Promise((r) => setTimeout(r, 250));
|
||||
if (kind.exitCode !== null) break;
|
||||
try { const a = await fetch(BASIS + "/workspace/", { redirect: "manual" });
|
||||
if (a.status < 500) return; } catch { /* noch nicht oben */ }
|
||||
}
|
||||
throw new Error("Server startete nicht:\n" + aus.slice(-1500));
|
||||
}
|
||||
|
||||
await starten();
|
||||
kind.kill();
|
||||
await new Promise((r) => setTimeout(r, 600));
|
||||
|
||||
const jetzt = new Date().toISOString();
|
||||
function anlegen(d, name, rolle, code) {
|
||||
const salz = randomBytes(16).toString("hex");
|
||||
const hash = scryptSync(code, salz, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
||||
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)")
|
||||
.run(name, rolle, hash, salz, 32768, jetzt);
|
||||
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||
}
|
||||
|
||||
/* Zwei Manager mit je einem eigenen Scout -- der zweite ist der ganze
|
||||
Grund dieser Pruefung: An IHM zeigt sich, ob "nur die eigenen" wirklich
|
||||
gilt. Mit nur einem Manager waere jede Zuteilung zufaellig richtig. */
|
||||
let idDogi, idMax, idMia, idScoutMax, idScoutMia, idCreator;
|
||||
{
|
||||
const d = new DatabaseSync(DB);
|
||||
idDogi = anlegen(d, "Filipe", "admin", "CODE-DOGI-0001");
|
||||
idMax = anlegen(d, "Max", "manager", "CODE-MAX-00001");
|
||||
idMia = anlegen(d, "Mia", "manager", "CODE-MIA-00001");
|
||||
idScoutMax = anlegen(d, "SamVonMax", "scout", "CODE-SAM-00001");
|
||||
idScoutMia = anlegen(d, "TimVonMia", "scout", "CODE-TIM-00001");
|
||||
idCreator = anlegen(d, "Luna", "creator", "CODE-LUNA-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 (?,?,?)")
|
||||
.run(idScoutMia, idMia, jetzt);
|
||||
d.close();
|
||||
}
|
||||
await starten();
|
||||
|
||||
const anmelden = async (rolle, code) => {
|
||||
const a = await fetch(BASIS + "/workspace/api/anmelden", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ rolle, code }) });
|
||||
const keks = (a.headers.getSetCookie?.() || []).map((k) => k.split(";")[0]).join("; ");
|
||||
if (!keks) throw new Error(`Anmeldung ${rolle} fehlgeschlagen (${a.status})`);
|
||||
return keks;
|
||||
};
|
||||
const keksDogi = await anmelden("admin", "CODE-DOGI-0001");
|
||||
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 ruf = async (art, weg, keks, koerper) => {
|
||||
const a = await fetch(BASIS + weg, { method: art,
|
||||
headers: { Cookie: keks, "Content-Type": "application/json", Origin: BASIS },
|
||||
body: koerper ? JSON.stringify(koerper) : undefined });
|
||||
let daten = null; try { daten = await a.json(); } catch { /* leer ist erlaubt */ }
|
||||
return { status: a.status, daten };
|
||||
};
|
||||
const WEG = "/workspace/api/creator-anlegen";
|
||||
|
||||
/* =======================================================================
|
||||
1. Der Alltagsfall
|
||||
======================================================================= */
|
||||
console.log("=== Ein Manager legt einen Creator an ===");
|
||||
let neuerId = null;
|
||||
{
|
||||
const { status, daten } = await ruf("POST", WEG, keksMax, { name: "Nova" });
|
||||
ok(status === 201, `Max legt "Nova" an (HTTP ${status})`);
|
||||
ok(daten?.rolle === "creator", `sie ist ein Creator (${daten?.rolle})`);
|
||||
ok(typeof daten?.code === "string" && daten.code.length >= 8,
|
||||
`der Zugangscode kommt genau einmal mit (${daten?.code ? daten.code.length + " Zeichen" : "fehlt"})`);
|
||||
neuerId = daten?.id ?? null;
|
||||
|
||||
const d = new DatabaseSync(DB, { readOnly: true });
|
||||
const b = d.prepare("SELECT betreuer_id FROM betreuung WHERE creator_id = ?").get(neuerId);
|
||||
d.close();
|
||||
ok(b?.betreuer_id === idMax,
|
||||
`und gehoert sofort Max (Betreuung: ${b?.betreuer_id ?? "keine"})`);
|
||||
}
|
||||
{
|
||||
/* DER PUNKT DAHINTER: Ein Creator ohne Betreuung ist fuer alle ausser
|
||||
DogFather unsichtbar. Ohne die Zuteilung haette Max ihn angelegt und
|
||||
danach nicht mehr gesehen -- ein Fehler, den man erst beim Suchen
|
||||
bemerkt. */
|
||||
const { daten } = await ruf("GET", "/workspace/api/personen", keksMax);
|
||||
const namen = (daten?.personen || []).map((p) => p.name);
|
||||
ok(namen.includes("Nova"), `Max sieht sie danach auch (${namen.join(", ")})`);
|
||||
}
|
||||
{
|
||||
const { daten } = await ruf("GET", "/workspace/api/personen", keksMia);
|
||||
const namen = (daten?.personen || []).map((p) => p.name);
|
||||
ok(!namen.includes("Nova"),
|
||||
`die ANDERE Managerin sieht sie NICHT (${namen.join(", ")})`);
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
2. Was nicht geht
|
||||
======================================================================= */
|
||||
console.log("\n=== Was ein Manager NICHT kann ===");
|
||||
{
|
||||
/* Die Rolle steht nicht im Aufruf -- also kann sie auch niemand
|
||||
setzen. Geprueft wird trotzdem, was passiert, wenn es jemand
|
||||
versucht: Das Feld muss WIRKUNGSLOS sein, nicht bloss abgelehnt. */
|
||||
const { status, daten } = await ruf("POST", WEG, keksMax, { name: "Versuch1", rolle: "admin" });
|
||||
ok(status === 201, `ein mitgeschicktes Feld "rolle" bricht nichts ab (HTTP ${status})`);
|
||||
ok(daten?.rolle === "creator",
|
||||
`es bleibt wirkungslos -- angelegt wurde ein ${daten?.rolle}`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("GET", "/workspace/api/verwaltung/personen", keksMax);
|
||||
ok(status === 404, `die Personenverwaltung bleibt fuer Max zu (HTTP ${status})`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("POST", "/workspace/api/verwaltung/personen", keksMax,
|
||||
{ name: "Schleichweg", rolle: "creator" });
|
||||
ok(status === 404, `auch schreibend (HTTP ${status})`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("POST", `/workspace/api/verwaltung/personen/${idCreator}/code`, keksMax);
|
||||
ok(status === 404, `einen fremden Code neu setzen geht nicht (HTTP ${status})`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("POST", WEG, keksScout, { name: "VonScout" });
|
||||
ok(status === 404, `ein Scout kann diesen Weg nicht benutzen (HTTP ${status})`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("POST", WEG, keksLuna, { name: "VonCreator" });
|
||||
ok(status === 404, `ein Creator auch nicht (HTTP ${status})`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("POST", WEG, keksMax, { name: "A" });
|
||||
ok(status === 400, `ein zu kurzer Name wird abgelehnt (HTTP ${status})`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("POST", WEG, keksMax, { name: "Nova" });
|
||||
ok(status === 409, `ein doppelter Name wird abgelehnt (HTTP ${status})`);
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
3. Die Zuteilung -- der eigentliche Prüfstein
|
||||
======================================================================= */
|
||||
console.log("\n=== Zugeteilt wird nur der EIGENE Scout ===");
|
||||
{
|
||||
const { status, daten } = await ruf("POST", WEG, keksMax,
|
||||
{ name: "MitEigenemScout", betreuer_id: idScoutMax });
|
||||
ok(status === 201, `Max gibt seinen eigenen Scout mit (HTTP ${status})`);
|
||||
const d = new DatabaseSync(DB, { readOnly: true });
|
||||
const b = d.prepare("SELECT betreuer_id FROM betreuung WHERE creator_id = ?").get(daten?.id);
|
||||
d.close();
|
||||
ok(b?.betreuer_id === idScoutMax, `und der steht auch da (${b?.betreuer_id})`);
|
||||
}
|
||||
{
|
||||
/* DER FALL, AUF DEN ES ANKOMMT. Eine Zahl im Aufruf darf keine
|
||||
Zuordnung erzeugen, die es nicht geben soll. */
|
||||
const { status } = await ruf("POST", WEG, keksMax,
|
||||
{ name: "MitFremdemScout", betreuer_id: idScoutMia });
|
||||
ok(status === 403, `Mias Scout kann Max NICHT zuteilen (HTTP ${status})`);
|
||||
const d = new DatabaseSync(DB, { readOnly: true });
|
||||
const z = d.prepare("SELECT 1 FROM personen WHERE name = 'MitFremdemScout'").get();
|
||||
d.close();
|
||||
ok(!z, "und der Creator wurde dabei GAR NICHT ERST angelegt");
|
||||
}
|
||||
{
|
||||
/* Stillschweigend auf sich selbst zurueckzufallen waere schlimmer als
|
||||
die Absage: Der Manager glaubte dann, er haette zugeteilt. */
|
||||
const { status } = await ruf("POST", WEG, keksMax, { name: "MitUnfug", betreuer_id: 99999 });
|
||||
ok(status === 403, `eine erfundene Nummer wird abgelehnt statt ignoriert (HTTP ${status})`);
|
||||
}
|
||||
{
|
||||
const { daten } = await ruf("GET", WEG + "/scouts", keksMax);
|
||||
const namen = (daten?.scouts || []).map((s) => s.name);
|
||||
ok(namen.length === 1 && namen[0] === "SamVonMax",
|
||||
`die angebotene Liste enthaelt genau seinen Scout (${namen.join(", ") || "leer"})`);
|
||||
}
|
||||
{
|
||||
/* GEGENPROBE zur Liste: Sie muss fuer die andere Managerin ANDERS
|
||||
aussehen. Waere sie fuer alle gleich, pruefte die Zeile darueber
|
||||
nur, dass ueberhaupt etwas zurueckkommt. */
|
||||
const { daten } = await ruf("GET", WEG + "/scouts", keksMia);
|
||||
const namen = (daten?.scouts || []).map((s) => s.name);
|
||||
ok(namen.length === 1 && namen[0] === "TimVonMia",
|
||||
`und fuer Mia eine andere (${namen.join(", ") || "leer"})`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("GET", WEG + "/scouts", keksScout);
|
||||
ok(status === 404, `ein Scout bekommt die Liste nicht (HTTP ${status})`);
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
4. DogFather kann weiterhin alles
|
||||
======================================================================= */
|
||||
console.log("\n=== DogFather bleibt unangetastet ===");
|
||||
{
|
||||
const { status, daten } = await ruf("POST", WEG, keksDogi,
|
||||
{ name: "VonDogi", betreuer_id: idScoutMia });
|
||||
ok(status === 201, `er darf jeden Scout zuteilen, auch Mias (HTTP ${status})`);
|
||||
ok(daten?.betreuer_id === idScoutMia, `und es wirkt (${daten?.betreuer_id})`);
|
||||
}
|
||||
{
|
||||
const { status } = await ruf("GET", "/workspace/api/verwaltung/personen", keksDogi);
|
||||
ok(status === 200, `die Personenverwaltung steht ihm weiter offen (HTTP ${status})`);
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
5. Der Knopf auf der Seite
|
||||
======================================================================= */
|
||||
console.log("\n=== Der Knopf auf dem Dashboard ===");
|
||||
{
|
||||
const pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||||
const browser = await pw.chromium.launch();
|
||||
const sichtbarFuer = 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/uebersicht.html", { waitUntil: "networkidle" });
|
||||
await seite.waitForTimeout(1400);
|
||||
const da = await seite.$eval("#creator-neu", (e) => !e.hidden).catch(() => null);
|
||||
await ctx.close();
|
||||
return da;
|
||||
};
|
||||
ok(await sichtbarFuer("manager", "CODE-MAX-00001") === true, "ein Manager sieht ihn");
|
||||
ok(await sichtbarFuer("admin", "CODE-DOGI-0001") === true, "DogFather auch");
|
||||
/* Ein Scout kommt auf dieselbe Seite -- der Knopf muss dort fehlen.
|
||||
Das ist keine Sicherung (die steht im Server), sondern die Zusage,
|
||||
dass niemand einen Knopf sieht, der nur eine Absage bringt. */
|
||||
ok(await sichtbarFuer("scout", "CODE-SAM-00001") === false, "ein Scout nicht");
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
kind.kill();
|
||||
await new Promise((r) => setTimeout(r, 400));
|
||||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Zugabe */ }
|
||||
|
||||
console.log(`\n${gemacht} Pruefungen`);
|
||||
console.log(fehler === 0 ? "ALLES IN ORDNUNG" : `${fehler} FEHLER`);
|
||||
process.exit(fehler ? 1 : 0);
|
||||
@@ -275,6 +275,117 @@ personenRouter.put("/workspace/api/verwaltung/scout-zuteilung/:id", gleicheHerku
|
||||
}
|
||||
});
|
||||
|
||||
/* =======================================================================
|
||||
EIN MANAGER LEGT EINEN CREATOR AN (07.09.2026)
|
||||
|
||||
Wunsch Filipe: "ich will auch dass manager ab jetzt creator und auch
|
||||
wirklich nur creator hinzufuegen koennen auf die seite. die creator
|
||||
sollen ... auch nur der person hinzugefuegt werden wo dan diesen
|
||||
creator hinzufuegt oder den scouts von diesem manager."
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
WARUM DAS EIN EIGENER WEG IST UND NICHT DIE ALTE TUER
|
||||
|
||||
Alles unter `/workspace/api/verwaltung` haengt an EINER Schranke:
|
||||
`nurAdmin`. Dahinter liegen acht Wege -- Rollen aendern, Codes neu
|
||||
setzen, sperren, loeschen, das Protokoll lesen. Einen Manager dort
|
||||
hineinzulassen und danach in jedem der acht Wege einzeln zu pruefen,
|
||||
was er darf, ist genau die Bauweise, durch die am 31.08.2026 schon
|
||||
einmal ein Loch entstanden ist: Zwei von drei Stellen waren
|
||||
abgesichert, die dritte vergessen.
|
||||
|
||||
Deshalb bleibt die Tuer zu, und daneben steht eine neue mit genau
|
||||
EINEM Zweck. Sie kann nichts anderes, als einen Creator anzulegen --
|
||||
nicht weil eine Abfrage es verbietet, sondern weil es hier gar keinen
|
||||
anderen Weg gibt. Das ist der Unterschied zwischen "darf nicht" und
|
||||
"kann nicht".
|
||||
|
||||
DIE ROLLE STEHT NICHT IM AUFRUF. Sie wird hier gesetzt. Ein Feld
|
||||
`rolle` im Koerper waere die naheliegende Loesung und die falsche:
|
||||
Dann muesste eine Abfrage sie pruefen, und eine vergessene Abfrage
|
||||
ist ein zweiter Zugang mit vollen Rechten.
|
||||
|
||||
DIE ZUTEILUNG PASSIERT SOFORT UND HIER. Ein Creator ohne Betreuung
|
||||
ist fuer alle ausser DogFather unsichtbar -- der Manager haette ihn
|
||||
angelegt und danach nicht mehr gesehen. Wer anlegt, betreut; ein Scout
|
||||
des Managers kann gleich mitgegeben werden ("oder den scouts von
|
||||
diesem manager").
|
||||
======================================================================= */
|
||||
function nurLeitung(req, res, next) {
|
||||
const person = sitzungLesen(req);
|
||||
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||||
if (!istLeitung(person)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
req.person = person;
|
||||
next();
|
||||
}
|
||||
|
||||
personenRouter.post("/workspace/api/creator-anlegen", gleicheHerkunft, nurLeitung, (req, res) => {
|
||||
try {
|
||||
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." });
|
||||
|
||||
/* Wem der neue Creator gehoert. Vorgabe: dem, der ihn anlegt.
|
||||
Ein Manager darf stattdessen einen SEINER Scouts angeben -- und
|
||||
nur einen seiner eigenen. Wer eine fremde Nummer hineinschreibt,
|
||||
bekommt sie nicht, sondern eine Absage: Stillschweigend auf die
|
||||
Vorgabe zurueckzufallen waere schlimmer, weil der Manager dann
|
||||
glaubt, er haette zugeteilt. */
|
||||
let betreuerId = req.person.id;
|
||||
const gewuenscht = req.body?.betreuer_id;
|
||||
if (gewuenscht !== undefined && gewuenscht !== null && gewuenscht !== "") {
|
||||
const z = Number(gewuenscht);
|
||||
if (!Number.isInteger(z) || z < 1) return res.status(400).json({ fehler: "Ungültige Zuordnung." });
|
||||
if (z !== req.person.id) {
|
||||
const erlaubt = istDogFather(req.person)
|
||||
? db().prepare("SELECT 1 FROM personen WHERE id = ? AND aktiv = 1 AND rolle IN ('admin','manager','scout')").get(z)
|
||||
: db().prepare(`SELECT 1 FROM personen p
|
||||
JOIN scout_zuteilung s ON s.scout_id = p.id
|
||||
WHERE p.id = ? AND p.aktiv = 1 AND p.rolle = 'scout'
|
||||
AND s.manager_id = ?`).get(z, req.person.id);
|
||||
if (!erlaubt) {
|
||||
return res.status(403).json({
|
||||
fehler: "Diesen Scout betreust du nicht – zuteilen kannst du nur deine eigenen.",
|
||||
});
|
||||
}
|
||||
}
|
||||
betreuerId = z;
|
||||
}
|
||||
|
||||
const neu = personAnlegen(name, "creator", { ...req.person, ip: echteIp(req) });
|
||||
betreuungSetzen(neu.id, betreuerId, { ...req.person, ip: echteIp(req) });
|
||||
|
||||
/* Der Code steht NUR hier in der Antwort. Danach ist er weg. */
|
||||
res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code,
|
||||
betreuer_id: betreuerId });
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] Creator anlegen:", fehler?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/* Welche Scouts kann ich einem neuen Creator gleich mitgeben?
|
||||
Dieselbe Regel wie oben, nur lesend -- damit die Oberflaeche genau
|
||||
die Liste anbietet, die der Server auch annimmt. Zwei getrennte
|
||||
Listen waeren die naechste Stelle, an der beide auseinanderlaufen. */
|
||||
personenRouter.get("/workspace/api/creator-anlegen/scouts", nurLeitung, (req, res) => {
|
||||
try {
|
||||
const zeilen = istDogFather(req.person)
|
||||
? db().prepare("SELECT id, name FROM personen WHERE aktiv = 1 AND rolle = 'scout' ORDER BY name").all()
|
||||
: db().prepare(`SELECT p.id, p.name FROM personen p
|
||||
JOIN scout_zuteilung s ON s.scout_id = p.id
|
||||
WHERE p.aktiv = 1 AND p.rolle = 'scout' AND s.manager_id = ?
|
||||
ORDER BY p.name`).all(req.person.id);
|
||||
res.json({ scouts: zeilen });
|
||||
} catch {
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------- Anlegen ----------------------------------------------------- */
|
||||
|
||||
personenRouter.post("/workspace/api/verwaltung/personen", gleicheHerkunft, (req, res) => {
|
||||
|
||||
@@ -813,3 +813,11 @@
|
||||
.abgebrochen, .abbruch-karte { border: 1px solid CanvasText; }
|
||||
.abgebrochen__zahl, .abbruch-karte__vorher { border: 1px solid CanvasText; }
|
||||
}
|
||||
|
||||
/* Hinweis unter einem Formularfeld -- erklaert das Feld, statt es
|
||||
kommentarlos wegzunehmen. */
|
||||
.feld-hinweis {
|
||||
margin: 5px 0 0;
|
||||
font-size: .76rem; line-height: 1.45;
|
||||
color: var(--text-still);
|
||||
}
|
||||
|
||||
@@ -382,13 +382,11 @@
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
/* Hinweis unter einem Formularfeld -- erklaert das Feld, statt es
|
||||
kommentarlos wegzunehmen. */
|
||||
.feld-hinweis {
|
||||
margin: 5px 0 0;
|
||||
font-size: .76rem; line-height: 1.45;
|
||||
color: var(--text-still);
|
||||
}
|
||||
/* .feld-hinweis steht seit dem 07.09.2026 in aufgaben.css -- bei den
|
||||
uebrigen Formularstilen. Grund: Die Dashboard-Seite braucht ihn jetzt
|
||||
auch (Creator anlegen), laedt aber diese Datei hier nicht. Ein
|
||||
Formularbaustein, der in der Bereichsdatei liegt, ist nur so lange
|
||||
richtig, wie ihn keine zweite Seite braucht. */
|
||||
|
||||
@media (max-width: 560px) {
|
||||
/* Auf dem Handy untereinander: Marke und Spanne nebeneinander
|
||||
|
||||
@@ -344,3 +344,23 @@ a.wert:hover { border-color: var(--rand-hell); background: rgba(255, 255, 255, .
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.wert, .fortschritt, .fortschritt__fuellung { transition: none; }
|
||||
}
|
||||
|
||||
/* Der einmalige Zugangscode. Gross, gesperrt, in gleicher Zeichenbreite
|
||||
und zum Markieren freigegeben -- er wird abgeschrieben oder kopiert,
|
||||
und beides muss auf Anhieb gelingen. `user-select: all` markiert ihn
|
||||
mit einem Klick vollstaendig; ein halb markierter Code ist ein Anruf
|
||||
am naechsten Tag. */
|
||||
.code-anzeige {
|
||||
margin: 6px 0 0;
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius-klein);
|
||||
border: 1px solid color-mix(in srgb, var(--k-gold) 34%, transparent);
|
||||
background: rgba(0, 0, 0, .34);
|
||||
box-shadow: inset 0 2px 5px -2px rgba(0, 0, 0, .7);
|
||||
color: #f0d9a8;
|
||||
font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
|
||||
font-size: 1.16rem; font-weight: 700; letter-spacing: .1em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
overflow-wrap: anywhere;
|
||||
user-select: all; -webkit-user-select: all;
|
||||
}
|
||||
|
||||
@@ -284,6 +284,107 @@
|
||||
$('fruehwarnung').hidden = false;
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
CREATOR ANLEGEN (07.09.2026)
|
||||
|
||||
Wunsch Filipe: "manager sollen ab jetzt creator und auch wirklich
|
||||
nur creator hinzufuegen koennen".
|
||||
|
||||
Der Weg dahin ist ein EIGENER (POST /workspace/api/creator-anlegen)
|
||||
und nicht die Personenverwaltung -- die bleibt DogFather
|
||||
vorbehalten, dort liegen Rollen, Codes, Sperren und das Protokoll.
|
||||
Dieser Weg kann nichts anderes, als einen Creator anzulegen; die
|
||||
Rolle steht nicht einmal im Aufruf.
|
||||
|
||||
DER KNOPF WIRD VERSTECKT, NICHT DIE ABSAGE ABGEFANGEN. Beides
|
||||
zusammen: Der Server laesst Scouts und Creator ohnehin nicht durch
|
||||
(404). Das Verstecken erspart ihnen nur einen Knopf, der eine
|
||||
Absage bringt -- es IST nicht die Sicherung.
|
||||
|
||||
DER CODE WIRD EINMAL GEZEIGT UND NIE NACHGELADEN. Er steht nur in
|
||||
der Antwort auf das Anlegen; danach gibt es ihn nirgends mehr.
|
||||
Deshalb bleibt der Dialog nach dem Anlegen OFFEN und schliesst sich
|
||||
nicht von selbst -- ein Fenster, das sich mit dem einzigen
|
||||
Zugangscode schliesst, bevor jemand ihn kopiert hat, ist ein
|
||||
Datenverlust mit Ansage. */
|
||||
function creatorAnlegenBereit(ich) {
|
||||
const knopf = document.getElementById('creator-neu');
|
||||
const dialog = document.getElementById('creator-dialog');
|
||||
if (!knopf || !dialog || !ich) return;
|
||||
if (!['admin', 'manager'].includes(ich.rolle)) return;
|
||||
knopf.hidden = false;
|
||||
|
||||
const f = (id) => document.getElementById(id);
|
||||
const zuruecksetzen = () => {
|
||||
f('creator-form').reset();
|
||||
f('cn-code-zeile').hidden = true;
|
||||
f('cn-fehler').textContent = '';
|
||||
f('cn-speichern').hidden = false;
|
||||
f('cn-zu').textContent = 'Abbrechen';
|
||||
};
|
||||
|
||||
knopf.addEventListener('click', async () => {
|
||||
zuruecksetzen();
|
||||
/* Die Scoutliste kommt vom Server -- und zwar dieselbe, die er
|
||||
beim Anlegen auch annimmt. Eine im Browser zusammengestellte
|
||||
Liste waere die zweite Wahrheit, und die beiden laufen
|
||||
auseinander. */
|
||||
try {
|
||||
const a = await hole('/workspace/api/creator-anlegen/scouts');
|
||||
const scouts = a.ok ? (await a.json()).scouts || [] : [];
|
||||
const w = f('cn-scout');
|
||||
w.textContent = '';
|
||||
const eigen = document.createElement('option');
|
||||
eigen.value = ''; eigen.textContent = `${ich.name} (du)`;
|
||||
w.append(eigen);
|
||||
for (const sc of scouts) {
|
||||
const o = document.createElement('option');
|
||||
o.value = String(sc.id); o.textContent = sc.name;
|
||||
w.append(o);
|
||||
}
|
||||
/* Gibt es keinen Scout, gibt es auch nichts zu waehlen -- ein
|
||||
Auswahlfeld mit genau einem Eintrag ist eine Frage ohne
|
||||
Antwortmoeglichkeit. */
|
||||
f('cn-scout-zeile').hidden = scouts.length === 0;
|
||||
} catch { f('cn-scout-zeile').hidden = true; }
|
||||
dialog.showModal();
|
||||
f('cn-name').focus();
|
||||
});
|
||||
|
||||
f('cn-zu').addEventListener('click', () => dialog.close());
|
||||
|
||||
f('creator-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const name = f('cn-name').value.trim();
|
||||
if (name.length < 2) { f('cn-fehler').textContent = 'Der Name fehlt.'; return; }
|
||||
const knopfS = f('cn-speichern');
|
||||
knopfS.disabled = true;
|
||||
f('cn-fehler').textContent = '';
|
||||
try {
|
||||
const a = await hole('/workspace/api/creator-anlegen', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, betreuer_id: f('cn-scout').value || null }),
|
||||
});
|
||||
const antwort = await a.json().catch(() => ({}));
|
||||
if (!a.ok) {
|
||||
f('cn-fehler').textContent = antwort.fehler || 'Ging nicht.';
|
||||
return;
|
||||
}
|
||||
f('cn-code-name').textContent = antwort.name;
|
||||
f('cn-code').textContent = antwort.code;
|
||||
f('cn-code-zeile').hidden = false;
|
||||
knopfS.hidden = true;
|
||||
f('cn-zu').textContent = 'Fertig';
|
||||
/* Die Liste dahinter wird neu geladen, sobald der Dialog zugeht
|
||||
-- der neue Creator soll da stehen, wo man ihn erwartet. */
|
||||
dialog.addEventListener('close', () => location.reload(), { once: true });
|
||||
} catch {
|
||||
f('cn-fehler').textContent = 'Ging nicht.';
|
||||
} finally { knopfS.disabled = false; }
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
let ich;
|
||||
try {
|
||||
@@ -292,6 +393,7 @@
|
||||
ich = await a.json();
|
||||
} catch { return; }
|
||||
window.werZeigen?.(ich);
|
||||
creatorAnlegenBereit(ich);
|
||||
|
||||
let d;
|
||||
try {
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -211,10 +211,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -134,10 +134,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -143,14 +143,14 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -76,11 +76,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+8
-8
@@ -9,16 +9,16 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
||||
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
||||
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
||||
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
||||
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
||||
Dialog zu sehen bekam. -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -154,9 +154,9 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609070414"></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414"></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414"></script>
|
||||
<script src="assets/js/chat.js?v=202609070414"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423"></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423"></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423"></script>
|
||||
<script src="assets/js/chat.js?v=202609070423"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -166,12 +166,12 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/content.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/content.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -95,10 +95,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="gate">
|
||||
@@ -186,6 +186,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/gate.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/gate.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -257,11 +257,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -211,10 +211,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414"></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414"></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414"></script>
|
||||
<script src="assets/js/leistung.js?v=202609070414"></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423"></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423"></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423"></script>
|
||||
<script src="assets/js/leistung.js?v=202609070423"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -123,10 +123,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -201,11 +201,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -93,11 +93,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/report.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/report.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -126,10 +126,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
||||
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -221,10 +221,10 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/start.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/start.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -72,11 +72,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -113,10 +113,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||
Dialog "Creator anlegen". -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -45,10 +48,61 @@
|
||||
Eine Karte je Creator – mit einem Blick sehen, wo es hakt und wo es läuft.
|
||||
</p>
|
||||
</div>
|
||||
<!-- Der Knopf steht HIER und nicht in der Personenverwaltung
|
||||
(07.09.2026). Ein Manager kommt dort gar nicht hinein, und er
|
||||
soll es auch nicht -- dahinter liegen Rollen, Codes, Sperren und
|
||||
das Protokoll. Hier dagegen sieht er ohnehin seine Creator; der
|
||||
neue erscheint eine Zeile weiter unten. Das Skript blendet den
|
||||
Knopf für Scouts und Creator aus, der Server lässt sie ohnehin
|
||||
nicht durch. -->
|
||||
<button type="button" class="knopf knopf--klein" id="creator-neu" hidden>Creator anlegen</button>
|
||||
</section>
|
||||
|
||||
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
|
||||
|
||||
<!-- Creator anlegen. Derselbe Dialog-Aufbau wie überall sonst. -->
|
||||
<dialog class="dialog" id="creator-dialog">
|
||||
<form method="dialog" class="neu neu--blank" id="creator-form">
|
||||
<h2 class="dialog__titel">Creator anlegen</h2>
|
||||
<p class="dialog__unter">
|
||||
Der Zugangscode erscheint genau einmal – danach ist er weg und
|
||||
kann nur neu gesetzt werden.
|
||||
</p>
|
||||
|
||||
<div class="neu__zeile">
|
||||
<label class="feld-schild" for="cn-name">Name</label>
|
||||
<input id="cn-name" maxlength="60" required autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="neu__zeile" id="cn-scout-zeile" hidden>
|
||||
<label class="feld-schild" for="cn-scout">Betreuung</label>
|
||||
<select id="cn-scout"></select>
|
||||
<p class="feld-hinweis">
|
||||
Ohne Auswahl betreust du ihn selbst. Zur Auswahl stehen nur
|
||||
deine eigenen Scouts.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Der Code. Steht erst nach dem Anlegen da und wird nie
|
||||
nachgeladen -- die Seite ist die einzige Stelle, an der er je
|
||||
zu sehen ist. -->
|
||||
<div class="neu__zeile" id="cn-code-zeile" hidden>
|
||||
<p class="feld-schild">Zugangscode für <span id="cn-code-name"></span></p>
|
||||
<p class="code-anzeige" id="cn-code"></p>
|
||||
<p class="feld-hinweis">
|
||||
Jetzt weitergeben. Nach dem Schließen ist er nicht mehr abrufbar.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="fehler" id="cn-fehler" role="alert" aria-live="polite"></p>
|
||||
|
||||
<div class="dialog__knoepfe">
|
||||
<button type="submit" class="knopf knopf--klein" id="cn-speichern">Anlegen</button>
|
||||
<button type="button" class="abmelden" id="cn-zu">Schließen</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<!-- FRÜHWARNUNG (06.09.2026).
|
||||
|
||||
Steht GANZ OBEN, noch vor den Summen. Die Summen beantworten "wie
|
||||
@@ -82,10 +136,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609070414" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609070423" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609070423" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -177,10 +177,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609070414" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609070423" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609070423" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user