Eigene Checklisten fuer die Modis -- und eine Seite fuer euch beide
Wunsch Filipe (10.09.2026): *"diese kategorien mussen auf dieser seite auch noch auf die modis perfektionniert werden, da mussen lauter sachen sein die vanvan und ich danach verteilen können und da anpassen können ob gut oder nicht."* Und: *"da soll es eine kategorie geben für uns beide nur sonst keinen wo wir dass alles sehen und behandeln können."* === TEIL 1: SEIN EIGENER PUNKTESATZ === 101 Punkte in workspace-modi-punkte.js -- Live-Ablauf 40, Community 36, Technik 25. Die vorhandenen sagen "Upload messen", "Sendeplan festlegen", "Verweildauer vergleichen": Ein Modi sendet nicht, er moderiert. Ihm dieselbe Liste vorzulegen hiesse, ihn an Dingen zu messen, die nicht seine Arbeit sind. DIESELBE EINTEILUNG (Vor/Waehrend/Nach usw.), nur andere Punkte darin. Zwei verschiedene Einteilungen waeren zwei Dinge zum Lernen statt einem, und die Oberflaeche kaeme ohne Umbau nicht damit zurecht. DIE SCHLUESSEL BEGINNEN MIT "m-". Der Stand haengt an (bereich, schluessel, person) -- ohne Praefix koennte ein Modi-Punkt eines Tages denselben Namen tragen wie ein Creator-Punkt und dessen Bewertung erben. BEIDE SAETZE STEHEN IN GUELTIG. Stuenden dort nur die Creator-Punkte, kaeme die Liste an und jeder Klick darauf brachte einen 404 -- die Sorte Fehler, die man erst beim Benutzen merkt. Ein Modi sieht SEINE Liste, ohne jemanden auszuwaehlen (wie ein Creator), und bewertet sich nicht selbst. Ohne diese Zeile waere er in den Betreuer-Zweig gefallen, haette eine Auswahl fremder Creator vorgesetzt bekommen und seine eigene Liste gar nicht gesehen. DER HEIKELSTE PUNKT WAR EIN ANDERER: `darfCreator` sagt bei siehtAlles() pauschal ja -- und darin steckt auch Spicy Media. Wer die Modi-Auswahl daran haengt, oeffnet sie ihr nebenbei mit, ohne dass an der Stelle etwas davon steht. Deshalb eine eigene Regel (darfPerson), und die Pruefung versucht es ueber die Liste, ueber die Adresse UND ueber das Bewerten. Die Auswahl heisst bei der DogFather-Rolle jetzt "Person" statt "Creator" -- ein Feld namens "Creator", in dem ein Modi steht, ist falsch beschriftet. Das Wort kommt vom Server; ein Rollenvergleich im Browser waere die Stelle, an der der Name in einer ausgelieferten Datei landet. === TEIL 2: DIE GEMEINSAME SEITE === teamlage.html, nur fuer die DogFather-Rolle -- also Filipe und VanVan. Fuer alle anderen gibt es weder die Kachel noch die Seite noch die Schnittstelle (404, wie bei einer Adresse, die es nicht gibt). Zwei Schloesser, absichtlich: die Schranke und die Abfrage. Faellt eines weg, haelt das andere. Je Person: offene und ueberfaellige Aufgaben, beigetragene Ideen, Rueckmeldungen, und je Bereich gut/verbessern/offen. Jede Zeile fuehrt in den Bereich -- MIT DER PERSON VORAUSGEWAEHLT. Dafuer liest die Checkliste jetzt `creator_id` aus der Adresse; ohne das landet man auf der erstbesten Person und weiss beim zweiten Suchen nicht mehr, warum man hier war. SIE ZAEHLT, SIE BEWERTET NICHT. Bewertet wird dort, wo die Punkte stehen. Eine zweite Stelle dafuer waere eine zweite Stelle, an der es auseinanderlaeuft. UND SIE IST KEINE UEBERWACHUNG. Ueber team.html steht schon, dass eine Seite mit Zahlen ueber Kollegen als Kontrolle gelesen wird -- und dann arbeitet niemand mehr offen damit. "Offen" heisst hier deshalb ausdruecklich: darueber habt ihr noch nicht geredet. Eine Merkliste fuer euch beide, keine Note. ALLES IN VIER ABFRAGEN, nicht vier je Person -- bei zehn Modis waeren das vierzig. Denselben Fehler hat das Haus bei den Terminen schon gemacht und ihn dort vermerkt. KEINE NEUEN CSS-KLASSEN: Fuer genau diese Karten gibt es sie schon (tperson, tz, schritt -- aus team.html). Elf neue haetten gepflegt werden muessen fuer ein Aussehen, das bereits da ist. ZUSATZKACHELN sind ein neuer, kleiner Mechanismus: `bereiche` ERSETZT die Liste im Browser (fuer Rollen, die dort nicht vorkommen), `bereiche_zusatz` HAENGT an. Gebraucht fuer Kacheln, die nur eine einzige Rolle bekommt -- in bereiche.js duerften sie nicht stehen, weil eine Kachel, die nur bei einem erscheint, die Frage aufwirft, fuer wen sie ist. Findet sich die genannte Gruppe nicht, wird eine eigene angelegt: Sonst faellt die Kachel lautlos weg, sobald jemand eine Gruppe umbenennt. EHRLICH ZUM UMFANG: Technik ist mit 25 Punkten der duennste Bereich. Das ist Absicht -- der technische Spielraum eines Moderators ist kleiner als der eines Streamers. Auffuellen haette Fuellmaterial in eine Liste gebracht, die Filipe und VanVan durchgehen muessen. GEPRUEFT: pruef-modi-checkliste (39, neu), pruef-rollen (244 statt 243 -- die neue Kachel wird angeklickt und muss ankommen), pruef-modi-verborgen (75), pruef-workspace-seiten. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -42,6 +42,7 @@ import { teamRouter } from "./workspace-team.js";
|
||||
import { schulungRouter } from "./workspace-schulung.js";
|
||||
import { ampelRouter } from "./workspace-ampel.js";
|
||||
import { checklisteRouter } from "./workspace-checkliste.js";
|
||||
import { teamlageRouter } from "./workspace-teamlage.js";
|
||||
import { pushRouter, pushStarten } from "./workspace-push.js";
|
||||
import { chatRouter } from "./workspace-chat.js";
|
||||
import { leistungRouter } from "./workspace-leistung.js";
|
||||
@@ -211,6 +212,7 @@ app.use(teamRouter);
|
||||
app.use(schulungRouter);
|
||||
app.use(ampelRouter);
|
||||
app.use(checklisteRouter);
|
||||
app.use(teamlageRouter);
|
||||
app.use(aufgabenRouter);
|
||||
app.use(personenRouter);
|
||||
app.use(profilRouter);
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/* =====================================================================
|
||||
DIE CHECKLISTEN EINES MODIS
|
||||
|
||||
Wunsch Filipe (10.09.2026): *"diese kategorien mussen auf dieser
|
||||
seite auch noch auf die modis perfektionniert werden, da mussen
|
||||
lauter sachen sein die vanvan und ich danach verteilen können und da
|
||||
anpassen können ob gut oder nicht."*
|
||||
|
||||
VIER FRAGEN:
|
||||
|
||||
1. Bekommt ein Modi SEINEN Satz -- nicht den der Creator?
|
||||
2. Sieht er seine EIGENE Liste, ohne jemanden auswaehlen zu muessen?
|
||||
3. Kann die DogFather-Rolle ihn auswaehlen und bewerten?
|
||||
4. Bekommt Spicy Media ihn NICHT in die Auswahl?
|
||||
|
||||
Die vierte ist die heikelste. `darfCreator` sagt bei siehtAlles()
|
||||
pauschal ja -- und darin steckt auch Spicy Media. Wer die
|
||||
Modi-Auswahl daran haengt, oeffnet sie ihr nebenbei mit, ohne dass an
|
||||
der Stelle etwas davon steht.
|
||||
===================================================================== */
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-mcheck-"));
|
||||
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||
process.env.PORT = "4369";
|
||||
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-checkliste");
|
||||
await new Promise((r) => setTimeout(r, 800));
|
||||
const BASIS = "http://127.0.0.1:4369";
|
||||
|
||||
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); };
|
||||
|
||||
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 gh = randomBytes(32).toString("hex");
|
||||
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
|
||||
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
|
||||
.run("code_kennung_schluessel", gh, 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", gh).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 idVan = anlegen("VanVan", "admin", "CODE-VAN-0001");
|
||||
const idSpicy = anlegen("Cigdem", "spicy", "CODE-SPIC-0001");
|
||||
const idCreat = anlegen("Tili", "creator", "CODE-ANNA-0001");
|
||||
const idModi = anlegen("Marina", "modi", "CODE-MODI-0001");
|
||||
d.close();
|
||||
void idDogi;
|
||||
|
||||
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 vanAn = await anmelden("admin", "CODE-VAN-0001");
|
||||
const spicyAn = await anmelden("spicy", "CODE-SPIC-0001");
|
||||
const creatAn = await anmelden("creator", "CODE-ANNA-0001");
|
||||
|
||||
/* ---------- 1. Sein eigener Satz ------------------------------------- */
|
||||
console.log("\n=== 1. Der Modi bekommt seinen eigenen Punktesatz ===\n");
|
||||
const BEREICHE = ["live", "community", "technik"];
|
||||
let ersterPunkt = null;
|
||||
for (const b of BEREICHE) {
|
||||
const a = await hole(modiAn.keks, `/workspace/api/checkliste/${b}`);
|
||||
if (a.status !== 200) { unklar(`${b}: nicht lesbar (${a.status})`); continue; }
|
||||
const punkte = (a.daten?.gruppen || []).flatMap((g) => g.punkte || []);
|
||||
/* DIE ZAHL IN DIE BEDINGUNG: Eine leere Liste wuerde sonst als
|
||||
"keine Creator-Punkte dabei" durchgehen. */
|
||||
ok(punkte.length > 0 && punkte.every((p) => p.schluessel.startsWith("m-")),
|
||||
`${b.padEnd(10)} ${punkte.length} Punkte, alle aus seinem Satz`);
|
||||
if (b === "live" && punkte.length) ersterPunkt = punkte[0].schluessel;
|
||||
/* Und er sieht SEINE Liste, ohne jemanden auszuwaehlen. */
|
||||
ok(a.daten?.creator_id === idModi,
|
||||
`${b.padEnd(10)} es ist seine eigene Liste (${a.daten?.creator_id})`);
|
||||
ok(a.daten?.darf_bewerten === false,
|
||||
`${b.padEnd(10)} bewerten darf er sich nicht selbst`);
|
||||
}
|
||||
|
||||
/* GEGENPROBE: Ein Creator bekommt weiterhin SEINEN Satz -- sonst waere
|
||||
oben auch gruen, wenn ich allen denselben gegeben haette. */
|
||||
{
|
||||
const a = await hole(creatAn.keks, "/workspace/api/checkliste/live");
|
||||
const punkte = (a.daten?.gruppen || []).flatMap((g) => g.punkte || []);
|
||||
ok(punkte.length > 0 && punkte.every((p) => !p.schluessel.startsWith("m-")),
|
||||
`der Creator bekommt unveraendert seinen Satz (${punkte.length} Punkte)`);
|
||||
}
|
||||
|
||||
/* ---------- 2. DogFather und VanVan waehlen ihn aus ------------------ */
|
||||
console.log("\n=== 2. DogFather und VanVan sehen ihn ===\n");
|
||||
for (const [name, an] of [["DogFather", dogiAn], ["VanVan", vanAn]]) {
|
||||
const a = await hole(an.keks, "/workspace/api/checkliste/live");
|
||||
const drin = (a.daten?.creatoren || []).some((z) => z.id === idModi);
|
||||
ok(drin, `${name.padEnd(10)} hat ihn in der Auswahl`);
|
||||
const b = await hole(an.keks, `/workspace/api/checkliste/live?creator_id=${idModi}`);
|
||||
const punkte = (b.daten?.gruppen || []).flatMap((g) => g.punkte || []);
|
||||
ok(punkte.length > 0 && punkte.every((p) => p.schluessel.startsWith("m-")),
|
||||
`${name.padEnd(10)} bekommt SEINEN Satz, nicht den der Creator (${punkte.length})`);
|
||||
ok(b.daten?.darf_bewerten === true, `${name.padEnd(10)} darf bewerten`);
|
||||
}
|
||||
|
||||
/* ---------- 3. Bewerten ---------------------------------------------- */
|
||||
console.log("\n=== 3. Gut oder verbessern ===\n");
|
||||
if (!ersterPunkt) {
|
||||
unklar("ohne Punkt laesst sich das Bewerten nicht pruefen");
|
||||
} else {
|
||||
const a = await hole(dogiAn.keks, `/workspace/api/checkliste/live/${ersterPunkt}`, {
|
||||
method: "PUT", headers: jsonKopf,
|
||||
body: JSON.stringify({ stufe: "gut", begruendung: "sauber gemacht", creator_id: idModi }),
|
||||
});
|
||||
ok(a.status === 200, `DogFather setzt "gut" (${a.status})`);
|
||||
|
||||
const b = await hole(modiAn.keks, "/workspace/api/checkliste/live");
|
||||
ok(b.daten?.stand?.[ersterPunkt]?.stufe === "gut",
|
||||
`der Modi sieht den Stand (${b.daten?.stand?.[ersterPunkt]?.stufe})`);
|
||||
|
||||
const c = await hole(vanAn.keks, `/workspace/api/checkliste/live/${ersterPunkt}`, {
|
||||
method: "PUT", headers: jsonKopf,
|
||||
body: JSON.stringify({ stufe: "verbessern", begruendung: "beim nächsten Mal früher", creator_id: idModi }),
|
||||
});
|
||||
ok(c.status === 200, `VanVan setzt "verbessern" (${c.status})`);
|
||||
|
||||
/* Und er selbst kann es NICHT umstellen -- sonst waere die Bewertung
|
||||
eine Selbsteinschaetzung. */
|
||||
const e = await hole(modiAn.keks, `/workspace/api/checkliste/live/${ersterPunkt}`, {
|
||||
method: "PUT", headers: jsonKopf, body: JSON.stringify({ stufe: "gut" }),
|
||||
});
|
||||
ok(e.status === 403, `er selbst kann sie nicht umstellen (${e.status})`);
|
||||
}
|
||||
|
||||
/* ---------- 4. Spicy Media geht das nichts an ------------------------ */
|
||||
console.log("\n=== 4. Spicy Media bekommt ihn nicht ===\n");
|
||||
{
|
||||
const a = await hole(spicyAn.keks, "/workspace/api/checkliste/live");
|
||||
const drin = (a.daten?.creatoren || []).some((z) => z.id === idModi);
|
||||
ok(!drin,
|
||||
`nicht in der Auswahl (${(a.daten?.creatoren || []).length} Personen: `
|
||||
+ `${(a.daten?.creatoren || []).map((z) => z.name).join(", ")})`);
|
||||
/* GEGENPROBE: Den Creator hat sie sehr wohl -- sonst waere der Haken
|
||||
darueber auch dann gruen, wenn die Auswahl ueberhaupt leer bliebe. */
|
||||
ok((a.daten?.creatoren || []).some((z) => z.id === idCreat),
|
||||
"den Creator dagegen schon");
|
||||
/* Und ueber die Adresse auch nicht. Eine Regel, die nur in der
|
||||
Auswahlliste gilt, ist keine. */
|
||||
const b = await hole(spicyAn.keks, `/workspace/api/checkliste/live?creator_id=${idModi}`);
|
||||
ok(b.status === 404, `und ueber die Adresse ebenfalls nicht (${b.status})`);
|
||||
const c = await hole(spicyAn.keks, `/workspace/api/checkliste/live/${ersterPunkt}`, {
|
||||
method: "PUT", headers: jsonKopf,
|
||||
body: JSON.stringify({ stufe: "gut", creator_id: idModi }),
|
||||
});
|
||||
ok(c.status === 404, `bewerten erst recht nicht (${c.status})`);
|
||||
}
|
||||
|
||||
|
||||
/* ---------- 5. Die gemeinsame Lage ----------------------------------- */
|
||||
console.log("\n=== 5. Die Seite für DogFather und VanVan ===\n");
|
||||
{
|
||||
for (const [name, an] of [["DogFather", dogiAn], ["VanVan", vanAn]]) {
|
||||
const a = await hole(an.keks, "/workspace/api/teamlage");
|
||||
if (a.status !== 200) { unklar(`${name}: nicht lesbar (${a.status})`); continue; }
|
||||
const eintrag = (a.daten?.modis || []).find((m) => m.id === idModi);
|
||||
ok(!!eintrag, `${name.padEnd(10)} sieht den Modi in der Lage`);
|
||||
/* Die Zahlen muessen zum Punktesatz passen -- eine Uebersicht, die
|
||||
andere Zahlen zeigt als die Seite dahinter, ist schlimmer als
|
||||
keine. */
|
||||
ok(eintrag?.bereiche?.live?.gesamt === 40,
|
||||
`${name.padEnd(10)} und die Gesamtzahl stimmt (${eintrag?.bereiche?.live?.gesamt})`);
|
||||
/* Ein Punkt wurde in Abschnitt 3 auf "verbessern" gesetzt. */
|
||||
ok(eintrag?.bereiche?.live?.verbessern === 1
|
||||
&& eintrag?.bereiche?.live?.offen === 39,
|
||||
`${name.padEnd(10)} der bewertete Punkt ist abgezogen `
|
||||
+ `(${eintrag?.bereiche?.live?.verbessern} verbessern, ${eintrag?.bereiche?.live?.offen} offen)`);
|
||||
/* Und die Kachel dorthin bekommt sie auch. */
|
||||
const b = await hole(an.keks, "/workspace/api/ich");
|
||||
const kachel = (b.daten?.bereiche_zusatz || []).some((k) => k.ziel === "teamlage.html");
|
||||
ok(kachel, `${name.padEnd(10)} hat die Kachel auf der Startseite`);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- 6. Für alle anderen gibt es sie nicht -------------------- */
|
||||
console.log("\n=== 6. Und für alle anderen gibt es sie nicht ===\n");
|
||||
for (const [name, an] of [["Spicy Media", spicyAn], ["Creator", creatAn], ["Modi", modiAn]]) {
|
||||
const a = await hole(an.keks, "/workspace/api/teamlage");
|
||||
const b = await hole(an.keks, "/workspace/api/gibtesnicht");
|
||||
ok(a.status === 404,
|
||||
`${name.padEnd(12)} bekommt 404 -- wie bei einer Adresse, die es nicht gibt (${a.status}/${b.status})`);
|
||||
const c = await hole(an.keks, "/workspace/api/ich");
|
||||
ok((c.daten?.bereiche_zusatz || []).length === 0,
|
||||
`${name.padEnd(12)} bekommt auch die Kachel nicht`);
|
||||
}
|
||||
|
||||
/* GEGENPROBE ZUR KACHEL: Sie ist ueberhaupt vorhanden -- sonst waeren
|
||||
die drei Haken darueber auch dann gruen, wenn es sie gar nicht gibt. */
|
||||
{
|
||||
const a = await hole(dogiAn.keks, "/workspace/api/ich");
|
||||
ok((a.daten?.bereiche_zusatz || []).length === 1,
|
||||
`bei DogFather ist genau eine Zusatzkachel da (${(a.daten?.bereiche_zusatz || []).length})`);
|
||||
}
|
||||
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);
|
||||
@@ -36,7 +36,9 @@ import express from "express";
|
||||
import {
|
||||
db, sitzungLesen, istLeitung, istDogFather, siehtAlles, darfCreator, betreuteIds,
|
||||
protokolliere, echteIp,
|
||||
siehtModis,
|
||||
} from "./workspace.js";
|
||||
import { MODI_PUNKTE } from "./workspace-modi-punkte.js";
|
||||
import {
|
||||
CONTENT_IDEEN, LIVE_PUNKTE, COMMUNITY_PUNKTE, TECHNIK_PUNKTE,
|
||||
} from "./workspace-vorlagen.js";
|
||||
@@ -121,9 +123,45 @@ export const LISTEN = {
|
||||
|
||||
/* Alle gültigen Schlüssel je Bereich -- damit niemand einen Stand für
|
||||
einen Punkt setzen kann, den es gar nicht gibt. */
|
||||
/* =====================================================================
|
||||
ZWEI PUNKTESAETZE JE BEREICH (10.09.2026)
|
||||
|
||||
Wunsch Filipe: *"diese kategorien mussen auf dieser seite auch noch
|
||||
auf die modis perfektionniert werden."*
|
||||
|
||||
Die Punkte in LISTEN sind fuer CREATOR geschrieben -- "Upload
|
||||
messen", "Sendeplan festlegen". Ein Modi sendet nicht, er moderiert;
|
||||
ihm dieselbe Liste vorzulegen hiesse, ihn an Dingen zu messen, die
|
||||
nicht seine Arbeit sind. Sein Satz steht in workspace-modi-punkte.js.
|
||||
|
||||
DIE EINTEILUNG BLEIBT DIESELBE (Vor/Waehrend/Nach usw.) -- getauscht
|
||||
werden nur die Punkte darin. Zwei verschiedene Einteilungen waeren
|
||||
zwei Dinge zum Lernen statt einem, und die Oberflaeche kaeme ohne
|
||||
Umbau nicht damit zurecht.
|
||||
===================================================================== */
|
||||
function gruppenFuer(bereich, fuerModi) {
|
||||
const liste = LISTEN[bereich];
|
||||
if (!liste) return null;
|
||||
const satz = fuerModi ? MODI_PUNKTE[bereich] : null;
|
||||
if (!satz) return liste.gruppen;
|
||||
/* Eine Gruppe ohne Punkte faellt heraus statt leer dazustehen -- ein
|
||||
Abschnitt mit einer Ueberschrift und nichts darunter sieht aus wie
|
||||
ein Fehler. */
|
||||
return liste.gruppen
|
||||
.map((g) => ({ ...g, punkte: satz[g.schluessel] || [] }))
|
||||
.filter((g) => g.punkte.length);
|
||||
}
|
||||
|
||||
/* BEIDE SAETZE sind gueltige Schluessel. Stuenden hier nur die
|
||||
Creator-Punkte, liesse sich der Stand eines Modi-Punktes nicht
|
||||
setzen -- die Liste kaeme an, und jeder Klick darauf brachte einen
|
||||
404. Genau die Sorte Fehler, die man erst beim Benutzen merkt. */
|
||||
const GUELTIG = {};
|
||||
for (const [bereich, liste] of Object.entries(LISTEN)) {
|
||||
GUELTIG[bereich] = new Set(liste.gruppen.flatMap((g) => g.punkte.map((p) => p.schluessel)));
|
||||
for (const punkte of Object.values(MODI_PUNKTE[bereich] || {})) {
|
||||
for (const p of punkte) GUELTIG[bereich].add(p.schluessel);
|
||||
}
|
||||
}
|
||||
|
||||
let bereit = false;
|
||||
@@ -183,12 +221,37 @@ function gleicheHerkunft(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
/** Um welchen Creator geht es -- und darf die Person das? */
|
||||
/** Um WESSEN Liste geht es -- und darf die Person das?
|
||||
*
|
||||
* Ein Creator und ein Modi arbeiten beide an ihrer EIGENEN Liste; sie
|
||||
* koennen keine fremde waehlen. Alle anderen waehlen aus, wen sie
|
||||
* betreuen.
|
||||
*
|
||||
* DIE SPALTE HEISST WEITERHIN creator_id. Das ist Geschichte, kein
|
||||
* Inhalt: Sie haelt eine Personennummer. Sie umzubenennen hiesse, acht
|
||||
* Abfragen und eine Tabelle anzufassen, damit ein Wort besser passt --
|
||||
* und jede davon waere eine Gelegenheit fuer einen Fehler.
|
||||
*/
|
||||
function creatorAus(req) {
|
||||
if (req.person.rolle === "creator") return req.person.id;
|
||||
if (req.person.rolle === "creator" || req.person.rolle === "modi") return req.person.id;
|
||||
const id = Number(req.body?.creator_id || req.query?.creator_id) || 0;
|
||||
if (!id) return 0;
|
||||
return darfCreator(req.person, id) ? id : -1; /* -1 = verboten */
|
||||
return darfPerson(req.person, id) ? id : -1; /* -1 = verboten */
|
||||
}
|
||||
|
||||
/** Darf diese Person die Liste jener Person sehen und bewerten?
|
||||
*
|
||||
* Fuer Creator gilt die gewohnte Betreuungsregel (darfCreator). Bei
|
||||
* einem MODI reicht sie nicht: `darfCreator` sagt bei siehtAlles()
|
||||
* pauschal ja -- und dazu gehoert auch Spicy Media. Die Modis gehen
|
||||
* Spicy Media aber nichts an; das ist die Regel des ganzen Hauses,
|
||||
* und sie darf nicht ausgerechnet hier eine Ausnahme haben.
|
||||
*/
|
||||
function darfPerson(person, zielId) {
|
||||
const ziel = db().prepare("SELECT id, rolle FROM personen WHERE id = ?").get(Number(zielId));
|
||||
if (!ziel) return false;
|
||||
if (ziel.rolle === "modi") return siehtModis(person);
|
||||
return darfCreator(person, ziel.id);
|
||||
}
|
||||
|
||||
/* ---------- Die Liste ------------------------------------------------------ */
|
||||
@@ -204,13 +267,29 @@ checklisteRouter.get("/workspace/api/checkliste/:bereich", (req, res) => {
|
||||
|
||||
/* Wen darf die Person auswaehlen? */
|
||||
let creatoren = [];
|
||||
if (req.person.rolle !== "creator") {
|
||||
/* Ein Modi arbeitet -- wie ein Creator -- an seiner EIGENEN Liste.
|
||||
Ohne diese Zeile fiele er in den Zweig darunter, bekaeme eine
|
||||
Auswahl fremder Creator vorgesetzt und seine eigene Liste gar
|
||||
nicht zu sehen. */
|
||||
if (req.person.rolle !== "creator" && req.person.rolle !== "modi") {
|
||||
/* Nur DogFather waehlt aus allen Creatorn; ein Manager aus
|
||||
denen, die ihm zugeteilt sind (01.09.2026). */
|
||||
const ids = siehtAlles(req.person)
|
||||
? db().prepare("SELECT id FROM personen WHERE rolle = 'creator' AND aktiv = 1")
|
||||
.all().map((z) => z.id)
|
||||
: betreuteIds(req.person);
|
||||
/* DIE MODIS KOMMEN DAZU -- aber nur fuer die DogFather-Rolle
|
||||
(10.09.2026). Wunsch Filipe: "dass vanvan und ich diese sachen
|
||||
von den modis auch in der dogfather rolle sehen".
|
||||
|
||||
AUSDRUECKLICH NICHT UEBER siehtAlles(): Darin steckt auch Spicy
|
||||
Media, und die Modis gehen sie nichts an. Das ist die Regel des
|
||||
ganzen Hauses; sie darf nicht ausgerechnet hier eine Ausnahme
|
||||
haben, nur weil die Zeile daneben schon passte. */
|
||||
if (req.person.rolle === "admin") {
|
||||
for (const z of db().prepare(
|
||||
"SELECT id FROM personen WHERE rolle = 'modi' AND aktiv = 1").all()) ids.push(z.id);
|
||||
}
|
||||
creatoren = ids.length
|
||||
? db().prepare(`SELECT id, name FROM personen
|
||||
WHERE id IN (${ids.map(() => "?").join(",")}) ORDER BY name`).all(...ids)
|
||||
@@ -240,15 +319,34 @@ checklisteRouter.get("/workspace/api/checkliste/:bereich", (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
/* WELCHER PUNKTESATZ? Der der Person, um die es geht -- nicht der
|
||||
der Person, die zusieht. DogFather waehlt einen Modi aus und
|
||||
bekommt dessen Liste, nicht die eines Creators. */
|
||||
const zielRolle = creatorId > 0
|
||||
? db().prepare("SELECT rolle FROM personen WHERE id = ?").get(creatorId)?.rolle
|
||||
: req.person.rolle;
|
||||
const gruppen = gruppenFuer(bereich, zielRolle === "modi");
|
||||
|
||||
res.json({
|
||||
bereich, name: liste.name,
|
||||
gruppen: liste.gruppen,
|
||||
gruppen,
|
||||
stand, nachrichten,
|
||||
creatoren, creator_id: creatorId > 0 ? creatorId : null,
|
||||
/* WIE DIE AUSWAHL HEISST. Fuer einen Manager oder Scout stehen
|
||||
dort nur Creator -- "Creator" ist dann richtig. Bei der
|
||||
DogFather-Rolle stehen seit dem 10.09.2026 auch Modis darin,
|
||||
und ein Auswahlfeld namens "Creator", in dem ein Modi steht,
|
||||
ist schlicht falsch beschriftet. Das Wort kommt vom Server:
|
||||
Ein Rollenvergleich im Browser waere die Stelle, an der der
|
||||
Rollenname in einer ausgelieferten Datei landet. */
|
||||
wahl_name: req.person.rolle === "admin" ? "Person" : "Creator",
|
||||
/* Die Oberflaeche soll nicht raten muessen, wer was darf -- sonst
|
||||
zeigt sie einem Creator einen Knopf, der beim Klicken 403 sagt.
|
||||
Das ist schlechter als kein Knopf. */
|
||||
darf_bewerten: req.person.rolle !== "creator",
|
||||
/* Wer an der EIGENEN Liste arbeitet, bewertet sie nicht selbst --
|
||||
weder Creator noch Modi. Die Stufe setzt die Betreuung
|
||||
beziehungsweise die DogFather-Rolle. */
|
||||
darf_bewerten: req.person.rolle !== "creator" && req.person.rolle !== "modi",
|
||||
ich: req.person.id,
|
||||
});
|
||||
} catch (fehler) {
|
||||
@@ -264,7 +362,7 @@ checklisteRouter.put("/workspace/api/checkliste/:bereich/:schluessel", gleicheHe
|
||||
try {
|
||||
/* Ein Creator bewertet sich nicht selbst. Waere das moeglich,
|
||||
stuende alles auf gruen und die Ampel waere wertlos. */
|
||||
if (req.person.rolle === "creator") {
|
||||
if (req.person.rolle === "creator" || req.person.rolle === "modi") {
|
||||
return res.status(403).json({
|
||||
fehler: "Die Bewertung setzt deine Betreuung. Du kannst hier antworten.",
|
||||
});
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/* =====================================================================
|
||||
DIE CHECKLISTEN-PUNKTE EINES MODIS (10.09.2026)
|
||||
|
||||
Wunsch Filipe: *"diese kategorien mussen auf dieser seite auch noch
|
||||
auf die modis perfektionniert werden, da mussen lauter sachen sein
|
||||
die vanvan und ich danach verteilen können und da anpassen können ob
|
||||
gut oder nicht."*
|
||||
|
||||
WARUM EIN ZWEITER SATZ UND NICHT EIN ERWEITERTER: Die vorhandenen
|
||||
Punkte (LIVE_PUNKTE, COMMUNITY_PUNKTE, TECHNIK_PUNKTE) sind fuer
|
||||
CREATOR geschrieben -- "Upload messen", "Sendeplan festlegen",
|
||||
"Verweildauer vergleichen". Ein Modi sendet nicht, er moderiert. Ihm
|
||||
dieselbe Liste vorzulegen hiesse, ihn an Dingen zu messen, die nicht
|
||||
seine Arbeit sind.
|
||||
|
||||
DIESELBEN DREI BEREICHE UND DIESELBEN GRUPPEN wie beim Creator --
|
||||
Vor/Waehrend/Nach, Moderation/Aktion/Konflikt, Setup/Problem/Loesung.
|
||||
Das ist Absicht: Zwei verschiedene Einteilungen fuer dieselbe Sache
|
||||
waeren zwei Dinge zum Lernen statt einem, und die Oberflaeche kaeme
|
||||
ohne Umbau nicht damit zurecht.
|
||||
|
||||
DIESELBEN VIER STUFEN (anfaenger, fort, profi, meister). Sie
|
||||
entscheiden, was jemandem zuerst gezeigt wird. Ein Modi in der
|
||||
Probezeit soll nicht an "Muster ueber mehrere Sendungen erkennen"
|
||||
gemessen werden.
|
||||
|
||||
DIE SCHLUESSEL BEGINNEN MIT "m-". Der Stand haengt in `punkt_stand`
|
||||
an (bereich, schluessel, person) -- ein Modi und ein Creator im
|
||||
selben Bereich duerfen sich also nicht in die Quere kommen. Mit dem
|
||||
Praefix kann das nicht passieren, auch wenn jemand spaeter einen
|
||||
Punkt gleich benennt.
|
||||
|
||||
BEWERTET WIRD NICHT VON IHM SELBST, sondern von der DogFather-Rolle
|
||||
-- offen / gut / verbessern, wie beim Creator auch. Genau das war
|
||||
Filipes Wunsch: "verteilen ... und anpassen ob gut oder nicht".
|
||||
===================================================================== */
|
||||
|
||||
export const MODI_LIVE_PUNKTE = {
|
||||
vorbereitung: [
|
||||
{ schluessel: "m-schicht-bestaetigt", titel: "Schicht bestätigt", stufe: "anfaenger",
|
||||
text: "Vor dem Tag gesagt, ob du kannst. Ein „ich schau mal“ ist keine Zusage – und das Team plant danach." },
|
||||
{ schluessel: "m-ankuendigung-gesehen", titel: "Ankündigung gesehen", stufe: "anfaenger",
|
||||
text: "Du weißt, was draußen versprochen wurde. Sonst beantwortest du im Chat Fragen zu etwas, das du nicht kennst." },
|
||||
{ schluessel: "m-ablauf-gelesen", titel: "Ablaufplan gelesen", stufe: "anfaenger",
|
||||
text: "Anfang, Mitte, Schluss – grob im Kopf. Wer den Ablauf kennt, kann den Chat darauf vorbereiten." },
|
||||
{ schluessel: "m-regeln-griffbereit", titel: "Regeln griffbereit", stufe: "anfaenger",
|
||||
text: "Der Link oder der Text, den du hineinstellst, ohne zu suchen. Im Ernstfall zählen Sekunden." },
|
||||
{ schluessel: "m-eigene-technik", titel: "Eigene Technik geprüft", stufe: "anfaenger",
|
||||
text: "Dein Gerät, deine Verbindung, dein Zugang. Ein Modi, der mittendrin herausfliegt, fehlt genau dann." },
|
||||
{ schluessel: "m-wer-ist-dabei", titel: "Wer moderiert heute noch?", stufe: "fort",
|
||||
text: "Zu zweit teilt man sich auf – wer begrüßt, wer greift ein. Allein macht man beides halb." },
|
||||
{ schluessel: "m-ziel-bekannt", titel: "Ziel der Sendung bekannt", stufe: "fort",
|
||||
text: "Eine Zahl oder eine Sache. Darauf kannst du im Chat hinweisen, ohne zu fragen." },
|
||||
{ schluessel: "m-gaeste-bekannt", titel: "Gäste bekannt", stufe: "fort",
|
||||
text: "Wer kommt, wann, und wie heißt er richtig. Ein falsch geschriebener Name im Chat bleibt hängen." },
|
||||
{ schluessel: "m-wortfilter-durchgesehen", titel: "Wortfilter durchgesehen", stufe: "fort",
|
||||
text: "Was letzte Woche durchrutschte, gehört heute hinein. Ein Filter, den niemand pflegt, veraltet." },
|
||||
{ schluessel: "m-eskalationsweg", titel: "Eskalationsweg im Kopf", stufe: "fort",
|
||||
text: "Ab wann gibst du weiter, statt selbst zu entscheiden – und an wen. Vorher klären, nicht im Moment." },
|
||||
{ schluessel: "m-begruessung-bereit", titel: "Begrüßungssätze bereit", stufe: "profi",
|
||||
text: "Drei verschiedene, nicht einer dreimal. Wer merkt, dass ein Baustein antwortet, antwortet nicht zurück." },
|
||||
{ schluessel: "m-umfrage-vorbereitet", titel: "Umfrage vorbereitet", stufe: "profi",
|
||||
text: "Höchstens drei Antworten, vorher aufgeschrieben. Mitten im Stream formuliert man keine gute Frage." },
|
||||
{ schluessel: "m-vertretung-geklaert", titel: "Vertretung geklärt", stufe: "profi",
|
||||
text: "Wer übernimmt, wenn du wegmusst. Ein Chat ohne Aufsicht ist schlimmer als einer, der zu ist." },
|
||||
{ schluessel: "m-uebergabe-vorbereitet", titel: "Übergabe an die nächste Schicht", stufe: "meister",
|
||||
text: "Was die andere Person wissen muss, bevor du gehst. Zwei Zeilen reichen – aber sie müssen da sein." },
|
||||
],
|
||||
|
||||
mitschrift: [
|
||||
{ schluessel: "m-chat-im-blick", titel: "Chat wirklich im Blick", stufe: "anfaenger",
|
||||
text: "Nicht nebenbei. Wer mitschaut statt mitliest, sieht den Verstoß erst, wenn zehn Leute geantwortet haben." },
|
||||
{ schluessel: "m-neue-begruesst", titel: "Neue begrüßt", stufe: "anfaenger",
|
||||
text: "Mit Namen, wenn es geht. Wer beim ersten Mal angesprochen wird, kommt deutlich häufiger wieder." },
|
||||
{ schluessel: "m-spam-sofort-weg", titel: "Spam sofort weg", stufe: "anfaenger",
|
||||
text: "Löschen, bevor jemand antwortet. Eine Diskussion über einen gelöschten Beitrag kostet mehr als der Beitrag." },
|
||||
{ schluessel: "m-fragen-gesammelt", titel: "Fragen gesammelt", stufe: "anfaenger",
|
||||
text: "An einer Stelle, nicht im Kopf. Was im Chat vorbeirauscht, ist nach zwei Minuten weg." },
|
||||
{ schluessel: "m-auf-ziel-hingewiesen", titel: "Auf das Ziel hingewiesen", stufe: "fort",
|
||||
text: "Lieber zweimal beiläufig als einmal ausführlich. Wer später dazukommt, hat es sonst nie gehört." },
|
||||
{ schluessel: "m-interaktion-angestossen", titel: "Interaktion angestoßen", stufe: "fort",
|
||||
text: "Eine Frage in den Chat, wenn es still wird. Stille im Chat sieht für Neue aus wie ein leerer Raum." },
|
||||
{ schluessel: "m-clipmomente-markiert", titel: "Clip-Momente markiert", stufe: "fort",
|
||||
text: "Mit Uhrzeit, sofort. Am nächsten Tag findet niemand die Stelle wieder, an der alle gelacht haben." },
|
||||
{ schluessel: "m-zeit-im-blick", titel: "Zeit im Blick behalten", stufe: "fort",
|
||||
text: "Ein kurzes Zeichen genügt. Wer sendet, hat kein Gefühl für die Uhr – das ist normal." },
|
||||
{ schluessel: "m-verwarnen-vor-sperren", titel: "Erst verwarnen, dann sperren", stufe: "profi",
|
||||
text: "Außer bei den Sachen, die sofort gehen müssen. Eine Verwarnung, die jeder sieht, wirkt auf mehr als einen." },
|
||||
{ schluessel: "m-stammzuschauer-angesprochen", titel: "Stammzuschauer angesprochen", stufe: "profi",
|
||||
text: "Wiedererkannt zu werden ist der halbe Grund wiederzukommen." },
|
||||
{ schluessel: "m-eskalation-weitergegeben", titel: "Eskalation weitergegeben", stufe: "profi",
|
||||
text: "Nach der Regel, nicht nach Gefühl. Allein zu entscheiden, wo die Regel etwas anderes sagt, bringt dich in die Schusslinie." },
|
||||
{ schluessel: "m-nichts-oeffentlich-ausdiskutiert", titel: "Nichts öffentlich ausdiskutiert", stufe: "profi",
|
||||
text: "Im Chat gewinnt niemand. Kurz sagen, was gilt, und weiter – der Rest gehört ins Team." },
|
||||
{ schluessel: "m-stimmung-gemeldet", titel: "Stimmung gemeldet", stufe: "meister",
|
||||
text: "Wenn es kippt, bevor es kippt. Du siehst es früher als jeder, der gerade sendet." },
|
||||
{ schluessel: "m-ruhig-geblieben", titel: "Ruhig geblieben, als es eng wurde", stufe: "meister",
|
||||
text: "Der Ton eines Modis wird zum Ton des Chats. Das ist der schwerste Punkt auf dieser Liste." },
|
||||
],
|
||||
|
||||
auswertung: [
|
||||
{ schluessel: "m-auffaelligkeiten-notiert", titel: "Auffälligkeiten notiert", stufe: "anfaenger",
|
||||
text: "Solange du es noch weißt. Am nächsten Tag fehlt die Hälfte, und zwar die interessante." },
|
||||
{ schluessel: "m-clipmarken-weitergegeben", titel: "Clip-Marken weitergegeben", stufe: "anfaenger",
|
||||
text: "An die, die schneiden. Eine Marke, die niemand bekommt, ist keine." },
|
||||
{ schluessel: "m-feedback-im-teamchat", titel: "Kurzes Feedback im Team-Chat", stufe: "anfaenger",
|
||||
text: "Eine Sache, die gut lief, eine, die man ändert. Wer sich fünf vornimmt, ändert keine." },
|
||||
{ schluessel: "m-vorfaelle-festgehalten", titel: "Vorfälle festgehalten", stufe: "fort",
|
||||
text: "Wer, wann, was – und was du getan hast. Beim zweiten Mal mit derselben Person zählt genau das." },
|
||||
{ schluessel: "m-chatverlauf-gesichert", titel: "Chatverlauf gesichert", stufe: "fort",
|
||||
text: "Vor dem Schließen. Er ist die einzige Quelle dafür, was wirklich gesagt wurde." },
|
||||
{ schluessel: "m-offene-fragen-beantwortet", titel: "Offene Fragen nachgereicht", stufe: "fort",
|
||||
text: "Was im Stream unterging, gehört danach beantwortet – sonst fragt niemand ein zweites Mal." },
|
||||
{ schluessel: "m-eine-sache-fuers-naechste-mal", titel: "Eine Sache fürs nächste Mal", stufe: "fort",
|
||||
text: "Aufgeschrieben, nicht vorgenommen. Ein Vorsatz ohne Zettel ist am Freitag weg." },
|
||||
{ schluessel: "m-regel-vorgeschlagen", titel: "Regel vorgeschlagen, wo eine fehlte", stufe: "profi",
|
||||
text: "Wenn du zweimal improvisieren musstest, fehlt eine Regel – nicht Erfahrung." },
|
||||
{ schluessel: "m-wiederkehrende-stoerer", titel: "Wiederkehrende Störer notiert", stufe: "profi",
|
||||
text: "Dieselben Namen tauchen wieder auf. Eine Liste macht aus drei Einzelfällen ein Muster." },
|
||||
{ schluessel: "m-zahlen-angesehen", titel: "Zahlen angesehen", stufe: "profi",
|
||||
text: "Wie viele kamen, wie lange blieben sie. Nicht um zu bewerten, sondern um zu wissen, wann der Chat kippte." },
|
||||
{ schluessel: "m-uebergabe-geschrieben", titel: "Übergabe geschrieben", stufe: "meister",
|
||||
text: "So, dass jemand anderes die nächste Sendung übernehmen könnte. Wer nicht ersetzbar ist, kann nicht krank werden." },
|
||||
{ schluessel: "m-muster-ueber-sendungen", titel: "Muster über mehrere Sendungen erkannt", stufe: "meister",
|
||||
text: "Immer dieselbe Uhrzeit, immer dieselbe Sorte Kommentar. Das sieht nur, wer mehrere Abende nebeneinanderlegt." },
|
||||
],
|
||||
};
|
||||
|
||||
export const MODI_COMMUNITY_PUNKTE = {
|
||||
moderation: [
|
||||
{ schluessel: "m-regeln-gelesen", titel: "Regeln gelesen und verstanden", stufe: "anfaenger",
|
||||
text: "Nicht überflogen. Du wirst danach gefragt, und eine falsche Auskunft ist schwerer zurückzunehmen als keine." },
|
||||
{ schluessel: "m-eigene-rechte-bekannt", titel: "Eigene Rechte bekannt", stufe: "anfaenger",
|
||||
text: "Was du darfst: löschen, stummschalten, sperren – und was nicht. Wer es nicht weiß, tut zu viel oder zu wenig." },
|
||||
{ schluessel: "m-tonfall", titel: "Tonfall gefunden", stufe: "anfaenger",
|
||||
text: "Freundlich und kurz. Ein Modi, der sich rechtfertigt, verliert; einer, der herrisch wird, auch." },
|
||||
{ schluessel: "m-haeufige-fragen", titel: "Häufige Fragen im Kopf", stufe: "anfaenger",
|
||||
text: "Die fünf, die jeden Abend kommen. Wer sie kennt, muss nicht jedes Mal nachsehen." },
|
||||
{ schluessel: "m-warnung-vor-sperre", titel: "Reihenfolge klar: Hinweis, Warnung, Sperre", stufe: "fort",
|
||||
text: "Vorher entscheiden, nicht im Moment. Wer im Ärger sperrt, sperrt irgendwann falsch." },
|
||||
{ schluessel: "m-minderjaehrige", titel: "Umgang mit Minderjährigen", stufe: "fort",
|
||||
text: "Wie erkennt man sie, was gilt dann. Das ist keine Ermessensfrage." },
|
||||
{ schluessel: "m-privat-vs-oeffentlich", titel: "Privat statt öffentlich", stufe: "fort",
|
||||
text: "Was eine Person betrifft, klärt man mit ihr – nicht vor zweihundert Leuten." },
|
||||
{ schluessel: "m-keine-versprechen", titel: "Keine Versprechen im Namen anderer", stufe: "fort",
|
||||
text: "\"Das machen wir\" bindet das ganze Team. \"Ich gebe es weiter\" bindet nur dich." },
|
||||
{ schluessel: "m-entscheidung-begruenden", titel: "Entscheidung in einem Satz begründen können", stufe: "profi",
|
||||
text: "Nicht im Chat – aber wenn DogFather fragt. Eine Sperre, die niemand erklären kann, wird zurückgenommen." },
|
||||
{ schluessel: "m-eigene-grenze", titel: "Eigene Grenze kennen", stufe: "profi",
|
||||
text: "Wann du aussteigst und jemanden holst. Weiterzumachen, wenn es dich mitnimmt, hilft niemandem." },
|
||||
{ schluessel: "m-gleich-behandeln", titel: "Alle gleich behandeln", stufe: "profi",
|
||||
text: "Auch die, die man mag. Eine Ausnahme sehen mehr Leute, als man denkt." },
|
||||
{ schluessel: "m-neue-modis-eingearbeitet", titel: "Neue Modis eingearbeitet", stufe: "meister",
|
||||
text: "Einen Abend nebenher laufen lassen und danach besprechen. Zusehen lernt mehr als jede Regelliste." },
|
||||
],
|
||||
|
||||
aktion: [
|
||||
{ schluessel: "m-willkommen-gepflegt", titel: "Willkommens-Routine gepflegt", stufe: "anfaenger",
|
||||
text: "Ein Satz, der nicht nach Textbaustein klingt. Wer merkt, dass ein Automat schreibt, antwortet nicht." },
|
||||
{ schluessel: "m-namen-gemerkt", titel: "Namen gemerkt", stufe: "anfaenger",
|
||||
text: "Die zehn, die immer da sind. Mehr braucht es nicht, damit sich eine Community erkannt fühlt." },
|
||||
{ schluessel: "m-fragen-in-den-stream", titel: "Fragen in den Stream gegeben", stufe: "anfaenger",
|
||||
text: "Gesammelt, ausgewählt, weitergereicht. Nicht alles – die drei, die etwas bringen." },
|
||||
{ schluessel: "m-umfrage-ausgewertet", titel: "Umfrage ausgewertet", stufe: "fort",
|
||||
text: "Und das Ergebnis gesagt. Eine Abstimmung ohne Ergebnis fragt beim nächsten Mal niemand mehr mit." },
|
||||
{ schluessel: "m-aktion-vorgeschlagen", titel: "Eine Aktion vorgeschlagen", stufe: "fort",
|
||||
text: "Challenge, Q&A, gemeinsamer Abend. Du hörst im Chat, was ankommt, früher als alle anderen." },
|
||||
{ schluessel: "m-stille-belebt", titel: "Stille belebt statt ausgesessen", stufe: "fort",
|
||||
text: "Eine Frage, ein Bezug auf vorhin. Ein stiller Chat wirkt auf Neue wie ein leerer Raum." },
|
||||
{ schluessel: "m-rueckmeldung-eingesammelt", titel: "Rückmeldungen eingesammelt", stufe: "fort",
|
||||
text: "Was Zuschauer sich wünschen, gehört weitergegeben – auch wenn es nicht umgesetzt wird." },
|
||||
{ schluessel: "m-stammzuschauer-liste", titel: "Liste der Stammzuschauer gepflegt", stufe: "profi",
|
||||
text: "Wer seit Wochen da ist, verdient mehr als ein Herz. Ohne Liste vergisst man ihn." },
|
||||
{ schluessel: "m-neue-in-die-runde", titel: "Neue in die Runde geholt", stufe: "profi",
|
||||
text: "Sie einmal namentlich ins Gespräch ziehen. Der Unterschied zwischen Zuschauen und Dabeisein." },
|
||||
{ schluessel: "m-eigene-idee-eingetragen", titel: "Eigene Idee ins Ideen-Board", stufe: "profi",
|
||||
text: "Was dir im Chat auffällt, ist die beste Quelle für Inhalte – aber nur, wenn es aufgeschrieben wird." },
|
||||
{ schluessel: "m-aktion-durchgezogen", titel: "Eine Aktion von Anfang bis Ende begleitet", stufe: "meister",
|
||||
text: "Ankündigen, durchführen, hinterher sagen, was daraus wurde. Der letzte Teil wird fast immer vergessen." },
|
||||
{ schluessel: "m-community-uebersetzt", titel: "Zwischen Community und Team übersetzt", stufe: "meister",
|
||||
text: "Was im Chat gemeint ist, und was im Team ankommt, sind oft zwei Dinge. Diese Lücke zu schließen ist die eigentliche Arbeit." },
|
||||
],
|
||||
|
||||
konflikt: [
|
||||
{ schluessel: "m-erst-lesen", titel: "Erst lesen, dann handeln", stufe: "anfaenger",
|
||||
text: "Zwei Sätze zurückscrollen. Die Hälfte der Streitfälle sieht mit dem Satz davor anders aus." },
|
||||
{ schluessel: "m-nicht-mitstreiten", titel: "Nicht mitstreiten", stufe: "anfaenger",
|
||||
text: "Du bist nicht Partei. In dem Moment, in dem du eine wirst, hast du deine Rolle verloren." },
|
||||
{ schluessel: "m-kurz-und-klar", titel: "Kurz und klar sagen, was gilt", stufe: "anfaenger",
|
||||
text: "Ein Satz, keine Begründungskette. Je länger die Erklärung, desto mehr Angriffsfläche." },
|
||||
{ schluessel: "m-abkuehlen-lassen", titel: "Abkühlen lassen statt eskalieren", stufe: "fort",
|
||||
text: "Stummschalten auf Zeit ist oft besser als sperren. Man kann es zurücknehmen." },
|
||||
{ schluessel: "m-zweite-meinung", titel: "Zweite Meinung geholt", stufe: "fort",
|
||||
text: "Bei allem, was größer ist als ein blöder Kommentar. Zu zweit entscheidet es sich leichter und besser." },
|
||||
{ schluessel: "m-belege-gesichert", titel: "Belege gesichert", stufe: "fort",
|
||||
text: "Bildschirmfoto, bevor der Beitrag weg ist. Ohne Beleg steht später Aussage gegen Aussage." },
|
||||
{ schluessel: "m-danach-gemeldet", titel: "Danach im Team gemeldet", stufe: "fort",
|
||||
text: "Auch wenn es gut ausging. DogFather und die rechte Hand sollten es von dir erfahren, nicht von anderen." },
|
||||
{ schluessel: "m-keine-drohungen", titel: "Keine Drohungen, nur Folgen", stufe: "profi",
|
||||
text: "\"Noch einmal und du bist raus\" ist eine Ansage. Alles darüber hinaus ist Streit." },
|
||||
{ schluessel: "m-nach-der-sperre", titel: "Nach der Sperre nicht nachtreten", stufe: "profi",
|
||||
text: "Kein Kommentar hinterher, kein Witz darüber. Der Chat merkt sich das." },
|
||||
{ schluessel: "m-eigene-fehler", titel: "Eigenen Fehler zugegeben", stufe: "profi",
|
||||
text: "Falsch gesperrt? Zurücknehmen und einmal sagen. Das kostet weniger als der Versuch, recht zu behalten." },
|
||||
{ schluessel: "m-ernstfall-geprobt", titel: "Ernstfall einmal durchgesprochen", stufe: "meister",
|
||||
text: "Wer tut was, wenn es wirklich kippt. Im Ernstfall zum ersten Mal darüber nachzudenken ist zu spät." },
|
||||
{ schluessel: "m-daraus-gelernt", titel: "Aus dem Fall eine Regel gemacht", stufe: "meister",
|
||||
text: "Ein Vorfall, aus dem nichts folgt, kommt wieder. Der Ertrag ist die Regel, nicht die Sperre." },
|
||||
],
|
||||
};
|
||||
|
||||
export const MODI_TECHNIK_PUNKTE = {
|
||||
setup: [
|
||||
{ schluessel: "m-zugaenge-funktionieren", titel: "Zugänge funktionieren", stufe: "anfaenger",
|
||||
text: "Einmal vor der Sendung anmelden, nicht währenddessen. Ein abgelaufener Zugang fällt sonst im schlechtesten Moment auf." },
|
||||
{ schluessel: "m-benachrichtigungen-an", titel: "Benachrichtigungen an", stufe: "anfaenger",
|
||||
text: "Für Aufgaben und Team-Nachrichten. Ein Hinweis, den niemand bekommt, ist keiner." },
|
||||
{ schluessel: "m-zweites-geraet", titel: "Zweites Gerät griffbereit", stufe: "anfaenger",
|
||||
text: "Handy neben dem Rechner. Wenn eines hängt, moderierst du mit dem anderen weiter." },
|
||||
{ schluessel: "m-verbindung-stabil", titel: "Verbindung stabil", stufe: "anfaenger",
|
||||
text: "Kabel schlägt WLAN. Ein Modi, der alle zehn Minuten neu lädt, sieht die Hälfte nicht." },
|
||||
{ schluessel: "m-chatfenster-eingerichtet", titel: "Chatfenster richtig eingerichtet", stufe: "fort",
|
||||
text: "Groß genug, ohne anderes darüber. Wer scrollen muss, um mitzulesen, liest nicht mit." },
|
||||
{ schluessel: "m-werkzeuge-kennen", titel: "Moderations-Werkzeuge kennen", stufe: "fort",
|
||||
text: "Wo Löschen, Stummschalten, Sperren sitzt – ohne zu suchen. Im Ernstfall zählen Sekunden." },
|
||||
{ schluessel: "m-automod-verstanden", titel: "Automatische Filter verstanden", stufe: "fort",
|
||||
text: "Was sie wegnehmen und was nicht. Wer sich darauf verlässt, ohne es zu wissen, übersieht genau die Lücke." },
|
||||
{ schluessel: "m-app-installiert", titel: "Die App wirklich installiert", stufe: "fort",
|
||||
text: "Nicht als Lesezeichen. Nur so kommen Erinnerungen an, auch wenn der Browser zu ist." },
|
||||
{ schluessel: "m-tastenkuerzel", titel: "Tastenkürzel eingerichtet", stufe: "profi",
|
||||
text: "Die drei, die du am häufigsten brauchst. Aus zwei Sekunden werden über einen Abend Minuten." },
|
||||
{ schluessel: "m-ruhe-eingestellt", titel: "Ruhezeiten eingestellt", stufe: "profi",
|
||||
text: "Damit die App nachts still ist. Wer um drei geweckt wird, schaltet sie irgendwann ganz ab." },
|
||||
{ schluessel: "m-ersatzweg", titel: "Ersatzweg ins Netz", stufe: "meister",
|
||||
text: "Mobilfunk, einmal wirklich benutzt. Ein Ersatz, den man erst im Ausfall einrichtet, ist keiner." },
|
||||
],
|
||||
|
||||
problem: [
|
||||
{ schluessel: "m-was-genau-faellt-aus", titel: "Was genau fällt aus?", stufe: "anfaenger",
|
||||
text: "Chat, Bild, dein Zugang oder das ganze Netz – das sind vier verschiedene Probleme mit vier Lösungen." },
|
||||
{ schluessel: "m-sofort-gesagt", titel: "Sofort gesagt, dass etwas ist", stufe: "anfaenger",
|
||||
text: "Lieber einmal zu viel im Team-Chat. Ein stiller Modi sieht aus wie ein anwesender." },
|
||||
{ schluessel: "m-notiert-solange-frisch", titel: "Notiert, solange es frisch ist", stufe: "anfaenger",
|
||||
text: "Uhrzeit, Fehlermeldung, was du zuletzt getan hast. Am nächsten Tag fehlt genau das." },
|
||||
{ schluessel: "m-eine-aenderung", titel: "Nur eine Änderung auf einmal", stufe: "fort",
|
||||
text: "Wer drei Dinge gleichzeitig ändert und es geht wieder, weiß nicht warum – und steht beim nächsten Mal am Anfang." },
|
||||
{ schluessel: "m-uebergeben-statt-kaempfen", titel: "Übergeben statt kämpfen", stufe: "fort",
|
||||
text: "Nach fünf Minuten Basteln moderiert jemand anderes. Der Chat wartet nicht auf deine Technik." },
|
||||
{ schluessel: "m-bildschirmfoto", titel: "Bildschirmfoto gemacht", stufe: "fort",
|
||||
text: "Eine Fehlermeldung, die man nachher beschreibt, ist eine andere als die, die dastand." },
|
||||
{ schluessel: "m-wiederholbar", titel: "Geprüft, ob es wiederkommt", stufe: "profi",
|
||||
text: "Einmal weg ist nicht gelöst. Beim nächsten Mal nachsehen gehört dazu." },
|
||||
{ schluessel: "m-nicht-alleine-suchen", titel: "Nicht allein stundenlang suchen", stufe: "profi",
|
||||
text: "Nach zwanzig Minuten jemanden dazuholen. Zu zweit findet man es in fünf." },
|
||||
],
|
||||
|
||||
loesung: [
|
||||
{ schluessel: "m-was-geholfen-hat", titel: "Was geholfen hat", stufe: "anfaenger",
|
||||
text: "Genau die eine Änderung – nicht alles, was probiert wurde. Sonst ist es beim nächsten Mal wieder Raten." },
|
||||
{ schluessel: "m-fuer-alle-aufgeschrieben", titel: "Für alle aufgeschrieben", stufe: "anfaenger",
|
||||
text: "In die Bibliothek, nicht in den Chat. Was im Chat steht, ist nach zwei Tagen unauffindbar." },
|
||||
{ schluessel: "m-anleitung-verstaendlich", titel: "Anleitung verständlich", stufe: "fort",
|
||||
text: "Jemanden gegenlesen lassen, der es nicht erlebt hat. Was du selbst geschrieben hast, verstehst du immer." },
|
||||
{ schluessel: "m-dauerhaft-oder-pflaster", titel: "Dauerhaft oder Pflaster?", stufe: "fort",
|
||||
text: "Beides ist in Ordnung – aber es gehört dabeigeschrieben, damit niemand darauf baut." },
|
||||
{ schluessel: "m-vorbeugen-vorgeschlagen", titel: "Vorbeugen vorgeschlagen", stufe: "profi",
|
||||
text: "Wenn es zweimal passiert ist, ist es kein Zufall mehr, sondern eine fehlende Einstellung." },
|
||||
{ schluessel: "m-wissen-weitergegeben", titel: "Wissen weitergegeben", stufe: "meister",
|
||||
text: "Einmal im Team erklärt, statt es dreimal allein zu lösen. Ein Modi, der als Einziger etwas kann, ist ein Engpass." },
|
||||
],
|
||||
};
|
||||
|
||||
/* Alle drei unter den Bereichsnamen, unter denen sie gebraucht werden --
|
||||
damit die Auswahl an einer Stelle steht und nicht in jeder Abfrage
|
||||
neu zusammengesucht wird. */
|
||||
export const MODI_PUNKTE = {
|
||||
live: MODI_LIVE_PUNKTE,
|
||||
community: MODI_COMMUNITY_PUNKTE,
|
||||
technik: MODI_TECHNIK_PUNKTE,
|
||||
};
|
||||
@@ -0,0 +1,127 @@
|
||||
/* =====================================================================
|
||||
DIE LAGE DES MODI-TEAMS -- an einer Stelle (10.09.2026)
|
||||
|
||||
Wunsch Filipe: *"dan will ich auch dass vanvan und ich diese sachen
|
||||
von den modis auch in der dogfather rolle sehen. da soll es eine
|
||||
kategorie geben für uns beide nur sonst keinen wo wir dass alles
|
||||
sehen und behandeln können. so wie mit dem ideen-board."*
|
||||
|
||||
NUR DIE DOGFATHER-ROLLE. Das sind Filipe und VanVan -- sie tragen
|
||||
beide `admin`. Fuer alle anderen gibt es diese Seite nicht: 404 wie
|
||||
bei einer Adresse, die niemand kennt. Ein "kein Zugriff" waere die
|
||||
Auskunft, dass es sie gibt.
|
||||
|
||||
SEHEN UND BEHANDELN, in dieser Reihenfolge: Die Seite zaehlt nur --
|
||||
bewertet wird dort, wo die Punkte stehen. Jede Zahl ist deshalb ein
|
||||
Verweis in den jeweiligen Bereich, mit der Person schon
|
||||
vorausgewaehlt. Eine zweite Stelle zum Bewerten waere eine zweite
|
||||
Stelle, an der es auseinanderlaeuft.
|
||||
|
||||
UND SIE IST KEINE UEBERWACHUNG. Dieselbe Ueberlegung steht schon
|
||||
ueber team.html: Eine Seite mit Zahlen ueber Kollegen wird als
|
||||
Kontrolle gelesen, und dann arbeitet niemand mehr offen damit.
|
||||
Deshalb zaehlt sie, was NOCH NICHT BESPROCHEN ist -- nicht, wer wie
|
||||
fleissig war. "Offen" heisst hier: Darueber habt ihr noch nicht
|
||||
geredet. Es ist eine Liste fuer euch, keine Note fuer ihn.
|
||||
===================================================================== */
|
||||
import express from "express";
|
||||
import {
|
||||
db, sitzungLesen, istDogFather, heuteLokal,
|
||||
} from "./workspace.js";
|
||||
import { MODI_PUNKTE } from "./workspace-modi-punkte.js";
|
||||
|
||||
export const teamlageRouter = express.Router();
|
||||
|
||||
/* Wie viele Punkte es je Bereich ueberhaupt gibt. Einmal gerechnet --
|
||||
die Saetze aendern sich nicht zur Laufzeit. */
|
||||
const GESAMT = Object.fromEntries(
|
||||
Object.entries(MODI_PUNKTE).map(([b, g]) => [b, Object.values(g).flat().length]));
|
||||
|
||||
teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
|
||||
try {
|
||||
const person = sitzungLesen(req);
|
||||
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||||
/* 404 und nicht 403 -- siehe Kopf. */
|
||||
if (!istDogFather(person)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
|
||||
const modis = db().prepare(
|
||||
`SELECT id, name, bild, letzter_login FROM personen
|
||||
WHERE rolle = 'modi' AND aktiv = 1 ORDER BY name`).all();
|
||||
|
||||
if (!modis.length) {
|
||||
return res.json({ modis: [], bereiche: GESAMT, heute: heuteLokal() });
|
||||
}
|
||||
const platz = modis.map(() => "?").join(",");
|
||||
const nummern = modis.map((m) => m.id);
|
||||
const heute = heuteLokal();
|
||||
|
||||
/* ALLES IN VIER ABFRAGEN, nicht vier je Person. Bei zehn Modis
|
||||
waeren das vierzig -- den Fehler hat das Haus bei den Terminen
|
||||
schon einmal gemacht und ihn dort ausdruecklich vermerkt. */
|
||||
const stand = new Map(); // person -> bereich -> {gut, verbessern}
|
||||
for (const z of db().prepare(
|
||||
`SELECT creator_id, bereich, stufe, COUNT(*) AS n FROM punkt_stand
|
||||
WHERE creator_id IN (${platz}) GROUP BY creator_id, bereich, stufe`).all(...nummern)) {
|
||||
if (!stand.has(z.creator_id)) stand.set(z.creator_id, {});
|
||||
const b = stand.get(z.creator_id);
|
||||
b[z.bereich] = b[z.bereich] || { gut: 0, verbessern: 0 };
|
||||
if (z.stufe === "gut" || z.stufe === "verbessern") b[z.bereich][z.stufe] = z.n;
|
||||
}
|
||||
|
||||
const aufgaben = new Map();
|
||||
for (const z of db().prepare(
|
||||
`SELECT verantwortlich_id AS p,
|
||||
COUNT(*) AS offen,
|
||||
SUM(CASE WHEN frist IS NOT NULL AND frist < ? THEN 1 ELSE 0 END) AS ueberfaellig
|
||||
FROM aufgaben
|
||||
WHERE verantwortlich_id IN (${platz})
|
||||
AND status NOT IN ('erledigt', 'abgebrochen')
|
||||
GROUP BY verantwortlich_id`).all(heute, ...nummern)) {
|
||||
aufgaben.set(z.p, { offen: z.offen, ueberfaellig: z.ueberfaellig || 0 });
|
||||
}
|
||||
|
||||
const ideen = new Map();
|
||||
for (const z of db().prepare(
|
||||
`SELECT erstellt_von AS p, COUNT(*) AS n FROM eintraege
|
||||
WHERE bereich = 'ideen' AND erstellt_von IN (${platz}) GROUP BY erstellt_von`)
|
||||
.all(...nummern)) {
|
||||
ideen.set(z.p, z.n);
|
||||
}
|
||||
|
||||
/* Wie viele Rueckmeldungen an seinen Punkten haengen -- das ist
|
||||
das Gespraech, nicht die Note. */
|
||||
const nachrichten = new Map();
|
||||
for (const z of db().prepare(
|
||||
`SELECT creator_id AS p, COUNT(*) AS n FROM punkt_nachricht
|
||||
WHERE creator_id IN (${platz}) GROUP BY creator_id`).all(...nummern)) {
|
||||
nachrichten.set(z.p, z.n);
|
||||
}
|
||||
|
||||
res.json({
|
||||
heute,
|
||||
bereiche: GESAMT,
|
||||
modis: modis.map((m) => {
|
||||
const s = stand.get(m.id) || {};
|
||||
const je = {};
|
||||
for (const [b, gesamt] of Object.entries(GESAMT)) {
|
||||
const gut = s[b]?.gut || 0;
|
||||
const verbessern = s[b]?.verbessern || 0;
|
||||
je[b] = { gesamt, gut, verbessern, offen: Math.max(gesamt - gut - verbessern, 0) };
|
||||
}
|
||||
return {
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
bild: m.bild ? `/workspace/api/steckbrief/bild/${m.bild}` : null,
|
||||
letzter_login: m.letzter_login,
|
||||
aufgaben: aufgaben.get(m.id) || { offen: 0, ueberfaellig: 0 },
|
||||
ideen: ideen.get(m.id) || 0,
|
||||
nachrichten: nachrichten.get(m.id) || 0,
|
||||
bereiche: je,
|
||||
};
|
||||
}),
|
||||
});
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] Team-Lage:", fehler?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
@@ -460,6 +460,30 @@ export function markeFuer(person) {
|
||||
return person?.rolle === "modi" ? MODI_MARKE : null;
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
KACHELN, DIE ZU EINER VORHANDENEN LISTE DAZUKOMMEN (10.09.2026)
|
||||
|
||||
bereicheFuer() ersetzt die Liste im Browser ganz -- das passt fuer
|
||||
eine Rolle, die dort gar nicht vorkommt. Fuer die DogFather-Rolle
|
||||
passt es nicht: Sie hat ihre 23 Kacheln, und es soll EINE dazu.
|
||||
|
||||
Sie steht hier und nicht in assets/js/bereiche.js, weil dort jeder
|
||||
mitliest. "Team-Lage" allein verraet zwar nichts -- aber eine
|
||||
Kachel, die nur bei einer einzigen Rolle erscheint, wirft die Frage
|
||||
auf, fuer wen sie ist. Genau die soll niemand stellen.
|
||||
===================================================================== */
|
||||
const ZUSATZ_BEREICHE = {
|
||||
admin: [{
|
||||
gruppe: "Rund um das Team",
|
||||
name: "Team-Lage", unter: "Was noch nicht besprochen ist",
|
||||
zeichen: "teamlage", ton: 22, ziel: "teamlage.html", szene: "halle",
|
||||
}],
|
||||
};
|
||||
|
||||
export function zusatzBereicheFuer(person) {
|
||||
return ZUSATZ_BEREICHE[person?.rolle] || [];
|
||||
}
|
||||
|
||||
/* scrypt-Parameter. N=2^15 braucht auf diesem Server rund 150 ms — spürbar
|
||||
genug, um Rateversuche auszubremsen, und unauffällig für einen echten
|
||||
Anmeldevorgang. Die Werte werden je Datensatz mitgespeichert, damit sie
|
||||
@@ -2499,6 +2523,12 @@ const GESCHUETZT = {
|
||||
Sicherungen, die KI und der Zustand des Servers -- das ist Betrieb,
|
||||
nicht Betreuung. */
|
||||
"/workspace/automation.html": ["admin"],
|
||||
/* Die gemeinsame Lage des Teams -- nur die DogFather-Rolle, also
|
||||
Filipe und VanVan (10.09.2026). Fuer alle anderen leitet die
|
||||
Schranke auf die Startseite; die Schnittstelle dahinter antwortet
|
||||
zusaetzlich mit 404. Zwei Schloesser, absichtlich: Faellt eines
|
||||
weg, haelt das andere. */
|
||||
"/workspace/teamlage.html": ["admin"],
|
||||
};
|
||||
|
||||
/* Die einzige Seite unter /workspace, die offen sein MUSS -- man kann
|
||||
@@ -2802,6 +2832,9 @@ workspaceRouter.get("/workspace/api/ich", (req, res) => {
|
||||
bereiche: bereicheFuer(angesehen || person),
|
||||
rolle_text: rollentextFuer(angesehen || person),
|
||||
marke: markeFuer(angesehen || person),
|
||||
/* Kacheln, die zur eigenen Liste DAZUkommen -- im Gegensatz zu
|
||||
`bereiche`, das sie ersetzt. Leer fuer alle, die keine haben. */
|
||||
bereiche_zusatz: zusatzBereicheFuer(angesehen || person),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -13,25 +13,25 @@
|
||||
"theme_color": "#06090f",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609100240",
|
||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609100303",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609100240",
|
||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609100303",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609100240",
|
||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609100303",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609100240",
|
||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609100303",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
|
||||
@@ -83,6 +83,20 @@
|
||||
Klappzustände: Es soll beim Neuzeichnen nicht zurückspringen. */
|
||||
const stufenFilter = {};
|
||||
|
||||
/* EIN VERWEIS KANN JEMANDEN VORAUSWAEHLEN (10.09.2026).
|
||||
|
||||
Ohne das fuehrt ein Link aus einer Uebersicht zwar auf die
|
||||
richtige Seite, aber auf die erstbeste Person darin -- man muesste
|
||||
danach noch einmal auswaehlen, und beim zweiten Mal vergisst man,
|
||||
warum man hier war. Gelesen wird es EINMAL; danach entscheidet die
|
||||
Auswahl im Feld, sonst spraenge sie bei jedem Neuzeichnen zurueck. */
|
||||
try {
|
||||
const ausAdresse = Number(new URLSearchParams(location.search).get('creator_id'));
|
||||
if (Number.isInteger(ausAdresse) && ausAdresse > 0) {
|
||||
for (const b of ['live', 'community', 'technik', 'content']) gewaehlt[b] = ausAdresse;
|
||||
}
|
||||
} catch { /* ohne Adresse bleibt es wie bisher */ }
|
||||
|
||||
window.checkliste = async function (behaelter, bereich) {
|
||||
if (!behaelter) return;
|
||||
let daten;
|
||||
@@ -120,7 +134,7 @@
|
||||
|
||||
if (daten.darf_bewerten && daten.creatoren.length) {
|
||||
const feld = el('div', 'liste-fest__wahl');
|
||||
feld.append(el('label', 'feld-schild', 'Creator'));
|
||||
feld.append(el('label', 'feld-schild', daten.wahl_name || 'Creator'));
|
||||
const s = el('select');
|
||||
for (const c of daten.creatoren) s.append(new Option(c.name, String(c.id)));
|
||||
if (daten.creator_id) s.value = String(daten.creator_id);
|
||||
|
||||
@@ -171,9 +171,29 @@
|
||||
}
|
||||
return [...nach.values()];
|
||||
}
|
||||
return GRUPPEN
|
||||
/* KACHELN, DIE DAZUKOMMEN (10.09.2026).
|
||||
|
||||
Anders als `ich.bereiche` (das die Liste ERSETZT) haengen
|
||||
diese sich an eine vorhandene Gruppe an. Gebraucht wird das
|
||||
fuer Kacheln, die nur eine einzige Rolle bekommt: In dieser
|
||||
Datei duerften sie nicht stehen -- sie wird an jeden
|
||||
ausgeliefert, und eine Kachel, die nur bei einem erscheint,
|
||||
wirft die Frage auf, fuer wen sie ist.
|
||||
|
||||
Findet sich die genannte Gruppe nicht, wird eine eigene
|
||||
angelegt. Sonst faellt die Kachel lautlos weg, sobald jemand
|
||||
eine Gruppe umbenennt -- und niemand merkt es, weil nichts
|
||||
kaputtgeht. */
|
||||
const zusatz = Array.isArray(ich?.bereiche_zusatz) ? ich.bereiche_zusatz : [];
|
||||
const gruppen = GRUPPEN
|
||||
.map((g) => ({ ...g, bereiche: g.bereiche.filter((b) => b.rollen.includes(wer.rolle)) }))
|
||||
.filter((g) => g.bereiche.length);
|
||||
for (const b of zusatz) {
|
||||
const g = gruppen.find((x) => x.name === b.gruppe);
|
||||
if (g) g.bereiche = [...g.bereiche, b];
|
||||
else gruppen.push({ name: b.gruppe, unter: b.gruppeUnter || '', bereiche: [b] });
|
||||
}
|
||||
return gruppen;
|
||||
}
|
||||
|
||||
let laufNr = 0;
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/* =====================================================================
|
||||
DIE LAGE DES TEAMS -- eine Seite fuer die, die sie sehen duerfen.
|
||||
|
||||
Wunsch Filipe (10.09.2026): eine Stelle, an der er und VanVan alles
|
||||
sehen und behandeln koennen.
|
||||
|
||||
SIE ZAEHLT NUR, SIE BEWERTET NICHT. Bewertet wird dort, wo die
|
||||
Punkte stehen -- je Bereich fuehrt ein Verweis dorthin, mit der
|
||||
Person schon vorausgewaehlt. Eine zweite Stelle zum Bewerten waere
|
||||
eine zweite Stelle, an der es auseinanderlaeuft.
|
||||
|
||||
KEINE NEUEN CSS-KLASSEN. Fuer genau diese Art Karte gibt es sie
|
||||
schon (`tperson`, `tz`, `schritt` -- benutzt von team.html). Elf
|
||||
neue haetten in mehreren Stilvorlagen gepflegt werden muessen, fuer
|
||||
ein Aussehen, das es bereits gibt. Zwei Bauarten fuer dieselbe Sache
|
||||
sind ausserdem zwei Dinge zum Lernen statt einem.
|
||||
|
||||
IN DIESER DATEI STEHT KEIN ROLLENNAME. Sie wird wie jede Datei unter
|
||||
/workspace an JEDEN ausgeliefert, der die Seite oeffnet. Wer sie
|
||||
bekommt, entscheiden der Server (404 fuer alle anderen) und die
|
||||
Kachel, die ebenfalls vom Server kommt.
|
||||
===================================================================== */
|
||||
(() => {
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const el = (tag, klasse, text) => {
|
||||
const k = document.createElement(tag);
|
||||
if (klasse) k.className = klasse;
|
||||
if (text !== undefined) k.textContent = text;
|
||||
return k;
|
||||
};
|
||||
const melde = (t) => { const f = $('fehler'); if (f) f.textContent = t || ''; };
|
||||
|
||||
async function hole(pfad, optionen = {}) {
|
||||
const a = await fetch(pfad, { credentials: 'same-origin', ...optionen });
|
||||
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
|
||||
if (a.status === 404) { location.assign('/workspace/start.html'); throw new Error('nicht erlaubt'); }
|
||||
return a;
|
||||
}
|
||||
|
||||
const tageHer = (iso) => {
|
||||
if (!iso) return null;
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return null;
|
||||
return Math.floor((Date.now() - d.getTime()) / 86400000);
|
||||
};
|
||||
|
||||
/* Die drei Bereiche in derselben Reihenfolge wie auf der Startseite.
|
||||
Die Namen stehen hier, weil die Seite sonst eine zweite Abfrage
|
||||
braeuchte, nur um drei Woerter zu holen. */
|
||||
const BEREICHE = [
|
||||
{ schluessel: 'live', name: 'Live-Ablauf' },
|
||||
{ schluessel: 'community', name: 'Community' },
|
||||
{ schluessel: 'technik', name: 'Technik' },
|
||||
];
|
||||
|
||||
function zahl(wert, schild) {
|
||||
const k = el('div', 'tz');
|
||||
k.append(el('strong', 'tz__wert', String(wert)));
|
||||
k.append(el('span', 'tz__schild', schild));
|
||||
return k;
|
||||
}
|
||||
|
||||
function karte(m) {
|
||||
const k = el('article', 'tperson');
|
||||
|
||||
const kopf = el('div', 'tperson__kopf');
|
||||
const zeichen = el('span', 'tperson__zeichen');
|
||||
if (m.bild) {
|
||||
const b = document.createElement('img');
|
||||
b.src = m.bild; b.alt = ''; b.loading = 'lazy';
|
||||
zeichen.append(b);
|
||||
} else {
|
||||
zeichen.textContent = (m.name || '?').trim().charAt(0).toUpperCase();
|
||||
}
|
||||
kopf.append(zeichen);
|
||||
|
||||
const text = el('div', 'tperson__text');
|
||||
text.append(el('p', 'tperson__namen', m.name));
|
||||
/* WANN ZULETZT DA -- als Tatsache, nicht als Vorwurf. Wer zwei
|
||||
Wochen nicht da war, hat vielleicht Urlaub; die Zahl sagt nur,
|
||||
dass man nachfragen koennte. */
|
||||
const her = tageHer(m.letzter_login);
|
||||
text.append(el('span', 'tperson__zuletzt', her === null
|
||||
? 'war noch nie angemeldet'
|
||||
: her === 0 ? 'heute da gewesen'
|
||||
: her === 1 ? 'gestern da gewesen' : `vor ${her} Tagen da gewesen`));
|
||||
kopf.append(text);
|
||||
k.append(kopf);
|
||||
|
||||
const zahlen = el('div', 'tperson__zahlen');
|
||||
zahlen.append(zahl(m.aufgaben.offen, 'Aufgaben offen'));
|
||||
zahlen.append(zahl(m.aufgaben.ueberfaellig, 'überfällig'));
|
||||
zahlen.append(zahl(m.ideen, 'Ideen'));
|
||||
zahlen.append(zahl(m.nachrichten, 'Rückmeldungen'));
|
||||
for (const b of BEREICHE) {
|
||||
const z = m.bereiche[b.schluessel];
|
||||
if (z) zahlen.append(zahl(z.offen, `${b.name}: offen`));
|
||||
}
|
||||
k.append(zahlen);
|
||||
|
||||
/* UND HIER WIRD BEHANDELT. Ein Verweis je Bereich, der die Person
|
||||
mitnimmt -- ohne sie muesste man dort noch einmal auswaehlen,
|
||||
und beim zweiten Mal weiss man nicht mehr, warum man hier war. */
|
||||
const wege = el('div', 'tpipe__reihe');
|
||||
for (const b of BEREICHE) {
|
||||
const z = m.bereiche[b.schluessel];
|
||||
if (!z) continue;
|
||||
const a = el('a', 'schritt',
|
||||
`${b.name} ansehen (${z.gut} gut · ${z.verbessern} verbessern)`);
|
||||
a.href = `bereich.html?b=${b.schluessel}&creator_id=${m.id}`;
|
||||
wege.append(a);
|
||||
}
|
||||
k.append(wege);
|
||||
return k;
|
||||
}
|
||||
|
||||
async function laden() {
|
||||
const ziel = $('lage');
|
||||
try {
|
||||
const a = await hole('/workspace/api/teamlage');
|
||||
if (!a.ok) { melde('Die Lage konnte nicht geladen werden.'); return; }
|
||||
const daten = await a.json();
|
||||
ziel.textContent = '';
|
||||
ziel.setAttribute('aria-busy', 'false');
|
||||
|
||||
const leute = daten.modis || [];
|
||||
const offenGesamt = leute.reduce((s, m) =>
|
||||
s + Object.values(m.bereiche).reduce((x, b) => x + b.offen, 0), 0);
|
||||
/* Eine leere Seite ohne Erklaerung sieht aus wie ein Fehler --
|
||||
deshalb steht hier auch, was zu tun waere. */
|
||||
$('kopf-unter').textContent = leute.length
|
||||
? `${leute.length} ${leute.length === 1 ? 'Person' : 'Personen'} · `
|
||||
+ `${offenGesamt} Punkte noch nicht besprochen`
|
||||
: 'Noch niemand im Team. Zugänge legst du unter „Personen & Zugänge“ an.';
|
||||
|
||||
for (const m of leute) ziel.append(karte(m));
|
||||
} catch { /* umgeleitet */ }
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const a = await hole('/workspace/api/ich');
|
||||
if (!a.ok) { location.assign('/workspace/'); return; }
|
||||
window.werZeigen?.(await a.json());
|
||||
} catch { return; }
|
||||
$('abmelden')?.addEventListener('click', async () => {
|
||||
try { await fetch('/workspace/api/abmelden', { method: 'POST', credentials: 'same-origin' }); }
|
||||
catch { /* egal */ }
|
||||
location.assign('/workspace/');
|
||||
});
|
||||
await laden();
|
||||
})();
|
||||
})();
|
||||
+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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -245,10 +245,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -140,10 +140,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -149,14 +149,14 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -82,11 +82,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<!-- 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=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -247,10 +247,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609100240"></script>
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240"></script>
|
||||
<script src="assets/js/chat.js?v=202609100240"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303"></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303"></script>
|
||||
<script src="assets/js/chat.js?v=202609100303"></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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -172,12 +172,12 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/content.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/content.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -101,10 +101,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="gate">
|
||||
@@ -401,6 +401,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/gate.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/gate.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -370,11 +370,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -217,10 +217,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240"></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240"></script>
|
||||
<script src="assets/js/leistung.js?v=202609100240"></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303"></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303"></script>
|
||||
<script src="assets/js/leistung.js?v=202609100303"></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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -335,10 +335,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -207,11 +207,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -99,11 +99,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/report.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/report.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -132,10 +132,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<!-- 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=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -667,10 +667,10 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/start.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/start.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -78,11 +78,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -119,10 +119,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -103,10 +103,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/team.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/team.js?v=202609100303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>Team-Lage · Spicy & Dogi</title>
|
||||
<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=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
|
||||
<div class="schleier" aria-hidden="true"></div>
|
||||
|
||||
<header class="kopfleiste">
|
||||
<button type="button" class="zurueck-knopf" id="zurueck" hidden>
|
||||
<svg class="zurueck-knopf__pfeil" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M14.5 5.5 8 12l6.5 6.5" />
|
||||
</svg>
|
||||
<span class="zurueck-knopf__text">Zurück</span>
|
||||
</button>
|
||||
<p class="marke"><span class="marke__text"><a class="zurueck" href="start.html">Creator Workspace</a> · Team-Lage</span></p>
|
||||
<div class="kopfleiste__rechts">
|
||||
<span class="wer" id="wer">…</span>
|
||||
<button type="button" class="abmelden" id="abmelden">Abmelden</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="inhalt inhalt--breit">
|
||||
|
||||
<!-- ==== DER KOPF ================================================
|
||||
Er sagt, was diese Seite ist UND was sie nicht ist. Der zweite
|
||||
Teil ist der wichtigere -- dieselbe Ueberlegung steht schon
|
||||
ueber team.html: Eine Seite mit Zahlen ueber Kollegen wird sonst
|
||||
als Kontrolle gelesen, und dann arbeitet niemand mehr offen
|
||||
damit.
|
||||
|
||||
"Offen" heisst hier deshalb ausdruecklich: darueber habt ihr
|
||||
noch nicht geredet. Es ist eine Liste fuer euch beide, keine
|
||||
Note fuer den anderen. -->
|
||||
<section class="t-kachel">
|
||||
<div class="t-kopf">
|
||||
<div class="t-kopf__text">
|
||||
<p class="feldschild">Zusammen</p>
|
||||
<h1 class="t-kopf__titel">Die Lage im Team</h1>
|
||||
<p class="t-kopf__unter" id="kopf-unter">…</p>
|
||||
<p class="t-kopf__grenze">
|
||||
Was hier steht, ist eine Merkliste für euch beide – keine Bewertung
|
||||
von Menschen. „Offen“ heißt: darüber habt ihr noch nicht gesprochen.
|
||||
Bewertet wird dort, wo die Punkte stehen; jede Zahl führt dorthin.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
|
||||
</section>
|
||||
|
||||
<section class="t-lage" id="lage" aria-busy="true"></section>
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/teamlage.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||
Dialog "Creator anlegen". -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -142,11 +142,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609100303" 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=202609100240" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609100240" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100240" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100303" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609100303" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -183,10 +183,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609100240" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100303" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609100303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user