Der Eingang: Mitglieder-Kachel, drei Stufen -- und lesbar statt leer
Zwei Auftraege in einem Zug: "mach weiter" (Blueprint Kapitel 4/4.1)
und, zum Bildschirmfoto der Seite, "das muss viel krasser sein".
KAPITEL 4 -- DIE MITGLIEDER-KACHEL
Der Blueprint nennt: "Name & Foto · Rolle(n)/Kategorie(n) · Status
(aktiv/pausiert) · Anzahl offener Aufgaben · Datum 'Modi seit' ·
Schnellaktionen". Name, Foto und Zahlen standen schon; Status, "dabei
seit", Stufe und ein Knopf zum Schreiben kommen dazu.
KAPITEL 4.1 -- DIE DREI STUFEN
Probe, Standard, Senior. Sie sind eine ARBEITSEINTEILUNG, keine
Rechtegrenze -- was ein Modi darf, haengt an der Rolle; die Stufe sagt,
wo er im Team steht. Gesetzt werden sie nur von DogFather: Der
Blueprint gibt der rechten Hand den gleichen UEBERBLICK, aber
"Verwaltungsrechte optional durch Owner freischaltbar", also aus.
NULL heisst "Probe" und nicht "unbekannt" -- ein dritter Zustand waere
eine Frage, die niemand beantworten kann.
ZWEI ECHTE FEHLER, BEIDE VON DER NEUEN PRUEFUNG GEFUNDEN
1. PAUSIERTE VERSCHWANDEN KOMPLETT. In der Abfrage stand `AND aktiv =
1`. Wer jemanden pausierte, bei dem verschwand er samt seiner
OFFENEN RUECKMELDUNGEN aus dem Eingang -- die warteten weiter auf
eine Antwort, nur sah sie niemand mehr.
2. DER EINGANG HAETTE AUF FRISCHER ANLAGE 503 GELIEFERT. Die
Checklisten-Tabellen entstehen beim ersten Aufruf einer Checkliste;
diese Seite liest sie aber auch. Wer sie oeffnete, bevor je jemand
eine Checkliste angesehen hatte, bekam einen Fehler ohne Erklaerung
-- auf einer neuen Anlage also beim allerersten Blick. Das Schema
hat jetzt einen Besitzer, der es herausgibt; ein zweites CREATE
TABLE waere der Anfang von zwei Schemata gewesen.
"VIEL KRASSER" -- UND ZWAR MIT INFORMATION, NICHT MIT LAERM
* ZWEI REIHEN STATT EINER. Alle fuenf Kaesten lagen in EINEM Raster
mit 150 px Mindestbreite: Im Bildschirmfoto stand "Community 12
von" -- abgeschnitten mitten in der Zahl -- und die laengste Liste
machte die ganze Reihe so hoch wie sich selbst. Jetzt oben, was
eine Antwort braucht, darunter das Team; 300 px Mindestbreite.
* DIE KARTE WAR FAHL, und das war ein Fehler: `--r` fiel auf ein
helles Grau zurueck, aus dem das Kantenlicht einen Nebel ueber die
ganze Karte legte. Sie traegt jetzt die Farbe der Stufe -- kein
Nebel, und man sieht am Rand, wer wo steht.
* SECHS ZAHLENKAESTEN WURDEN DREI BALKEN. "40 offen" beantwortet
nicht, wie weit man ist: 40 von 40 ist etwas anderes als 40 von
100. Gruen (in Ordnung) und Bernstein (zu besprechen) fuellen den
Balken; die ganze Zeile ist der Weg dorthin.
* DIE DREI ZAHLEN DER SEITE stehen oben als Zahlen statt in einem
Satz. Die Warnfarbe erscheint NUR, wenn wirklich etwas wartet --
eine Null in Bernstein waere ein Alarm ohne Anlass, und ab dem
dritten Mal sieht man ihn nicht mehr.
GEMESSEN
pruef-team-stufen (neu) 24, mit Gegenprobe: hoch- und zurueckstufen,
und dieselbe Stufe zweimal zu setzen darf
keine zweite Protokollzeile erzeugen
pruef-rollen 274 pruef-modi-checkliste 59
pruef-zwischenspeicher 21
Ansicht: Rechner 1440 und Handy 412 -- keine Skriptfehler, nichts
ragt seitlich heraus (0 px).
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,239 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
DIE MITGLIEDER-KACHEL UND DIE DREI STUFEN (Blueprint Kapitel 4/4.1)
|
||||||
|
|
||||||
|
Vier Fragen, und die dritte ist die, an der es schiefgeht:
|
||||||
|
|
||||||
|
1. Stehen die Angaben aus Kapitel 4 in der Antwort -- Status,
|
||||||
|
"dabei seit", Stufe?
|
||||||
|
2. Erscheinen auch PAUSIERTE? Vorher stand in der Abfrage
|
||||||
|
`AND aktiv = 1`: Wer jemanden pausierte, bei dem verschwand er
|
||||||
|
samt seiner offenen Rueckmeldungen aus dem Eingang. Die
|
||||||
|
warteten weiter auf eine Antwort, nur sah sie niemand mehr.
|
||||||
|
3. WER DARF DIE STUFE SETZEN? Der Blueprint trennt das
|
||||||
|
ausdruecklich: gleicher Ueberblick fuer die rechte Hand,
|
||||||
|
"Verwaltungsrechte optional durch Owner freischaltbar" -- also
|
||||||
|
standardmaessig aus. Eine Rechtegrenze, die man nicht misst,
|
||||||
|
ist eine Absichtserklaerung.
|
||||||
|
4. Ueberlebt die Stufe den naechsten Aufruf? Ein Wert, der nur im
|
||||||
|
Browser steht, sieht bis zum Neuladen richtig aus.
|
||||||
|
|
||||||
|
MIT GEGENPROBE: Am Ende wird dieselbe Person auf eine andere Stufe
|
||||||
|
gesetzt und wieder zurueck. Ohne diesen Teil waere nicht bewiesen,
|
||||||
|
dass die Pruefung ueberhaupt einen Unterschied bemerkt.
|
||||||
|
===================================================================== */
|
||||||
|
import { mkdtempSync, rmSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
const ordner = mkdtempSync(join(tmpdir(), "ws-stufen-"));
|
||||||
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||||
|
process.env.PORT = "4375";
|
||||||
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
||||||
|
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-team-stufen");
|
||||||
|
await new Promise((r) => setTimeout(r, 700));
|
||||||
|
const BASIS = "http://127.0.0.1:4375";
|
||||||
|
|
||||||
|
let fehler = 0, geprueft = 0, offen = 0;
|
||||||
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||||
|
const unklar = (t) => { offen++; console.log(" -- " + t); };
|
||||||
|
|
||||||
|
/* ---------- Personen ------------------------------------------------- */
|
||||||
|
const { DatabaseSync } = await import("node:sqlite");
|
||||||
|
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
||||||
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
||||||
|
const jetzt = new Date().toISOString();
|
||||||
|
|
||||||
|
const schluessel = randomBytes(32).toString("hex");
|
||||||
|
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
|
||||||
|
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
|
||||||
|
.run("code_kennung_schluessel", schluessel, jetzt);
|
||||||
|
|
||||||
|
function anlegen(name, rolle, code, aktiv = 1, erstellt = jetzt) {
|
||||||
|
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 (?,?,?,?,?,?,?,?)")
|
||||||
|
.run(name, rolle, hash, salt, 32768,
|
||||||
|
createHmac("sha256", schluessel).update(code).digest("hex"), aktiv, erstellt);
|
||||||
|
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ein Datum weit genug zurueck, damit "seit ... Tagen dabei" etwas
|
||||||
|
anderes ergibt als "seit heute" -- und RELATIV gerechnet, nicht fest
|
||||||
|
hingeschrieben: Ein festes Datum waere in einem halben Jahr eine
|
||||||
|
andere Aussage (Lehre vom 06.09.2026). */
|
||||||
|
const vor40Tagen = new Date(Date.now() - 40 * 86400000).toISOString();
|
||||||
|
|
||||||
|
anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
||||||
|
anlegen("Rieke", "hand", "CODE-HAND-0001");
|
||||||
|
const idMarina = anlegen("Marina", "modi", "CODE-MODI-0001", 1, vor40Tagen);
|
||||||
|
const idKim = anlegen("Kim", "modi", "CODE-MODI-0002", 0); // pausiert
|
||||||
|
const idCreator = anlegen("Tili", "creator", "CODE-TILI-0001");
|
||||||
|
|
||||||
|
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 }),
|
||||||
|
});
|
||||||
|
return (a.headers.getSetCookie?.() || []).map((z) => z.split(";")[0]).join("; ");
|
||||||
|
}
|
||||||
|
const holeLage = (keks) => fetch(`${BASIS}/workspace/api/teamlage`, { headers: { Cookie: keks } });
|
||||||
|
const setzeStufe = (keks, person_id, stufe) =>
|
||||||
|
fetch(`${BASIS}/workspace/api/teamlage/stufe`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { Cookie: keks, "Content-Type": "application/json", Origin: BASIS },
|
||||||
|
body: JSON.stringify({ person_id, stufe }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const keksDogi = await anmelden("admin", "CODE-DOGI-0001");
|
||||||
|
const keksHand = await anmelden("creator", "CODE-HAND-0001");
|
||||||
|
const keksModi = await anmelden("creator", "CODE-MODI-0001");
|
||||||
|
if (!keksDogi || !keksHand || !keksModi) {
|
||||||
|
console.error("FEHLER: Anmeldung misslang -- ohne sie misst diese Datei nichts.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
1. WAS IN DER ANTWORT STEHT
|
||||||
|
===================================================================== */
|
||||||
|
console.log("");
|
||||||
|
console.log("--- Die Mitglieder-Kachel (Kapitel 4) ---");
|
||||||
|
|
||||||
|
const a1 = await holeLage(keksDogi);
|
||||||
|
ok(a1.status === 200, `DogFather bekommt die Lage (${a1.status})`);
|
||||||
|
const lage = await a1.json();
|
||||||
|
const leute = lage.modis || [];
|
||||||
|
|
||||||
|
ok(leute.length === 2, `beide Modis stehen da, auch der pausierte (${leute.length})`);
|
||||||
|
|
||||||
|
const marina = leute.find((m) => m.id === idMarina);
|
||||||
|
const kim = leute.find((m) => m.id === idKim);
|
||||||
|
ok(!!marina && !!kim, "beide sind namentlich wiederzufinden");
|
||||||
|
|
||||||
|
ok(marina?.aktiv === true && kim?.aktiv === false,
|
||||||
|
`Status stimmt (Marina aktiv: ${marina?.aktiv}, Kim aktiv: ${kim?.aktiv})`);
|
||||||
|
/* AKTIVE ZUERST -- wer pausiert ist, steht unten und nicht mittendrin. */
|
||||||
|
ok(leute[0]?.id === idMarina, "die Aktiven stehen oben");
|
||||||
|
|
||||||
|
ok(typeof marina?.seit === "string" && marina.seit.startsWith(vor40Tagen.slice(0, 10)),
|
||||||
|
`"dabei seit" kommt mit (${(marina?.seit || "").slice(0, 10)})`);
|
||||||
|
|
||||||
|
/* NULL HEISST PROBE, nicht "unbekannt" -- an EINER Stelle im Server
|
||||||
|
abgebildet. Beide Modis wurden ohne Stufe angelegt. */
|
||||||
|
ok(marina?.stufe === "probe" && kim?.stufe === "probe",
|
||||||
|
`ohne Eintrag ist die Stufe "Probe" (${marina?.stufe} / ${kim?.stufe})`);
|
||||||
|
|
||||||
|
/* Die Liste der Stufen kommt VOM SERVER -- eine zweite im Browser
|
||||||
|
waere die Stelle, an der beim naechsten Mal eine fehlt. */
|
||||||
|
const stufen = (lage.stufen || []).map((s) => s.wert);
|
||||||
|
ok(JSON.stringify(stufen) === JSON.stringify(["probe", "standard", "senior"]),
|
||||||
|
`die drei Stufen kommen mit, in ihrer Reihenfolge (${stufen.join(", ")})`);
|
||||||
|
ok((lage.stufen || []).every((s) => s.name && s.name !== s.wert),
|
||||||
|
"und jede hat einen lesbaren Namen");
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
2. WER DARF SIE SETZEN
|
||||||
|
===================================================================== */
|
||||||
|
console.log("");
|
||||||
|
console.log("--- Wer die Stufe setzen darf ---");
|
||||||
|
|
||||||
|
const gesetzt = await setzeStufe(keksDogi, idMarina, "senior");
|
||||||
|
ok(gesetzt.status === 200, `DogFather darf (${gesetzt.status})`);
|
||||||
|
|
||||||
|
/* DIE RECHTE HAND SIEHT ALLES UND SETZT NICHTS. Blueprint 3.1:
|
||||||
|
"Verwaltungsrechte optional durch Owner freischaltbar" -- also aus.
|
||||||
|
404 und nicht 403: Wer nicht darf, muss nicht erfahren, dass es den
|
||||||
|
Weg gibt. */
|
||||||
|
const handSieht = await holeLage(keksHand);
|
||||||
|
ok(handSieht.status === 200, `die rechte Hand sieht die Lage (${handSieht.status})`);
|
||||||
|
const handSetzt = await setzeStufe(keksHand, idMarina, "probe");
|
||||||
|
ok(handSetzt.status === 404, `aber sie setzt keine Stufe (${handSetzt.status})`);
|
||||||
|
|
||||||
|
const modiSetzt = await setzeStufe(keksModi, idMarina, "probe");
|
||||||
|
ok(modiSetzt.status === 404, `ein Modi erst recht nicht (${modiSetzt.status})`);
|
||||||
|
const modiSieht = await holeLage(keksModi);
|
||||||
|
ok(modiSieht.status === 404, `und er sieht die Seite gar nicht (${modiSieht.status})`);
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
3. WAS ABGEWIESEN WIRD
|
||||||
|
===================================================================== */
|
||||||
|
console.log("");
|
||||||
|
console.log("--- Was nicht durchgeht ---");
|
||||||
|
|
||||||
|
const erfunden = await setzeStufe(keksDogi, idMarina, "chef");
|
||||||
|
ok(erfunden.status === 400, `eine erfundene Stufe wird abgewiesen (${erfunden.status})`);
|
||||||
|
|
||||||
|
/* An einem Creator hat die Stufe nichts verloren -- sie stuende in der
|
||||||
|
Datenbank und wuerde nirgends angezeigt. */
|
||||||
|
const anCreator = await setzeStufe(keksDogi, idCreator, "senior");
|
||||||
|
ok(anCreator.status === 404, `an einem Creator geht sie nicht (${anCreator.status})`);
|
||||||
|
const nachCreator = d.prepare("SELECT stufe FROM personen WHERE id = ?").get(idCreator);
|
||||||
|
ok(!nachCreator?.stufe, "und es steht auch nichts in der Datenbank");
|
||||||
|
|
||||||
|
const anNiemand = await setzeStufe(keksDogi, 99999, "senior");
|
||||||
|
ok(anNiemand.status === 404, `an einer unbekannten Nummer auch nicht (${anNiemand.status})`);
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
4. UEBERLEBT SIE DEN NAECHSTEN AUFRUF?
|
||||||
|
===================================================================== */
|
||||||
|
console.log("");
|
||||||
|
console.log("--- Beim naechsten Laden ---");
|
||||||
|
|
||||||
|
const a2 = await holeLage(keksDogi);
|
||||||
|
const marina2 = ((await a2.json()).modis || []).find((m) => m.id === idMarina);
|
||||||
|
ok(marina2?.stufe === "senior", `die neue Stufe kommt zurueck (${marina2?.stufe})`);
|
||||||
|
const inDb = d.prepare("SELECT stufe FROM personen WHERE id = ?").get(idMarina);
|
||||||
|
ok(inDb?.stufe === "senior", `und steht in der Datenbank (${inDb?.stufe})`);
|
||||||
|
|
||||||
|
/* Eine Personalentscheidung gehoert ins Protokoll -- gerade wenn
|
||||||
|
jemand zurueckgestuft wird, muss spaeter nachvollziehbar sein, wer
|
||||||
|
das wann getan hat. */
|
||||||
|
const spur = d.prepare(
|
||||||
|
"SELECT COUNT(*) AS n FROM protokoll WHERE aktion = 'stufe_gesetzt'").get()?.n || 0;
|
||||||
|
ok(spur >= 1, `die Entscheidung steht im Protokoll (${spur})`);
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DIE GEGENPROBE
|
||||||
|
|
||||||
|
Bis hierhin koennte alles auch dann gruen sein, wenn der Server
|
||||||
|
jeden Wert einfach annimmt und die Anzeige ihn nie wieder aendert.
|
||||||
|
Also zurueckstufen und noch einmal nachsehen.
|
||||||
|
===================================================================== */
|
||||||
|
console.log("");
|
||||||
|
console.log("--- Gegenprobe: wieder zurueck ---");
|
||||||
|
|
||||||
|
const zurueck = await setzeStufe(keksDogi, idMarina, "standard");
|
||||||
|
ok(zurueck.status === 200, `zurueckstufen geht (${zurueck.status})`);
|
||||||
|
const a3 = await holeLage(keksDogi);
|
||||||
|
const marina3 = ((await a3.json()).modis || []).find((m) => m.id === idMarina);
|
||||||
|
ok(marina3?.stufe === "standard",
|
||||||
|
`und die Anzeige folgt (${marina3?.stufe}) -- sie bleibt also nicht auf "senior" stehen`);
|
||||||
|
|
||||||
|
/* Dieselbe Stufe noch einmal: kein Fehler, aber auch keine zweite
|
||||||
|
Protokollzeile -- sonst waere das Protokoll nach einem Tag Klicken
|
||||||
|
unlesbar. */
|
||||||
|
const vorher = d.prepare(
|
||||||
|
"SELECT COUNT(*) AS n FROM protokoll WHERE aktion = 'stufe_gesetzt'").get()?.n || 0;
|
||||||
|
const nochmal = await setzeStufe(keksDogi, idMarina, "standard");
|
||||||
|
const nachher = d.prepare(
|
||||||
|
"SELECT COUNT(*) AS n FROM protokoll WHERE aktion = 'stufe_gesetzt'").get()?.n || 0;
|
||||||
|
ok(nochmal.status === 200 && nachher === vorher,
|
||||||
|
`dieselbe Stufe noch einmal aendert nichts (${vorher} -> ${nachher} Zeilen)`);
|
||||||
|
|
||||||
|
/* ---------- Ende ------------------------------------------------------ */
|
||||||
|
d.close();
|
||||||
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Windows haelt manchmal */ }
|
||||||
|
|
||||||
|
console.log("");
|
||||||
|
console.log(`${fehler === 0 ? "BESTANDEN" : "FEHLGESCHLAGEN"}`
|
||||||
|
+ ` -- ${geprueft} geprueft, ${fehler} Fehler, ${offen} nicht nachsehbar`);
|
||||||
|
process.exit(fehler === 0 ? 0 : 1);
|
||||||
@@ -174,6 +174,22 @@ for (const [bereich, liste] of Object.entries(LISTEN)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let bereit = false;
|
let bereit = false;
|
||||||
|
/* AUSGEGEBEN, seit die Eingang-Seite dieselben Tabellen liest
|
||||||
|
(10.09.2026).
|
||||||
|
|
||||||
|
Sie entstehen beim ersten Aufruf einer Checkliste. Wer die
|
||||||
|
Eingang-Seite oeffnete, BEVOR das je passiert war, bekam dort eine
|
||||||
|
503 ohne Erklaerung -- auf einer frischen Anlage also beim ersten
|
||||||
|
Blick ueberhaupt. Gefunden hat das nicht der Betrieb, sondern eine
|
||||||
|
neue Pruefung, die genau diesen Zustand herstellt: Server frisch,
|
||||||
|
Checkliste nie geoeffnet.
|
||||||
|
|
||||||
|
Die Alternative waere ein zweites CREATE TABLE in der Eingang-Seite
|
||||||
|
gewesen. Genau daraus werden zwei Schemata, die sich langsam
|
||||||
|
auseinanderentwickeln. Das Schema hat einen Besitzer; er gibt es
|
||||||
|
heraus. */
|
||||||
|
export function checklistenTabellen() { tabellen(); }
|
||||||
|
|
||||||
function tabellen() {
|
function tabellen() {
|
||||||
if (bereit) return;
|
if (bereit) return;
|
||||||
db().exec(`
|
db().exec(`
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
db, sitzungLesen, istDogFather, heuteLokal, protokolliere, echteIp,
|
db, sitzungLesen, istDogFather, heuteLokal, protokolliere, echteIp,
|
||||||
} from "./workspace.js";
|
} from "./workspace.js";
|
||||||
import { MODI_PUNKTE } from "./workspace-modi-punkte.js";
|
import { MODI_PUNKTE } from "./workspace-modi-punkte.js";
|
||||||
|
import { checklistenTabellen } from "./workspace-checkliste.js";
|
||||||
|
|
||||||
/* Titel und Bereich zu einem Schluessel -- damit im Eingang steht,
|
/* Titel und Bereich zu einem Schluessel -- damit im Eingang steht,
|
||||||
WORUM es geht, und nicht nur ein Schluessel. */
|
WORUM es geht, und nicht nur ein Schluessel. */
|
||||||
@@ -74,6 +75,43 @@ const GESAMT = Object.fromEntries(
|
|||||||
* vierte kommt mit der naechsten Route. Wer eine davon vergisst,
|
* vierte kommt mit der naechsten Route. Wer eine davon vergisst,
|
||||||
* merkt es nicht -- eine vergessene Schranke faellt nur dem auf, der
|
* merkt es nicht -- eine vergessene Schranke faellt nur dem auf, der
|
||||||
* hindurchgeht. */
|
* hindurchgeht. */
|
||||||
|
/* =====================================================================
|
||||||
|
DIE DREI STUFEN (Blueprint V3.0, Kapitel 4.1)
|
||||||
|
|
||||||
|
Probe -- in der Einarbeitung, wird eingeschaetzt.
|
||||||
|
Standard -- der Normalfall: moderiert, arbeitet eigene Aufgaben ab.
|
||||||
|
Senior -- kann zusaetzlich neue Probe-Modis einschaetzen, mehrere
|
||||||
|
Kategorien gleichzeitig tragen und die rechte Hand bei
|
||||||
|
Abwesenheit vertreten.
|
||||||
|
|
||||||
|
SIE SIND EINE ARBEITSEINTEILUNG, KEINE RECHTEGRENZE -- jedenfalls
|
||||||
|
heute noch nicht. Das gehoert ausgesprochen, damit niemand sie
|
||||||
|
spaeter fuer eine haelt: Was ein Modi in dieser App darf, haengt an
|
||||||
|
der ROLLE. Die Stufe sagt, wo er im Team steht, und macht den
|
||||||
|
naechsten Schritt sichtbar. Die Rechte, die der Blueprint dort
|
||||||
|
nennt, liegen ausserdem groesstenteils gar nicht hier, sondern im
|
||||||
|
Stream (Timeout, Bann).
|
||||||
|
|
||||||
|
Die Reihenfolge ist die Steigerung -- sie steht hier einmal, damit
|
||||||
|
keine Liste im Browser eine eigene erfindet. */
|
||||||
|
export const STUFEN = ["probe", "standard", "senior"];
|
||||||
|
|
||||||
|
export const STUFEN_NAME = {
|
||||||
|
probe: "Probe",
|
||||||
|
standard: "Standard",
|
||||||
|
senior: "Senior",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** NULL heisst "Probe", nicht "unbekannt".
|
||||||
|
*
|
||||||
|
* Ein dritter Zustand waere eine Frage, die niemand beantworten kann,
|
||||||
|
* und ein neuer Mensch faengt ohnehin in der Einarbeitung an. Die
|
||||||
|
* Abbildung steht an EINER Stelle -- stuende sie in jeder Abfrage,
|
||||||
|
* waere sie beim naechsten Mal in einer davon anders. */
|
||||||
|
function stufeVon(wert) {
|
||||||
|
return STUFEN.includes(String(wert || "")) ? String(wert) : "probe";
|
||||||
|
}
|
||||||
|
|
||||||
function darfEingang(person) {
|
function darfEingang(person) {
|
||||||
return istDogFather(person) || person?.rolle === "hand";
|
return istDogFather(person) || person?.rolle === "hand";
|
||||||
}
|
}
|
||||||
@@ -85,9 +123,30 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
|
|||||||
/* 404 und nicht 403 -- siehe Kopf. */
|
/* 404 und nicht 403 -- siehe Kopf. */
|
||||||
if (!darfEingang(person)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
if (!darfEingang(person)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
|
||||||
|
/* DIE CHECKLISTEN-TABELLEN ENTSTEHEN BEIM ERSTEN AUFRUF EINER
|
||||||
|
CHECKLISTE -- diese Seite liest sie aber auch.
|
||||||
|
|
||||||
|
Wer den Eingang oeffnete, BEVOR je jemand eine Checkliste
|
||||||
|
angesehen hatte, bekam hier eine 503 ohne Erklaerung. Auf einer
|
||||||
|
frischen Anlage also beim allerersten Blick. Gefunden hat das
|
||||||
|
nicht der Betrieb, sondern pruef-team-stufen.mjs, die genau
|
||||||
|
diesen Zustand herstellt. */
|
||||||
|
checklistenTabellen();
|
||||||
|
|
||||||
|
/* AUCH DIE PAUSIERTEN (10.09.2026, Blueprint Kapitel 4).
|
||||||
|
|
||||||
|
Die Mitglieder-Kachel dort nennt ausdruecklich "Status
|
||||||
|
(aktiv/pausiert)" -- also muss man Pausierte auch SEHEN. Vorher
|
||||||
|
stand hier `AND aktiv = 1`: Wer jemanden pausierte, bei dem
|
||||||
|
verschwand er samt seiner offenen Rueckmeldungen aus dem
|
||||||
|
Eingang. Die warteten weiter auf eine Antwort, nur sah sie
|
||||||
|
niemand mehr.
|
||||||
|
|
||||||
|
Aktive zuerst, dann nach Namen -- wer pausiert ist, steht unten
|
||||||
|
und nicht mittendrin. */
|
||||||
const modis = db().prepare(
|
const modis = db().prepare(
|
||||||
`SELECT id, name, bild, letzter_login FROM personen
|
`SELECT id, name, bild, letzter_login, aktiv, erstellt, stufe FROM personen
|
||||||
WHERE rolle = 'modi' AND aktiv = 1 ORDER BY name`).all();
|
WHERE rolle = 'modi' ORDER BY aktiv DESC, name`).all();
|
||||||
|
|
||||||
if (!modis.length) {
|
if (!modis.length) {
|
||||||
/* `eingang` auch hier -- die Oberflaeche darf nicht raten
|
/* `eingang` auch hier -- die Oberflaeche darf nicht raten
|
||||||
@@ -187,6 +246,14 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
|
|||||||
bereiche: GESAMT,
|
bereiche: GESAMT,
|
||||||
eingang,
|
eingang,
|
||||||
angebote,
|
angebote,
|
||||||
|
/* DIE STUFEN KOMMEN VOM SERVER, nicht aus einer Liste im Browser.
|
||||||
|
|
||||||
|
Eine zweite Aufzaehlung dort waere die Stelle, an der beim
|
||||||
|
naechsten Mal eine Stufe fehlt oder anders heisst -- genau so
|
||||||
|
stand am 07.09. unter dem Codefeld "PERSOENLICHER ZUGANGSCODE ·
|
||||||
|
undefined", weil eine Rolle in der Browser-Liste vergessen
|
||||||
|
worden war. Einmal geschrieben, einmal geaendert. */
|
||||||
|
stufen: STUFEN.map((wert) => ({ wert, name: STUFEN_NAME[wert] })),
|
||||||
modis: modis.map((m) => {
|
modis: modis.map((m) => {
|
||||||
const s = stand.get(m.id) || {};
|
const s = stand.get(m.id) || {};
|
||||||
const je = {};
|
const je = {};
|
||||||
@@ -200,6 +267,10 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
|
|||||||
name: m.name,
|
name: m.name,
|
||||||
bild: m.bild ? `/workspace/api/steckbrief/bild/${m.bild}` : null,
|
bild: m.bild ? `/workspace/api/steckbrief/bild/${m.bild}` : null,
|
||||||
letzter_login: m.letzter_login,
|
letzter_login: m.letzter_login,
|
||||||
|
/* Die drei Felder aus Kapitel 4: Status, "dabei seit", Stufe. */
|
||||||
|
aktiv: !!m.aktiv,
|
||||||
|
seit: m.erstellt || null,
|
||||||
|
stufe: stufeVon(m.stufe),
|
||||||
aufgaben: aufgaben.get(m.id) || { offen: 0, ueberfaellig: 0 },
|
aufgaben: aufgaben.get(m.id) || { offen: 0, ueberfaellig: 0 },
|
||||||
ideen: ideen.get(m.id) || 0,
|
ideen: ideen.get(m.id) || 0,
|
||||||
nachrichten: nachrichten.get(m.id) || 0,
|
nachrichten: nachrichten.get(m.id) || 0,
|
||||||
@@ -232,6 +303,59 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
|
|||||||
annimmt, nimmt sie sich. Sie einem Dritten zuzuweisen waere eine
|
annimmt, nimmt sie sich. Sie einem Dritten zuzuweisen waere eine
|
||||||
Entscheidung ueber jemand anderen, ohne ihn zu fragen.
|
Entscheidung ueber jemand anderen, ohne ihn zu fragen.
|
||||||
===================================================================== */
|
===================================================================== */
|
||||||
|
/* =====================================================================
|
||||||
|
DIE STUFE SETZEN (Blueprint Kapitel 4.1, Punkt 3: "Rechte anpassen")
|
||||||
|
|
||||||
|
NUR DOGFATHER, nicht die rechte Hand. Der Blueprint trennt das
|
||||||
|
ausdruecklich: Sie hat den gleichen UEBERBLICK, aber
|
||||||
|
"Verwaltungsrechte optional durch Owner freischaltbar" -- also
|
||||||
|
standardmaessig aus. Wer im Team auf welcher Stufe steht, ist eine
|
||||||
|
Personalentscheidung; sie gehoert dem, der die Leute holt.
|
||||||
|
|
||||||
|
404 statt 403, wie ueberall auf dieser Seite: Wer nicht darf, muss
|
||||||
|
nicht erfahren, dass es den Weg gibt.
|
||||||
|
===================================================================== */
|
||||||
|
teamlageRouter.put("/workspace/api/teamlage/stufe", gleicheHerkunft,
|
||||||
|
express.json({ limit: "2kb" }), (req, res) => {
|
||||||
|
try {
|
||||||
|
const person = sitzungLesen(req);
|
||||||
|
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||||||
|
if (!istDogFather(person)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
|
||||||
|
const personId = Number(req.body?.person_id) || 0;
|
||||||
|
const stufe = String(req.body?.stufe || "");
|
||||||
|
if (!STUFEN.includes(stufe)) {
|
||||||
|
return res.status(400).json({ fehler: "Unbekannte Stufe." });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NUR AN EINEM MODI. Ein Manager oder Creator hat keine Stufe --
|
||||||
|
sie stuende in der Datenbank und wuerde nirgends angezeigt,
|
||||||
|
also waere sie ein stiller Datensatz, den niemand mehr
|
||||||
|
zuordnen kann. Und die rechte Hand hat auch keine: Sie ist
|
||||||
|
eine Rolle, keine Stufe darin. */
|
||||||
|
const ziel = db().prepare(
|
||||||
|
"SELECT id, name, rolle, stufe FROM personen WHERE id = ?").get(personId);
|
||||||
|
if (!ziel || ziel.rolle !== "modi") {
|
||||||
|
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
}
|
||||||
|
const vorher = STUFEN.includes(String(ziel.stufe || "")) ? String(ziel.stufe) : "probe";
|
||||||
|
if (vorher === stufe) return res.json({ ok: true, stufe, geaendert: false });
|
||||||
|
|
||||||
|
db().prepare("UPDATE personen SET stufe = ? WHERE id = ?").run(stufe, ziel.id);
|
||||||
|
/* Ins Protokoll, weil es eine Personalentscheidung ist: Wer sie
|
||||||
|
wann getroffen hat, muss spaeter nachvollziehbar sein -- gerade
|
||||||
|
wenn jemand zurueckgestuft wird. */
|
||||||
|
protokolliere("stufe_gesetzt", {
|
||||||
|
personId: ziel.id, rolle: ziel.rolle, ip: echteIp(req),
|
||||||
|
detail: `${ziel.name}: ${vorher} -> ${stufe}`,
|
||||||
|
});
|
||||||
|
return res.json({ ok: true, stufe, geaendert: true });
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[workspace] Stufe setzen:", fehler?.message);
|
||||||
|
return res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
teamlageRouter.post("/workspace/api/teamlage/behandeln", gleicheHerkunft,
|
teamlageRouter.post("/workspace/api/teamlage/behandeln", gleicheHerkunft,
|
||||||
express.json({ limit: "16kb" }), (req, res) => {
|
express.json({ limit: "16kb" }), (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1037,6 +1037,32 @@ function umstellungen(d) {
|
|||||||
allein scrypt. Ein Treffer hier allein laesst niemanden herein. */
|
allein scrypt. Ein Treffer hier allein laesst niemanden herein. */
|
||||||
["personen", "code_kennung", "TEXT"],
|
["personen", "code_kennung", "TEXT"],
|
||||||
|
|
||||||
|
/* DIE STUFE IM TEAM (10.09.2026).
|
||||||
|
|
||||||
|
Blueprint V3.0, Kapitel 4.1: Probe, Standard, Senior. Ein
|
||||||
|
Probe-Modi ist in der Einarbeitung, ein Senior kann die rechte
|
||||||
|
Hand bei Abwesenheit vertreten.
|
||||||
|
|
||||||
|
WARUM OHNE CHECK-REGEL, anders als bei `rolle`: Eine CHECK-Liste
|
||||||
|
laesst sich in SQLite nur ueber einen Tabellenneubau erweitern
|
||||||
|
(siehe checkListeErweitern) -- und die Stufen sind eine
|
||||||
|
ARBEITSEINTEILUNG, keine Rechtegrenze. Kaeme morgen eine vierte
|
||||||
|
dazu, waere ein Tabellenneubau auf einer Live-Datenbank ein
|
||||||
|
hoher Preis fuer eine Beschriftung. Geprueft wird deshalb im
|
||||||
|
Code, an genau einer Stelle (STUFEN), und was dort nicht
|
||||||
|
draufsteht, wird abgewiesen.
|
||||||
|
|
||||||
|
NULL HEISST "Probe" und nicht "unbekannt". Ein dritter Zustand
|
||||||
|
waere eine Frage, die niemand beantworten kann ("was ist er
|
||||||
|
denn nun?"); und ein neuer Mensch faengt ohnehin in der
|
||||||
|
Einarbeitung an. Beim Lesen wird NULL deshalb auf 'probe'
|
||||||
|
abgebildet -- an einer Stelle, nicht in jeder Abfrage.
|
||||||
|
|
||||||
|
Die Spalte heisst wie `punkt_stand.stufe`, meint aber etwas
|
||||||
|
anderes (dort: gut/verbessern). Zwei Tabellen, kein Konflikt --
|
||||||
|
aber wer beides im selben Kopf hat, sollte es wissen. */
|
||||||
|
["personen", "stufe", "TEXT"],
|
||||||
|
|
||||||
/* DIE KATEGORIE EINER AUFGABE (10.09.2026).
|
/* DIE KATEGORIE EINER AUFGABE (10.09.2026).
|
||||||
|
|
||||||
Kapitel 6.1 des Anforderungsdokuments verlangt sie an jeder
|
Kapitel 6.1 des Anforderungsdokuments verlangt sie an jeder
|
||||||
|
|||||||
@@ -13,25 +13,25 @@
|
|||||||
"theme_color": "#06090f",
|
"theme_color": "#06090f",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609101602",
|
"src": "/assets/img/app-symbole/workspace-192.png?v=202609101626",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609101602",
|
"src": "/assets/img/app-symbole/workspace-512.png?v=202609101626",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609101602",
|
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609101626",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609101602",
|
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609101626",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
|
|||||||
@@ -316,3 +316,251 @@
|
|||||||
.tz, .tl, .tperson { border: 1px solid CanvasText; }
|
.tz, .tl, .tperson { border: 1px solid CanvasText; }
|
||||||
.tperson::after { background: CanvasText; }
|
.tperson::after { background: CanvasText; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
DIE MITGLIEDER-KACHEL AUS KAPITEL 4 (10.09.2026)
|
||||||
|
|
||||||
|
Der Blueprint verlangt dort neben Name, Foto und Zahlen auch Status
|
||||||
|
(aktiv/pausiert), das Datum "dabei seit" und -- nach 4.1 -- die
|
||||||
|
Stufe. Drei kurze Angaben, die zusammen in eine Zeile gehoeren:
|
||||||
|
Einzeln untereinander waeren sie drei Zeilen fuer neun Woerter.
|
||||||
|
|
||||||
|
ALLE DREI IM SELBEN GEWAND wie `tperson__zuletzt` daneben (Pille,
|
||||||
|
999px, .74rem). Ein zweites Gewand fuer dieselbe Sorte Angabe waere
|
||||||
|
der Anfang davon, dass die Karte nach Baustelle aussieht.
|
||||||
|
==================================================================== */
|
||||||
|
.tperson__merkmale {
|
||||||
|
display: flex; flex-wrap: wrap; gap: 6px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.tmerkmal {
|
||||||
|
padding: 4px 10px; border-radius: 999px;
|
||||||
|
font-size: .74rem; font-weight: 600;
|
||||||
|
color: var(--text-leise);
|
||||||
|
background: rgba(255, 255, 255, .05);
|
||||||
|
border: 1px solid var(--rand);
|
||||||
|
}
|
||||||
|
/* "dabei seit" ist die leiseste der drei -- sie beantwortet keine
|
||||||
|
Frage, sie gibt nur Zusammenhang. */
|
||||||
|
.tmerkmal--leise { opacity: .78; }
|
||||||
|
|
||||||
|
/* PAUSIERT FAELLT AUF, aber warm und nicht alarmierend: Eine Pause ist
|
||||||
|
kein Fehler. Derselbe Ton wie die Warnung daneben, nur ruhiger --
|
||||||
|
sonst liest man "pausiert" als "kaputt". */
|
||||||
|
.tmerkmal--pause {
|
||||||
|
color: color-mix(in srgb, var(--warn, #ff9aa2) 70%, #ffffff);
|
||||||
|
border-color: color-mix(in srgb, var(--warn, #ff9aa2) 38%, transparent);
|
||||||
|
background: rgba(255, 154, 162, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DIE DREI STUFEN steigern sich sichtbar, ohne dass eine davon nach
|
||||||
|
Rang aussieht: gleiche Form, nur mehr Farbe. Probe bleibt bewusst
|
||||||
|
das ruhigste Schild -- wer neu ist, soll nicht markiert wirken. */
|
||||||
|
.tmerkmal--probe {
|
||||||
|
color: color-mix(in srgb, #9fb6c6 82%, #ffffff);
|
||||||
|
border-color: rgba(159, 182, 198, .3);
|
||||||
|
background: rgba(159, 182, 198, .08);
|
||||||
|
}
|
||||||
|
.tmerkmal--standard {
|
||||||
|
color: color-mix(in srgb, #7fc4e8 82%, #ffffff);
|
||||||
|
border-color: rgba(127, 196, 232, .34);
|
||||||
|
background: rgba(127, 196, 232, .1);
|
||||||
|
}
|
||||||
|
.tmerkmal--senior {
|
||||||
|
color: color-mix(in srgb, #8fd6a6 84%, #ffffff);
|
||||||
|
border-color: rgba(143, 214, 166, .38);
|
||||||
|
background: rgba(143, 214, 166, .12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Die Stufe setzen (nur DogFather) --------------------------- */
|
||||||
|
.tstufen {
|
||||||
|
display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
|
||||||
|
margin-top: 12px; padding-top: 11px;
|
||||||
|
border-top: 1px solid var(--rand);
|
||||||
|
}
|
||||||
|
/* DAS SCHILD NIMMT DIE GANZE ZEILE.
|
||||||
|
|
||||||
|
Ohne `flex-basis: 100%` teilt es sich die erste Zeile mit der ersten
|
||||||
|
Stufe -- im Bildschirmfoto stand "STUFE Probe" oben und darunter
|
||||||
|
"Standard Senior". Drei gleichwertige Knoepfe, ungleich verteilt:
|
||||||
|
Man liest sie dann als zwei Gruppen statt als eine Reihe. */
|
||||||
|
.tstufen__schild {
|
||||||
|
flex-basis: 100%;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
font-size: .72rem; font-weight: 700; letter-spacing: .1em;
|
||||||
|
text-transform: uppercase; color: var(--text-still);
|
||||||
|
}
|
||||||
|
.tstufen__knopf {
|
||||||
|
padding: 6px 12px; border-radius: 999px;
|
||||||
|
font: inherit; font-size: .78rem; font-weight: 600;
|
||||||
|
color: var(--text-leise);
|
||||||
|
background: rgba(255, 255, 255, .04);
|
||||||
|
border: 1px solid var(--rand);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background .15s ease, color .15s ease, border-color .15s ease;
|
||||||
|
}
|
||||||
|
.tstufen__knopf:hover:not([disabled]) {
|
||||||
|
color: var(--text); background: rgba(255, 255, 255, .08);
|
||||||
|
}
|
||||||
|
.tstufen__knopf:focus-visible { outline: 2px solid var(--akzent); outline-offset: 2px; }
|
||||||
|
/* Die gewaehlte Stufe ist gefuellt, nicht bloss umrandet: Bei drei
|
||||||
|
gleich aussehenden Pillen erkennt man einen Rahmenwechsel zu
|
||||||
|
langsam, gerade auf einem hellen Bildschirm. */
|
||||||
|
.tstufen__knopf[data-an="ja"] {
|
||||||
|
color: #0a121e;
|
||||||
|
background: color-mix(in srgb, var(--akzent, #7fc4e8) 82%, #ffffff);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
.tstufen__knopf[disabled] { opacity: .55; cursor: default; }
|
||||||
|
@media (prefers-reduced-motion: reduce) { .tstufen__knopf { transition: none; } }
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
DER EINGANG WIRD LESBAR STATT LEER (10.09.2026)
|
||||||
|
|
||||||
|
Filipe zum Bildschirmfoto: "das muss viel krasser sein."
|
||||||
|
|
||||||
|
Er hatte recht, und der Grund war nicht Geschmack: Auf der Seite
|
||||||
|
standen drei gleich hohe Spalten, von denen zwei leer waren und
|
||||||
|
trotzdem ueber den halben Bildschirm gingen. Die einzige Karte mit
|
||||||
|
Inhalt lag darin wie ein Zettel in einem Umzugskarton -- und war
|
||||||
|
ausserdem fahl, weil ihre Rollenfarbe auf ein helles Grau zurueckfiel.
|
||||||
|
|
||||||
|
Vier Aenderungen, und jede loest etwas Konkretes:
|
||||||
|
1. Leere Spalten schrumpfen (`align-items: start`).
|
||||||
|
2. Die Karte traegt die Farbe der Stufe -- kein Nebel mehr, und
|
||||||
|
man sieht am Rand, wer wo steht.
|
||||||
|
3. Aus sechs Zahlenkaesten je Person werden drei Balken: "wie
|
||||||
|
weit" statt "wie viele offen".
|
||||||
|
4. Die drei Zahlen der Seite stehen oben als Zahlen, nicht in
|
||||||
|
einem Satz.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
/* 1. ZWEI REIHEN, UND LEERE KAESTEN SCHRUMPFEN.
|
||||||
|
|
||||||
|
`.t-lage` war ein Raster mit `auto-fit, minmax(150px, 1fr)` fuer
|
||||||
|
ALLES: die zwei Wartelisten und jede Person. Bei fuenf Kaesten
|
||||||
|
nebeneinander blieben rund 150 px je Spalte, und im
|
||||||
|
Bildschirmfoto stand "Community 12 von" -- abgeschnitten mitten
|
||||||
|
in der Zahl. Ausserdem machte die laengste Liste die ganze Reihe
|
||||||
|
so hoch wie sich selbst.
|
||||||
|
|
||||||
|
Jetzt untereinander: erst was eine Antwort braucht, dann das
|
||||||
|
Team. `align-items: start` haelt leere Kaesten kurz -- `stretch`
|
||||||
|
ist die Vorgabe und zieht sonst jeden auf die Hoehe des
|
||||||
|
hoechsten. */
|
||||||
|
.t-lage {
|
||||||
|
display: grid; gap: 18px;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
/* 300 px ist nachgemessen und nicht geraten: Darunter bricht
|
||||||
|
"Community" von der Zahl daneben ab, und die Stufenknoepfe fallen in
|
||||||
|
drei Zeilen. Auf einem Handy bleibt es bei einer Spalte. */
|
||||||
|
.t-wartet, .t-leute {
|
||||||
|
display: grid; gap: 12px;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. DIE STUFE ALS FARBE DER KARTE.
|
||||||
|
Dieselben drei Toene wie die Schilder darin -- ein vierter waere
|
||||||
|
eine zweite Sprache fuer dieselbe Sache. */
|
||||||
|
.tperson[data-stufe="probe"] { --r: #9fb6c6; }
|
||||||
|
.tperson[data-stufe="standard"] { --r: #7fc4e8; }
|
||||||
|
.tperson[data-stufe="senior"] { --r: #8fd6a6; }
|
||||||
|
/* Pausiert wird leiser, nicht ausgegraut: 0,72 statt 0,6 (das ist der
|
||||||
|
Wert fuer GESPERRT). Eine Pause ist kein Entzug. */
|
||||||
|
.tperson[data-pause="ja"] { opacity: .72; }
|
||||||
|
|
||||||
|
/* 3. DIE BEREICHE ALS BALKEN --------------------------------------- */
|
||||||
|
.tfort {
|
||||||
|
display: grid; gap: 8px;
|
||||||
|
margin-top: 14px; padding-top: 13px;
|
||||||
|
border-top: 1px solid var(--rand);
|
||||||
|
}
|
||||||
|
.tfort__zeile {
|
||||||
|
display: grid; gap: 6px;
|
||||||
|
padding: 10px 12px; border-radius: 12px;
|
||||||
|
text-decoration: none; color: inherit;
|
||||||
|
background: rgba(255, 255, 255, .028);
|
||||||
|
border: 1px solid var(--rand);
|
||||||
|
transition: background .15s ease, border-color .15s ease;
|
||||||
|
}
|
||||||
|
.tfort__zeile:hover {
|
||||||
|
background: rgba(255, 255, 255, .06);
|
||||||
|
border-color: color-mix(in srgb, var(--r) 40%, transparent);
|
||||||
|
}
|
||||||
|
.tfort__zeile:focus-visible { outline: 2px solid var(--akzent); outline-offset: 2px; }
|
||||||
|
.tfort__kopf {
|
||||||
|
display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
|
||||||
|
}
|
||||||
|
.tfort__name { font-size: .88rem; font-weight: 650; color: var(--text); }
|
||||||
|
.tfort__zahl {
|
||||||
|
flex: none;
|
||||||
|
font-size: .78rem; font-weight: 700; color: var(--text-leise);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
/* DIE BAHN. 8 px hoch -- hoch genug, um die Farbe zu erkennen, flach
|
||||||
|
genug, um nicht wie ein Ladebalken zu wirken. Der leere Teil bleibt
|
||||||
|
sichtbar dunkel: Ein Balken ohne Bahn zeigt nur, was da ist, nicht
|
||||||
|
wie viel fehlt. */
|
||||||
|
.tfort__bahn {
|
||||||
|
display: flex; overflow: hidden;
|
||||||
|
height: 8px; border-radius: 999px;
|
||||||
|
background: rgba(255, 255, 255, .07);
|
||||||
|
}
|
||||||
|
.tfort__bahn > i { display: block; height: 100%; }
|
||||||
|
/* GRUEN = besprochen und in Ordnung. BERNSTEIN = besprochen, da ist
|
||||||
|
was. Beide fuellen den Balken, denn beide heissen "angesehen" --
|
||||||
|
der Unterschied liegt im Ergebnis, nicht im Fortschritt. */
|
||||||
|
.tfort__gut { background: color-mix(in srgb, #8fd6a6 78%, #5f8a9f); }
|
||||||
|
.tfort__verbessern { background: color-mix(in srgb, #ffc978 82%, #b07a3a); }
|
||||||
|
.tfort__unter {
|
||||||
|
font-size: .76rem; color: var(--text-still); line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. DIE DREI ZAHLEN DER SEITE -------------------------------------- */
|
||||||
|
.tkopfzahlen {
|
||||||
|
grid-column: 1 / -1; /* ueber die ganze Breite, keine vierte Spalte */
|
||||||
|
display: grid; gap: 10px;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.tkz {
|
||||||
|
display: grid; gap: 3px; align-content: start;
|
||||||
|
padding: 15px 17px; border-radius: 14px;
|
||||||
|
background:
|
||||||
|
linear-gradient(160deg, rgba(127, 196, 232, .07), transparent 60%),
|
||||||
|
linear-gradient(178deg, rgba(10, 14, 22, .95), rgba(5, 8, 14, .98));
|
||||||
|
border: 1px solid var(--rand);
|
||||||
|
}
|
||||||
|
.tkz__wert {
|
||||||
|
font-size: clamp(1.6rem, 4vw, 2.2rem); font-weight: 750;
|
||||||
|
line-height: 1.05; color: var(--text);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
.tkz__schild {
|
||||||
|
font-size: .8rem; color: var(--text-leise); line-height: 1.4;
|
||||||
|
}
|
||||||
|
/* WAS WARTET, FAELLT AUF -- aber nur, wenn wirklich etwas wartet. Eine
|
||||||
|
Null in Warnfarbe waere ein Alarm ohne Anlass, und ab dem dritten Mal
|
||||||
|
sieht man ihn nicht mehr. */
|
||||||
|
.tkz[data-dran="ja"] {
|
||||||
|
border-color: color-mix(in srgb, #ffc978 42%, transparent);
|
||||||
|
background:
|
||||||
|
linear-gradient(160deg, rgba(255, 201, 120, .12), transparent 62%),
|
||||||
|
linear-gradient(178deg, rgba(10, 14, 22, .95), rgba(5, 8, 14, .98));
|
||||||
|
}
|
||||||
|
.tkz[data-dran="ja"] .tkz__wert { color: color-mix(in srgb, #ffc978 78%, #ffffff); }
|
||||||
|
.tkz__bahn {
|
||||||
|
display: block; overflow: hidden;
|
||||||
|
height: 8px; margin-top: 8px; border-radius: 999px;
|
||||||
|
background: rgba(255, 255, 255, .07);
|
||||||
|
}
|
||||||
|
.tkz__bahn > i {
|
||||||
|
display: block; height: 100%;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
color-mix(in srgb, #5f8a9f 80%, #ffffff),
|
||||||
|
color-mix(in srgb, #8fd6a6 76%, #5f8a9f));
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) { .tfort__zeile { transition: none; } }
|
||||||
|
|||||||
+266
-19
@@ -30,6 +30,13 @@
|
|||||||
};
|
};
|
||||||
const melde = (t) => { const f = $('fehler'); if (f) f.textContent = t || ''; };
|
const melde = (t) => { const f = $('fehler'); if (f) f.textContent = t || ''; };
|
||||||
|
|
||||||
|
let ich = null;
|
||||||
|
/* Die Stufen kommen mit der Antwort des Servers -- eine eigene Liste
|
||||||
|
hier waere die zweite, und die zweite laeuft irgendwann auseinander. */
|
||||||
|
let STUFEN = [];
|
||||||
|
const stufenName = (wert) =>
|
||||||
|
STUFEN.find((s) => s.wert === wert)?.name || STUFEN[0]?.name || '';
|
||||||
|
|
||||||
async function hole(pfad, optionen = {}) {
|
async function hole(pfad, optionen = {}) {
|
||||||
const a = await fetch(pfad, { credentials: 'same-origin', ...optionen });
|
const a = await fetch(pfad, { credentials: 'same-origin', ...optionen });
|
||||||
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
|
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
|
||||||
@@ -62,6 +69,18 @@
|
|||||||
|
|
||||||
function karte(m) {
|
function karte(m) {
|
||||||
const k = el('article', 'tperson');
|
const k = el('article', 'tperson');
|
||||||
|
/* DIE KARTE TRAEGT DIE FARBE DER STUFE (10.09.2026).
|
||||||
|
|
||||||
|
Vorher fiel `--r` auf `--text-still` zurueck -- ein helles Grau,
|
||||||
|
aus dem das Kantenlicht einen fahlen Schleier ueber die ganze
|
||||||
|
Karte legte. Auf dem Bildschirmfoto sah sie neben den beiden
|
||||||
|
dunklen Spalten daneben aus wie ueberbelichtet.
|
||||||
|
|
||||||
|
Die Stufe als Farbe zu nehmen loest zwei Dinge auf einmal: Der
|
||||||
|
Schleier wird zu einem klaren Ton, und man sieht schon am Rand
|
||||||
|
der Karte, wer wo steht -- ohne das Schild zu lesen. */
|
||||||
|
k.dataset.stufe = m.stufe || 'probe';
|
||||||
|
if (!m.aktiv) k.dataset.pause = 'ja';
|
||||||
|
|
||||||
const kopf = el('div', 'tperson__kopf');
|
const kopf = el('div', 'tperson__kopf');
|
||||||
const zeichen = el('span', 'tperson__zeichen');
|
const zeichen = el('span', 'tperson__zeichen');
|
||||||
@@ -87,30 +106,128 @@
|
|||||||
kopf.append(text);
|
kopf.append(text);
|
||||||
k.append(kopf);
|
k.append(kopf);
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
DIE MITGLIEDER-KACHEL AUS KAPITEL 4 (10.09.2026)
|
||||||
|
|
||||||
|
Der Blueprint nennt dort: "Name & Foto · Rolle(n)/Kategorie(n) ·
|
||||||
|
Status (aktiv/pausiert) · Anzahl offener Aufgaben · Datum 'Modi
|
||||||
|
seit' · Schnellaktionen". Name, Foto und die Zahlen standen
|
||||||
|
schon; Status, "dabei seit" und die Stufe kommen hier dazu.
|
||||||
|
================================================================ */
|
||||||
|
const merkmale = el('div', 'tperson__merkmale');
|
||||||
|
|
||||||
|
/* PAUSIERT ZUERST. Wer pausiert ist, erklaert jede andere Zahl
|
||||||
|
darunter -- null Aufgaben sind dann kein Grund nachzufragen. */
|
||||||
|
if (!m.aktiv) merkmale.append(el('span', 'tmerkmal tmerkmal--pause', 'pausiert'));
|
||||||
|
|
||||||
|
merkmale.append(el('span', `tmerkmal tmerkmal--${m.stufe || 'probe'}`,
|
||||||
|
stufenName(m.stufe)));
|
||||||
|
|
||||||
|
if (m.seit) {
|
||||||
|
const tage = tageHer(m.seit);
|
||||||
|
merkmale.append(el('span', 'tmerkmal tmerkmal--leise',
|
||||||
|
tage === null ? 'dabei'
|
||||||
|
: tage < 1 ? 'seit heute dabei'
|
||||||
|
: tage < 31 ? `seit ${tage} Tagen dabei`
|
||||||
|
: `seit ${Math.round(tage / 30.4)} Monaten dabei`));
|
||||||
|
}
|
||||||
|
k.append(merkmale);
|
||||||
|
|
||||||
|
/* DIE STUFE SETZEN -- nur DogFather. Die rechte Hand sieht sie,
|
||||||
|
aendert sie aber nicht: Wer im Team auf welcher Stufe steht, ist
|
||||||
|
eine Personalentscheidung (Blueprint 4.1, "Rechte anpassen"
|
||||||
|
liegt beim Owner). Der Server sagt dasselbe noch einmal. */
|
||||||
|
if (ich?.rolle === 'admin') {
|
||||||
|
const wahl = el('div', 'tstufen');
|
||||||
|
wahl.append(el('span', 'tstufen__schild', 'Stufe'));
|
||||||
|
for (const st of STUFEN) {
|
||||||
|
const b = el('button', 'tstufen__knopf', st.name);
|
||||||
|
b.type = 'button';
|
||||||
|
b.dataset.stufe = st.wert;
|
||||||
|
if ((m.stufe || STUFEN[0]?.wert) === st.wert) b.dataset.an = 'ja';
|
||||||
|
b.addEventListener('click', () => stufeSetzen(wahl, m, st.wert));
|
||||||
|
wahl.append(b);
|
||||||
|
}
|
||||||
|
k.append(wahl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCHNELLAKTION: schreiben. `?mit=` legt das Gespraech beim ersten
|
||||||
|
Mal an und findet beim zweiten dasselbe wieder -- der Weg gibt es
|
||||||
|
seit dem 06.09. und wird hier nur benutzt, nicht neu gebaut.
|
||||||
|
Nachgemessen: Die rechte Hand und die Modis duerfen einander
|
||||||
|
schreiben, ein Manager ist fuer beide nicht erreichbar. */
|
||||||
|
const aktionen = el('div', 'tpipe__reihe');
|
||||||
|
const schreiben = el('a', 'schritt', `${m.name} schreiben`);
|
||||||
|
schreiben.href = `chat.html?mit=${m.id}`;
|
||||||
|
aktionen.append(schreiben);
|
||||||
|
k.append(aktionen);
|
||||||
|
|
||||||
const zahlen = el('div', 'tperson__zahlen');
|
const zahlen = el('div', 'tperson__zahlen');
|
||||||
zahlen.append(zahl(m.aufgaben.offen, 'Aufgaben offen'));
|
zahlen.append(zahl(m.aufgaben.offen, 'Aufgaben offen'));
|
||||||
zahlen.append(zahl(m.aufgaben.ueberfaellig, 'überfällig'));
|
zahlen.append(zahl(m.aufgaben.ueberfaellig, 'überfällig'));
|
||||||
zahlen.append(zahl(m.ideen, 'Ideen'));
|
zahlen.append(zahl(m.ideen, 'Ideen'));
|
||||||
zahlen.append(zahl(m.nachrichten, 'Rückmeldungen'));
|
zahlen.append(zahl(m.nachrichten, 'Rückmeldungen'));
|
||||||
|
k.append(zahlen);
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
DIE DREI BEREICHE ALS BALKEN (10.09.2026)
|
||||||
|
|
||||||
|
Hier standen bis eben drei weitere Zahlenkaesten ("40 Live-Ablauf:
|
||||||
|
offen") und darunter noch einmal drei Verweise mit denselben
|
||||||
|
Zahlen in Klammern. Sechs Kaesten fuer drei Aussagen -- und die
|
||||||
|
wichtigste stand in keinem davon: WIE WEIT sind wir?
|
||||||
|
|
||||||
|
"40 offen" beantwortet das nicht. Vierzig von vierzig ist nicht
|
||||||
|
dasselbe wie vierzig von hundert, und genau das ist die Frage,
|
||||||
|
die man beim Hinsehen hat.
|
||||||
|
|
||||||
|
Ein Balken beantwortet sie ohne Rechnen: gut und "verbessern"
|
||||||
|
fuellen ihn von links, der Rest ist offen. Zwei Farben, weil es
|
||||||
|
zwei verschiedene Dinge sind -- "besprochen und in Ordnung" und
|
||||||
|
"besprochen, da ist was". Beide sind ERLEDIGT im Sinne von
|
||||||
|
"angesehen", und deshalb fuellen beide den Balken.
|
||||||
|
|
||||||
|
Die ganze Zeile ist der Verweis. Vorher war es ein eigener Knopf
|
||||||
|
darunter; ein Balken, der aussieht wie eine Anzeige, aber klickbar
|
||||||
|
ist, muss auch wie ein Ziel aussehen -- deshalb Rahmen und
|
||||||
|
Hover. */
|
||||||
|
const fortschritt = el('div', 'tfort');
|
||||||
for (const b of BEREICHE) {
|
for (const b of BEREICHE) {
|
||||||
const z = m.bereiche[b.schluessel];
|
const z = m.bereiche[b.schluessel];
|
||||||
if (z) zahlen.append(zahl(z.offen, `${b.name}: offen`));
|
if (!z) continue;
|
||||||
|
const gesamt = Math.max(z.gesamt || 0, 1);
|
||||||
|
const beurteilt = (z.gut || 0) + (z.verbessern || 0);
|
||||||
|
|
||||||
|
const a = el('a', 'tfort__zeile');
|
||||||
|
a.href = `bereich.html?b=${b.schluessel}&creator_id=${m.id}`;
|
||||||
|
|
||||||
|
const kopfz = el('span', 'tfort__kopf');
|
||||||
|
kopfz.append(el('span', 'tfort__name', b.name));
|
||||||
|
kopfz.append(el('span', 'tfort__zahl', `${beurteilt} von ${z.gesamt}`));
|
||||||
|
a.append(kopfz);
|
||||||
|
|
||||||
|
const bahn = el('span', 'tfort__bahn');
|
||||||
|
const gut = el('i', 'tfort__gut');
|
||||||
|
gut.style.width = `${((z.gut || 0) / gesamt) * 100}%`;
|
||||||
|
const schlecht = el('i', 'tfort__verbessern');
|
||||||
|
schlecht.style.width = `${((z.verbessern || 0) / gesamt) * 100}%`;
|
||||||
|
bahn.append(gut, schlecht);
|
||||||
|
a.append(bahn);
|
||||||
|
|
||||||
|
/* Die Aufschluesselung in Worten -- fuer alle, die den Balken
|
||||||
|
nicht sehen koennen, und fuer die Faelle, in denen zwei
|
||||||
|
Prozentpunkte Unterschied nicht auffallen. */
|
||||||
|
a.append(el('span', 'tfort__unter',
|
||||||
|
z.verbessern
|
||||||
|
? `${z.gut} in Ordnung · ${z.verbessern} zu besprechen · ${z.offen} offen`
|
||||||
|
: `${z.gut} in Ordnung · ${z.offen} offen`));
|
||||||
|
fortschritt.append(a);
|
||||||
}
|
}
|
||||||
k.append(zahlen);
|
if (fortschritt.children.length) k.append(fortschritt);
|
||||||
|
|
||||||
/* UND HIER WIRD BEHANDELT. Ein Verweis je Bereich, der die Person
|
/* UND HIER WIRD BEHANDELT. Ein Verweis je Bereich, der die Person
|
||||||
mitnimmt -- ohne sie muesste man dort noch einmal auswaehlen,
|
mitnimmt -- ohne sie muesste man dort noch einmal auswaehlen,
|
||||||
und beim zweiten Mal weiss man nicht mehr, warum man hier war. */
|
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;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +242,52 @@
|
|||||||
die nichts folgt, sieht fuer ihn aus wie eine, die niemand gelesen
|
die nichts folgt, sieht fuer ihn aus wie eine, die niemand gelesen
|
||||||
hat -- und beim dritten Mal meldet er nichts mehr.
|
hat -- und beim dritten Mal meldet er nichts mehr.
|
||||||
===================================================================== */
|
===================================================================== */
|
||||||
|
/* ===================================================================
|
||||||
|
DIE STUFE SETZEN
|
||||||
|
|
||||||
|
Sofort sichtbar, dann speichern. Andersherum -- erst der Server,
|
||||||
|
dann die Anzeige -- fuehlt sich bei jedem Druck traege an, obwohl
|
||||||
|
nichts langsam ist. Schlaegt es fehl, springt die Auswahl zurueck
|
||||||
|
UND es steht eine Meldung da: Eine Anzeige, die sich lautlos
|
||||||
|
zurueckdreht, sieht wie ein Wackelkontakt aus.
|
||||||
|
================================================================ */
|
||||||
|
async function stufeSetzen(wahl, m, stufe) {
|
||||||
|
const knoepfe = [...wahl.querySelectorAll('.tstufen__knopf')];
|
||||||
|
const vorher = knoepfe.find((b) => b.dataset.an === 'ja')?.dataset.stufe || null;
|
||||||
|
if (vorher === stufe) return;
|
||||||
|
|
||||||
|
const zeigen = (wert) => {
|
||||||
|
for (const b of knoepfe) {
|
||||||
|
if (b.dataset.stufe === wert) b.dataset.an = 'ja';
|
||||||
|
else delete b.dataset.an;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
zeigen(stufe);
|
||||||
|
for (const b of knoepfe) b.disabled = true;
|
||||||
|
try {
|
||||||
|
const a = await hole('/workspace/api/teamlage/stufe', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ person_id: m.id, stufe }),
|
||||||
|
});
|
||||||
|
if (!a.ok) throw new Error('abgelehnt');
|
||||||
|
m.stufe = stufe;
|
||||||
|
/* Das Schild oben in derselben Karte zieht mit -- sonst stuende
|
||||||
|
dort die alte Stufe neben der neuen Auswahl. */
|
||||||
|
const schild = wahl.parentElement?.querySelector('[class*="tmerkmal--"]:not(.tmerkmal--pause):not(.tmerkmal--leise)');
|
||||||
|
if (schild) {
|
||||||
|
schild.className = `tmerkmal tmerkmal--${stufe}`;
|
||||||
|
schild.textContent = stufenName(stufe);
|
||||||
|
}
|
||||||
|
melde('');
|
||||||
|
} catch {
|
||||||
|
zeigen(vorher);
|
||||||
|
melde('Die Stufe konnte nicht gespeichert werden.');
|
||||||
|
} finally {
|
||||||
|
for (const b of knoepfe) b.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function eingangBauen(liste) {
|
function eingangBauen(liste) {
|
||||||
const k = el('section', 'tperson');
|
const k = el('section', 'tperson');
|
||||||
k.append(el('p', 'feldschild', 'Rückmeldungen aus dem Team'));
|
k.append(el('p', 'feldschild', 'Rückmeldungen aus dem Team'));
|
||||||
@@ -279,6 +442,11 @@
|
|||||||
const a = await hole('/workspace/api/teamlage');
|
const a = await hole('/workspace/api/teamlage');
|
||||||
if (!a.ok) { melde('Die Lage konnte nicht geladen werden.'); return; }
|
if (!a.ok) { melde('Die Lage konnte nicht geladen werden.'); return; }
|
||||||
const daten = await a.json();
|
const daten = await a.json();
|
||||||
|
/* VOR dem Bauen der Karten: Sie fragen die Liste ab. Stuende
|
||||||
|
diese Zeile darunter, waere die Stufe beim ersten Aufbau leer
|
||||||
|
und erst nach dem naechsten Laden da -- ein Fehler, den man
|
||||||
|
beim Entwickeln nie sieht, weil man ohnehin neu laedt. */
|
||||||
|
STUFEN = Array.isArray(daten.stufen) && daten.stufen.length ? daten.stufen : STUFEN;
|
||||||
ziel.textContent = '';
|
ziel.textContent = '';
|
||||||
ziel.setAttribute('aria-busy', 'false');
|
ziel.setAttribute('aria-busy', 'false');
|
||||||
|
|
||||||
@@ -289,14 +457,85 @@
|
|||||||
deshalb steht hier auch, was zu tun waere. */
|
deshalb steht hier auch, was zu tun waere. */
|
||||||
const wartend = (daten.eingang || []).length + (daten.angebote || []).length;
|
const wartend = (daten.eingang || []).length + (daten.angebote || []).length;
|
||||||
$('kopf-unter').textContent = leute.length
|
$('kopf-unter').textContent = leute.length
|
||||||
? `${leute.length} ${leute.length === 1 ? 'Person' : 'Personen'} · `
|
? `${leute.length} ${leute.length === 1 ? 'Person' : 'Personen'} im Team`
|
||||||
+ `${wartend} ${wartend === 1 ? 'Rückmeldung wartet' : 'Rückmeldungen warten'} · `
|
|
||||||
+ `${offenGesamt} Punkte noch nicht beurteilt`
|
|
||||||
: 'Noch niemand im Team. Zugänge legst du unter „Personen & Zugänge“ an.';
|
: 'Noch niemand im Team. Zugänge legst du unter „Personen & Zugänge“ an.';
|
||||||
|
|
||||||
ziel.append(eingangBauen(daten.eingang || []));
|
/* ===================================================================
|
||||||
ziel.append(angeboteBauen(daten.angebote || []));
|
DIE DREI ZAHLEN, DIE DIE SEITE BEANTWORTET (10.09.2026)
|
||||||
for (const m of leute) ziel.append(karte(m));
|
|
||||||
|
Sie standen als Fliesstext in einer Zeile: "1 Person · 0
|
||||||
|
Rueckmeldungen warten · 101 Punkte noch nicht beurteilt".
|
||||||
|
Genau das sind aber die drei Fragen, wegen derer man diese
|
||||||
|
Seite ueberhaupt aufmacht -- und in einem Satz muss man sie
|
||||||
|
lesen statt zu sehen.
|
||||||
|
|
||||||
|
DIE DRITTE BEKOMMT EINEN BALKEN. "101 noch nicht beurteilt"
|
||||||
|
beantwortet nicht, wie weit man ist: Hundertein von hundertzwei
|
||||||
|
ist etwas anderes als hundertein von dreihundert. Der Balken
|
||||||
|
beantwortet es ohne Rechnen.
|
||||||
|
|
||||||
|
BEI NIEMANDEM IM TEAM bleibt die Leiste ganz weg. Drei Nullen
|
||||||
|
mit Balken auf null sehen aus wie ein Fehler; der Satz oben
|
||||||
|
sagt dann, was zu tun ist. */
|
||||||
|
const punkteGesamt = leute.reduce((sum, m) =>
|
||||||
|
sum + Object.values(m.bereiche).reduce((x, b) => x + (b.gesamt || 0), 0), 0);
|
||||||
|
const beurteilt = Math.max(punkteGesamt - offenGesamt, 0);
|
||||||
|
const pausiert = leute.filter((m) => !m.aktiv).length;
|
||||||
|
|
||||||
|
if (leute.length) {
|
||||||
|
const leiste = el('div', 'tkopfzahlen');
|
||||||
|
|
||||||
|
const eins = el('div', 'tkz');
|
||||||
|
eins.append(el('strong', 'tkz__wert', String(leute.length)));
|
||||||
|
eins.append(el('span', 'tkz__schild',
|
||||||
|
pausiert ? `im Team · ${pausiert} pausiert` : 'im Team'));
|
||||||
|
leiste.append(eins);
|
||||||
|
|
||||||
|
const zwei = el('div', 'tkz');
|
||||||
|
if (wartend) zwei.dataset.dran = 'ja';
|
||||||
|
zwei.append(el('strong', 'tkz__wert', String(wartend)));
|
||||||
|
zwei.append(el('span', 'tkz__schild',
|
||||||
|
wartend === 1 ? 'wartet auf dich' : 'warten auf dich'));
|
||||||
|
leiste.append(zwei);
|
||||||
|
|
||||||
|
const drei = el('div', 'tkz');
|
||||||
|
drei.append(el('strong', 'tkz__wert', `${beurteilt}`));
|
||||||
|
drei.append(el('span', 'tkz__schild', `von ${punkteGesamt} Punkten besprochen`));
|
||||||
|
const bahn = el('span', 'tkz__bahn');
|
||||||
|
const voll = el('i', '');
|
||||||
|
voll.style.width = `${punkteGesamt ? (beurteilt / punkteGesamt) * 100 : 0}%`;
|
||||||
|
bahn.append(voll);
|
||||||
|
drei.append(bahn);
|
||||||
|
leiste.append(drei);
|
||||||
|
|
||||||
|
ziel.append(leiste);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
ZWEI REIHEN STATT EINER (10.09.2026)
|
||||||
|
|
||||||
|
Vorher lagen alle fuenf Kaesten in EINEM Raster: die beiden
|
||||||
|
Wartelisten und je eine Karte pro Person. Zwei Folgen, beide
|
||||||
|
schlecht:
|
||||||
|
|
||||||
|
* DIE SPALTEN WURDEN ZU SCHMAL. Bei fuenf nebeneinander blieb
|
||||||
|
jeder rund 150 px, und im Bildschirmfoto stand "Community
|
||||||
|
12 von" -- abgeschnitten mitten in der Zahl.
|
||||||
|
* DIE LAENGSTE BESTIMMTE DIE HOEHE ALLER. Zehn Rueckmeldungen
|
||||||
|
machten die Reihe so hoch wie zehn Rueckmeldungen; daneben
|
||||||
|
standen drei kurze Karten in sehr viel Luft.
|
||||||
|
|
||||||
|
Es sind auch zwei verschiedene Dinge: oben, was eine ANTWORT
|
||||||
|
braucht, unten, wer im Team ist. Getrennte Reihen sagen das,
|
||||||
|
ohne es hinzuschreiben. */
|
||||||
|
const wartet = el('div', 't-wartet');
|
||||||
|
wartet.append(eingangBauen(daten.eingang || []));
|
||||||
|
wartet.append(angeboteBauen(daten.angebote || []));
|
||||||
|
ziel.append(wartet);
|
||||||
|
|
||||||
|
const runde = el('div', 't-leute');
|
||||||
|
for (const m of leute) runde.append(karte(m));
|
||||||
|
ziel.append(runde);
|
||||||
} catch { /* umgeleitet */ }
|
} catch { /* umgeleitet */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +543,15 @@
|
|||||||
try {
|
try {
|
||||||
const a = await hole('/workspace/api/ich');
|
const a = await hole('/workspace/api/ich');
|
||||||
if (!a.ok) { location.assign('/workspace/'); return; }
|
if (!a.ok) { location.assign('/workspace/'); return; }
|
||||||
window.werZeigen?.(await a.json());
|
/* Wer zusieht, entscheidet, ob die Stufenwahl erscheint. Die
|
||||||
|
Antwort wurde bisher nur weitergereicht -- gemerkt wird sie
|
||||||
|
erst, seit es hier etwas gibt, das nicht jeder darf.
|
||||||
|
|
||||||
|
DER SERVER ENTSCHEIDET TROTZDEM: Diese Zeile blendet nur aus.
|
||||||
|
Wer den Aufruf von Hand nachbaut, bekommt 404 -- das steht in
|
||||||
|
workspace-teamlage.js und nicht hier. */
|
||||||
|
ich = await a.json();
|
||||||
|
window.werZeigen?.(ich);
|
||||||
} catch { return; }
|
} catch { return; }
|
||||||
$('abmelden')?.addEventListener('click', async () => {
|
$('abmelden')?.addEventListener('click', async () => {
|
||||||
try { await fetch('/workspace/api/abmelden', { method: 'POST', credentials: 'same-origin' }); }
|
try { await fetch('/workspace/api/abmelden', { method: 'POST', credentials: 'same-origin' }); }
|
||||||
|
|||||||
+11
-11
@@ -12,12 +12,12 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -245,10 +245,10 @@
|
|||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/aufgaben.js?v=202609101602" defer></script>
|
<script src="assets/js/aufgaben.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/automation.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -140,10 +140,10 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/automation.js?v=202609101602" defer></script>
|
<script src="assets/js/automation.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/bereich.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -157,14 +157,14 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/checkliste.js?v=202609101602" defer></script>
|
<script src="assets/js/checkliste.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/ampel.js?v=202609101602" defer></script>
|
<script src="assets/js/ampel.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/schulung.js?v=202609101602" defer></script>
|
<script src="assets/js/schulung.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/vorlagen.js?v=202609101602" defer></script>
|
<script src="assets/js/vorlagen.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereich.js?v=202609101602" defer></script>
|
<script src="assets/js/bereich.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+13
-13
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/calls.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -82,11 +82,11 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202609101602" defer></script>
|
<script src="assets/js/ki.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/calls.js?v=202609101602" defer></script>
|
<script src="assets/js/calls.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -12,19 +12,19 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
||||||
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
||||||
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
||||||
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
||||||
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
||||||
Dialog zu sehen bekam. -->
|
Dialog zu sehen bekam. -->
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/chat.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -247,10 +247,10 @@
|
|||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<script src="assets/js/bereiche.js?v=202609101602"></script>
|
<script src="assets/js/bereiche.js?v=202609101626"></script>
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602"></script>
|
<script src="assets/js/kopf.js?v=202609101626"></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602"></script>
|
<script src="assets/js/glocke.js?v=202609101626"></script>
|
||||||
<script src="assets/js/chat.js?v=202609101602"></script>
|
<script src="assets/js/chat.js?v=202609101626"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+14
-14
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/content.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/content.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -172,12 +172,12 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/checkliste.js?v=202609101602" defer></script>
|
<script src="assets/js/checkliste.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/vorlagen.js?v=202609101602" defer></script>
|
<script src="assets/js/vorlagen.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/content.js?v=202609101602" defer></script>
|
<script src="assets/js/content.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
|
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
|
||||||
type="image/webp" media="(max-width: 899px)" />
|
type="image/webp" media="(max-width: 899px)" />
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="gate gate--crew">
|
<body class="gate gate--crew">
|
||||||
@@ -246,6 +246,6 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="assets/js/gate.js?v=202609101602" defer></script>
|
<script src="assets/js/gate.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -13,25 +13,25 @@
|
|||||||
"theme_color": "#06090f",
|
"theme_color": "#06090f",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/crew-192.png?v=202609101602",
|
"src": "/assets/img/app-symbole/crew-192.png?v=202609101626",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/crew-512.png?v=202609101602",
|
"src": "/assets/img/app-symbole/crew-512.png?v=202609101626",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609101602",
|
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609101626",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609101602",
|
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609101626",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
|
|||||||
+12
-12
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/dateien.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -101,10 +101,10 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/dateien.js?v=202609101602" defer></script>
|
<script src="assets/js/dateien.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
|
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
|
||||||
type="image/webp" media="(max-width: 899px)" />
|
type="image/webp" media="(max-width: 899px)" />
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="gate">
|
<body class="gate">
|
||||||
@@ -401,6 +401,6 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="assets/js/gate.js?v=202609101602" defer></script>
|
<script src="assets/js/gate.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+13
-13
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/kalender.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -370,11 +370,11 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kalender.js?v=202609101602" defer></script>
|
<script src="assets/js/kalender.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kalender-abo.js?v=202609101602" defer></script>
|
<script src="assets/js/kalender-abo.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/leistung.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -217,10 +217,10 @@
|
|||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602"></script>
|
<script src="assets/js/wahl.js?v=202609101626"></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602"></script>
|
<script src="assets/js/bereiche.js?v=202609101626"></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602"></script>
|
<script src="assets/js/kopf.js?v=202609101626"></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602"></script>
|
<script src="assets/js/glocke.js?v=202609101626"></script>
|
||||||
<script src="assets/js/leistung.js?v=202609101602"></script>
|
<script src="assets/js/leistung.js?v=202609101626"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/personen.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -364,10 +364,10 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/personen.js?v=202609101602" defer></script>
|
<script src="assets/js/personen.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+13
-13
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/profil.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -207,11 +207,11 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/steckbrief.js?v=202609101602" defer></script>
|
<script src="assets/js/steckbrief.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/profil.js?v=202609101602" defer></script>
|
<script src="assets/js/profil.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+13
-13
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/report.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/report.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -99,11 +99,11 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202609101602" defer></script>
|
<script src="assets/js/ki.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/report.js?v=202609101602" defer></script>
|
<script src="assets/js/report.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/scouting.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -132,10 +132,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/scouting.js?v=202609101602" defer></script>
|
<script src="assets/js/scouting.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+11
-11
@@ -12,14 +12,14 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
||||||
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
||||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/heim.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -667,10 +667,10 @@
|
|||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/start.js?v=202609101602" defer></script>
|
<script src="assets/js/start.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+13
-13
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -78,11 +78,11 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202609101602" defer></script>
|
<script src="assets/js/ki.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/startcheck.js?v=202609101602" defer></script>
|
<script src="assets/js/startcheck.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/profil.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -119,10 +119,10 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/steckbrief.js?v=202609101602" defer></script>
|
<script src="assets/js/steckbrief.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -7,13 +7,13 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/team.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/team.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -103,10 +103,10 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/team.js?v=202609101602" defer></script>
|
<script src="assets/js/team.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+10
-10
@@ -7,13 +7,13 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/team.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/team.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -66,8 +66,8 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/teamlage.js?v=202609101602" defer></script>
|
<script src="assets/js/teamlage.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -12,15 +12,15 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||||
Dialog "Creator anlegen". -->
|
Dialog "Creator anlegen". -->
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -142,11 +142,11 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/uebersicht.js?v=202609101602" defer></script>
|
<script src="assets/js/uebersicht.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+12
-12
@@ -12,13 +12,13 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101602" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101626" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101602" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/wissen.css?v=202609101626" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101602" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609101626" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -183,10 +183,10 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609101602" defer></script>
|
<script src="assets/js/wahl.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609101602" defer></script>
|
<script src="assets/js/bereiche.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609101602" defer></script>
|
<script src="assets/js/kopf.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609101602" defer></script>
|
<script src="assets/js/glocke.js?v=202609101626" defer></script>
|
||||||
<script src="assets/js/wissen.js?v=202609101602" defer></script>
|
<script src="assets/js/wissen.js?v=202609101626" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user