Die 60 Aufgaben aus Teil 2 -- und kein Wort zu viel im Browser
Kapitel 14 des Anforderungsdokuments: "Alle Aufgaben dieses Katalogs
koennen 1:1 als Vorlagen in die App importiert werden -- inklusive
Kategorie, empfohlener Rolle und Frequenz. So ist das Aufgaben-Board ab
dem ersten Tag vollstaendig befuellt, statt leer zu starten."
DER KATALOG. 60 Aufgaben, Verteilung wie im Dokument: 12 Aufbau, 9
Alltag, 5 vor / 5 waehrend / 4 nach dem Live, 8 Content, 5 Woche, 5
Monat, 7 Community. Neun Etappen statt sechs Phasen -- Phase 3 zerfaellt
in Vor/Waehrend/Nach und Phase 5 in Woche/Monat, und das sind fuer den,
der davorsitzt, verschiedene Momente. "Waehrend des Lives" sucht man
nicht in derselben Liste wie "einmal im Monat".
Die TEXTE sind neu. Das Dokument nennt nur die Titel, und ein Titel
allein ("Eskalationsregeln definieren") sagt nicht, woran man erkennt,
dass man fertig ist. Jeder Satz nennt das EINE, was zaehlt -- nicht
drei, denn wer sich fuenf Dinge vornimmt, macht keines.
Uebernehmen legt eine ganz normale Aufgabe an, einzeln oder eine ganze
Etappe. Die Kategorie wandert mit: Ohne sie muesste man 60-mal von Hand
einsortieren, was im Dokument bereits danebensteht -- und niemand
merkte es, weil die Aufgabe ja da ist. Genau das prueft die neue
Pruefung ausdruecklich.
KEINE NEUE ADRESSE, kein neuer Feldname mit dem Rollennamen darin: Der
Katalog kommt unter "katalog" in der vorhandenen Antwort, das
Uebernehmen ueber die vorhandene Route mit einer neuen Art. Wer ihn
nicht bekommt, sieht `null` -- und ein Manager, der die Art trotzdem
schickt, bekommt WORTGLEICH dieselbe Absage wie fuer eine erfundene.
ZWEI SELBSTKORREKTUREN, beide von derselben Sorte:
* Die Kategorien waren auf ACHT zusammengefasst, begruendet damit,
dreizehn Knoepfe seien auf einem Handy unbedienbar. Gebaut ist aber
ein AUSWAHLFELD, keine Knopfleiste -- die Begruendung passte nicht
zu dem, was ich getan hatte, und haette eine Uebersetzungstabelle
noetig gemacht ("Branding gehoert zu Planung"), die spaeter niemand
nachvollzieht. Jetzt sind es die vierzehn des Dokuments, und jede
Aufgabe traegt genau die Kategorie, die danebensteht.
* Zwei Erwartungen in meiner eigenen Pruefung waren veraltet, beide
durch Aenderungen, die ich absichtlich gemacht hatte. Die eine
suchte woertlich nach "Clipping & Schnitt" -- nach dem Umbenennen
haette sie nach etwas gesucht, das es nicht mehr gibt, und waere
gruen gewesen, ohne etwas zu pruefen. Die Namen kommen jetzt aus
derselben Quelle wie die Oberflaeche.
UND WIEDER HAT ES DAS BILDSCHIRMFOTO GEZEIGT, nicht der Code: Die
Kopfleiste sagte auf jeder Seite "Creator Workspace" -- fuer jemanden,
der moderiert statt einen Kanal aufzubauen, der falsche Name. Sie folgt
jetzt demselben Weg wie die Zierzeile auf der Startseite. Nur der
Verweis wird umgeschrieben, der Seitenname dahinter bleibt; das
geschuetzte Leerzeichen ebenfalls, sonst faellt die Leiste auf schmalen
Handys in zwei Zeilen.
BEWUSST NICHT ANGEFASST: Im Hintergrundbild steht schwach "SPICY
MEDIA". Es ist kein Element im HTML, sondern in die buehne-*.webp
eingebacken -- dafuer braeuchte es einen zweiten Bildersatz. Nachgesehen
statt vermutet: Im DOM der Seite kommt der Text nicht vor.
GEPRUEFT: pruef-modi-katalog (29, neu), pruef-modi-kategorien (25),
pruef-modi-wortleck (4), pruef-rollen (113), pruef-kopf-messen,
pruef-vorlagen, pruef-aufgabenbrett.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
/* =====================================================================
|
||||
DER AUFGABENKATALOG AUS TEIL 2 DES ANFORDERUNGSDOKUMENTS
|
||||
|
||||
Kapitel 14: "Alle Aufgaben dieses Katalogs koennen 1:1 als Vorlagen in
|
||||
die App importiert werden -- inklusive Kategorie, empfohlener Rolle
|
||||
und Frequenz. So ist das Aufgaben-Board ab dem ersten Tag vollstaendig
|
||||
befuellt, statt leer zu starten."
|
||||
|
||||
GEPRUEFT WIRD NICHT NUR, DASS ER DA IST. Ein Katalog, den man ansehen,
|
||||
aber nicht uebernehmen kann, ist eine Speisekarte ohne Kueche. Und ein
|
||||
uebernommener Eintrag, bei dem die Kategorie unterwegs verlorengeht,
|
||||
ist die Haelfte wert -- dann sortiert man 60-mal von Hand ein, was im
|
||||
Dokument bereits danebensteht.
|
||||
|
||||
DAZU DIE VERBORGENHEIT: Wer den Katalog nicht bekommt, darf auch nicht
|
||||
erfahren, dass es ihn gibt. Eine Absage muss deshalb wortgleich mit
|
||||
der fuer eine erfundene Art sein.
|
||||
===================================================================== */
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-kat2-"));
|
||||
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||
process.env.PORT = "4363";
|
||||
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
||||
process.env.SITE_ACCESS_CODE_DOGI = "DOGI-TEST-1";
|
||||
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
||||
|
||||
const express = (await import("express")).default;
|
||||
const ec = express.response.cookie;
|
||||
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
||||
import { notbremse } from "./helfer-notbremse.mjs";
|
||||
await import("./index.js");
|
||||
notbremse(180_000, "pruef-modi-katalog");
|
||||
await new Promise((r) => setTimeout(r, 700));
|
||||
const BASIS = "http://127.0.0.1:4363";
|
||||
|
||||
let fehler = 0;
|
||||
let geprueft = 0;
|
||||
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||
let offen = 0;
|
||||
const unklar = (t) => { offen++; console.log(" -- " + t); };
|
||||
|
||||
/* ---------- Personen ------------------------------------------------- */
|
||||
const { DatabaseSync } = await import("node:sqlite");
|
||||
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
||||
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
||||
const jetzt = new Date().toISOString();
|
||||
const schluessel = randomBytes(32).toString("hex");
|
||||
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
|
||||
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
|
||||
.run("code_kennung_schluessel", schluessel, jetzt);
|
||||
|
||||
function anlegen(name, rolle, code) {
|
||||
const salt = randomBytes(16).toString("hex");
|
||||
const hash = scryptSync(code, salt, 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, code_kennung, aktiv, erstellt)"
|
||||
+ " VALUES (?,?,?,?,?,?,1,?)")
|
||||
.run(name, rolle, hash, salt, 32768,
|
||||
createHmac("sha256", schluessel).update(code).digest("hex"), jetzt);
|
||||
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||
}
|
||||
const idDogi = anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
||||
const idMan = anlegen("Schulle", "manager", "CODE-MAN-0001");
|
||||
const idCreat = anlegen("Tili", "creator", "CODE-ANNA-0001");
|
||||
const idModi = anlegen("Marina", "modi", "CODE-MODI-0001");
|
||||
d.close();
|
||||
void idDogi;
|
||||
void idMan;
|
||||
|
||||
async function anmelden(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((z) => z.split(";")[0]).join("; ");
|
||||
return { status: a.status, keks };
|
||||
}
|
||||
async function hole(keks, pfad, aufruf = {}) {
|
||||
const a = await fetch(BASIS + pfad, {
|
||||
...aufruf, headers: { ...(aufruf.headers || {}), cookie: keks },
|
||||
});
|
||||
return { status: a.status, daten: await a.json().catch(() => null) };
|
||||
}
|
||||
const jsonKopf = { "Content-Type": "application/json" };
|
||||
|
||||
const modiAn = await anmelden("creator", "CODE-MODI-0001");
|
||||
const dogiAn = await anmelden("admin", "CODE-DOGI-0001");
|
||||
const manAn = await anmelden("manager", "CODE-MAN-0001");
|
||||
const creatAn = await anmelden("creator", "CODE-ANNA-0001");
|
||||
|
||||
/* ---------- 1. Ist er vollstaendig da? ------------------------------- */
|
||||
console.log("\n=== 1. Der Katalog, wie ihn das Dokument beschreibt ===\n");
|
||||
|
||||
let katalog = null;
|
||||
{
|
||||
const a = await hole(modiAn.keks, "/workspace/api/vorlagen");
|
||||
katalog = a.daten?.katalog || null;
|
||||
/* SECHZIG, fest hingeschrieben. Das Dokument wirbt mit "60+ konkrete
|
||||
Aufgaben"; faellt eine heraus, muss das auffallen. Eine Zahl, die
|
||||
sich selbst nachzaehlt, kann das nicht. */
|
||||
ok(katalog && katalog.aufgaben.length === 60,
|
||||
`${katalog?.aufgaben?.length ?? 0} Aufgaben (erwartet 60)`);
|
||||
ok(katalog && katalog.gruppen.length === 9,
|
||||
`${katalog?.gruppen?.length ?? 0} Etappen (erwartet 9)`);
|
||||
/* Die Verteilung aus Teil 2, Phase fuer Phase. Sie steht hier, weil
|
||||
eine reine Gesamtzahl auch dann stimmt, wenn zwoelf Aufgaben in der
|
||||
falschen Etappe liegen. */
|
||||
const soll = { aufbau: 12, alltag: 9, vor: 5, waehrend: 5, nach: 4,
|
||||
content: 8, woche: 5, monat: 5, community: 7 };
|
||||
const ist = {};
|
||||
for (const v of katalog?.aufgaben || []) ist[v.gruppe] = (ist[v.gruppe] || 0) + 1;
|
||||
ok(JSON.stringify(ist) === JSON.stringify(soll),
|
||||
`die Verteilung stimmt (${Object.entries(ist).map(([k, n]) => `${k}:${n}`).join(" ")})`);
|
||||
ok((katalog?.aufgaben || []).every((v) => v.titel && v.text && v.kategorie && v.rolle && v.tage),
|
||||
"jede Aufgabe hat Titel, Text, Kategorie, empfohlene Rolle und Frist");
|
||||
}
|
||||
|
||||
/* Jede Kategorie im Katalog muss es auch wirklich geben -- sonst nimmt
|
||||
der Server sie beim Uebernehmen an und lehnt sie beim Aendern ab. */
|
||||
{
|
||||
const a = await hole(modiAn.keks, "/workspace/api/aufgaben");
|
||||
const gueltig = new Set((a.daten?.kategorien || []).map((k) => k.wert));
|
||||
const unbekannt = [...new Set((katalog?.aufgaben || []).map((v) => v.kategorie))]
|
||||
.filter((k) => !gueltig.has(k));
|
||||
ok(gueltig.size > 0 && unbekannt.length === 0,
|
||||
unbekannt.length
|
||||
? `unbekannte Kategorie(n) im Katalog: ${unbekannt.join(", ")}`
|
||||
: `alle ${gueltig.size} Kategorien passen zum Katalog`);
|
||||
}
|
||||
|
||||
/* ---------- 2. Wer bekommt ihn nicht? -------------------------------- */
|
||||
console.log("\n=== 2. Und wer ihn nicht bekommt ===\n");
|
||||
for (const [name, an] of [["Manager", manAn], ["Creator", creatAn]]) {
|
||||
const a = await hole(an.keks, "/workspace/api/vorlagen");
|
||||
ok(a.status === 200 && a.daten?.katalog === null,
|
||||
`${name}: kein Katalog (${JSON.stringify(a.daten?.katalog)})`);
|
||||
/* Und der ANDERE Katalog ist bei ihnen sehr wohl da -- sonst waere
|
||||
oben auch gruen, wenn die ganze Antwort leer kaeme. */
|
||||
ok(a.daten?.aufgaben && Object.keys(a.daten.aufgaben).length > 0,
|
||||
`${name}: der Creator-Katalog dagegen schon`);
|
||||
}
|
||||
{
|
||||
const a = await hole(modiAn.keks, "/workspace/api/vorlagen");
|
||||
ok(a.daten?.aufgaben === null,
|
||||
"und der Modi bekommt umgekehrt den Creator-Katalog nicht");
|
||||
}
|
||||
|
||||
/* ---------- 3. Uebernehmen ------------------------------------------- */
|
||||
console.log("\n=== 3. Aus der Vorlage wird eine echte Aufgabe ===\n");
|
||||
{
|
||||
const nr = katalog.aufgaben.findIndex((v) => v.gruppe === "waehrend");
|
||||
const vorlage = katalog.aufgaben[nr];
|
||||
const a = await hole(modiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "katalog", nr, gruppe: "waehrend" }),
|
||||
});
|
||||
ok(a.status === 200 && a.daten?.angelegt === 1, `übernommen (${a.status})`);
|
||||
|
||||
const b = await hole(modiAn.keks, "/workspace/api/aufgaben");
|
||||
const neu = (b.daten?.aufgaben || []).find((x) => x.vorlage === vorlage.schluessel);
|
||||
ok(!!neu, `sie liegt auf seinem Brett (${neu?.titel || "nicht gefunden"})`);
|
||||
/* DIE KATEGORIE IST DER HALBE WERT DER VORLAGE. Ginge sie unterwegs
|
||||
verloren, muesste man 60-mal von Hand einsortieren, was im Dokument
|
||||
danebensteht -- und niemand merkte es, weil die Aufgabe ja da ist. */
|
||||
ok(neu?.kategorie === vorlage.kategorie,
|
||||
`mit ihrer Kategorie (${neu?.kategorie} / erwartet ${vorlage.kategorie})`);
|
||||
ok(!!neu?.frist, `und mit Frist (${neu?.frist})`);
|
||||
ok(neu?.verantwortlich_id === idModi, "und er selbst ist verantwortlich");
|
||||
}
|
||||
|
||||
/* Eine ganze Etappe auf einmal. */
|
||||
{
|
||||
const wieViele = katalog.aufgaben.filter((v) => v.gruppe === "nach").length;
|
||||
const a = await hole(modiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "katalog_alle", gruppe: "nach" }),
|
||||
});
|
||||
ok(a.status === 200 && a.daten?.angelegt === wieViele,
|
||||
`eine ganze Etappe auf einmal: ${a.daten?.angelegt} von ${wieViele}`);
|
||||
}
|
||||
|
||||
/* ---------- 4. DogFather traegt fuer jemanden ein -------------------- */
|
||||
console.log("\n=== 4. DogFather trägt für einen Modi ein ===\n");
|
||||
{
|
||||
const nr = katalog.aufgaben.findIndex((v) => v.gruppe === "monat");
|
||||
const a = await hole(dogiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "katalog", nr, gruppe: "monat", person_id: idModi }),
|
||||
});
|
||||
ok(a.status === 200 && a.daten?.angelegt === 1, `für den Modi geht es (${a.status})`);
|
||||
|
||||
/* Aber nicht an einen Creator -- sonst laege eine Aufgabe mit
|
||||
Modi-Kategorie an jemandem, der die Kategorien gar nicht kennt, und
|
||||
die Trennung waere von innen aufgeweicht. */
|
||||
const b = await hole(dogiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "katalog", nr, gruppe: "monat", person_id: idCreat }),
|
||||
});
|
||||
ok(b.status === 404, `an einen Creator dagegen nicht (${b.status})`);
|
||||
|
||||
/* Und ohne Person gar nicht -- eine Aufgabe ohne Adressaten ist ein
|
||||
Zettel. */
|
||||
const c = await hole(dogiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "katalog", nr, gruppe: "monat" }),
|
||||
});
|
||||
ok(c.status === 400 && /Person/i.test(c.daten?.fehler || ""),
|
||||
`ohne Person wird gefragt (${c.status} ${c.daten?.fehler || ""})`);
|
||||
}
|
||||
|
||||
/* ---------- 5. Der heikelste Fall ------------------------------------ */
|
||||
console.log("\n=== 5. Ein Manager versucht es trotzdem ===\n");
|
||||
{
|
||||
const a = await hole(manAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "katalog", nr: 0, gruppe: "aufbau", person_id: idModi }),
|
||||
});
|
||||
const b = await hole(manAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "gibtesnicht", nr: 0 }),
|
||||
});
|
||||
/* WORTGLEICH. Ein eigener Fehlertext waere die Auskunft, dass es die
|
||||
Art gibt -- und damit, dass es den Katalog gibt. */
|
||||
ok(a.status === b.status && JSON.stringify(a.daten) === JSON.stringify(b.daten),
|
||||
`klingt wie eine erfundene Art (${a.status} ${JSON.stringify(a.daten)})`);
|
||||
}
|
||||
|
||||
/* ---------- 6. Im Browser -------------------------------------------- */
|
||||
console.log("\n=== 6. Das Brett im echten Browser ===\n");
|
||||
{
|
||||
const { chromium } = await import(
|
||||
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||||
const browser = await chromium.launch();
|
||||
try {
|
||||
const kontext = await browser.newContext({ viewport: { width: 1280, height: 1000 } });
|
||||
const seite = await kontext.newPage();
|
||||
const konsole = [];
|
||||
seite.on("console", (e) => { if (e.type() === "error") konsole.push(e.text()); });
|
||||
seite.on("pageerror", (e) => konsole.push("Absturz: " + e.message));
|
||||
await seite.goto(BASIS + "/workspace/", { waitUntil: "domcontentloaded" });
|
||||
await seite.click('.rolle[data-rolle="creator"]');
|
||||
await seite.fill("#code", "CODE-MODI-0001");
|
||||
await seite.click("button[type=submit]");
|
||||
await seite.waitForURL("**/start.html", { timeout: 15000 });
|
||||
await seite.goto(BASIS + "/workspace/aufgaben.html", { waitUntil: "networkidle" });
|
||||
await seite.waitForTimeout(1200);
|
||||
|
||||
/* Aufklappen, falls es zugeklappt startet. */
|
||||
const zu = await seite.evaluate(() =>
|
||||
document.getElementById("vorlagenbrett")?.dataset.auf !== "ja");
|
||||
if (zu) {
|
||||
await seite.click(".vorlagenbrett__kopf").catch(() => {});
|
||||
await seite.waitForTimeout(700);
|
||||
}
|
||||
|
||||
const bild = await seite.evaluate(() => ({
|
||||
sichtbar: !document.getElementById("vorlagenbrett")?.hidden,
|
||||
etappen: [...document.querySelectorAll(".stufenleiter__knopf .stufenleiter__name")]
|
||||
.map((z) => z.textContent.trim()),
|
||||
karten: document.querySelectorAll(".vorlagenbrett__karte").length,
|
||||
text: document.body.innerText,
|
||||
kopf: (document.querySelector(".kopfleiste .marke__text") || {}).textContent || "",
|
||||
}));
|
||||
ok(bild.sichtbar, "das Brett steht da");
|
||||
ok(bild.etappen.length === 9, `mit neun Etappen (${bild.etappen.join(" · ")})`);
|
||||
ok(bild.karten > 0, `und ${bild.karten} Karten in der offenen Etappe`);
|
||||
/* Der Katalog des ANDEREN darf nirgends durchschlagen. */
|
||||
ok(!/vier Bereichen und vier Stufen/.test(bild.text),
|
||||
"kein Wort vom Creator-Katalog");
|
||||
/* DIE KOPFLEISTE. Auf jeder Seite steht dort fest "Creator
|
||||
Workspace" -- fuer jemanden, der moderiert statt einen Kanal
|
||||
aufzubauen, ist das schlicht der falsche Name. Aufgefallen auf
|
||||
dem Bildschirmfoto, wie schon die Zierzeile auf der Startseite. */
|
||||
ok(bild.kopf.includes("Team") && !bild.kopf.includes("Creator"),
|
||||
`die Kopfleiste nennt seinen Betrieb ("${bild.kopf.trim()}")`);
|
||||
/* Und der Seitenname dahinter steht noch da -- ihn beim Austausch
|
||||
zu verlieren waere lautlos passiert. */
|
||||
ok(/Aufgaben/.test(bild.kopf), "der Seitenname dahinter blieb erhalten");
|
||||
ok(konsole.length === 0,
|
||||
konsole.length ? `Fehler in der Konsole: ${konsole[0]}` : "keine Fehler in der Konsole");
|
||||
await seite.screenshot({ path: "pruef-modi-katalog.png", fullPage: false });
|
||||
console.log(" -- Bildschirmfoto: server/pruef-modi-katalog.png");
|
||||
await kontext.close();
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- 7. Gegenprobe -------------------------------------------- */
|
||||
console.log("\n=== 7. Gegenprobe: kann das hier ueberhaupt scheitern? ===\n");
|
||||
{
|
||||
/* Eine Vorlagennummer, die es nicht gibt. Kaeme dafuer ein 200,
|
||||
wuerden die Haken oben nichts beweisen -- dann sagt der Server zu
|
||||
allem ja. */
|
||||
const a = await hole(modiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "katalog", nr: 9999, gruppe: "aufbau" }),
|
||||
});
|
||||
ok(a.status === 400, `eine erfundene Vorlagennummer wird abgelehnt (${a.status})`);
|
||||
const b = await hole(modiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
|
||||
method: "POST", headers: jsonKopf,
|
||||
body: JSON.stringify({ art: "katalog_alle", gruppe: "gibtesnicht" }),
|
||||
});
|
||||
ok(b.status === 400, `und eine erfundene Etappe ebenso (${b.status})`);
|
||||
}
|
||||
|
||||
console.log(`\n${fehler ? "FEHLGESCHLAGEN" : "BESTANDEN"} -- `
|
||||
+ `${geprueft} geprueft, ${fehler} Fehler, ${offen} nicht nachsehbar`);
|
||||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
||||
process.exit(fehler ? 1 : 0);
|
||||
@@ -242,17 +242,21 @@ console.log("\n=== 5. Das Feld im echten Formular ===\n");
|
||||
ok(z.sichtbar, "der Modi sieht das Feld");
|
||||
ok(z.auswahl.length >= 7,
|
||||
`mit ${z.auswahl.length} Eintraegen (${z.auswahl.slice(1, 4).join(", ")} …)`);
|
||||
/* KEIN CREATOR-VORLAGENBRETT. Es haelt 80 Aufgaben fuer den Aufbau
|
||||
eines Kanals bereit -- fuer jemanden, der moderiert, ist davon
|
||||
nichts gedacht. Gefunden auf dem Bildschirmfoto, nicht im Code:
|
||||
Die Kachel laedt fehlerfrei, sie zeigt nur das Falsche. */
|
||||
/* DAS VORLAGENBRETT (berichtigt 10.09.2026).
|
||||
|
||||
Hier stand "und kein Creator-Vorlagenbrett" -- richtig, solange
|
||||
es fuer einen Modi ueberhaupt keinen Katalog gab. Seit der
|
||||
Katalog aus Teil 2 da ist, steht das Brett wieder da, nur mit
|
||||
SEINEM Inhalt. Geprueft wird deshalb ab jetzt beides: dass es
|
||||
da ist, und dass der fremde nicht durchschlaegt. Was der
|
||||
Katalog selbst enthaelt, prueft pruef-modi-katalog. */
|
||||
const vorlagen = await seite.evaluate(() => {
|
||||
const v = document.getElementById("vorlagenbrett");
|
||||
return { da: !!v && !v.hidden, text: document.body.innerText };
|
||||
});
|
||||
ok(!vorlagen.da, "und kein Creator-Vorlagenbrett");
|
||||
ok(vorlagen.da, "das Vorlagenbrett steht da -- mit seinem eigenen Katalog");
|
||||
ok(!/vier Bereichen und vier Stufen/.test(vorlagen.text),
|
||||
"auch nicht als Text auf der Seite");
|
||||
"und kein Wort vom Creator-Katalog");
|
||||
ok(konsole.length === 0,
|
||||
konsole.length ? `Fehler in der Konsole: ${konsole[0]}` : "keine Fehler in der Konsole");
|
||||
await seite.screenshot({ path: "pruef-modi-kategorie.png", fullPage: false });
|
||||
@@ -277,8 +281,15 @@ console.log("\n=== 5. Das Feld im echten Formular ===\n");
|
||||
ok(!z.sichtbar, "der Manager sieht das Feld nicht");
|
||||
ok(z.eintraege <= 1,
|
||||
`und die Auswahl ist gar nicht erst gefuellt (${z.eintraege} Eintrag)`);
|
||||
ok(!/Chat-Moderation|Clipping & Schnitt/.test(z.text),
|
||||
"kein Kategoriename steht auf seiner Seite");
|
||||
/* DIE NAMEN NICHT ABSCHREIBEN, SONDERN FRAGEN (berichtigt
|
||||
10.09.2026). Hier stand "Chat-Moderation|Clipping & Schnitt"
|
||||
woertlich -- und beim Umbenennen einer Kategorie suchte die
|
||||
Pruefung ploetzlich nach etwas, das es nicht mehr gibt. Sie war
|
||||
damit gruen, ohne etwas zu pruefen: genau die Sorte Haken, die
|
||||
nichts wert ist. Jetzt kommt die Liste aus derselben Quelle wie
|
||||
die Oberflaeche. */
|
||||
ok(kategorien.length > 0 && !kategorien.some((k) => z.text.includes(k.name)),
|
||||
`keiner der ${kategorien.length} Kategorienamen steht auf seiner Seite`);
|
||||
ok(konsole.length === 0,
|
||||
konsole.length ? `Fehler in der Konsole: ${konsole[0]}` : "keine Fehler in der Konsole");
|
||||
await kontext.close();
|
||||
@@ -328,8 +339,8 @@ console.log("\n=== 5. Das Feld im echten Formular ===\n");
|
||||
{
|
||||
const { kontext, seite } = await formular("creator", "CODE-MODI-0001");
|
||||
const text = await seite.evaluate(() => document.body.innerText);
|
||||
ok(/Clipping & Schnitt/.test(text),
|
||||
"beim Modi findet dieselbe Suche den Namen sehr wohl");
|
||||
ok(kategorien.length > 0 && kategorien.some((k) => text.includes(k.name)),
|
||||
"beim Modi findet dieselbe Suche die Namen sehr wohl");
|
||||
await kontext.close();
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
import express from "express";
|
||||
import {
|
||||
db, sitzungLesen, istLeitung, darfCreator, protokolliere, echteIp,
|
||||
db, sitzungLesen, istLeitung, darfCreator, protokolliere, echteIp, kategorienFuer,
|
||||
} from "./workspace.js";
|
||||
|
||||
export const vorlagenRouter = express.Router();
|
||||
@@ -796,6 +796,261 @@ function angemeldet(req, res, next) {
|
||||
Eskalation in vier Stufen.
|
||||
===================================================================== */
|
||||
|
||||
/* =====================================================================
|
||||
DER AUFGABENKATALOG AUS TEIL 2 DES ANFORDERUNGSDOKUMENTS (10.09.2026)
|
||||
|
||||
60 fertig ausformulierte Aufgaben, sortiert nach den sechs Etappen des
|
||||
Dokuments. Kapitel 14 sagt dazu: "Alle Aufgaben dieses Katalogs koennen
|
||||
1:1 als Vorlagen in die App importiert werden -- inklusive Kategorie,
|
||||
empfohlener Rolle und Frequenz. So ist das Aufgaben-Board ab dem
|
||||
ersten Tag vollstaendig befuellt, statt leer zu starten."
|
||||
|
||||
NEUN GRUPPEN, NICHT SECHS. Das Dokument hat sechs Phasen, aber Phase 3
|
||||
zerfaellt in Vor/Waehrend/Nach und Phase 5 in Woche/Monat -- und das
|
||||
sind fuer den, der davorsitzt, verschiedene Momente. "Waehrend des
|
||||
Lives" sucht man nicht in derselben Liste wie "einmal im Monat".
|
||||
|
||||
DIE KATEGORIEN SIND DIE DES DOKUMENTS, nicht meine. Beim ersten Anlauf
|
||||
hatte ich sie auf acht zusammengefasst, mit der Begruendung, dreizehn
|
||||
Knoepfe seien auf einem Handy unbedienbar. Das war eine plausible
|
||||
Erklaerung fuer etwas Falsches: Gebaut ist ein AUSWAHLFELD, keine
|
||||
Knopfleiste, und darin sind vierzehn Eintraege voellig unauffaellig.
|
||||
Jetzt passt jede Aufgabe genau auf die Kategorie, die im Dokument
|
||||
danebensteht -- ohne Uebersetzung, die spaeter niemand nachvollziehen
|
||||
kann.
|
||||
|
||||
DIE TEXTE SIND NEU. Das Dokument nennt nur die Titel; ein Titel allein
|
||||
("Eskalationsregeln definieren") sagt nicht, woran man erkennt, dass
|
||||
man fertig ist. Jeder Satz hier nennt deshalb das eine, was zaehlt --
|
||||
nicht drei, denn wer sich fuenf Dinge vornimmt, macht keines.
|
||||
|
||||
EINIGE AUFGABEN AUS PHASE 1 SIND LAENGST ERLEDIGT (die Subdomain
|
||||
steht, das App-Symbol auch). Sie bleiben trotzdem drin: Der Katalog
|
||||
ist eine Speisekarte, kein Auftrag. Wer ihn spaeter fuer ein zweites
|
||||
Team benutzt, braucht sie wieder.
|
||||
===================================================================== */
|
||||
|
||||
export const MODI_GRUPPEN = [
|
||||
{ schluessel: "aufbau", name: "Aufbau & Einrichtung",
|
||||
text: "Einmaliges, bevor das Team startklar ist." },
|
||||
{ schluessel: "alltag", name: "Täglicher Betrieb",
|
||||
text: "Was an (fast) jedem Tag anfällt." },
|
||||
{ schluessel: "vor", name: "Vor dem Live",
|
||||
text: "Die letzten 30 bis 60 Minuten davor." },
|
||||
{ schluessel: "waehrend", name: "Während des Lives",
|
||||
text: "Was jetzt passiert, lässt sich später nicht nachholen." },
|
||||
{ schluessel: "nach", name: "Nach dem Live",
|
||||
text: "Solange man es noch weiß." },
|
||||
{ schluessel: "content", name: "Content & Reichweite",
|
||||
text: "Aus einer Sendung werden viele Videos." },
|
||||
{ schluessel: "woche", name: "Jede Woche",
|
||||
text: "Der Rhythmus, der das Team zusammenhält." },
|
||||
{ schluessel: "monat", name: "Jeden Monat",
|
||||
text: "Der Blick, den der Alltag nicht hergibt." },
|
||||
{ schluessel: "community", name: "Community & Wachstum",
|
||||
text: "Wiedererkannt zu werden ist der halbe Grund wiederzukommen." },
|
||||
];
|
||||
|
||||
export const MODI_KATALOG = [
|
||||
/* ---------- Phase 1 — Aufbau & Einrichtung ------------------------- */
|
||||
{ schluessel: "mk-domain", gruppe: "aufbau", kategorie: "technik", rolle: "DogFather", tage: 14,
|
||||
titel: "Domain und Subdomain für die App einrichten",
|
||||
text: "Eine eigene Subdomain, nicht ein Unterordner. Nur so lässt sich die App auf dem Handy wirklich installieren statt nur als Verknüpfung abzulegen." },
|
||||
{ schluessel: "mk-symbol", gruppe: "aufbau", kategorie: "branding", rolle: "DogFather", tage: 14,
|
||||
titel: "App-Symbol, Startbild und Farben aus dem Logo ableiten",
|
||||
text: "Einmal auf einem echten Handy ansehen, nicht nur am großen Bildschirm. Ein Symbol, das bei 48 Pixeln zu Matsch wird, fällt sonst erst nach der Installation auf." },
|
||||
{ schluessel: "mk-kategorien", gruppe: "aufbau", kategorie: "organisation", rolle: "DogFather", tage: 7,
|
||||
titel: "Grundkategorien im Aufgaben-Board anlegen",
|
||||
text: "Chat-Moderation, Clipping, Social Media, Events, Technik. Lieber mit wenigen anfangen und ergänzen als mit zwanzig, von denen zwölf leer bleiben." },
|
||||
{ schluessel: "mk-rechte-hand", gruppe: "aufbau", kategorie: "team", rolle: "DogFather", tage: 7,
|
||||
titel: "Die rechte Hand festlegen und einladen",
|
||||
text: "Eine Person, nicht zwei. Wer vertreten soll, muss im Zweifel allein entscheiden dürfen – sonst ist es keine Vertretung." },
|
||||
{ schluessel: "mk-erste-modis", gruppe: "aufbau", kategorie: "team", rolle: "DogFather & rechte Hand", tage: 14,
|
||||
titel: "Den ersten Kreis an Modis einladen (Probezeit)",
|
||||
text: "Lieber drei, die man kennt, als zehn, die man einschätzen muss. Erweitern geht immer, zurücknehmen kostet Vertrauen." },
|
||||
{ schluessel: "mk-rulebook", gruppe: "aufbau", kategorie: "organisation", rolle: "DogFather & rechte Hand", tage: 14,
|
||||
titel: "Team-Regeln und Erwartungen aufschreiben",
|
||||
text: "Was schriftlich steht, muss man nicht dreimal erklären – und niemand muss raten, was gemeint war. Eine Seite reicht." },
|
||||
{ schluessel: "mk-eskalation", gruppe: "aufbau", kategorie: "organisation", rolle: "DogFather & rechte Hand", tage: 7,
|
||||
titel: "Eskalationsregeln festlegen",
|
||||
text: "Ab wann entscheidet ein Modi nicht mehr allein? Wer das erst im Ernstfall klärt, klärt es zu spät und vor Publikum." },
|
||||
{ schluessel: "mk-kanaele", gruppe: "aufbau", kategorie: "kommunikation", rolle: "DogFather", tage: 3,
|
||||
titel: "Team-Gruppenchat und Kategorie-Kanäle anlegen",
|
||||
text: "Ein Kanal je Zuständigkeit, nicht je Person. Wer wechselt, nimmt sonst das Gespräch mit." },
|
||||
{ schluessel: "mk-kalender-start", gruppe: "aufbau", kategorie: "planung", rolle: "rechte Hand", tage: 3,
|
||||
titel: "Team-Kalender mit den ersten Live-Terminen füllen",
|
||||
text: "Zwei feste Tage schlagen fünf zufällige. Ein Termin, der im Kalender steht, lässt sich einteilen – einer im Kopf nicht." },
|
||||
{ schluessel: "mk-automod", gruppe: "aufbau", kategorie: "chat", rolle: "Modi (Senior)", tage: 7,
|
||||
titel: "Automatische Wortfilter für den Live-Chat einrichten",
|
||||
text: "Erst die Liste, dann der Stream. Was der Filter wegnimmt, muss niemand von Hand wegnehmen – und niemand liest es." },
|
||||
{ schluessel: "mk-checkliste-vorlage", gruppe: "aufbau", kategorie: "organisation", rolle: "DogFather & rechte Hand", tage: 7,
|
||||
titel: "Vorlage für die Vor-/Während-/Nach-Live-Checkliste anlegen",
|
||||
text: "Einmal geschrieben, bei jedem Live benutzt. Nach drei Sendungen anpassen – dann weiß man, was wirklich draufgehört." },
|
||||
{ schluessel: "mk-social-verknuepfen", gruppe: "aufbau", kategorie: "social", rolle: "Modi (Social Media)", tage: 14,
|
||||
titel: "Social-Media-Kanäle mit der App verknüpfen",
|
||||
text: "TikTok, Instagram, YouTube. Ohne Verknüpfung trägt jemand die Zahlen von Hand ab – und irgendwann nicht mehr." },
|
||||
|
||||
/* ---------- Phase 2 — Täglicher Betrieb ---------------------------- */
|
||||
{ schluessel: "mk-teamchat-lesen", gruppe: "alltag", kategorie: "kommunikation", rolle: "rechte Hand", tage: 1,
|
||||
titel: "Team-Chat auf offene Fragen durchsehen",
|
||||
text: "Einmal am Tag reicht, aber es muss wirklich einmal sein. Eine Frage, die zwei Tage steht, wird nicht mehr gestellt." },
|
||||
{ schluessel: "mk-board-sichten", gruppe: "alltag", kategorie: "organisation", rolle: "DogFather & rechte Hand", tage: 1,
|
||||
titel: "Aufgaben-Board sichten: offen, überfällig, erledigt",
|
||||
text: "Die überfälligen zuerst. Eine Aufgabe, die dauerhaft rot steht, ist entweder zu groß oder gehört abgebrochen." },
|
||||
{ schluessel: "mk-termin-bestaetigen", gruppe: "alltag", kategorie: "planung", rolle: "rechte Hand", tage: 1,
|
||||
titel: "Den heutigen Live-Termin bestätigen",
|
||||
text: "Bestätigen oder absagen – beides früh. Wer nichts sagt, lässt das halbe Team auf Verdacht bereitstehen." },
|
||||
{ schluessel: "mk-ankuendigung", gruppe: "alltag", kategorie: "social", rolle: "Modi (Social Media)", tage: 1,
|
||||
titel: "Ankündigung für das heutige Live posten",
|
||||
text: "30 bis 60 Minuten vorher. Sie bündelt die, die ohnehin kommen wollten – und ein voller Start zieht neue nach." },
|
||||
{ schluessel: "mk-kommentare", gruppe: "alltag", kategorie: "community", rolle: "Modi (Community)", tage: 1,
|
||||
titel: "Kommentare und Nachrichten beantworten",
|
||||
text: "Auch die kurzen. Eine Antwort nach drei Tagen liest sich wie keine." },
|
||||
{ schluessel: "mk-begruessen", gruppe: "alltag", kategorie: "community", rolle: "Modi", tage: 1,
|
||||
titel: "Neue Mitglieder im Chat begrüßen",
|
||||
text: "Mit Namen. Wer beim ersten Mal angesprochen wird, kommt deutlich häufiger wieder." },
|
||||
{ schluessel: "mk-technikcheck", gruppe: "alltag", kategorie: "technik", rolle: "DogFather", tage: 1,
|
||||
titel: "Technik-Kurzcheck vor dem Live",
|
||||
text: "Ton, Bild, Verbindung – in dieser Reihenfolge. Schlechter Ton ist der Grund Nummer eins, warum Leute wieder gehen." },
|
||||
{ schluessel: "mk-clip-taeglich", gruppe: "alltag", kategorie: "clipping", rolle: "Modi (Clipping)", tage: 1,
|
||||
titel: "Mindestens einen Clip vom Live des Vortags veröffentlichen",
|
||||
text: "Einer täglich schlägt fünf am Sonntag. Der Feed hat mehr Reichweite als der Stream – er ist der Weg dorthin." },
|
||||
{ schluessel: "mk-trial-beobachten", gruppe: "alltag", kategorie: "team", rolle: "rechte Hand", tage: 1,
|
||||
titel: "Stand der Modis in der Probezeit kurz festhalten",
|
||||
text: "Zwei Zeilen genügen. Am Ende der Probezeit will man nicht aus der Erinnerung entscheiden müssen." },
|
||||
|
||||
/* ---------- Phase 3 — Vor dem Live --------------------------------- */
|
||||
{ schluessel: "mk-vor-technik", gruppe: "vor", kategorie: "technik", rolle: "DogFather", tage: 1,
|
||||
titel: "Technik-Check: Ton, Bild, Verbindung",
|
||||
text: "Eine Minute aufnehmen und mit Kopfhörern anhören. Während der Sendung hört man sich selbst nie." },
|
||||
{ schluessel: "mk-vor-ankuendigung", gruppe: "vor", kategorie: "social", rolle: "Modi (Social Media)", tage: 1,
|
||||
titel: "Ankündigung auf Social Media und in den Community-Kanälen",
|
||||
text: "Überall dasselbe Bild, überall dieselbe Uhrzeit. Zwei verschiedene Zeiten kosten mehr Zuschauer als gar keine Ankündigung." },
|
||||
{ schluessel: "mk-vor-ablauf", gruppe: "vor", kategorie: "planung", rolle: "DogFather & rechte Hand", tage: 1,
|
||||
titel: "Themen- und Ablaufplan abstimmen",
|
||||
text: "Anfang, Mitte, Schluss – je zwei Stichworte. Mehr hält beim Senden ohnehin niemand im Kopf." },
|
||||
{ schluessel: "mk-vor-schicht", gruppe: "vor", kategorie: "team", rolle: "rechte Hand", tage: 1,
|
||||
titel: "Moderatoren für die heutige Schicht einteilen",
|
||||
text: "Namentlich, nicht \"wer Zeit hat\". Zuständig ist sonst niemand, und im Ernstfall schaut man sich gegenseitig an." },
|
||||
{ schluessel: "mk-vor-ziel", gruppe: "vor", kategorie: "planung", rolle: "DogFather", tage: 1,
|
||||
titel: "Ein Ziel für die heutige Sendung festlegen",
|
||||
text: "Eine einzige Zahl. Zwei Ziele sind keines – man verfehlt beide und weiß hinterher nicht, woran es lag." },
|
||||
|
||||
/* ---------- Phase 3 — Während des Lives ---------------------------- */
|
||||
{ schluessel: "mk-live-chat", gruppe: "waehrend", kategorie: "chat", rolle: "Modi (Chat-Moderation)", tage: 1,
|
||||
titel: "Chat moderieren und neue Zuschauer begrüßen",
|
||||
text: "Spam weg, bevor jemand antwortet. Eine Diskussion über einen gelöschten Beitrag kostet mehr als der Beitrag." },
|
||||
{ schluessel: "mk-live-zeit", gruppe: "waehrend", kategorie: "planung", rolle: "rechte Hand", tage: 1,
|
||||
titel: "Ablaufplan und Zeit im Blick behalten",
|
||||
text: "Ein kurzes Zeichen genügt. Wer sendet, hat kein Gefühl für die Uhr – das ist normal und braucht jemanden daneben." },
|
||||
{ schluessel: "mk-live-interaktion", gruppe: "waehrend", kategorie: "community", rolle: "Modi", tage: 1,
|
||||
titel: "Umfragen anstoßen und auf das Ziel hinweisen",
|
||||
text: "Lieber zweimal beiläufig als einmal ausführlich. Wer erst in Minute 40 dazukommt, hat den Hinweis sonst nie gehört." },
|
||||
{ schluessel: "mk-live-clips-merken", gruppe: "waehrend", kategorie: "clipping", rolle: "Modi (Clipping)", tage: 1,
|
||||
titel: "Besondere Momente für Clips markieren",
|
||||
text: "Mit Uhrzeit, sofort. Am nächsten Tag findet niemand die Stelle wieder, an der alle gelacht haben." },
|
||||
{ schluessel: "mk-live-eskalation", gruppe: "waehrend", kategorie: "chat", rolle: "Modi (Senior)", tage: 1,
|
||||
titel: "Regelverstöße nach der Eskalationsregel behandeln",
|
||||
text: "Nach der Regel, nicht nach Gefühl. Im Chat gewinnt niemand – und eine begründete Sperre diskutiert man hinterher." },
|
||||
|
||||
/* ---------- Phase 3 — Nach dem Live -------------------------------- */
|
||||
{ schluessel: "mk-nach-aufzeichnung", gruppe: "nach", kategorie: "technik", rolle: "DogFather", tage: 1,
|
||||
titel: "Aufzeichnung sichern und ablegen",
|
||||
text: "Noch am selben Abend. Eine Aufzeichnung, die nur auf dem Sendegerät liegt, ist keine Sicherung." },
|
||||
{ schluessel: "mk-nach-clips", gruppe: "nach", kategorie: "clipping", rolle: "Modi (Clipping)", tage: 2,
|
||||
titel: "Clips schneiden und veröffentlichen",
|
||||
text: "Unter 60 Sekunden, Untertitel drauf. Der größte Teil wird ohne Ton geschaut – was dann nicht ankommt, kommt gar nicht an." },
|
||||
{ schluessel: "mk-nach-feedback", gruppe: "nach", kategorie: "kommunikation", rolle: "alle", tage: 1,
|
||||
titel: "Kurzes Team-Feedback im Team-Chat",
|
||||
text: "Eine Sache, die gut lief, eine, die man ändert. Wer sich fünf vornimmt, ändert keine." },
|
||||
{ schluessel: "mk-nach-zahlen", gruppe: "nach", kategorie: "analyse", rolle: "DogFather & rechte Hand", tage: 2,
|
||||
titel: "Statistiken und Reichweite durchsehen",
|
||||
text: "Vor allem die Verweildauer. Über 15 Minuten heißt: Die Stimmung trägt. Darunter liegt es fast nie am Thema, sondern am Anfang." },
|
||||
|
||||
/* ---------- Phase 4 — Content & Reichweite ------------------------- */
|
||||
{ schluessel: "mk-momente-sichten", gruppe: "content", kategorie: "clipping", rolle: "Modi (Clipping)", tage: 1,
|
||||
titel: "Beste Live-Momente sichten und auswählen",
|
||||
text: "Nach jedem Live, solange man noch weiß, wo es gut war. Drei Kandidaten reichen." },
|
||||
{ schluessel: "mk-clips-schneiden", gruppe: "content", kategorie: "clipping", rolle: "Modi (Clipping)", tage: 3,
|
||||
titel: "Clips schneiden, Untertitel und Cover hinzufügen",
|
||||
text: "Der erste Satz entscheidet. Über 70 Prozent steigen in den ersten drei Sekunden aus oder bleiben." },
|
||||
{ schluessel: "mk-redaktionsplan", gruppe: "content", kategorie: "planung", rolle: "rechte Hand", tage: 7,
|
||||
titel: "Redaktionsplan für die Woche befüllen",
|
||||
text: "Thema, Kanal, Zeitpunkt – je Eintrag eine Zeile. Eine Idee ohne Termin ist eine Notiz." },
|
||||
{ schluessel: "mk-veroeffentlichung-planen", gruppe: "content", kategorie: "social", rolle: "Modi (Social Media)", tage: 3,
|
||||
titel: "Veröffentlichung auf den Plattformen planen",
|
||||
text: "TikTok, Instagram, YouTube Shorts – nicht alle zur selben Minute. Dieselbe Sache dreimal gleichzeitig konkurriert mit sich selbst." },
|
||||
{ schluessel: "mk-kennzahlen", gruppe: "content", kategorie: "analyse", rolle: "rechte Hand", tage: 7,
|
||||
titel: "Reichweiten-Kennzahlen je Plattform erfassen",
|
||||
text: "Immer dieselben Zahlen, immer am selben Wochentag. Wechselnde Zahlen ergeben eine Erzählung, keinen Vergleich." },
|
||||
{ schluessel: "mk-bestformate", gruppe: "content", kategorie: "analyse", rolle: "DogFather & rechte Hand", tage: 30,
|
||||
titel: "Bestlaufende Formate und Zeiten heraussuchen",
|
||||
text: "Die drei besten und die drei schlechtesten nebeneinanderlegen. Der Unterschied steht meistens in den ersten Sekunden." },
|
||||
{ schluessel: "mk-ideen-uebernehmen", gruppe: "content", kategorie: "planung", rolle: "rechte Hand", tage: 7,
|
||||
titel: "Ideen aus dem Ideen-Board in den Redaktionsplan überführen",
|
||||
text: "Wöchentlich zwei. Ein Ideen-Board, aus dem nie etwas herausgeht, ist ein Friedhof." },
|
||||
{ schluessel: "mk-crosspromo", gruppe: "content", kategorie: "social", rolle: "Modi (Social Media)", tage: 30,
|
||||
titel: "Cross-Promotion zwischen den Kanälen prüfen",
|
||||
text: "Von TikTok zu YouTube, nicht umgekehrt. Wer kurz schaut, klickt weiter; wer lang schaut, ist schon da." },
|
||||
|
||||
/* ---------- Phase 5 — Jede Woche ----------------------------------- */
|
||||
{ schluessel: "mk-woche-kalender", gruppe: "woche", kategorie: "planung", rolle: "rechte Hand", tage: 7,
|
||||
titel: "Wochenübersicht der Live-Termine aktualisieren",
|
||||
text: "Am selben Wochentag, bevor die Woche anfängt. Ein Kalender, der der Woche hinterherläuft, wird nicht mehr gelesen." },
|
||||
{ schluessel: "mk-woche-meeting", gruppe: "woche", kategorie: "team", rolle: "DogFather & rechte Hand", tage: 7,
|
||||
titel: "Kurze Abstimmung mit allen aktiven Modis",
|
||||
text: "Fünfzehn Minuten, feste Uhrzeit. Ein Termin, der jede Woche verschoben wird, findet nicht statt." },
|
||||
{ schluessel: "mk-woche-offene", gruppe: "woche", kategorie: "organisation", rolle: "rechte Hand", tage: 7,
|
||||
titel: "Offene Aufgaben aus der Vorwoche nachverfolgen",
|
||||
text: "Erledigen, verschieben oder abbrechen – aber entscheiden. Eine Aufgabe, die dreimal mitwandert, wird nie gemacht." },
|
||||
{ schluessel: "mk-woche-trial", gruppe: "woche", kategorie: "team", rolle: "DogFather & rechte Hand", tage: 7,
|
||||
titel: "Modis in der Probezeit bewerten",
|
||||
text: "Verlängern, aufnehmen oder absagen. Eine Probezeit ohne Ende ist keine, und beide Seiten wissen es." },
|
||||
{ schluessel: "mk-woche-redaktion", gruppe: "woche", kategorie: "planung", rolle: "rechte Hand", tage: 7,
|
||||
titel: "Redaktionsplan für die kommende Woche abschließen",
|
||||
text: "Vor dem Wochenende fertig. Wer montags plant, dreht mittwochs." },
|
||||
|
||||
/* ---------- Phase 5 — Jeden Monat ---------------------------------- */
|
||||
{ schluessel: "mk-monat-bericht", gruppe: "monat", kategorie: "analyse", rolle: "DogFather & rechte Hand", tage: 30,
|
||||
titel: "Reichweiten- und Wachstumsbericht erstellen",
|
||||
text: "Follower, Aufrufe, Beteiligung – dieselben drei, jeden Monat am selben Tag. Ein Bericht mit wechselnden Zahlen ist eine Erzählung." },
|
||||
{ schluessel: "mk-monat-feedback", gruppe: "monat", kategorie: "team", rolle: "DogFather", tage: 30,
|
||||
titel: "Rückmeldung der Modis einholen",
|
||||
text: "Einzeln fragen, nicht in der Runde. In der Runde sagt niemand, was ihn stört." },
|
||||
{ schluessel: "mk-monat-regeln", gruppe: "monat", kategorie: "organisation", rolle: "DogFather", tage: 30,
|
||||
titel: "Regeln und Eskalationsregeln nachziehen",
|
||||
text: "Nur, wenn ein Fall dazu Anlass gab. Regeln, die sich monatlich ändern, kennt niemand auswendig." },
|
||||
{ schluessel: "mk-monat-shoutouts", gruppe: "monat", kategorie: "team", rolle: "DogFather", tage: 30,
|
||||
titel: "Wertschätzung für besonders aktive Modis planen",
|
||||
text: "Öffentlich und konkret. \"Danke euch allen\" erreicht niemanden." },
|
||||
{ schluessel: "mk-monat-formate", gruppe: "monat", kategorie: "planung", rolle: "DogFather & rechte Hand", tage: 30,
|
||||
titel: "Große Content-Formate für den nächsten Monat planen",
|
||||
text: "Eines, nicht drei. Ein großes Format, das stattfindet, schlägt drei, die verschoben werden." },
|
||||
|
||||
/* ---------- Phase 6 — Community & Wachstum ------------------------- */
|
||||
{ schluessel: "mk-umfrage", gruppe: "community", kategorie: "community", rolle: "Modi (Community)", tage: 30,
|
||||
titel: "Community-Umfrage zu neuen Formaten starten",
|
||||
text: "Höchstens drei Antwortmöglichkeiten. Bei acht entscheidet die Reihenfolge, nicht die Meinung." },
|
||||
{ schluessel: "mk-event", gruppe: "community", kategorie: "events", rolle: "rechte Hand", tage: 30,
|
||||
titel: "Community-Event planen",
|
||||
text: "Zuschauer-Challenge, Q&A-Live, gemeinsamer Abend. Termin zuerst, Inhalt danach – sonst wird nie einer daraus." },
|
||||
{ schluessel: "mk-stammzuschauer", gruppe: "community", kategorie: "community", rolle: "Modi", tage: 7,
|
||||
titel: "Stammzuschauer erkennen und wertschätzen",
|
||||
text: "Eine kurze Liste führen. Wiedererkannt zu werden ist der halbe Grund wiederzukommen." },
|
||||
{ schluessel: "mk-willkommen", gruppe: "community", kategorie: "community", rolle: "Modi", tage: 7,
|
||||
titel: "Willkommens-Routine für neue Mitglieder pflegen",
|
||||
text: "Ein Satz, der nicht nach Textbaustein klingt. Wer merkt, dass ein Automat schreibt, antwortet nicht." },
|
||||
{ schluessel: "mk-feedbackkanal", gruppe: "community", kategorie: "community", rolle: "rechte Hand", tage: 7,
|
||||
titel: "Feedback-Kanal für Community-Wünsche auswerten",
|
||||
text: "Und zurückmelden, was daraus wurde – auch ein Nein. Ein Wunschkanal ohne Antwort verstummt." },
|
||||
{ schluessel: "mk-kooperation", gruppe: "community", kategorie: "wachstum", rolle: "DogFather", tage: 30,
|
||||
titel: "Zusammenarbeit mit anderen Creators prüfen",
|
||||
text: "Ähnliche Größe, andere Community. Wer viel größer ist, bringt Zuschauer, die nicht bleiben." },
|
||||
{ schluessel: "mk-richtlinien", gruppe: "community", kategorie: "community", rolle: "Modi (Senior)", tage: 7,
|
||||
titel: "Community-Richtlinien verständlich kommunizieren",
|
||||
text: "In eigenen Worten, nicht als Rechtstext. Regeln, die man erklären muss, werden nicht befolgt." },
|
||||
];
|
||||
|
||||
export const AUFGABEN_VORLAGEN = {
|
||||
live: {
|
||||
anfaenger: [
|
||||
@@ -1127,6 +1382,20 @@ vorlagenRouter.get("/workspace/api/vorlagen", (req, res) => {
|
||||
technik: TECHNIK_PUNKTE,
|
||||
calls: CALL_LEITFAEDEN,
|
||||
aufgaben: ohneKatalog ? null : AUFGABEN_VORLAGEN,
|
||||
/* DER KATALOG AUS TEIL 2 DES ANFORDERUNGSDOKUMENTS.
|
||||
|
||||
"katalog" und nicht der Rollenname -- weder hier noch im Browser
|
||||
darf stehen, fuer wen er gedacht ist. Wer ihn nicht bekommt, sieht
|
||||
`null`, und die Oberflaeche baut dann nichts. Ein leeres Feld sagt
|
||||
nichts; ein Feld namens "modi_katalog" saehe jeder im
|
||||
Netzwerk-Protokoll seines eigenen Browsers.
|
||||
|
||||
An denselben Personenkreis wie die Kategorien -- es ist dieselbe
|
||||
Frage, und zwei Abfragen dafuer waeren zwei Gelegenheiten, dass
|
||||
sie auseinanderlaufen. */
|
||||
katalog: kategorienFuer(req.person).length
|
||||
? { gruppen: MODI_GRUPPEN, aufgaben: MODI_KATALOG }
|
||||
: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1245,6 +1514,64 @@ vorlagenRouter.post("/workspace/api/vorlagen/uebernehmen", gleicheHerkunft,
|
||||
angelegt.push(v.titel);
|
||||
}
|
||||
|
||||
} else if (art === "katalog" || art === "katalog_alle") {
|
||||
/* =============================================================
|
||||
DER KATALOG AUS TEIL 2 (10.09.2026)
|
||||
|
||||
Wie der Zweig darueber legt er echte Aufgaben an, nicht
|
||||
Eintraege. Zwei Unterschiede, und beide haben einen Grund:
|
||||
|
||||
WER DIE AUFGABE BEKOMMT. Bei den Creator-Vorlagen ist es der
|
||||
gewaehlte Creator. Hier ist es die Person selbst, wenn sie an
|
||||
eigenen Aufgaben arbeitet -- sonst muesste sie sich in einer
|
||||
Auswahl selbst heraussuchen, um sich etwas vorzunehmen. Traegt
|
||||
jemand fuer einen anderen ein (das kann nur die
|
||||
DogFather-Rolle), muss dieser andere ausdruecklich genannt
|
||||
werden.
|
||||
|
||||
DIE KATEGORIE WANDERT MIT. Sie steht am Katalogeintrag und ist
|
||||
der halbe Wert der Vorlage: Ohne sie muesste man 60-mal von
|
||||
Hand einsortieren, was im Dokument bereits danebensteht. */
|
||||
if (!kategorienFuer(req.person).length) {
|
||||
/* Wortgleich mit einer erfundenen Art -- wer den Katalog nicht
|
||||
bekommt, soll nicht erfahren, dass es ihn gibt. */
|
||||
return res.status(400).json({ fehler: "Unbekannte Art." });
|
||||
}
|
||||
const zielId = req.person.rolle === "modi"
|
||||
? req.person.id
|
||||
: Number(req.body.person_id) || null;
|
||||
if (!zielId) return res.status(400).json({ fehler: "Bitte zuerst eine Person wählen." });
|
||||
/* Nur an jemanden, fuer den die Kategorien ueberhaupt gelten --
|
||||
sonst landete eine Modi-Aufgabe samt Kategorie an einem
|
||||
Creator, und die Trennung waere von innen aufgeweicht. */
|
||||
const zielPerson = db().prepare(
|
||||
"SELECT id, rolle FROM personen WHERE id = ? AND aktiv = 1").get(zielId);
|
||||
if (!zielPerson || zielPerson.rolle !== "modi") {
|
||||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
}
|
||||
|
||||
const gruppe = String(req.body.gruppe || "");
|
||||
const auswahl = art === "katalog_alle"
|
||||
? MODI_KATALOG.filter((v) => v.gruppe === gruppe)
|
||||
: [MODI_KATALOG[nr]];
|
||||
if (!auswahl.length || auswahl.some((v) => !v)) {
|
||||
return res.status(400).json({ fehler: "Unbekannte Vorlage." });
|
||||
}
|
||||
for (const v of auswahl) {
|
||||
/* Frist in ORTSZEIT gerechnet. Ueber toISOString kaeme
|
||||
zwischen Mitternacht und zwei Uhr der Vortag heraus. */
|
||||
const d = new Date(Date.now() + (v.tage || 7) * 86400_000);
|
||||
const frist = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`
|
||||
+ `-${String(d.getDate()).padStart(2, "0")}`;
|
||||
db().prepare(`INSERT INTO aufgaben
|
||||
(titel, beschreibung, status, prioritaet, verantwortlich_id, frist,
|
||||
kategorie, vorlage, erstellt, erstellt_von)
|
||||
VALUES (?,?,'offen',?,?,?,?,?,?,?)`)
|
||||
.run(v.titel, v.text, v.tage <= 1 ? "hoch" : "mittel",
|
||||
zielPerson.id, frist, v.kategorie, v.schluessel, jetzt(), req.person.id);
|
||||
angelegt.push(v.titel);
|
||||
}
|
||||
|
||||
} else {
|
||||
return res.status(400).json({ fehler: "Unbekannte Art." });
|
||||
}
|
||||
|
||||
+28
-17
@@ -305,33 +305,44 @@ const MODI_MARKE = "Team Dogi";
|
||||
/* =====================================================================
|
||||
DIE KATEGORIEN EINER MODI-AUFGABE (10.09.2026)
|
||||
|
||||
Kapitel 6.1 des Anforderungsdokuments nennt sechs Beispiele, der
|
||||
Aufgabenkatalog in Teil 2 benutzt dreizehn. Dreizehn Knoepfe in einer
|
||||
Filterleiste sind auf einem Handy unbedienbar -- deshalb diese acht,
|
||||
und hier steht, wohin die uebrigen fallen:
|
||||
Es sind die des Anforderungsdokuments -- alle dreizehn, die der
|
||||
Aufgabenkatalog in Teil 2 tatsaechlich benutzt, dazu "Sonstiges" aus
|
||||
Kapitel 6.1.
|
||||
|
||||
Branding, Team, Kommunikation -> Planung & Organisation
|
||||
Wachstum -> Community & Events
|
||||
BEIM ERSTEN ANLAUF WAREN ES ACHT, mit der Begruendung, dreizehn
|
||||
Knoepfe seien auf einem Handy unbedienbar. Das war eine plausible
|
||||
Erklaerung fuer etwas Falsches: Gebaut ist ein AUSWAHLFELD, keine
|
||||
Knopfleiste -- vierzehn Eintraege darin sind voellig unauffaellig.
|
||||
Die Zusammenfassung haette eine Uebersetzungstabelle noetig gemacht
|
||||
("Branding gehoert zu Planung"), die spaeter niemand nachvollziehen
|
||||
kann. Jetzt steht an jeder Aufgabe die Kategorie, die im Dokument
|
||||
danebensteht.
|
||||
|
||||
Das ist eine Entscheidung und keine Uebersetzung. Wer sie spaeter
|
||||
anders will, aendert sie hier, an einer Stelle.
|
||||
DIE REIHENFOLGE IST DIE DER HAEUFIGKEIT, nicht die des Dokuments:
|
||||
Was taeglich vorkommt, steht oben. Wer eine Kategorie sucht, soll
|
||||
nicht scrollen muessen.
|
||||
|
||||
SIE STEHEN AUF DEM SERVER, nicht in assets/js/aufgaben.js. Nicht weil
|
||||
die Woerter etwas verraten wuerden -- "Clipping" sagt nichts ueber
|
||||
Modis --, sondern weil eine Liste, die im Browser jedes Menschen
|
||||
liegt und nur bei einem einzigen benutzt wird, die Frage aufwirft,
|
||||
fuer wen sie da ist. Genau diese Frage soll niemand stellen. */
|
||||
die Woerter etwas verraten wuerden -- "Clipping" sagt nichts --,
|
||||
sondern weil eine Liste, die im Browser jedes Menschen liegt und nur
|
||||
bei einem einzigen benutzt wird, die Frage aufwirft, fuer wen sie da
|
||||
ist. Genau diese Frage soll niemand stellen. */
|
||||
export const MODI_KATEGORIEN = [
|
||||
{ wert: "chat", name: "Chat-Moderation" },
|
||||
{ wert: "clipping", name: "Clipping & Schnitt" },
|
||||
{ wert: "community", name: "Community" },
|
||||
{ wert: "events", name: "Events" },
|
||||
{ wert: "clipping", name: "Clipping" },
|
||||
{ wert: "social", name: "Social Media" },
|
||||
{ wert: "community", name: "Community & Events" },
|
||||
{ wert: "technik", name: "Technik" },
|
||||
{ wert: "planung", name: "Planung & Organisation" },
|
||||
{ wert: "analyse", name: "Analyse & Zahlen" },
|
||||
{ wert: "planung", name: "Planung" },
|
||||
{ wert: "organisation", name: "Organisation" },
|
||||
{ wert: "team", name: "Team" },
|
||||
{ wert: "kommunikation", name: "Kommunikation" },
|
||||
{ wert: "analyse", name: "Analyse" },
|
||||
{ wert: "wachstum", name: "Wachstum" },
|
||||
{ wert: "branding", name: "Branding" },
|
||||
{ wert: "sonstiges", name: "Sonstiges" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Darf diese Person mit Kategorien arbeiten?
|
||||
*
|
||||
|
||||
@@ -13,25 +13,25 @@
|
||||
"theme_color": "#06090f",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609100105",
|
||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609100119",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609100105",
|
||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609100119",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609100105",
|
||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609100119",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609100105",
|
||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609100119",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
|
||||
@@ -994,6 +994,15 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe
|
||||
hilfe: 'Was auch ohne dich weiterläuft — Vorlage, Vertretung, Ablage.' },
|
||||
];
|
||||
let vorlagen = null;
|
||||
/* Der zweite Katalog -- welcher es ist, sagt der Server. Hier steht
|
||||
bewusst nur "katalog": Im Browser darf nichts stehen, woraus sich
|
||||
schliessen laesst, fuer wen er gedacht ist. */
|
||||
let katalog = null;
|
||||
let kGruppe = null;
|
||||
/* Getrennt vom Inhalt, weil "nichts bekommen" ein gueltiges Ergebnis
|
||||
ist. Ohne dieses Merkmal fragte die Seite bei jedem Neuzeichnen
|
||||
erneut nach -- und bekaeme jedes Mal wieder nichts. */
|
||||
let vorlagenGeladen = false;
|
||||
let vBereich = 'live';
|
||||
let vStufe = 'anfaenger';
|
||||
|
||||
@@ -1027,13 +1036,24 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe
|
||||
nichts, und `if (!vorlagen) return` weiter unten laesst das Brett
|
||||
verborgen. Dieselbe Wirkung, ohne eine Zeile, die verraet, fuer
|
||||
wen sie gilt. */
|
||||
if (!vorlagen) {
|
||||
if (!vorlagenGeladen) {
|
||||
try {
|
||||
const a = await hole('/workspace/api/vorlagen');
|
||||
if (!a.ok) return;
|
||||
vorlagen = (await a.json()).aufgaben || null;
|
||||
const d = await a.json();
|
||||
vorlagen = d.aufgaben || null;
|
||||
/* Ein Katalog mit null Eintraegen ist keiner. Ohne diese Pruefung
|
||||
haette ein leeres `{gruppen:[],aufgaben:[]}` einen Block mit
|
||||
Ueberschrift und nichts darin ergeben -- das sieht aus wie ein
|
||||
Fehler, und es waere auch einer. */
|
||||
katalog = d.katalog && Array.isArray(d.katalog.aufgaben) && d.katalog.aufgaben.length
|
||||
? d.katalog : null;
|
||||
vorlagenGeladen = true;
|
||||
} catch { return; }
|
||||
}
|
||||
/* Zwei Kataloge, einer je Person -- nie beide. Wer den einen
|
||||
bekommt, bekommt den anderen nicht; das entscheidet der Server. */
|
||||
if (katalog) { katalogBrett(ziel); return; }
|
||||
if (!vorlagen) return;
|
||||
ziel.hidden = false;
|
||||
ziel.textContent = '';
|
||||
@@ -1194,6 +1214,146 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* =====================================================================
|
||||
DAS ZWEITE VORLAGENBRETT (10.09.2026)
|
||||
|
||||
Derselbe Block, derselbe Kopf, dieselben Karten -- nur eine andere
|
||||
Einteilung: neun Etappen statt vier Bereichen mal vier Stufen. Die
|
||||
Bausteine sind bewusst dieselben (vorlagenbrett__karte,
|
||||
stufenleiter, ...): Ein zweites Aussehen fuer dieselbe Sache waere
|
||||
zwei Dinge zum Lernen statt einem -- und eine zweite Stelle, an der
|
||||
jemand eine neue CSS-Klasse in sieben Kopien pflegen muesste.
|
||||
|
||||
WAS DRINSTEHT, KOMMT VOM SERVER, samt Gruppennamen und Texten. In
|
||||
dieser Datei steht kein Wort darueber, fuer wen dieser Katalog ist.
|
||||
===================================================================== */
|
||||
function katalogBrett(ziel) {
|
||||
ziel.hidden = false;
|
||||
ziel.textContent = '';
|
||||
if (!kGruppe) kGruppe = (katalog.gruppen[0] || {}).schluessel || null;
|
||||
|
||||
const kopf = el('button', 'vorlagenbrett__kopf');
|
||||
kopf.type = 'button';
|
||||
kopf.setAttribute('aria-expanded', String(vOffen));
|
||||
kopf.setAttribute('aria-controls', 'vorlagenbrett-koerper');
|
||||
const kText = el('span', 'vorlagenbrett__kopftext');
|
||||
kText.append(el('span', 'feldschild', 'Aufgaben aus der Vorlage'));
|
||||
kText.append(el('span', 'vorlagenbrett__unter', vOffen
|
||||
? 'Nach Etappen sortiert. Übernommen wird als ganz normale Aufgabe '
|
||||
+ '– mit Frist und Kategorie, veränderbar, auf dem Brett darunter.'
|
||||
: `${katalog.aufgaben.length} fertige Aufgaben in `
|
||||
+ `${katalog.gruppen.length} Etappen – zum Übernehmen.`));
|
||||
kopf.append(kText);
|
||||
const pfeil = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
pfeil.setAttribute('viewBox', '0 0 24 24');
|
||||
pfeil.setAttribute('class', 'klapp-pfeil');
|
||||
pfeil.setAttribute('aria-hidden', 'true');
|
||||
const pfad = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
pfad.setAttribute('d', 'm6 9.5 6 6 6-6');
|
||||
pfeil.append(pfad);
|
||||
kopf.append(pfeil);
|
||||
kopf.addEventListener('click', () => {
|
||||
vOffen = !vOffen;
|
||||
try { localStorage.setItem(VORLAGEN_OFFEN, vOffen ? 'ja' : 'nein'); } catch { /* egal */ }
|
||||
vorlagenbrettZeigen();
|
||||
});
|
||||
ziel.append(kopf);
|
||||
ziel.dataset.auf = vOffen ? 'ja' : 'nein';
|
||||
/* Zugeklappt wird der Rest gar nicht erst gebaut -- sonst lägen bei
|
||||
jedem Neuzeichnen sechzig Karten da, die niemand sieht. */
|
||||
if (!vOffen) return;
|
||||
|
||||
const koerper = el('div', 'vorlagenbrett__koerper');
|
||||
koerper.id = 'vorlagenbrett-koerper';
|
||||
ziel.append(koerper);
|
||||
|
||||
/* Die Etappen als Reihe -- mit der Zahl darin, damit man sieht, wo
|
||||
etwas liegt, bevor man klickt. */
|
||||
const reihe = el('div', 'stufenleiter');
|
||||
reihe.setAttribute('role', 'group');
|
||||
reihe.setAttribute('aria-label', 'Etappe wählen');
|
||||
for (const g of katalog.gruppen) {
|
||||
const n = katalog.aufgaben.filter((v) => v.gruppe === g.schluessel).length;
|
||||
if (!n) continue;
|
||||
const k = el('button', 'stufenleiter__knopf');
|
||||
k.type = 'button';
|
||||
k.dataset.stufe = g.schluessel;
|
||||
k.setAttribute('aria-pressed', String(kGruppe === g.schluessel));
|
||||
k.append(el('span', 'stufenleiter__name', g.name));
|
||||
k.append(el('span', 'stufenleiter__zahl', String(n)));
|
||||
k.addEventListener('click', () => { kGruppe = g.schluessel; vorlagenbrettZeigen(); });
|
||||
reihe.append(k);
|
||||
}
|
||||
koerper.append(reihe);
|
||||
|
||||
const gruppe = katalog.gruppen.find((g) => g.schluessel === kGruppe);
|
||||
if (gruppe?.text) koerper.append(el('p', 'vorlagenbrett__stand', gruppe.text));
|
||||
|
||||
/* Was schon auf dem Brett liegt -- aus den Aufgaben, die ohnehin
|
||||
geladen sind. Verglichen wird die Kennung der Vorlage und nicht
|
||||
der Titel: Der darf geändert werden. */
|
||||
const schonDa = new Map();
|
||||
for (const a of alleAufgaben) if (a.vorlage) schonDa.set(a.vorlage, a);
|
||||
|
||||
const liste = katalog.aufgaben
|
||||
.map((v, nr) => ({ ...v, nr }))
|
||||
.filter((v) => v.gruppe === kGruppe);
|
||||
const box = el('div', 'vorlagenbrett__liste');
|
||||
for (const v of liste) {
|
||||
const karte = el('div', 'vorlagenbrett__karte');
|
||||
const schon = schonDa.get(v.schluessel);
|
||||
if (schon) karte.dataset.schon = 'ja';
|
||||
karte.append(el('h3', 'vorlagenbrett__titel', v.titel));
|
||||
karte.append(el('p', 'vorlagenbrett__text', v.text));
|
||||
const fuss = el('div', 'vorlagenbrett__fuss');
|
||||
fuss.append(el('span', 'vorlagenbrett__frist',
|
||||
v.tage === 1 ? 'Frist: morgen' : `Frist: in ${v.tage} Tagen`));
|
||||
/* Die empfohlene Rolle steht im Dokument neben jeder Aufgabe und
|
||||
ist die halbe Auskunft: "Technik-Check" heisst etwas anderes,
|
||||
je nachdem, wer ihn macht. */
|
||||
if (v.rolle) fuss.append(el('span', 'vorlagenbrett__frist', `für: ${v.rolle}`));
|
||||
if (schon) {
|
||||
fuss.append(el('span', 'vorlagenbrett__schon',
|
||||
`schon übernommen · ${STATUS_NAME[schon.status] || schon.status}`));
|
||||
}
|
||||
const k = el('button', 'knopf knopf--klein', schon ? 'Nochmal' : 'Übernehmen');
|
||||
k.type = 'button';
|
||||
if (schon) k.title = 'Diese Vorlage liegt schon auf dem Brett – noch einmal übernehmen?';
|
||||
k.addEventListener('click', () => uebernehmen(k,
|
||||
{ art: 'katalog', nr: v.nr, gruppe: kGruppe, person_id: katalogZiel() }));
|
||||
fuss.append(k);
|
||||
karte.append(fuss);
|
||||
box.append(karte);
|
||||
}
|
||||
|
||||
if (liste.length) {
|
||||
const offen = liste.filter((v) => !schonDa.has(v.schluessel)).length;
|
||||
koerper.append(el('p', 'vorlagenbrett__stand', offen
|
||||
? `${offen} von ${liste.length} noch nicht übernommen.`
|
||||
: `Alle ${liste.length} dieser Etappe sind übernommen.`));
|
||||
}
|
||||
koerper.append(box);
|
||||
|
||||
if (liste.length > 1) {
|
||||
const alle = el('button', 'abmelden', `Alle ${liste.length} übernehmen`);
|
||||
alle.title = 'Legt alle Aufgaben dieser Etappe an – auch die, die schon auf dem Brett liegen.';
|
||||
alle.type = 'button';
|
||||
alle.style.marginTop = '12px';
|
||||
alle.addEventListener('click', () => uebernehmen(alle,
|
||||
{ art: 'katalog_alle', gruppe: kGruppe, person_id: katalogZiel() }));
|
||||
koerper.append(alle);
|
||||
}
|
||||
}
|
||||
|
||||
/* FUER WEN wird uebernommen? Wer nur eigene Aufgaben hat, meint sich
|
||||
selbst -- der Server setzt das dann ein und braucht hier nichts.
|
||||
Wer fuer andere eintragen darf, muss die Person oben ausgewaehlt
|
||||
haben; sonst sagt der Server es. */
|
||||
function katalogZiel() {
|
||||
return $('f-verant')?.value || $('f-creator')?.value || null;
|
||||
}
|
||||
|
||||
/* Ein Weg fuer beide Knoepfe -- einzeln und alle. Zwei fast gleiche
|
||||
Funktionen waeren der sichere Weg dazu, dass die eine irgendwann
|
||||
anders reagiert als die andere. */
|
||||
|
||||
@@ -38,6 +38,27 @@
|
||||
mit, auch was es heute noch gar nicht gibt. */
|
||||
if (ich.rolle) document.documentElement.dataset.rolle = ich.rolle;
|
||||
|
||||
/* DER NAME IN DER KOPFLEISTE (10.09.2026).
|
||||
|
||||
Auf jeder Seite steht dort fest "Creator Workspace · <Seite>".
|
||||
Fuer wen das nicht stimmt, schickt der Server einen anderen Namen
|
||||
mit -- WER das ist, steht ausdruecklich nicht in dieser Datei:
|
||||
Sie bekommt jeder, der die Seite oeffnet.
|
||||
|
||||
Nur der Verweis wird umgeschrieben, nicht die ganze Zeile: Hinter
|
||||
ihm steht noch " · Aufgaben" bzw. der Name der jeweiligen Seite,
|
||||
und der bleibt richtig.
|
||||
|
||||
MIT GESCHUETZTEM LEERZEICHEN, wie im HTML. Die Kopfleiste rechnet
|
||||
damit, dass dieser Name nicht umbricht -- ein gewoehnliches
|
||||
Leerzeichen liesse ihn auf schmalen Handys in zwei Zeilen fallen
|
||||
und die Leiste mitwachsen. */
|
||||
if (ich.marke) {
|
||||
for (const verweis of document.querySelectorAll('.kopfleiste .marke__text .zurueck')) {
|
||||
verweis.textContent = String(ich.marke).replace(/ /g, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* Das eigene Bild, wenn es eines gibt -- sonst der Anfangsbuchstabe.
|
||||
Der Buchstabe bleibt als Unterlage LIEGEN und wird nicht ersetzt:
|
||||
Laedt das Bild nicht (Verbindung weg, Datei geloescht), steht dort
|
||||
|
||||
+11
-11
@@ -12,12 +12,12 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -245,10 +245,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -140,10 +140,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+16
-16
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -149,14 +149,14 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -82,11 +82,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,19 +12,19 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<!-- 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=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -247,10 +247,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609100105"></script>
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105"></script>
|
||||
<script src="assets/js/chat.js?v=202609100105"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119"></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119"></script>
|
||||
<script src="assets/js/chat.js?v=202609100119"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -172,12 +172,12 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/content.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/content.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -101,10 +101,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
|
||||
type="image/webp" media="(max-width: 899px)" />
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="gate">
|
||||
@@ -401,6 +401,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/gate.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/gate.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -370,11 +370,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -217,10 +217,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105"></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105"></script>
|
||||
<script src="assets/js/leistung.js?v=202609100105"></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119"></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119"></script>
|
||||
<script src="assets/js/leistung.js?v=202609100119"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -335,10 +335,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -207,11 +207,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -99,11 +99,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/report.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/report.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -132,10 +132,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -12,14 +12,14 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<!-- 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=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -667,10 +667,10 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/start.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/start.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -78,11 +78,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -119,10 +119,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -7,13 +7,13 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -103,10 +103,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/team.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/team.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,15 +12,15 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||
Dialog "Creator anlegen". -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -142,11 +142,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100105" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609100105" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100105" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100119" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609100119" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100119" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -183,10 +183,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609100105" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100119" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609100119" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user