Entwicklung: die Auswertung je Person -- und elf Seiten ohne Farbe
Filipe: "dan perfektionierst du eine neue wo diagramme texte und so zu
jedem modi gemacht wird und diese kategorie kannst du entwicklung nenen."
1. DAS EIGENTLICHE PROBLEM WAR, DASS ES KEINE VERGANGENHEIT GAB.
`entwicklung_stand` hat den Schluessel (person, punkt, beurteiler) und
wird bei jeder Aenderung UEBERSCHRIEBEN -- dort richtig, eine Beobachtung
ueber einen Menschen soll aktuell sein. Nur: Damit existierte nichts,
woraus sich ein Verlauf zeichnen liesse. Nachgemessen: 43 Zeilen Stand,
NULL Zeilen Geschichte. Ein Diagramm ueber einen einzigen Zeitpunkt ist
kein Diagramm, sondern ein Balken.
Neu: `entwicklung_lauf` haengt jede Aenderung an, statt zu ueberschreiben.
Sie steht in workspace-entwicklung.js, bei der Tabelle, zu der sie
gehoert -- die Auswertung liest nur. Andersherum gaebe es einen Ring
zwischen zwei Modulen, und Ringe halten in JavaScript genau so lange,
bis jemand eine Konstante auf oberster Ebene benutzt.
KEIN ANLASS, NIE. Neben jedem "da hakt es" steht ein Satz, woran man es
gemerkt hat -- fuer ein Gespraech aufgeschrieben. Eine Chronik dieser
Saetze waere das Belastendste, was dieses Haus speichern kann. Die Spur
hat gar keine Spalte dafuer, und die Pruefung liest die TABELLE, nicht
die Ausgabe: Eine Spalte, die es gibt, wird eines Tages gefuellt.
Die Startaufnahme uebernimmt den heutigen Stand mit seinen ECHTEN Daten,
damit die Seite am ersten Tag nicht leer ist -- und zaehlt ausdruecklich
NICHT als Bewegung. Der Tag, an dem die Spur entstand, war kein Tag, an
dem 43 Dinge gleichzeitig passiert sind.
2. WAS GEZEIGT WIRD: BEWEGUNG, KEINE NOTE.
Keine Punktzahl, kein Prozentwert, kein Durchschnitt, keine Rangliste,
kein Vergleich zwischen zwei Menschen. Das ist die Recherche, nicht
Vorsicht: Ueberrechtfertigung (Deci), sozialer Vergleich senkt die
Leistung derer, die schlechter dastehen, Schwaechenfokus ebenso.
Stattdessen das progress principle (Amabile/Kramer, ~12 000
Tagebucheintraege): Sichtbarer Fortschritt ist der staerkste einzelne
Treiber. Deshalb Diagramm 1 "Was sich bewegt hat" -- zwei Richtungen um
eine Nulllinie, ein Massstab fuer beide. Diagramm 2 der Streifen (Punkt
x Monat), fortgeschrieben, aber nach 90 Tagen sichtbar verblasst.
Und die Texte in dieser Reihenfolge: was sich bewegt hat, was traegt,
erst danach was liegt.
Selbst gezeichnet, kein Diagrammpaket: 60 kB ueber die Leitung von
jemandem, der mit dem Handy im Stream steht -- und es braechte eigene
Farben neben die Hausfarben. Jede Zelle traegt ihr Zeichen als Text,
jeder Balken seine Zahl, darunter eine Legende: Die Farbe ist nie die
Auskunft.
Zwei Gesichter: Die Leitung sieht die Karten, ein Modi ausschliesslich
die Bewegung in der eigenen -- ohne Namen, ohne Anlass, ohne Streifen.
3. NEBENBEFUND, GROESSER ALS ERWARTET: ELF SEITEN OHNE FARBE.
Der Umbau von heute frueh heisst "Jede Seite traegt die Farbe ihrer
Kachel". Nachgemessen traf das auf 18 von 34 Seiten zu. Elf Seiten mit
echter Kachel gingen leer aus -- ausgerechnet die von Team Dogi:
befinden, bewerben, bewerbungen, entwicklung, hilfe, rechte, talente,
teamlage, teilen, treff-moderation, treff-regeln, unsere-seiten.
Der Grund: `zuSeite()` sucht nur in `Bereiche.GRUPPEN`, und dort stehen
ausschliesslich die Kacheln der Agentur. Die von Team Dogi liefert der
Server (`ich.bereiche`, `ich.bereiche_zusatz`). Auffallen konnte das
nicht -- eine Seite ohne Farbe sieht aus wie eine, die eben keine hat.
kopf.js faerbt jetzt zweimal: sofort aus GRUPPEN (Agenturseiten ohne
Flackern wie bisher), und noch einmal aus den Serverkacheln, sobald
`werZeigen` sie bringt. Nur, wenn beim ersten Mal nichts gefunden wurde.
pruef-buehne misst den Kontrast an echten Bildpunkten und kannte diese
zwoelf Seiten nicht. Jetzt stehen sie in der Liste; ihre Frist waechst
mit der Laenge, statt als feste Zahl irgendwann zu reissen. Gemessen
fuer werdegang.html: 4,83:1 am Computer, 6,22:1 am Handy.
4. TON 38 SAH FREI AUS UND WAR ES NICHT.
Erster Anlauf war Ton 38, weil 37 die hoechste Nummer in workspace.js
ist. Gezaehlt in start.css sind es 39, alle vergeben. Genau davor warnt
das Farbwerkzeug im eigenen Kopf ("Ton 22, weil er frei AUSSAH").
Dabei aufgefallen: Die zwei Kacheln von heute Nacht wurden ohne das
Werkzeug gesetzt. Ton 39 lag 0,0362 von Ton 15 entfernt -- der engste
Abstand im ganzen Haus, zwei praktisch gleiche Cyan-Toene. Filipe dazu
mehrfach: "keine die sich irgendwie aehnlich sind".
Neues Werkzeug `tools/kachel-farbe-einzeln.mjs`: zieht EINE Farbe nach,
ohne die anderen anzufassen. Das grosse Werkzeug haette alle 40 neu
gefaerbt -- jede Kachel im Haus, ungefragt. Es bricht ab, wenn es nicht
alle Toene lesen kann: Der erste Anlauf las 31 von 40, weil einstellige
Nummern mit ZWEI Leerzeichen dastehen, und rechnete froehlich weiter.
Ergebnis: kleinster Abstand 0,0362 -> 0,0840, durch Aendern von zwei
Kacheln, die es erst seit gestern Nacht gibt.
5. anruf-probe.html lud `basis.css` und `kopf.css` -- beide gibt es
nicht. Ausgerechnet dort faellt es nicht auf, weil die Seite ihre eigene
Stilangabe im Kopf hat. pruef-struktur war deshalb rot (5 Befunde) und
ist jetzt gruen.
GEMESSEN: pruef-werdegang 95/0 (neu), pruef-entwicklung 43/0,
pruef-befinden 75/0, pruef-rechtetafel 19/0, pruef-zwischenspeicher 21/0,
pruef-start-ansicht 0 Fehler, pruef-struktur 0 Fehler (vorher 5),
pruef-buehne fuer werdegang.html 0 Fehler.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -25,6 +25,9 @@ import { dateienRouter } from "./workspace-dateien.js";
|
||||
import { bereicheRouter } from "./workspace-bereiche.js";
|
||||
import { treffRouter } from "./workspace-treff.js";
|
||||
import { entwicklungRouter } from "./workspace-entwicklung.js";
|
||||
/* Die Auswertung UEBER den Katalog -- eigene Datei, eigener Gegenstand.
|
||||
Sie liest nur; geschrieben wird die Spur in workspace-entwicklung.js. */
|
||||
import { werdegangRouter } from "./workspace-werdegang.js";
|
||||
import { rechteRouter, tafelLaden } from "./workspace-rechte.js";
|
||||
import { aufbewahrungRouter, aufbewahrungStarten } from "./workspace-aufbewahrung.js";
|
||||
import { auskunftRouter } from "./workspace-auskunft.js";
|
||||
@@ -287,6 +290,7 @@ app.use(treffRouter);
|
||||
-- beides nebeneinander ist Absicht: Der Katalog ist das, was man
|
||||
regelmaessig durchgeht, die Notiz das, was dazwischen auffaellt. */
|
||||
app.use(entwicklungRouter);
|
||||
app.use(werdegangRouter);
|
||||
/* Die Rechtetafel -- ansehen und umstellen (11.09.2026). */
|
||||
app.use(rechteRouter);
|
||||
app.use(aufbewahrungRouter);
|
||||
|
||||
+57
-4
@@ -48,6 +48,56 @@ function dunkelsterStopp(backgroundImage) {
|
||||
}
|
||||
const DUNKELSTER_QUELLE = dunkelsterStopp.toString();
|
||||
|
||||
/* ZWOELF SEITEN KAMEN AM 19.09.2026 DAZU -- und zwar nicht, weil sie neu
|
||||
sind, sondern weil sie bis dahin GAR KEINE Buehne hatten.
|
||||
|
||||
Der Umbau vom 19.09. frueh heisst "Jede Seite traegt die Farbe ihrer
|
||||
Kachel". Nachgemessen traf das auf 18 von 34 Seiten zu: `zuSeite()`
|
||||
sucht nur in `Bereiche.GRUPPEN`, und dort stehen ausschliesslich die
|
||||
Kacheln der AGENTUR. Die Kacheln von Team Dogi und vom Treff liefert
|
||||
der Server -- die kannte die Stelle nicht. Seit der Reparatur in
|
||||
kopf.js bekommen auch sie Ton UND Buehne, und damit steht auf ihnen
|
||||
zum ersten Mal Text ueber einem Foto. Genau das misst diese Datei.
|
||||
|
||||
NUR EINE TEILMENGE LAUFEN LASSEN: `BUEHNE_SEITEN=werdegang.html`.
|
||||
Ohne die Angabe laeuft die ganze Liste -- das dauert spuerbar und
|
||||
gehoert deshalb in einen abgesprochenen Lauf, nicht in jede
|
||||
Zwischenpruefung. */
|
||||
const ALLE_SEITEN = [
|
||||
"start.html", "uebersicht.html", "personen.html", "content.html",
|
||||
"aufgaben.html", "kalender.html",
|
||||
/* Die zwoelf ohne Buehne, bis zum 19.09.2026: */
|
||||
"werdegang.html", "entwicklung.html", "talente.html", "befinden.html",
|
||||
"hilfe.html", "rechte.html", "teamlage.html", "teilen.html",
|
||||
"bewerbungen.html", "treff-moderation.html", "treff-regeln.html",
|
||||
"unsere-seiten.html",
|
||||
];
|
||||
const SEITEN = process.env.BUEHNE_SEITEN
|
||||
? process.env.BUEHNE_SEITEN.split(",").map((s) => s.trim()).filter(Boolean)
|
||||
: ALLE_SEITEN;
|
||||
/* DRITTER AUSGANG: Wer sich vertippt, soll es merken, statt eine
|
||||
Prüfung über null Seiten für bestanden zu halten. */
|
||||
{
|
||||
const unbekannt = SEITEN.filter((s) => !ALLE_SEITEN.includes(s));
|
||||
if (!SEITEN.length || unbekannt.length) {
|
||||
console.log("ABBRUCH: unbekannte Seite(n) in BUEHNE_SEITEN: " + unbekannt.join(", "));
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
console.log(`Gemessen werden ${SEITEN.length} von ${ALLE_SEITEN.length} Seiten.`);
|
||||
|
||||
/* WIE LANGE DAS DAUERN DARF -- aus der Listenlaenge, nicht aus dem Kopf.
|
||||
|
||||
Sechs Seiten auf zwei Bildschirmgroessen brauchten bisher knapp
|
||||
220 Sekunden, also rund 18 s je Seite und Groesse. Aufgerundet auf 22,
|
||||
damit ein langsamer Lauf nicht faelschlich als Fehlschlag endet.
|
||||
|
||||
Eine feste Zahl waere hier eine Zeitbombe: Mit den zwoelf neuen
|
||||
Seiten waere die alte Frist gerissen, und der Abbruch saehe aus wie
|
||||
ein Befund. */
|
||||
const SEITEN_ZEIT = SEITEN.length * 2 * 22_000;
|
||||
console.log(`Zeitrahmen: ${Math.round((100_000 + SEITEN_ZEIT) / 1000)} s.`);
|
||||
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-buehne-"));
|
||||
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||
process.env.PORT = "4193";
|
||||
@@ -66,10 +116,14 @@ await import("./index.js");
|
||||
richtig fuer den Betrieb, fatal fuer eine Pruefung. Ohne diese
|
||||
Zeile bleibt der Prozess nach einem Fehler ewig stehen, weil der
|
||||
Server ihn am Leben haelt (siehe helfer-notbremse.mjs). */
|
||||
notbremse(480_000, "pruef-buehne");
|
||||
notbremse(120_000 + SEITEN_ZEIT, "pruef-buehne");
|
||||
await new Promise((r) => setTimeout(r, 700));
|
||||
const BASIS = "http://127.0.0.1:4193";
|
||||
setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 220_000).unref?.();
|
||||
/* Die Frist waechst mit der Liste: 220 s reichten fuer sechs Seiten.
|
||||
Eine feste Zahl waere hier eine Zeitbombe -- die zwoelf neuen
|
||||
haetten sie gerissen, und der Abbruch saehe aus wie ein Befund. */
|
||||
setTimeout(() => { console.log("ABBRUCH: Zeit ueberschritten"); process.exit(1); },
|
||||
100_000 + SEITEN_ZEIT).unref?.();
|
||||
|
||||
let fehler = 0;
|
||||
const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||
@@ -109,8 +163,7 @@ const browser = await chromium.launch();
|
||||
/* Der Kalender kam am 01.09.2026 dazu -- er bringt die meisten neuen
|
||||
Farbflächen mit (vier Arten, Feiertagsfärbung, Anlassmarken), und
|
||||
genau dort entscheidet sich, ob der hellere Hintergrund noch trägt. */
|
||||
const SEITEN = ["start.html", "uebersicht.html", "personen.html", "content.html",
|
||||
"aufgaben.html", "kalender.html"];
|
||||
|
||||
|
||||
for (const [breite, hoehe, wie] of [[1440, 950, "Computer"], [390, 780, "Handy"]]) {
|
||||
console.log(`\n=== ${wie} (${breite}x${hoehe})`);
|
||||
|
||||
@@ -0,0 +1,626 @@
|
||||
/* =====================================================================
|
||||
ENTWICKLUNG — die Auswertung je Person (19.09.2026)
|
||||
|
||||
Filipe: „dan perfektionierst du eine neue wo diagramme texte und so
|
||||
zu jedem modi gemacht wird und diese kategorie kannst du entwicklung
|
||||
nenen."
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WAS HIER GEPRÜFT WIRD — UND WARUM GENAU DAS
|
||||
|
||||
Eine Seite, die Diagramme über MENSCHEN zeigt, kann auf drei Arten
|
||||
falsch sein, und nur eine davon ist ein Programmfehler:
|
||||
|
||||
1. SIE ZEIGT ZU VIEL. Der Anlass eines „da hakt es“ ist für ein
|
||||
Gespräch aufgeschrieben. Landete er in der Spur, wäre jede
|
||||
längst geklärte Beobachtung auf Dauer lesbar. Block 5 („Wie
|
||||
geht es dir damit?“) darf überhaupt nicht hierherkommen.
|
||||
2. SIE BEWERTET. Punktzahl, Prozentwert, Rangliste, Vergleich
|
||||
zwischen zwei Menschen — nichts davon darf entstehen, auch
|
||||
nicht versehentlich über einen Umweg.
|
||||
3. SIE BEHAUPTET EINEN VERLAUF, DEN ES NICHT GIBT. Ein Diagramm
|
||||
über einen einzigen Zeitpunkt ist kein Diagramm. Dann muss die
|
||||
Seite das SAGEN — der dritte Ausgang.
|
||||
|
||||
ZU JEDER PRÜFUNG, DIE „ALLES IN ORDNUNG“ SAGEN KANN, GEHÖRT EINE
|
||||
GEGENPROBE, die beweist, dass sie auch „nicht in Ordnung“ sagen
|
||||
kann. Eine Prüfung, die immer bestätigt, bestätigt nichts.
|
||||
|
||||
Aufrufen mit: node server/pruef-werdegang.mjs
|
||||
===================================================================== */
|
||||
|
||||
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { request as httpAnfrage } from "node:http";
|
||||
import { portMussFreiSein } from "./helfer-port.mjs";
|
||||
|
||||
const PORT = await portMussFreiSein(4422, "pruef-werdegang");
|
||||
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-weg-"));
|
||||
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||
process.env.PORT = String(PORT);
|
||||
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-werdegang");
|
||||
await new Promise((r) => setTimeout(r, 700));
|
||||
|
||||
process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f); process.exit(7); });
|
||||
process.on("unhandledRejection", (f) => { console.error("ABSTURZ (Versprechen):", f); process.exit(7); });
|
||||
|
||||
let fehler = 0, geprueft = 0;
|
||||
const melde = (t) => console.log(t);
|
||||
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||
|
||||
const CREW = "crew.dogfather-universe.com";
|
||||
|
||||
const { DatabaseSync } = await import("node:sqlite");
|
||||
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
||||
const W = await import("./workspace-werdegang.js");
|
||||
const kat = await import("./workspace-entwicklungs-punkte.js");
|
||||
const rechte = await import("./rechte.js");
|
||||
const ent = await import("./workspace-entwicklung.js");
|
||||
|
||||
const oeffne = () => new DatabaseSync(process.env.WORKSPACE_DB);
|
||||
const jetzt = new Date().toISOString();
|
||||
|
||||
/* ABGELEITET, NICHT ABGESCHRIEBEN. Jede feste Zahl in einer Prüfung ist
|
||||
eine Rechnung von gestern -- am 17.09. wurden aus sechs Fragen zwölf
|
||||
und vier Prüfungen waren rot, ohne dass etwas kaputt war. */
|
||||
const PUNKTE = kat.ENTWICKLUNG_FREMD;
|
||||
const SELBST = kat.ENTWICKLUNG_SELBST;
|
||||
|
||||
let idDogi, idHand, idModi, idCreator;
|
||||
{
|
||||
const d = oeffne();
|
||||
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);
|
||||
const anlegen = (name, rolle, code) => {
|
||||
const salt = randomBytes(16).toString("hex");
|
||||
const hash = scryptSync(code, salt, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
||||
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, code_kennung, aktiv, erstellt)"
|
||||
+ " VALUES (?,?,?,?,?,?,1,?)")
|
||||
.run(name, rolle, hash, salt, 32768, createHmac("sha256", schluessel).update(code).digest("hex"), jetzt);
|
||||
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||
};
|
||||
idDogi = anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
||||
idHand = anlegen("Rieke", "hand", "CODE-HAND-0001");
|
||||
idModi = anlegen("Diene", "modi", "CODE-MODI-0001");
|
||||
idCreator = anlegen("Tili", "creator", "CODE-CREA-0001");
|
||||
d.close();
|
||||
}
|
||||
|
||||
function ruf(pfad, host, methode, kopf = {}, koerper = null) {
|
||||
return new Promise((fertig, schief) => {
|
||||
const a = httpAnfrage({ host: "127.0.0.1", port: PORT, path: pfad, method: methode,
|
||||
headers: { Host: host, ...(koerper ? { "Content-Type": "application/json" } : {}), ...kopf } },
|
||||
(antwort) => {
|
||||
let text = "";
|
||||
antwort.on("data", (s) => { text += s; });
|
||||
antwort.on("end", () => fertig({ code: antwort.statusCode, text, kopf: antwort.headers }));
|
||||
});
|
||||
a.on("error", schief);
|
||||
if (koerper) a.write(JSON.stringify(koerper));
|
||||
a.end();
|
||||
});
|
||||
}
|
||||
const json = (a) => { try { return JSON.parse(a.text); } catch { return {}; } };
|
||||
async function anmelden(rolle, code, host) {
|
||||
const r = await ruf("/workspace/api/anmelden", host, "POST", {}, { rolle, code });
|
||||
return [].concat(r.kopf?.["set-cookie"] || []).map((c) => c.split(";")[0]).join("; ");
|
||||
}
|
||||
|
||||
const kDogi = await anmelden("admin", "CODE-DOGI-0001", CREW);
|
||||
const kHand = await anmelden("hand", "CODE-HAND-0001", CREW);
|
||||
const kModi = await anmelden("modi", "CODE-MODI-0001", CREW);
|
||||
ok(!!kDogi && !!kHand && !!kModi, "DogFather, rechte Hand und ein Modi sind angemeldet");
|
||||
|
||||
/* =======================================================================
|
||||
1. DIE KACHEL HEISST „ENTWICKLUNG“ UND FÜHRT AUCH DORTHIN
|
||||
|
||||
Der ganze Auftrag hängt an einer Umbenennung: Die alte Kachel heißt
|
||||
seit heute früh „Eure Aufgaben“, damit der Name frei wird. Wenn die
|
||||
neue Kachel auf die alte Seite zeigt, ist genau nichts gewonnen.
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 1. Die Kachel ===");
|
||||
{
|
||||
const ich = json(await ruf("/workspace/api/ich", CREW, "GET", { cookie: kDogi }));
|
||||
const alle = [...(ich.bereiche || []), ...(ich.bereiche_zusatz || [])];
|
||||
|
||||
const neu = alle.find((k) => k.name === "Entwicklung");
|
||||
ok(!!neu, "eine Kachel „Entwicklung“ steht bei DogFather");
|
||||
ok(neu?.ziel === "werdegang.html",
|
||||
`und führt auf die Auswertung (${neu?.ziel})`);
|
||||
|
||||
/* GEGENPROBE. Ohne sie bewiese die Zeile darüber nichts: Der Katalog
|
||||
muss weiterhin erreichbar sein, unter seinem neuen Namen. */
|
||||
const alt = alle.find((k) => k.ziel === "entwicklung.html");
|
||||
ok(alt?.name === "Eure Aufgaben",
|
||||
`der Katalog heißt weiter „${alt?.name}“ und liegt woanders`);
|
||||
ok(neu?.ziel !== alt?.ziel, "beide Kacheln zeigen auf VERSCHIEDENE Seiten");
|
||||
|
||||
/* ZWEI KACHELN, ZWEI FARBEN. Gleiche Farbe hieße, dass man sie auf
|
||||
der Startseite verwechselt -- und sie stehen nebeneinander. */
|
||||
ok(neu?.ton && alt?.ton && neu.ton !== alt.ton,
|
||||
`verschiedene Farbtöne (${alt?.ton} / ${neu?.ton})`);
|
||||
|
||||
const css = readFileSync(
|
||||
new URL("../workspace/assets/css/start.css", import.meta.url), "utf8");
|
||||
ok(new RegExp(`\\[data-ton="${neu?.ton}"\\]`).test(css),
|
||||
`Ton ${neu?.ton} ist in start.css wirklich definiert`);
|
||||
|
||||
/* UND BEI DER RECHTEN HAND AUCH. Sie arbeitet an denselben Dingen --
|
||||
eine Kachel nur bei DogFather wäre die Über-/Unterordnung, die es
|
||||
in diesem Haus nicht gibt. */
|
||||
const beiHand = json(await ruf("/workspace/api/ich", CREW, "GET", { cookie: kHand }));
|
||||
ok([...(beiHand.bereiche || []), ...(beiHand.bereiche_zusatz || [])]
|
||||
.some((k) => k.ziel === "werdegang.html"),
|
||||
"die rechte Hand hat dieselbe Kachel");
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
2. WER HINEINDARF
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 2. Wer hineindarf ===");
|
||||
{
|
||||
const tafel = rechte.tafelJetzt();
|
||||
const erlaubt = tafel["/workspace/werdegang.html"] || [];
|
||||
ok(erlaubt.length > 0, "die Seite steht in der Rechtetafel");
|
||||
ok(erlaubt.includes("admin") && erlaubt.includes("hand"),
|
||||
"DogFather und die rechte Hand dürfen sie öffnen");
|
||||
ok(erlaubt.includes("modi"),
|
||||
"ein Modi auch — er sieht dort seine eigene Bewegung");
|
||||
/* GEGENPROBE: Die Agentur hat gar keine Entwicklungskarte. Eine Seite,
|
||||
die man öffnen kann und die dann leer bleibt, sieht aus wie ein
|
||||
Fehler -- und ist einer. */
|
||||
ok(!erlaubt.includes("creator") && !erlaubt.includes("gast"),
|
||||
"die Agentur und die Community nicht");
|
||||
|
||||
ok(json(await ruf("/workspace/api/werdegang/liste", CREW, "GET", { cookie: kDogi })).liste !== undefined,
|
||||
"DogFather bekommt die Liste");
|
||||
const beiModi = await ruf("/workspace/api/werdegang/liste", CREW, "GET", { cookie: kModi });
|
||||
ok(beiModi.code === 404,
|
||||
`ein Modi bekommt sie nicht (${beiModi.code} — nicht 403: wer „kein Zugriff“ liest, fragt als Nächstes, wer denn dann)`);
|
||||
const fremdeKarte = await ruf(`/workspace/api/werdegang/person/${idHand}`, CREW, "GET", { cookie: kModi });
|
||||
ok(fremdeKarte.code === 404, `und auch keine fremde Karte (${fremdeKarte.code})`);
|
||||
const ohne = await ruf("/workspace/api/werdegang/liste", CREW, "GET", {});
|
||||
ok(ohne.code === 401, `ohne Anmeldung gar nichts (${ohne.code})`);
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
3. DIE SPUR — SIE MERKT SICH, WAS DIE TABELLE VERGISST
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 3. Die Spur ===");
|
||||
{
|
||||
const p1 = PUNKTE[0].schluessel, p2 = PUNKTE[1].schluessel;
|
||||
|
||||
const a = await ruf(`/workspace/api/entwicklung/person/${idModi}/${p1}`, CREW, "PUT",
|
||||
{ cookie: kDogi }, { stufe: "waechst" });
|
||||
ok(a.code === 200, "eine Einschätzung lässt sich setzen");
|
||||
|
||||
const b = await ruf(`/workspace/api/entwicklung/person/${idModi}/${p1}`, CREW, "PUT",
|
||||
{ cookie: kDogi }, { stufe: "laeuft" });
|
||||
ok(b.code === 200, "und ändern");
|
||||
|
||||
const d = oeffne();
|
||||
const spur = d.prepare(
|
||||
"SELECT * FROM entwicklung_lauf WHERE person_id = ? ORDER BY id").all(idModi);
|
||||
ok(spur.length === 2, `beide Schritte stehen in der Spur (${spur.length})`);
|
||||
ok(spur[0].vorher === null && spur[0].stufe === "waechst",
|
||||
"der erste hat kein „vorher“ — er war der erste");
|
||||
ok(spur[1].vorher === "waechst" && spur[1].stufe === "laeuft",
|
||||
"der zweite weiß, was vorher dastand (waechst → laeuft)");
|
||||
ok(spur.every((z) => z.nachgetragen === 0),
|
||||
"beide sind beobachtet, nicht nachgetragen");
|
||||
|
||||
/* ==== DIE WICHTIGSTE PRÜFUNG DER GANZEN DATEI =====================
|
||||
|
||||
KEIN ANLASS IN DER SPUR. Neben einem „da hakt es“ steht ein Satz,
|
||||
woran man es gemerkt hat. Eine Chronik dieser Sätze wäre das
|
||||
Belastendste, was dieses Haus speichern kann.
|
||||
|
||||
GEPRÜFT WIRD DIE TABELLE SELBST, nicht die Ausgabe: Eine Spalte,
|
||||
die es gibt, wird eines Tages gefüllt.
|
||||
============================================================== */
|
||||
const spalten = d.prepare("PRAGMA table_info(entwicklung_lauf)").all().map((z) => z.name);
|
||||
ok(!spalten.includes("anlass"),
|
||||
`die Spur hat gar keine Spalte für den Anlass (${spalten.join(", ")})`);
|
||||
|
||||
/* Und der Anlass steht trotzdem dort, wo er hingehört -- sonst wäre
|
||||
die Zeile oben nur deshalb grün, weil die Funktion gar nichts tut. */
|
||||
await ruf(`/workspace/api/entwicklung/person/${idModi}/${p2}`, CREW, "PUT",
|
||||
{ cookie: kDogi }, { stufe: "hakt", anlass: "Am Dienstag im Stream, zweimal nacheinander." });
|
||||
const stand = d.prepare(
|
||||
"SELECT anlass FROM entwicklung_stand WHERE person_id = ? AND schluessel = ?")
|
||||
.get(idModi, p2);
|
||||
ok(/Dienstag/.test(stand?.anlass || ""),
|
||||
"im Katalog steht er weiterhin — dort gehört er hin");
|
||||
const inSpur = d.prepare(
|
||||
"SELECT COUNT(*) AS n FROM entwicklung_lauf WHERE person_id = ? AND schluessel = ?")
|
||||
.get(idModi, p2).n;
|
||||
ok(inSpur === 1, "die Spur kennt den Punkt, nicht den Satz dazu");
|
||||
|
||||
/* BLOCK 5 DARF NIE HIERHERKOMMEN. „Wie geht es dir damit?“ beantwortet
|
||||
nur die Person selbst; diese Antworten haben eine eigene Tabelle,
|
||||
die niemand sonst liest. */
|
||||
const selbst = SELBST[0].schluessel;
|
||||
await ruf(`/workspace/api/entwicklung/mich/${selbst}`, CREW, "PUT",
|
||||
{ cookie: kModi }, { stufe: "hakt" });
|
||||
const inSpur2 = d.prepare(
|
||||
"SELECT COUNT(*) AS n FROM entwicklung_lauf WHERE schluessel = ?").get(selbst).n;
|
||||
ok(inSpur2 === 0, "eine Selbstauskunft aus Block 5 landet NICHT in der Spur");
|
||||
/* Gegenprobe: sie ist trotzdem gespeichert worden. */
|
||||
const selbstStand = d.prepare(
|
||||
"SELECT stufe FROM entwicklung_stand WHERE person_id = ? AND schluessel = ?")
|
||||
.get(idModi, selbst);
|
||||
ok(selbstStand?.stufe === "hakt",
|
||||
"sie steht aber sehr wohl im Katalog — die Zeile darüber ist keine Ausrede");
|
||||
d.close();
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
4. DIE ÜBERGÄNGE HEISSEN RICHTIG
|
||||
|
||||
Reine Rechnung, ohne Server -- und mit Gegenprobe: Wer nur prüft,
|
||||
dass „vor“ herauskommt, wenn „vor“ gemeint ist, hätte auch mit einer
|
||||
Funktion recht, die IMMER „vor“ sagt.
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 4. Die Übergänge ===");
|
||||
{
|
||||
ok(W.artVon(null, "laeuft", 0) === "neu", "zum ersten Mal gesetzt → neu");
|
||||
ok(W.artVon("hakt", "waechst", 0) === "vor", "hakt → wächst ist ein Schritt nach vorn");
|
||||
ok(W.artVon("waechst", "laeuft", 0) === "vor", "wächst → läuft auch");
|
||||
ok(W.artVon("laeuft", "hakt", 0) === "zurueck", "läuft → hakt ist einer zurück");
|
||||
ok(W.artVon("unklar", "laeuft", 0) === "geklaert",
|
||||
"aus „kann ich nicht sagen“ wird etwas → geklärt, nicht „vor“");
|
||||
ok(W.artVon("laeuft", "unklar", 0) === "offen",
|
||||
"und umgekehrt → offen, nicht „zurück“");
|
||||
ok(W.artVon("laeuft", "laeuft", 0) === "gleich", "derselbe Wert ist keine Bewegung");
|
||||
ok(W.artVon(null, "laeuft", 1) === "anfang",
|
||||
"die Startaufnahme ist der Nullpunkt, nicht ein Ereignis");
|
||||
|
||||
/* GEGENPROBE zur Rangfolge: „kann ich nicht sagen“ hat KEINEN Rang.
|
||||
Bekäme sie einen, würde „ich habe es nicht gesehen“ irgendwann wie
|
||||
ein halbes „da hakt es“ gelesen. */
|
||||
ok(W.artVon("unklar", "hakt", 0) === "geklaert",
|
||||
"auch unklar → hakt ist geklärt und kein Rückschritt");
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
5. DIE MONATSSPALTEN — UND DIE FALLE AM MONATSENDE
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 5. Die Monatsspalten ===");
|
||||
{
|
||||
const s = W.monatsspalten();
|
||||
ok(s.length === W.MONATE, `${s.length} Spalten`);
|
||||
ok(s.join(",") === [...s].sort().join(","), "älteste zuerst");
|
||||
|
||||
/* DIE FALLE: Wer am 31. März einen Monat abzieht, landet in
|
||||
JavaScript wieder im März -- den 31. Februar gibt es nicht, und
|
||||
das Datum rollt über. Eine Spalte fiele aus, und zwar still. */
|
||||
const ende = W.monatsspalten(6, new Date("2026-03-31T12:00:00Z"));
|
||||
ok(ende.join(" ") === "2025-10 2025-11 2025-12 2026-01 2026-02 2026-03",
|
||||
`am 31. März stimmen alle sechs Monate (${ende.join(" ")})`);
|
||||
ok(new Set(ende).size === 6, "kein Monat doppelt, keiner übersprungen");
|
||||
|
||||
/* UND DIE ZWEITE FALLE: toISOString rechnet in UTC. Zwischen
|
||||
Mitternacht und zwei Uhr am Monatsersten liefert das den VORMONAT.
|
||||
Gerechnet wird deshalb in Europe/Berlin. */
|
||||
ok(W.monatVon("2026-04-01T00:30:00+02:00") === "2026-04",
|
||||
"am 1. April um 00:30 Ortszeit ist es April, nicht März");
|
||||
ok(W.monatVon("2026-03-31T23:30:00+02:00") === "2026-03",
|
||||
"und am 31. März um 23:30 noch März");
|
||||
ok(W.monatVon("kaputt") === null, "ein unlesbares Datum ergibt null, nicht „jetzt“");
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
6. DIE AUSWERTUNG — UND DER DRITTE AUSGANG
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 6. Die Auswertung ===");
|
||||
{
|
||||
const d = json(await ruf(`/workspace/api/werdegang/person/${idModi}`, CREW, "GET", { cookie: kDogi }));
|
||||
ok(d.person?.name === "Diene", "die Karte gehört der richtigen Person");
|
||||
ok(d.gesetzt === 2, `zwei Punkte sind beantwortet (${d.gesetzt})`);
|
||||
ok(d.gesamt === PUNKTE.length,
|
||||
`von ${PUNKTE.length} im Katalog (${d.gesamt}) — gezählt, nicht abgeschrieben`);
|
||||
|
||||
/* DER DRITTE AUSGANG. Alles ist heute passiert, also gibt es genau
|
||||
EINEN Monat mit Inhalt. Die Seite muss das SAGEN statt eine Linie
|
||||
zwischen einem Wert und dem Rand zu zeichnen. */
|
||||
ok(d.verlauf_moeglich === false,
|
||||
"mit einem einzigen Monat wird kein Verlauf behauptet");
|
||||
ok(d.monate_mit_inhalt === 1 && d.verlauf_ab === W.VERLAUF_AB,
|
||||
`und die Antwort sagt, warum (${d.monate_mit_inhalt} von ${d.verlauf_ab} nötigen Monaten)`);
|
||||
|
||||
/* KEINE NOTE. Nirgends im ganzen Datenpaket darf eine Punktzahl, ein
|
||||
Prozentwert oder ein Durchschnitt stehen. Geprüft am ROHTEXT --
|
||||
ein Feld, das ich hier namentlich abfrage, müsste ich kennen; ein
|
||||
neues, das jemand später hinzufügt, fiele durch. */
|
||||
const roh = JSON.stringify(d).toLowerCase();
|
||||
ok(!/"(punktzahl|score|prozent|durchschnitt|note|rang|platz)"/.test(roh),
|
||||
"kein Feld heißt Punktzahl, Prozent, Durchschnitt, Note oder Rang");
|
||||
ok(!/anlass/.test(roh), "und der Anlass kommt nicht mit");
|
||||
ok(!roh.includes("dienstag"),
|
||||
"auch nicht sein Inhalt — die Zeile darüber ist keine Wortklauberei");
|
||||
|
||||
/* KEIN FREMDER NAME. Auf einer Karte steht genau ein Mensch. */
|
||||
ok(!roh.includes("rieke") && !roh.includes("tili"),
|
||||
"auf der Karte steht kein anderer Name");
|
||||
|
||||
ok(typeof d.satz === "string" && d.satz.length > 20,
|
||||
"ein Satz fasst die Karte zusammen");
|
||||
ok(Array.isArray(d.texte), "und es gibt Texte dazu");
|
||||
const arten = d.texte.map((t) => t.art);
|
||||
ok(arten.includes("offen"),
|
||||
"darunter der Hinweis, worüber die Auswertung schweigt");
|
||||
/* DIE REIHENFOLGE IST DIE AUSSAGE: Was sich bewegt hat und was trägt,
|
||||
steht VOR dem, was liegt. Rückmeldung, die auf Defizite zeigt,
|
||||
senkt die Leistung messbar. */
|
||||
const iVor = arten.indexOf("vor"), iLiegt = arten.indexOf("liegt");
|
||||
ok(iLiegt === -1 || iVor === -1 || iVor < iLiegt,
|
||||
"und was sich bewegt hat steht vor dem, was liegt");
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
7. ZWEI MONATE — JETZT ENTSTEHT EIN VERLAUF
|
||||
|
||||
GEGENPROBE zu Abschnitt 6: Ohne sie wäre „kein Verlauf möglich“ nur
|
||||
deshalb grün, weil die Funktion vielleicht NIE einen Verlauf sieht.
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 7. Zwei Monate ===");
|
||||
{
|
||||
/* Eine Zeile mit einem Datum aus dem Vormonat -- von Hand in die
|
||||
Spur, weil man die Uhr nicht vorstellen kann. Der Weg dorthin ist
|
||||
derselbe, den die Anwendung benutzt. */
|
||||
const d = oeffne();
|
||||
const vormonat = new Date();
|
||||
vormonat.setDate(15);
|
||||
vormonat.setMonth(vormonat.getMonth() - 1);
|
||||
const p3 = PUNKTE[2].schluessel;
|
||||
d.prepare(`INSERT INTO entwicklung_lauf
|
||||
(person_id, schluessel, von_id, stufe, vorher, nachgetragen, am)
|
||||
VALUES (?,?,?,?,NULL,0,?)`)
|
||||
.run(idModi, p3, idDogi, "hakt", vormonat.toISOString());
|
||||
d.prepare(`INSERT INTO entwicklung_stand
|
||||
(person_id, schluessel, von_id, stufe, anlass, am)
|
||||
VALUES (?,?,?,?,NULL,?)`)
|
||||
.run(idModi, p3, idDogi, "hakt", vormonat.toISOString());
|
||||
d.close();
|
||||
|
||||
const k = json(await ruf(`/workspace/api/werdegang/person/${idModi}`, CREW, "GET", { cookie: kDogi }));
|
||||
ok(k.verlauf_moeglich === true, "mit zwei Monaten gibt es einen Verlauf");
|
||||
ok(k.spalten.length === W.MONATE, `${k.spalten.length} Monatsspalten`);
|
||||
ok(k.streifen.length === 3, `drei Punkte im Streifen (${k.streifen.length})`);
|
||||
|
||||
/* FORTGESCHRIEBEN, NICHT LÜCKENHAFT: Was im Vormonat galt, gilt auch
|
||||
diesen Monat -- niemand hat es geändert. Eine leere Zelle hieße
|
||||
„keine Einschätzung“, und das wäre falsch. */
|
||||
const zeile = k.streifen.find((z) => z.schluessel === p3);
|
||||
const letzte = zeile.zellen.at(-1);
|
||||
ok(letzte.stufe === "hakt",
|
||||
"eine Einschätzung aus dem Vormonat gilt auch diesen Monat weiter");
|
||||
ok(zeile.zellen.filter((c) => c.stufe).length === 2,
|
||||
"davor steht nichts — vor der ersten Einschätzung gab es keine");
|
||||
|
||||
/* DIE BEWEGUNG ZÄHLT DAS RICHTIGE. */
|
||||
const summe = (f) => k.bewegung.reduce((s, m) => s + m[f], 0);
|
||||
ok(summe("neu") === 3, `drei Punkte wurden zum ersten Mal gesetzt (${summe("neu")})`);
|
||||
ok(summe("vor") === 1, `einer ging nach vorn (${summe("vor")})`);
|
||||
ok(summe("zurueck") === 0, `keiner zurück (${summe("zurueck")})`);
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
8. DIE STARTAUFNAHME ZÄHLT NICHT ALS BEWEGUNG
|
||||
|
||||
Der Tag, an dem die Spur entstand, war kein Tag, an dem 43 Dinge
|
||||
gleichzeitig passiert sind.
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 8. Die Startaufnahme ===");
|
||||
{
|
||||
const d = oeffne();
|
||||
const p4 = PUNKTE[3].schluessel;
|
||||
const alt = new Date(); alt.setDate(15); alt.setMonth(alt.getMonth() - 2);
|
||||
d.prepare(`INSERT INTO entwicklung_lauf
|
||||
(person_id, schluessel, von_id, stufe, vorher, nachgetragen, am)
|
||||
VALUES (?,?,?,?,NULL,1,?)`)
|
||||
.run(idHand, p4, idDogi, "laeuft", alt.toISOString());
|
||||
d.prepare(`INSERT INTO entwicklung_stand
|
||||
(person_id, schluessel, von_id, stufe, anlass, am)
|
||||
VALUES (?,?,?,?,NULL,?)`)
|
||||
.run(idHand, p4, idDogi, "laeuft", alt.toISOString());
|
||||
d.close();
|
||||
|
||||
const k = json(await ruf(`/workspace/api/werdegang/person/${idHand}`, CREW, "GET", { cookie: kDogi }));
|
||||
const summe = (f) => k.bewegung.reduce((s, m) => s + m[f], 0);
|
||||
ok(summe("neu") + summe("vor") + summe("zurueck") + summe("geklaert") === 0,
|
||||
"eine nachgetragene Zeile ist keine Bewegung");
|
||||
/* Gegenprobe: sie ist trotzdem da -- im Streifen. Sonst wäre die
|
||||
Zeile oben nur deshalb grün, weil gar nichts gespeichert wurde. */
|
||||
ok(k.streifen.length === 1 && k.streifen[0].zellen.some((c) => c.stufe === "laeuft"),
|
||||
"im Streifen steht sie sehr wohl");
|
||||
ok(/nichts bewegt/.test(k.satz),
|
||||
`und der Satz sagt es ehrlich: ${(k.satz || "").slice(0, 60)}…`);
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
9. DIE EIGENE SICHT — OHNE NAMEN, OHNE ANLASS, OHNE STREIFEN
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 9. Die eigene Sicht ===");
|
||||
{
|
||||
const m = json(await ruf("/workspace/api/werdegang/mich", CREW, "GET", { cookie: kModi }));
|
||||
ok(m.hat_karte === true, "ein Modi hat eine eigene Karte");
|
||||
ok(Array.isArray(m.bewegung) && m.bewegung.length === W.MONATE,
|
||||
"und sieht seine Bewegung über dieselben Monate");
|
||||
ok(m.streifen === undefined,
|
||||
"aber KEINEN Streifen — welcher Punkt wackelte, sagt ihm nur ein Mensch");
|
||||
const roh = JSON.stringify(m).toLowerCase();
|
||||
ok(!roh.includes("filipe") && !roh.includes("rieke"),
|
||||
"kein Name eines Beurteilers");
|
||||
ok(!roh.includes("dienstag"), "kein Anlasstext");
|
||||
ok(/frag/i.test(m.hinweis || ""),
|
||||
"stattdessen der Hinweis, danach zu fragen");
|
||||
|
||||
/* Seine eigene Antwort aus Block 5 darf hier nicht auftauchen -- sie
|
||||
hat er über SICH gegeben, nicht jemand über ihn. */
|
||||
ok(m.bewegung.reduce((s, x) => s + x.neu, 0) === 3,
|
||||
"gezählt werden nur die Einschätzungen anderer");
|
||||
|
||||
/* GEGENPROBE: DogFather hat keine Karte -- und bekommt einen Satz
|
||||
statt einer leeren Seite. */
|
||||
const beiDogi = json(await ruf("/workspace/api/werdegang/mich", CREW, "GET", { cookie: kDogi }));
|
||||
ok(beiDogi.hat_karte === false, "für DogFather wird keine Karte geführt");
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
10. DIE LISTE VERGLEICHT NIEMANDEN
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 10. Die Liste ===");
|
||||
{
|
||||
const l = json(await ruf("/workspace/api/werdegang/liste", CREW, "GET", { cookie: kDogi }));
|
||||
ok(l.liste.length === 2, `zwei Karten — die rechte Hand und der Modi (${l.liste.length})`);
|
||||
/* GEGENPROBE: Ein Creator steht NICHT drin. Für die Agentur wird
|
||||
keine Entwicklungskarte geführt. */
|
||||
ok(!l.liste.some((p) => p.name === "Tili"), "ein Creator steht nicht darin");
|
||||
ok(!l.liste.some((p) => p.name === "Filipe"), "DogFather auch nicht");
|
||||
|
||||
/* NACH NAMEN SORTIERT, NICHT NACH AKTIVITÄT. Eine Liste, die die mit
|
||||
der meisten Bewegung nach oben stellt, ist eine Rangliste mit einem
|
||||
anderen Namen. */
|
||||
const namen = l.liste.map((p) => p.name);
|
||||
ok(namen.join(",") === [...namen].sort((a, b) => a.localeCompare(b, "de")).join(","),
|
||||
`alphabetisch (${namen.join(", ")})`);
|
||||
ok(/kein vergleich/i.test(l.hinweis || ""),
|
||||
"und die Seite sagt selbst, dass sie niemanden vergleicht");
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
11. IM BROWSER
|
||||
|
||||
Der Teil, den keine Schnittstellenprüfung ersetzt: Zeichnet die Seite
|
||||
wirklich etwas, und bleibt die Konsole still?
|
||||
|
||||
UND DER NEBENBEFUND VOM 19.09.: Bis heute bekamen elf Seiten von
|
||||
Team Dogi KEINE Kachelfarbe, weil `zuSeite()` nur die Agenturliste
|
||||
kennt. Hier wird nachgemessen, dass diese Seite ihre bekommt.
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 11. Im Browser ===");
|
||||
{
|
||||
const { chromium } = await import(
|
||||
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||||
const browser = await chromium.launch();
|
||||
const kontext = await browser.newContext({ viewport: { width: 1280, height: 1000 } });
|
||||
const seite = await kontext.newPage();
|
||||
const konsole = [];
|
||||
seite.on("console", (m) => { if (m.type() === "error" && !/favicon|ERR_/i.test(m.text())) konsole.push(m.text()); });
|
||||
seite.on("pageerror", (e) => konsole.push("Absturz: " + e.message));
|
||||
|
||||
await kontext.addCookies(kDogi.split("; ").filter(Boolean).map((teil) => {
|
||||
const i = teil.indexOf("=");
|
||||
return { name: teil.slice(0, i), value: teil.slice(i + 1), domain: "127.0.0.1", path: "/" };
|
||||
}));
|
||||
|
||||
await seite.goto(`http://127.0.0.1:${PORT}/workspace/werdegang.html`, { waitUntil: "networkidle" });
|
||||
await seite.waitForTimeout(900);
|
||||
|
||||
const liste = await seite.evaluate(() => ({
|
||||
karten: document.querySelectorAll('.w-karte').length,
|
||||
hinweis: document.getElementById('liste-hinweis')?.textContent || '',
|
||||
ton: document.body.dataset.ton || null,
|
||||
buehne: document.body.dataset.buehne || null,
|
||||
zeichen: !!document.querySelector('.kopf-zeichen'),
|
||||
}));
|
||||
ok(liste.karten === 2, `zwei Karten stehen auf der Seite (${liste.karten})`);
|
||||
ok(/kein vergleich/i.test(liste.hinweis), "der Hinweis steht sichtbar darüber");
|
||||
ok(liste.ton === "40",
|
||||
`die Seite trägt die Farbe ihrer Kachel (data-ton=${liste.ton}) — das ging bis heute nur auf den Agenturseiten`);
|
||||
ok(liste.buehne === "wald", `und ihre Bühne (${liste.buehne})`);
|
||||
ok(liste.zeichen, "das Zeichen der Kachel steht im Kopf");
|
||||
|
||||
await seite.locator('.w-karte').first().click();
|
||||
await seite.waitForTimeout(700);
|
||||
|
||||
const karte = await seite.evaluate(() => ({
|
||||
name: document.getElementById('karte-name')?.textContent || '',
|
||||
balken: document.querySelectorAll('.w-bewegung__balken').length,
|
||||
zellen: document.querySelectorAll('.w-streifen__zelle').length,
|
||||
zeichenInZellen: [...document.querySelectorAll('.w-streifen__zelle')]
|
||||
.every((z) => (z.textContent || '').trim().length > 0),
|
||||
legende: document.querySelectorAll('.w-legende__punkt').length,
|
||||
texte: document.querySelectorAll('.w-text').length,
|
||||
breiter: document.documentElement.scrollWidth > document.documentElement.clientWidth,
|
||||
}));
|
||||
ok(/Diene|Rieke/.test(karte.name), `eine Karte öffnet sich (${karte.name})`);
|
||||
ok(karte.zellen > 0, `der Streifen ist gezeichnet (${karte.zellen} Zellen)`);
|
||||
/* DIE FARBE IST NIE DIE AUSKUNFT. Jede Zelle trägt ihr Zeichen als
|
||||
Text -- wer Farben schlecht unterscheidet, liest dasselbe. */
|
||||
ok(karte.zeichenInZellen, "jede Zelle trägt ihr Zeichen, nicht nur ihre Farbe");
|
||||
ok(karte.legende === 2, `unter dem Diagramm steht eine Legende (${karte.legende} Einträge)`);
|
||||
ok(karte.texte > 0, `und die Texte dazu (${karte.texte})`);
|
||||
ok(!karte.breiter, "nichts steht über den rechten Rand hinaus");
|
||||
|
||||
/* HANDY. Zwölf Seiten hatten am 06.09. genau hier ihren Fehler: ein
|
||||
Knopf lag über einem anderen, weil eine feste Schwelle nicht mehr
|
||||
passte. */
|
||||
await seite.setViewportSize({ width: 412, height: 915 });
|
||||
await seite.waitForTimeout(500);
|
||||
const handy = await seite.evaluate(() => ({
|
||||
breiter: document.documentElement.scrollWidth > document.documentElement.clientWidth + 1,
|
||||
balkenSichtbar: [...document.querySelectorAll('.w-bewegung__balken')]
|
||||
.every((b) => b.getBoundingClientRect().width >= 8),
|
||||
}));
|
||||
ok(!handy.breiter, "auf 412 px steht nichts über den Rand hinaus");
|
||||
ok(handy.balkenSichtbar, "und die Balken sind noch breit genug, um sie zu sehen");
|
||||
|
||||
if (process.env.SCHIRM) {
|
||||
await seite.setViewportSize({ width: 1280, height: 1100 });
|
||||
await seite.waitForTimeout(400);
|
||||
await seite.screenshot({
|
||||
path: "C:/Users/qciga/Documents/Obelix/DogiHompage/pruef-werdegang.png",
|
||||
fullPage: true,
|
||||
});
|
||||
}
|
||||
ok(konsole.length === 0,
|
||||
konsole.length ? "Fehler im Browser: " + konsole[0].slice(0, 120) : "keine Fehler im Browser");
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
12. DIE BEIDEN ROLLENLISTEN DÜRFEN NICHT AUSEINANDERLAUFEN
|
||||
======================================================================= */
|
||||
melde("");
|
||||
melde("=== 12. Was zusammengehört ===");
|
||||
{
|
||||
const hier = W.KARTEN_ROLLEN.join(",");
|
||||
const quelle = readFileSync(
|
||||
new URL("./workspace-entwicklung.js", import.meta.url), "utf8");
|
||||
const dort = (quelle.match(/const KARTEN_ROLLEN = \[([^\]]+)\]/) || [])[1] || "";
|
||||
const sauber = dort.replace(/["\s]/g, "");
|
||||
ok(sauber === hier,
|
||||
`dieselben Rollen in beiden Dateien (${hier} / ${sauber})`);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
melde("");
|
||||
melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
|
||||
melde(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG");
|
||||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Aufraeumrest */ }
|
||||
process.exit(fehler ? 1 : 0);
|
||||
@@ -345,6 +345,21 @@ export const SEITEN = {
|
||||
"/workspace/befinden.html": ["admin", "hand", "modi"],
|
||||
"/workspace/entwicklung.html": ["admin", "hand", "modi"],
|
||||
|
||||
/* DIE AUSWERTUNG (19.09.2026) -- Kachel "Entwicklung".
|
||||
|
||||
MIT "modi", obwohl die Liste aller Karten nur die Leitung sieht.
|
||||
Die Seite hat zwei Gesichter: Wer das Team fuehrt, sieht die
|
||||
Karten; wer dazugehoert, sieht ausschliesslich die Bewegung in der
|
||||
EIGENEN -- ohne Namen, ohne Anlass, ohne wer-hat-was.
|
||||
|
||||
Dass die Person ihren eigenen Fortschritt sieht, ist kein
|
||||
Zugestaendnis, sondern der Punkt: Sichtbarer Fortschritt ist nach
|
||||
der Forschung der staerkste einzelne Treiber guter Arbeit. Die
|
||||
Schranke dahinter sitzt in der Route (`/werdegang/liste` und
|
||||
`/werdegang/person/:id` antworten einem Modi mit 404), nicht hier.
|
||||
Das hier ist der Riegel davor, nicht statt. */
|
||||
"/workspace/werdegang.html": ["admin", "hand", "modi"],
|
||||
|
||||
/* TALENTE. Hier stehen Namen von Menschen, die nichts davon wissen --
|
||||
ausdruecklich OHNE "modi": Wer beobachtet wird, ohne es zu wissen,
|
||||
hat ein Recht darauf, dass der Kreis klein bleibt. */
|
||||
|
||||
+161
-22
@@ -13,7 +13,7 @@
|
||||
---------------------------------------------------------------------
|
||||
EINE EIGENE TABELLE — UND WARUM DER PLAN DARIN FALSCH LAG
|
||||
|
||||
Der Plan im Vault sagt: „zwei Spalten, keine neue Tabelle" — der
|
||||
Der Plan im Vault sagt: „zwei Spalten, keine neue Tabelle“ — der
|
||||
Stand solle in `punkt_stand` liegen, derselben Tabelle wie die
|
||||
Checkliste. Nachgemessen geht das NICHT:
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
---------------------------------------------------------------------
|
||||
DREI RIEGEL, DIE VON ANFANG AN STEHEN
|
||||
|
||||
1. BLOCK 5 GEHOERT DER PERSON. „Wie geht es dir damit?" beantwortet
|
||||
1. BLOCK 5 GEHOERT DER PERSON. „Wie geht es dir damit?“ beantwortet
|
||||
nur sie selbst — lesend UND schreibend. Auch DogFather nicht. Was
|
||||
nach aussen dringt, ist eine Ampel ohne Namen.
|
||||
|
||||
@@ -50,10 +50,10 @@
|
||||
UND EINE ABWEICHUNG VOM PLAN, DIE FILIPES SATZ GESCHULDET IST
|
||||
|
||||
Der Plan verlangt einen Anlass (die drei SBI-Felder) bei JEDEM Klick
|
||||
ausser „kann ich nicht sagen". Bei 28 Punkten je Person waeren das 28
|
||||
Textfelder — und „so einfach wie moeglich" war die andere Haelfte des
|
||||
ausser „kann ich nicht sagen“. Bei 28 Punkten je Person waeren das 28
|
||||
Textfelder — und „so einfach wie moeglich“ war die andere Haelfte des
|
||||
Auftrags. Pflicht ist der Anlass deshalb dort, wo er etwas
|
||||
entscheidet: bei **„Da hakt es"**. Bei „Laeuft" und „Waechst gerade"
|
||||
entscheidet: bei **„Da hakt es“**. Bei „Laeuft“ und „Waechst gerade“
|
||||
steht dasselbe Feld da, freiwillig — wer etwas Konkretes gesehen hat,
|
||||
schreibt es hin, und beim Gespraech steht es da.
|
||||
===================================================================== */
|
||||
@@ -171,6 +171,55 @@ export function tabellen() {
|
||||
CREATE INDEX IF NOT EXISTS idx_entwicklung_person
|
||||
ON entwicklung_stand (person_id, am);
|
||||
|
||||
/* =================================================================
|
||||
DIE SPUR (19.09.2026) — jede Aenderung, angehaengt statt ueberschrieben.
|
||||
|
||||
Die Tabelle darueber hat den Schluessel (person, punkt, beurteiler)
|
||||
und wird bei jeder Aenderung UEBERSCHRIEBEN. Das ist dort richtig:
|
||||
Eine Beobachtung ueber einen Menschen soll aktuell sein, nicht
|
||||
historisch.
|
||||
|
||||
Nur: Damit existierte nichts, woraus man einen Verlauf zeichnen
|
||||
koennte. Am 19.09. nachgemessen: 43 Zeilen Stand, NULL Zeilen
|
||||
Geschichte. Ein Diagramm ueber einen einzigen Zeitpunkt ist kein
|
||||
Diagramm, sondern ein Balken.
|
||||
|
||||
SIE STEHT HIER UND NICHT IN workspace-werdegang.js, obwohl sie
|
||||
dort ausgewertet wird: Eine Tabelle gehoert zu dem Modul, das sie
|
||||
SCHREIBT. Andersherum gaebe es einen Ring zwischen zwei Modulen,
|
||||
und Ringe halten in JavaScript genau so lange, bis jemand eine
|
||||
Konstante auf oberster Ebene benutzt.
|
||||
|
||||
KEIN ANLASS, NIE. Neben jedem "da hakt es" steht in der Tabelle
|
||||
oben ein Satz, woran man es gemerkt hat. Der ist fuer ein
|
||||
Gespraech aufgeschrieben. Eine CHRONIK dieser Saetze waere das
|
||||
Belastendste, was dieses Haus speichern kann -- jede laengst
|
||||
geklaerte Beobachtung bliebe auf Dauer lesbar. Dasselbe Prinzip
|
||||
wie beim Befinden-Verlauf.
|
||||
|
||||
VORHER steht dabei, obwohl es aus der vorigen Zeile abzuleiten
|
||||
waere: Fuer die Startaufnahme (siehe unten) gibt es kein
|
||||
"vorher", und NULL sagt das, waehrend eine abgeleitete Kette es
|
||||
verschweigen wuerde.
|
||||
|
||||
NACHGETRAGEN unterscheidet beobachtete Uebergaenge von der
|
||||
Startaufnahme. Ohne dieses Feld saehe der Tag, an dem die Spur
|
||||
entstand, wie ein Tag aus, an dem 43 Dinge gleichzeitig passiert
|
||||
sind.
|
||||
============================================================= */
|
||||
CREATE TABLE IF NOT EXISTS entwicklung_lauf (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
person_id INTEGER NOT NULL REFERENCES personen(id) ON DELETE CASCADE,
|
||||
schluessel TEXT NOT NULL,
|
||||
von_id INTEGER NOT NULL REFERENCES personen(id) ON DELETE CASCADE,
|
||||
stufe TEXT NOT NULL CHECK (stufe IN ('laeuft','waechst','hakt','unklar')),
|
||||
vorher TEXT,
|
||||
nachgetragen INTEGER NOT NULL DEFAULT 0,
|
||||
am TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_lauf_person
|
||||
ON entwicklung_lauf (person_id, am);
|
||||
|
||||
/* Ein angeklicktes Merkmal an einem Kandidaten.
|
||||
|
||||
KEIN von_id IM SCHLUESSEL, anders als oben -- und das ist
|
||||
@@ -293,7 +342,7 @@ export function tabellen() {
|
||||
liegen -- und die Standzeit an der Karte sagt zwar, DASS etwas
|
||||
liegt, aber nicht, wer es aufheben sollte.
|
||||
|
||||
`zustaendig_seit` kommt mit: „seit gestern uebernommen" und „seit
|
||||
`zustaendig_seit` kommt mit: „seit gestern uebernommen“ und „seit
|
||||
drei Wochen uebernommen" sind zwei verschiedene Auskuenfte, und
|
||||
die zweite ist die, die man sehen will. */
|
||||
["zustaendig_id", "INTEGER"],
|
||||
@@ -307,9 +356,83 @@ export function tabellen() {
|
||||
}
|
||||
} catch (f) { console.error(`[talente] Spalte '${spalte}':`, f?.message); }
|
||||
}
|
||||
startaufnahme();
|
||||
bereit = true;
|
||||
}
|
||||
|
||||
/** Die Spur einmalig aus dem heutigen Stand fuellen (19.09.2026).
|
||||
*
|
||||
* WARUM UEBERHAUPT: Ohne sie waere die Auswertungsseite am ersten Tag
|
||||
* leer, und zwar fuer Monate -- bis jemand genug Einschaetzungen
|
||||
* geaendert hat. Eine Seite, die nach dem Bauen leer bleibt, sieht aus
|
||||
* wie ein Fehler und wird kein zweites Mal geoeffnet.
|
||||
*
|
||||
* WARUM DAS EHRLICH IST: Jede Zeile in `entwicklung_stand` traegt ihr
|
||||
* echtes `am` -- den Tag, an dem jemand wirklich geklickt hat. Es wird
|
||||
* nichts erfunden, nur uebernommen, was ohnehin dasteht. Was sich
|
||||
* NICHT rekonstruieren laesst -- ob vorher schon etwas anderes
|
||||
* dastand --, bleibt NULL und traegt `nachgetragen = 1`. Die
|
||||
* Auswertung zaehlt diese Zeilen ausdruecklich NICHT als Bewegung:
|
||||
* Der Tag der Startaufnahme ist der Nullpunkt, nicht ein Tag, an dem
|
||||
* etwas passiert ist.
|
||||
*
|
||||
* GENAU EINMAL: nur wenn die Spur leer ist. Ein zweiter Lauf traege
|
||||
* jeden Stand ein zweites Mal ein, und ab da zaehlte jede Bewegung
|
||||
* doppelt.
|
||||
*
|
||||
* UND SIE DARF NIE DEN SERVER AUFHALTEN. Schlaegt sie fehl, ist die
|
||||
* Auswertung duenn -- das ist aergerlich. Wuerde dadurch `tabellen()`
|
||||
* scheitern, waere die ganze Entwicklungsseite mit 503 tot, und das
|
||||
* waere ungleich schlimmer. */
|
||||
function startaufnahme() {
|
||||
try {
|
||||
const da = db().prepare("SELECT COUNT(*) AS n FROM entwicklung_lauf").get();
|
||||
if (da.n > 0) return;
|
||||
|
||||
const staende = db().prepare(
|
||||
`SELECT person_id, schluessel, von_id, stufe, am
|
||||
FROM entwicklung_stand ORDER BY am`).all();
|
||||
if (!staende.length) return;
|
||||
|
||||
const rein = db().prepare(
|
||||
`INSERT INTO entwicklung_lauf
|
||||
(person_id, schluessel, von_id, stufe, vorher, nachgetragen, am)
|
||||
VALUES (?,?,?,?,NULL,1,?)`);
|
||||
let n = 0;
|
||||
for (const s of staende) {
|
||||
/* DIE SELBSTAUSKUNFT FAELLT HERAUS. Block 5 ("Wie geht es dir
|
||||
damit?") hat mit dem Befinden-Verlauf eine eigene Tabelle, die
|
||||
niemand sonst liest. Hier waere sie eine Aussage, die ein
|
||||
Mensch ueber sich selbst gemacht hat -- und die ploetzlich in
|
||||
einer Auswertung fuer die Leitung stuende. */
|
||||
if (!ENTWICKLUNG_FREMD.some((x) => x.schluessel === s.schluessel)) continue;
|
||||
rein.run(s.person_id, s.schluessel, s.von_id, s.stufe, s.am);
|
||||
n++;
|
||||
}
|
||||
if (n) console.log(`[entwicklung] Startaufnahme: ${n} Staende in die Spur uebernommen.`);
|
||||
} catch (f) {
|
||||
console.error("[entwicklung] Startaufnahme:", f?.message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Eine Aenderung in der Spur festhalten.
|
||||
*
|
||||
* SIE DARF NIE DEN KLICK VERHINDERN. Geht hier etwas schief, ist die
|
||||
* Auswertung lueckenhaft. Wuerde dadurch die Einschaetzung nicht
|
||||
* gespeichert, waere die Arbeit weg. Deshalb faengt sie ihren Fehler
|
||||
* selbst und meldet ihn nur ins Protokoll der Konsole. */
|
||||
function spurLegen({ personId, schluessel, vonId, stufe, vorher }) {
|
||||
try {
|
||||
db().prepare(
|
||||
`INSERT INTO entwicklung_lauf
|
||||
(person_id, schluessel, von_id, stufe, vorher, nachgetragen, am)
|
||||
VALUES (?,?,?,?,?,0,?)`)
|
||||
.run(personId, schluessel, vonId, stufe, vorher ?? null, jetzt());
|
||||
} catch (f) {
|
||||
console.error("[entwicklung] Spur:", f?.message);
|
||||
}
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIE HUERDEN
|
||||
===================================================================== */
|
||||
@@ -498,7 +621,7 @@ entwicklungRouter.get("/workspace/api/entwicklung/person/:id", nurLeitung, (req,
|
||||
aufgefallen ist.
|
||||
|
||||
Das ist keine Nebensaechlichkeit. Beim ersten Beurteilen ist
|
||||
genau das die Frage: „Woher kommt der eigentlich?" -- und wer sie
|
||||
genau das die Frage: „Woher kommt der eigentlich?“ -- und wer sie
|
||||
nicht beantwortet bekommt, faengt bei null an, obwohl vier Wochen
|
||||
Beobachtung vorliegen.
|
||||
|
||||
@@ -556,7 +679,7 @@ entwicklungRouter.get("/workspace/api/entwicklung/person/:id", nurLeitung, (req,
|
||||
Die Quellen zur laufenden Entwicklungsbegleitung sagen übereinstimmend
|
||||
zweierlei: weg von der Bewertung, hin zum Gespräch -- und ein
|
||||
Entwicklungsplan wirkt dann, wenn er an einer ECHTEN Aufgabe hängt.
|
||||
Nicht „daran arbeiten wir", sondern etwas, das auf dem Aufgabenbrett
|
||||
Nicht „daran arbeiten wir“, sondern etwas, das auf dem Aufgabenbrett
|
||||
steht, einen Verantwortlichen hat und irgendwann erledigt ist.
|
||||
|
||||
Genau dieses Muster gibt es im Haus schon: Aus einer Idee auf einem
|
||||
@@ -610,8 +733,8 @@ entwicklungRouter.post("/workspace/api/entwicklung/person/:id/:schluessel/schrit
|
||||
const punkt = ENTWICKLUNG_FREMD.find((x) => x.schluessel === schluessel);
|
||||
if (!punkt) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
|
||||
/* NUR AUS EINEM „DA HAKT ES" WIRD EIN SCHRITT -- und nur aus dem
|
||||
EIGENEN. Aus „läuft" einen Schritt zu machen hieße, jemandem
|
||||
/* NUR AUS EINEM „DA HAKT ES“ WIRD EIN SCHRITT -- und nur aus dem
|
||||
EIGENEN. Aus „läuft“ einen Schritt zu machen hieße, jemandem
|
||||
Arbeit zu geben für etwas, das gut läuft; aus der Beobachtung
|
||||
eines anderen hieße, in seinem Namen zu handeln. */
|
||||
const stand = db().prepare(
|
||||
@@ -682,7 +805,7 @@ entwicklungRouter.put("/workspace/api/entwicklung/person/:id/:schluessel",
|
||||
if (!STUFEN_SATZ.has(stufe)) return res.status(400).json({ fehler: "unbekannte_stufe" });
|
||||
|
||||
/* DER ANLASS IST PFLICHT, WO ER ETWAS ENTSCHEIDET.
|
||||
„Da hakt es" ohne Beispiel ist eine Meinung; mit Beispiel ist
|
||||
„Da hakt es“ ohne Beispiel ist eine Meinung; mit Beispiel ist
|
||||
es ein Gespraech. Bei den anderen beiden steht dasselbe Feld
|
||||
freiwillig da. */
|
||||
if (stufe === "hakt" && anlass.length < 10) {
|
||||
@@ -692,6 +815,15 @@ entwicklungRouter.put("/workspace/api/entwicklung/person/:id/:schluessel",
|
||||
});
|
||||
}
|
||||
|
||||
/* WAS VORHER DASTAND -- VOR dem Schreiben gelesen.
|
||||
Danach ist es weg: Die Tabelle ueberschreibt. Genau deshalb
|
||||
gibt es die Spur, und genau deshalb steht diese Zeile hier
|
||||
oben und nicht unten. */
|
||||
const vorher = db().prepare(
|
||||
`SELECT stufe FROM entwicklung_stand
|
||||
WHERE person_id = ? AND schluessel = ? AND von_id = ?`)
|
||||
.get(id, schluessel, req.person.id)?.stufe || null;
|
||||
|
||||
db().prepare(`INSERT INTO entwicklung_stand
|
||||
(person_id, schluessel, von_id, stufe, anlass, am)
|
||||
VALUES (?,?,?,?,?,?)
|
||||
@@ -699,6 +831,13 @@ entwicklungRouter.put("/workspace/api/entwicklung/person/:id/:schluessel",
|
||||
stufe = excluded.stufe, anlass = excluded.anlass, am = excluded.am`)
|
||||
.run(id, schluessel, req.person.id, stufe, anlass || null, jetzt());
|
||||
|
||||
/* ERST SPEICHERN, DANN MITSCHREIBEN. Die Reihenfolge ist die
|
||||
Aussage: Wenn die Spur scheitert, fehlt eine Zeile in einer
|
||||
Auswertung. Wenn das Speichern scheitert, ist die Arbeit weg. */
|
||||
spurLegen({
|
||||
personId: id, schluessel, vonId: req.person.id, stufe, vorher,
|
||||
});
|
||||
|
||||
res.json({ ok: true });
|
||||
} catch (f) {
|
||||
console.error("[entwicklung] Setzen:", f?.message);
|
||||
@@ -851,7 +990,7 @@ entwicklungRouter.get("/workspace/api/entwicklung/meine-karte", (req, res) => {
|
||||
|
||||
/* WAS HIER HERAUSKOMMT UND WAS NICHT.
|
||||
Heraus kommt: wie viele im Team gerade mindestens einen Punkt aus
|
||||
Block 5 auf „da hakt es" stehen haben -- und ab zwei Personen auch
|
||||
Block 5 auf „da hakt es“ stehen haben -- und ab zwei Personen auch
|
||||
das nicht mehr genau, sondern als Spanne.
|
||||
Nicht heraus kommt: wer, welcher Punkt, seit wann.
|
||||
|
||||
@@ -861,7 +1000,7 @@ entwicklungRouter.get("/workspace/api/entwicklung/meine-karte", (req, res) => {
|
||||
daraus niemanden erkennen kann.
|
||||
|
||||
AB WELCHER ZAHL SIE UEBERHAUPT ETWAS SAGT: Bei einem Team von zwei
|
||||
Personen waere „einer hat gerade viel" dasselbe wie ein Name.
|
||||
Personen waere „einer hat gerade viel“ dasselbe wie ein Name.
|
||||
Deshalb schweigt sie unter MINDESTENS_TEAM Personen -- mit Grund,
|
||||
nicht stillschweigend. */
|
||||
const MINDESTENS_TEAM = 3;
|
||||
@@ -908,7 +1047,7 @@ entwicklungRouter.get("/workspace/api/entwicklung/ampel", nurLeitung, (req, res)
|
||||
|
||||
res.json({
|
||||
sagbar: true, team, geantwortet,
|
||||
/* Keine genaue Zahl: „einer" waere bei kleinen Teams wieder ein
|
||||
/* Keine genaue Zahl: „einer“ waere bei kleinen Teams wieder ein
|
||||
Name. Es gibt drei Antworten, und mehr braucht es nicht. */
|
||||
lage: betroffen === 0 ? "ruhig" : betroffen === 1 ? "jemand" : "mehrere",
|
||||
text: betroffen === 0
|
||||
@@ -1180,7 +1319,7 @@ entwicklungRouter.get("/workspace/api/talente/lage", (req, res) => {
|
||||
heraussuchen, Kategorie und Stufe waehlen und uebernehmen.
|
||||
Vier Schritte, die niemand macht, waehrend er gerade jemanden
|
||||
aufnimmt. Genau dieses Muster hat heute schon einmal zu einer
|
||||
Karte auf „Im Team" ohne Menschen darin gefuehrt.
|
||||
Karte auf „Im Team“ ohne Menschen darin gefuehrt.
|
||||
|
||||
ES BLEIBT EIN ANGEBOT, KEINE AUTOMATIK. Filipes Entscheidung
|
||||
bei der Live-Checkliste gilt hier genauso: „vierzehn Aufgaben,
|
||||
@@ -1189,8 +1328,8 @@ entwicklungRouter.get("/workspace/api/talente/lage", (req, res) => {
|
||||
hier nur, WAS es gaebe -- angelegt wird auf Klick, eine
|
||||
Kategorie auf einmal.
|
||||
|
||||
KEIN ROLLENNAME, nirgends. Die Kategorien heissen „chat",
|
||||
„team", „technik" -- aus keiner davon laesst sich schliessen,
|
||||
KEIN ROLLENNAME, nirgends. Die Kategorien heissen „chat“,
|
||||
„team“, „technik“ -- aus keiner davon laesst sich schliessen,
|
||||
fuer wen sie gedacht sind. Wer den Katalog nicht bekommen
|
||||
darf, bekommt `null` und die Seite baut nichts; ein leeres
|
||||
Feld sagt nichts, ein Feld mit sprechendem Namen saehe jeder
|
||||
@@ -1205,7 +1344,7 @@ entwicklungRouter.get("/workspace/api/talente/lage", (req, res) => {
|
||||
/* WAS SIE SCHON HABEN -- je Person, in EINER Abfrage.
|
||||
|
||||
Ohne diese Angabe legt man beim zweiten Hinsehen dasselbe noch
|
||||
einmal an; der Knopf sagt dann „6 Aufgaben" und meint die
|
||||
einmal an; der Knopf sagt dann „6 Aufgaben“ und meint die
|
||||
siebte bis zwoelfte. Der Server verhindert Doppelte nicht --
|
||||
er soll es auch nicht, denn dieselbe Aufgabe ein zweites Mal
|
||||
kann gewollt sein. Sichtbar muss es trotzdem sein. */
|
||||
@@ -1444,7 +1583,7 @@ entwicklungRouter.put("/workspace/api/talente/:id/kuemmern", gleicheHerkunft,
|
||||
} else {
|
||||
/* Noch keine Stufenzeile -- die entsteht erst beim ersten
|
||||
Schritt. Wer sich vorher kuemmern will, soll das koennen:
|
||||
„Ich sehe ihn mir an" ist der haeufigste erste Schritt
|
||||
„Ich sehe ihn mir an“ ist der haeufigste erste Schritt
|
||||
ueberhaupt, und er faellt sonst durch. */
|
||||
db().prepare(`INSERT INTO talent_stufe
|
||||
(eintrag_id, stufe, seit, von_id, zustaendig_id, zustaendig_seit)
|
||||
@@ -1506,7 +1645,7 @@ entwicklungRouter.put("/workspace/api/talente/:id/wieder", gleicheHerkunft,
|
||||
|
||||
/** Buddy und Datum prüfen. Gibt `{ fehlt: [...] }` oder `{ buddyId, schicht }`.
|
||||
*
|
||||
* EINE STELLE, ZWEI AUFRUFER: der Stufenwechsel auf „Probe" und das
|
||||
* EINE STELLE, ZWEI AUFRUFER: der Stufenwechsel auf „Probe“ und das
|
||||
* spaetere Aendern. Zwei Abschriften derselben Regeln waeren zwei
|
||||
* Regeln, und die zweite ist immer die, die etwas durchlaesst. */
|
||||
function uebergangPruefen(koerper, { pflicht }) {
|
||||
@@ -1576,13 +1715,13 @@ entwicklungRouter.put("/workspace/api/talente/:id/stufe", gleicheHerkunft, (req,
|
||||
|
||||
/* AUF DIE PROBE KOMMT NIEMAND OHNE BUDDY UND DATUM.
|
||||
|
||||
In der Stufenbeschreibung stand „fester Buddy" seit dem ersten
|
||||
In der Stufenbeschreibung stand „fester Buddy“ seit dem ersten
|
||||
Tag -- ein Feld dafuer gab es nicht. Ein Versprechen ohne Feld
|
||||
ist eines, an das sich nach zwei Wochen niemand erinnert, und
|
||||
genau an dieser Stelle verlieren Teams die Leute, die sie gerade
|
||||
gewonnen haben.
|
||||
|
||||
EINE ABSAGE MIT GRUND, nicht ein „ging nicht": Die Oberflaeche
|
||||
EINE ABSAGE MIT GRUND, nicht ein „ging nicht“: Die Oberflaeche
|
||||
fragt beides vorher ab, aber die Regel gehoert hierher. Was nur
|
||||
im Browser steht, gilt nur fuer den, der den Browser benutzt. */
|
||||
const p = stufe === "probe"
|
||||
|
||||
@@ -0,0 +1,753 @@
|
||||
/* =====================================================================
|
||||
ENTWICKLUNG — die Auswertung je Person (19.09.2026)
|
||||
|
||||
Filipe, mit dem Bildschirmfoto der alten Kachel:
|
||||
|
||||
„diese kategorie ist ja die aufgaben die ich an die modis habe,
|
||||
also eher aufgabe und dan perfektionierst du eine neue wo
|
||||
diagramme texte und so zu jedem modi gemacht wird und diese
|
||||
kategorie kannst du entwicklung nenen.“
|
||||
|
||||
Die Umbenennung war der erste Teil und ist seit dem 19.09. früh
|
||||
erledigt: Die alte Kachel heißt „Eure Aufgaben“, weil sie das ist —
|
||||
ein Katalog dessen, was ein Modi können soll. Hier kommt der zweite
|
||||
Teil: die Auswertung, die es noch nie gab.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WARUM DIESE DATEI NICHT „entwicklung.js“ HEISST
|
||||
|
||||
`workspace-entwicklung.js` gibt es seit dem 11.09. und es ist der
|
||||
KATALOG. Diese Datei ist die AUSWERTUNG darüber. Zwei Dateien mit
|
||||
fast demselben Namen wären die zuverlässigste Art, in einem halben
|
||||
Jahr die falsche zu ändern.
|
||||
|
||||
Dasselbe gilt für die Seite: `entwicklung.html` ist der Katalog
|
||||
(„Eure Aufgaben“), `werdegang.html` ist diese Auswertung
|
||||
(„Entwicklung“). Dass die Kachel anders heißt als die Datei, ist
|
||||
Absicht und geprüft — `pruef-werdegang` verlangt, dass die Kachel
|
||||
„Entwicklung“ auf `werdegang.html` zeigt und nicht woanders hin.
|
||||
Ein Umbenennen der alten Datei wäre die sauberere Endform, aber es
|
||||
wären zwei Änderungen in einer: Wenn danach etwas klemmt, weiß
|
||||
niemand, welche von beiden es war.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
DAS EIGENTLICHE PROBLEM: ES GAB KEINE VERGANGENHEIT
|
||||
|
||||
`entwicklung_stand` hat den Schlüssel (person, punkt, beurteiler)
|
||||
und wird bei jeder Änderung ÜBERSCHRIEBEN. Das ist dort richtig —
|
||||
eine Beobachtung über einen Menschen soll aktuell sein, nicht
|
||||
historisch. Nur: Damit existiert nichts, woraus man einen Verlauf
|
||||
zeichnen könnte. Am 19.09.2026 nachgemessen standen 43 Zeilen in der
|
||||
Tabelle und NULL Zeilen Geschichte.
|
||||
|
||||
Ein Diagramm über einen einzigen Zeitpunkt ist kein Diagramm,
|
||||
sondern ein Balken. Deshalb legt diese Datei als Erstes an, was
|
||||
gefehlt hat: eine Spur, die mitschreibt, statt zu überschreiben.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WAS HIER AUSDRÜCKLICH NICHT ENTSTEHT
|
||||
|
||||
Keine Punktzahl. Kein Prozentwert. Kein Durchschnitt. Keine
|
||||
Rangliste. Kein Vergleich zwischen zwei Menschen — nirgends, auch
|
||||
nicht versteckt als „Team-Mittelwert“.
|
||||
|
||||
Das ist keine Vorsicht, das ist die Recherche:
|
||||
|
||||
* ÜBERRECHTFERTIGUNG (Deci 1971, Selbstbestimmungstheorie). Wer
|
||||
für etwas, das er gern tut, eine äußere Bewertung bekommt,
|
||||
verliert die innere Motivation. Die Ausnahme — und die ist der
|
||||
ganze Trick — ist Rückmeldung, die KOMPETENZ BESTÄTIGT, ohne eine
|
||||
Zahl daneben.
|
||||
* SOZIALER VERGLEICH. Beurteilungen, die Menschen nebeneinander
|
||||
stellen, erzeugen Neid und senken die Leistung derer, die
|
||||
schlechter dastehen. Ein Balken „Diene 71 %, VanVan 88 %“ wäre
|
||||
genau das, und er wäre nie wieder aus den Köpfen zu bekommen.
|
||||
* SCHWÄCHENFOKUS. Rückmeldung, die auf Defizite zeigt, senkt die
|
||||
Leistung messbar. Deshalb steht hier IMMER zuerst, was sich
|
||||
bewegt hat und was trägt — und erst danach, was liegt.
|
||||
|
||||
Quellen:
|
||||
https://hbr.org/2011/05/the-power-of-small-wins
|
||||
https://www.simplypsychology.org/self-determination-theory.html
|
||||
https://www.profit.co/blog/performance-management/adverse-effects-of-employee-comparison-during-performance-appraisals/
|
||||
https://www.cambridge.org/core/journals/industrial-and-organizational-psychology/article/abs/working-with-social-comparisons-in-the-appraisal-and-management-of-performance/2366D9737F8A543A16E6C884D1E8A5F8
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WAS STATTDESSEN GEZEIGT WIRD: BEWEGUNG
|
||||
|
||||
Amabile und Kramer haben rund 12 000 Tagebucheinträge von 238
|
||||
Menschen ausgewertet. Das Ergebnis, das seitdem „progress principle“
|
||||
heißt: Der stärkste einzelne Treiber guter Arbeit ist SICHTBARER
|
||||
FORTSCHRITT in einer Sache, die einem wichtig ist. Nicht Lob, nicht
|
||||
Belohnung, nicht Druck — und die Schritte dürfen klein sein.
|
||||
|
||||
Deshalb ist die erste Zahl auf dieser Seite nicht, WIE GUT jemand
|
||||
ist, sondern WAS SICH BEWEGT HAT. Und die zweite sagt etwas über
|
||||
UNS: wie alt die Einschätzungen sind. Eine Karte, die seit vier
|
||||
Monaten niemand angefasst hat, ist keine Auskunft über den
|
||||
Menschen — sie ist eine über die, die hinsehen sollten.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
DER ANLASS KOMMT NICHT MIT
|
||||
|
||||
In `entwicklung_stand` steht neben jedem „da hakt es“ ein Satz, woran
|
||||
man es gemerkt hat. Der ist für ein Gespräch aufgeschrieben. Eine
|
||||
CHRONIK dieser Sätze wäre das Belastendste, was dieses Haus
|
||||
speichern könnte — jede alte Beobachtung bliebe auf Dauer lesbar,
|
||||
auch die, die längst geklärt ist.
|
||||
|
||||
Die Spur hier merkt sich deshalb nur: welcher Punkt, welche Stufe,
|
||||
wann, von wem. Kein Anlass, nie. Dasselbe Prinzip wie beim
|
||||
Befinden-Verlauf — dort steht im Protokoll auch nur, DASS eine Runde
|
||||
war.
|
||||
===================================================================== */
|
||||
|
||||
import express from "express";
|
||||
import {
|
||||
db, sitzungLesen, fuehrtTeamDogi, ROLLEN_NAME,
|
||||
} from "./workspace.js";
|
||||
import {
|
||||
ENTWICKLUNG_BLOECKE, ENTWICKLUNG_PUNKTE, ENTWICKLUNG_STUFEN,
|
||||
ENTWICKLUNG_ERWARTUNG, ENTWICKLUNG_FREMD, ENTWICKLUNG_NACH_SCHLUESSEL,
|
||||
} from "./workspace-entwicklungs-punkte.js";
|
||||
/* NUR IN EINE RICHTUNG. Diese Datei liest aus `workspace-entwicklung.js`;
|
||||
jene weiß von dieser nichts. Die Spur-Tabelle steht deshalb DORT, bei
|
||||
`entwicklung_stand`, zu dem sie gehört — nicht hier.
|
||||
|
||||
Der bequeme Weg wäre gewesen, die Tabelle hier anzulegen und von dort
|
||||
ein `spurLegen()` zu importieren. Das ergäbe einen Ring zwischen zwei
|
||||
Modulen, und Ringe funktionieren in JavaScript so lange, bis jemand
|
||||
eine Konstante auf oberster Ebene benutzt statt in einer Funktion.
|
||||
Dann kommt ein Fehler, den niemand mit dem Import in Verbindung
|
||||
bringt. */
|
||||
import {
|
||||
VERBLASST_NACH_TAGEN, tabellen as entwicklungTabellen,
|
||||
} from "./workspace-entwicklung.js";
|
||||
|
||||
export const werdegangRouter = express.Router();
|
||||
|
||||
const TAG = 86400000;
|
||||
|
||||
/** Die Rollen, über die eine Karte geführt wird.
|
||||
*
|
||||
* ABGELEITET WÄRE HIER FALSCH, und das ist die Ausnahme: Es ist
|
||||
* dieselbe Liste wie in `workspace-entwicklung.js`, und sie MUSS
|
||||
* dieselbe sein — eine Auswertung über jemanden, der gar keine Karte
|
||||
* hat, wäre leer. Sie steht trotzdem hier noch einmal, weil ein
|
||||
* Import nur wegen zweier Zeichenketten die beiden Module
|
||||
* aneinanderbindet; `pruef-werdegang` vergleicht sie und wird rot,
|
||||
* wenn sie auseinanderlaufen. */
|
||||
export const KARTEN_ROLLEN = ["hand", "modi"];
|
||||
|
||||
/** Wie viele Monatsspalten der Verlauf zeigt.
|
||||
*
|
||||
* SECHS. Genug, um ein halbes Jahr zu sehen — und wenig genug, dass
|
||||
* die Spalten auf einem Handy noch lesbar sind. Mehr Spalten heißt
|
||||
* nicht mehr Auskunft: Was vor einem Jahr war, ist für die Frage
|
||||
* „bewegt sich etwas?“ ohne Belang. */
|
||||
export const MONATE = 6;
|
||||
|
||||
/** Ab wann ein „läuft“ als getragen gilt und ausdrücklich gesagt wird.
|
||||
*
|
||||
* 60 TAGE. Anerkennung wirkt nur, wenn sie sich auf etwas Bestimmtes
|
||||
* bezieht — „du machst das gut“ ist Höflichkeit, „das steht seit zwei
|
||||
* Monaten und ist nie gekippt“ ist eine Beobachtung. Unter zwei
|
||||
* Monaten wäre es ein Zwischenstand, kein Befund. */
|
||||
export const TRAEGT_AB_TAGEN = 60;
|
||||
|
||||
/** Ab wann ein „da hakt es“ von selbst zur Sprache kommt.
|
||||
*
|
||||
* 30 TAGE OHNE BEWEGUNG. Die Hausregel dazu steht im Plan vom 11.09.:
|
||||
* „Ein ‚Da hakt es', das nirgends hinführt, ist eine Notiz über einen
|
||||
* Menschen — und die will hier niemand sammeln.“ */
|
||||
export const LIEGT_AB_TAGEN = 30;
|
||||
|
||||
/** Ab wie vielen Monatsspalten mit Inhalt überhaupt etwas gezeichnet wird.
|
||||
*
|
||||
* ZWEI. Ein Punkt ist kein Verlauf. Darunter sagt die Seite das —
|
||||
* sie zeigt keine Linie zwischen einem einzigen Wert und dem Rand.
|
||||
* Das ist der dritte Ausgang: nicht in Ordnung / in Ordnung / KONNTE
|
||||
* NOCH NICHT NACHSEHEN, mit Grund. */
|
||||
export const VERLAUF_AB = 2;
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
DIE SPUR LIEGT NEBENAN
|
||||
|
||||
`entwicklung_lauf` wird in `workspace-entwicklung.js` angelegt und
|
||||
dort auch geschrieben — bei `entwicklung_stand`, zu dem sie gehört.
|
||||
Diese Datei liest sie nur. Ein `tabellen()` hier wäre eine zweite
|
||||
Stelle, an der dieselbe Tabelle entstehen kann, und die zweite ist
|
||||
immer die, die eine Spalte vergisst.
|
||||
------------------------------------------------------------------- */
|
||||
function tabellen() { entwicklungTabellen(); }
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
RECHNEN
|
||||
------------------------------------------------------------------- */
|
||||
|
||||
/** Die Rangfolge der drei aussagekräftigen Stufen.
|
||||
*
|
||||
* „Kann ich nicht sagen“ hat KEINEN Rang und bekommt auch keinen. Es
|
||||
* ist keine schlechtere Antwort als „läuft“, sondern eine andere Art
|
||||
* von Antwort — sie sagt etwas über den Beurteiler, nicht über den
|
||||
* Beurteilten. Ihr eine Zahl zu geben hieße, sie in dieselbe Reihe zu
|
||||
* stellen, und ab da würde „ich habe es nicht gesehen“ wie ein
|
||||
* halbes „da hakt es“ gelesen. */
|
||||
const RANG = { hakt: 0, waechst: 1, laeuft: 2 };
|
||||
|
||||
/** Wie ein Übergang zu nennen ist. Gerechnet, nicht gespeichert. */
|
||||
export function artVon(vorher, stufe, nachgetragen) {
|
||||
if (nachgetragen) return "anfang";
|
||||
if (!vorher) return "neu";
|
||||
if (vorher === stufe) return "gleich";
|
||||
if (vorher === "unklar") return "geklaert";
|
||||
if (stufe === "unklar") return "offen";
|
||||
const a = RANG[vorher], b = RANG[stufe];
|
||||
if (a === undefined || b === undefined) return "gleich";
|
||||
return b > a ? "vor" : "zurueck";
|
||||
}
|
||||
|
||||
/** Der Monat eines Zeitpunkts in Ortszeit — „2026-09“.
|
||||
*
|
||||
* ÜBER Intl UND NICHT ÜBER slice(0,7). `toISOString()` rechnet in UTC;
|
||||
* zwischen Mitternacht und zwei Uhr liefert das den VORMONAT, und zwar
|
||||
* genau am Monatsersten. Ein Diagramm, das in den ersten Stunden des
|
||||
* Monats eine Spalte zu wenig hat, fällt niemandem auf — es sieht
|
||||
* einfach nach wenig Bewegung aus. Dieselbe Falle hat am 06.09. eine
|
||||
* Prüfung zwei Stunden pro Tag rot gemacht. */
|
||||
const MONATSFORM = new Intl.DateTimeFormat("sv-SE", {
|
||||
timeZone: "Europe/Berlin", year: "numeric", month: "2-digit",
|
||||
});
|
||||
export function monatVon(iso) {
|
||||
const t = Date.parse(iso || "");
|
||||
if (!Number.isFinite(t)) return null;
|
||||
return MONATSFORM.format(new Date(t)).slice(0, 7);
|
||||
}
|
||||
|
||||
/** Die letzten N Monate, älteste zuerst — „2026-04“ … „2026-09“. */
|
||||
export function monatsspalten(anzahl = MONATE, bis = new Date()) {
|
||||
const spalten = [];
|
||||
/* ÜBER DEN 15. GERECHNET und nicht über den heutigen Tag: Wer am
|
||||
31. März einen Monat abzieht, landet in JavaScript im März (der
|
||||
31. Februar existiert nicht und rollt über). Der 15. ist in jedem
|
||||
Monat vorhanden. */
|
||||
const roh = monatVon(bis.toISOString()) || monatVon(new Date().toISOString());
|
||||
const [j, m] = roh.split("-").map(Number);
|
||||
for (let i = anzahl - 1; i >= 0; i--) {
|
||||
const d = new Date(Date.UTC(j, m - 1 - i, 15, 12));
|
||||
spalten.push(`${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`);
|
||||
}
|
||||
return spalten;
|
||||
}
|
||||
|
||||
const tageSeit = (iso) => {
|
||||
const t = Date.parse(iso || "");
|
||||
return Number.isFinite(t) ? Math.floor((Date.now() - t) / TAG) : null;
|
||||
};
|
||||
|
||||
/** Alle Spur-Zeilen einer Person, älteste zuerst. */
|
||||
function spurVon(personId) {
|
||||
return db().prepare(
|
||||
`SELECT l.schluessel, l.von_id, l.stufe, l.vorher, l.nachgetragen, l.am,
|
||||
p.name AS von_name
|
||||
FROM entwicklung_lauf l
|
||||
LEFT JOIN personen p ON p.id = l.von_id
|
||||
WHERE l.person_id = ?
|
||||
ORDER BY l.am ASC, l.id ASC`).all(personId);
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIAGRAMM 1 — WAS SICH BEWEGT HAT
|
||||
|
||||
Je Monat: wie viele Punkte sind nach vorn gegangen, wie viele zurück,
|
||||
wie viele wurden zum ersten Mal angesehen.
|
||||
|
||||
ES IST BEWUSST EIN DIAGRAMM MIT ZWEI RICHTUNGEN und nicht zwei
|
||||
nebeneinander: Vorwärts und rückwärts sind dieselbe Größe mit
|
||||
verschiedenem Vorzeichen. Zwei getrennte Diagramme würden dazu
|
||||
verleiten, sie zu vergleichen, als wären es zwei Themen.
|
||||
|
||||
RÜCKWÄRTS IST NICHT ROT. Auf dieser Seite gibt es kein Rot — das ist
|
||||
seit dem 11.09. die wichtigste Farbentscheidung des Bereichs. Ein
|
||||
Rückschritt ist eine Beobachtung, kein Alarm, und oft genug heißt er
|
||||
nur, dass jemand genauer hingesehen hat als beim letzten Mal.
|
||||
===================================================================== */
|
||||
export function bewegungVon(spur, spalten) {
|
||||
const leer = () => ({ vor: 0, zurueck: 0, neu: 0, geklaert: 0, offen: 0 });
|
||||
const nach = new Map(spalten.map((s) => [s, leer()]));
|
||||
|
||||
for (const z of spur) {
|
||||
const m = monatVon(z.am);
|
||||
if (!nach.has(m)) continue;
|
||||
const art = artVon(z.vorher, z.stufe, z.nachgetragen);
|
||||
/* „anfang“ und „gleich“ zählen nicht als Bewegung. Die
|
||||
Startaufnahme ist keine Veränderung, sondern der Nullpunkt — und
|
||||
wer denselben Wert noch einmal setzt, hat bestätigt, nicht
|
||||
bewegt. Beides mitzuzählen wäre der bequemste Weg zu einem
|
||||
Diagramm, das immer etwas zeigt. */
|
||||
if (art === "anfang" || art === "gleich") continue;
|
||||
nach.get(m)[art] += 1;
|
||||
}
|
||||
|
||||
return spalten.map((m) => ({ monat: m, ...nach.get(m) }));
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIAGRAMM 2 — DER STREIFEN
|
||||
|
||||
Eine Zeile je Punkt, eine Spalte je Monat, jede Zelle die Stufe, die
|
||||
AM ENDE DIESES MONATS galt.
|
||||
|
||||
FORTGESCHRIEBEN, NICHT LÜCKENHAFT: Wenn im Juli niemand etwas
|
||||
geändert hat, galt im Juli weiter, was im Juni gesetzt wurde. Eine
|
||||
leere Zelle würde „keine Einschätzung“ bedeuten, und das wäre falsch.
|
||||
|
||||
ABER SICHTBAR ALT: Eine fortgeschriebene Zelle, deren Ursprung länger
|
||||
als 90 Tage zurückliegt, wird gedämpft gezeichnet. Sonst sähe eine
|
||||
vier Monate alte Einschätzung genauso frisch aus wie die von gestern
|
||||
— und das ist die Lüge mit Zeitstempel, vor der der Plan vom 11.09.
|
||||
ausdrücklich warnt.
|
||||
|
||||
DIE FARBE IST NICHT DIE AUSKUNFT. Jede Zelle trägt das Zeichen ihrer
|
||||
Stufe (✓ ↗ ! –) als Text. Wer Farben schlecht unterscheidet, liest
|
||||
dasselbe. Das ist im Haus seit dem Befinden-Verlauf so und bleibt so.
|
||||
===================================================================== */
|
||||
export function streifenVon(spur, spalten, beurteilerId) {
|
||||
/* NUR DER BLICK EINES BEURTEILERS je Streifen. Zwei Meinungen in
|
||||
einer Zelle zu mischen hieße, einen Mittelwert zu bilden — und ein
|
||||
Mittelwert aus „läuft“ und „da hakt es“ ist „wächst gerade“, was
|
||||
niemand gesagt hat. Die Abweichung zwischen den beiden ist das
|
||||
Ergebnis (so steht es im Plan) und hat ihren eigenen Platz. */
|
||||
const meine = spur.filter((z) => z.von_id === beurteilerId);
|
||||
if (!meine.length) return [];
|
||||
|
||||
/* Nur Punkte, zu denen es überhaupt etwas gibt — der ganze Katalog
|
||||
wäre eine Wand aus Strichen. */
|
||||
const punkte = [...new Set(meine.map((z) => z.schluessel))];
|
||||
const grenze = Date.now() - VERBLASST_NACH_TAGEN * TAG;
|
||||
|
||||
return punkte.map((schluessel) => {
|
||||
const pt = ENTWICKLUNG_NACH_SCHLUESSEL.get(schluessel);
|
||||
const zeilen = meine.filter((z) => z.schluessel === schluessel);
|
||||
let stufe = null, seit = null;
|
||||
const zellen = spalten.map((m) => {
|
||||
/* Alles, was BIS EINSCHLIESSLICH dieses Monats passiert ist. */
|
||||
for (const z of zeilen) {
|
||||
if (monatVon(z.am) <= m) { stufe = z.stufe; seit = z.am; }
|
||||
}
|
||||
if (!stufe) return { monat: m, stufe: null };
|
||||
return {
|
||||
monat: m, stufe, seit,
|
||||
/* Nur die LETZTE Spalte kann verblassen — bei den früheren
|
||||
sagt die Spalte selbst schon, wann es war. Ein gedämpfter
|
||||
Märzwert im März wäre Unsinn. */
|
||||
alt: m === spalten.at(-1) && Date.parse(seit) < grenze,
|
||||
};
|
||||
});
|
||||
return {
|
||||
schluessel,
|
||||
titel: pt?.titel || schluessel,
|
||||
block: pt?.block || null,
|
||||
zellen,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIE TEXTE
|
||||
|
||||
Filipe wollte „diagramme texte und so“. Die Texte sind der Teil, den
|
||||
ein Diagramm nicht kann: Es zeigt, DASS sich etwas bewegt hat — nicht
|
||||
WAS, und schon gar nicht, was man damit macht.
|
||||
|
||||
DIE REIHENFOLGE IST DIE AUSSAGE, und sie ist aus der Forschung
|
||||
abgeleitet, nicht aus Höflichkeit:
|
||||
|
||||
1. WAS SICH BEWEGT HAT — progress principle: sichtbarer
|
||||
Fortschritt ist der stärkste Treiber.
|
||||
2. WAS TRÄGT — Anerkennung wirkt nur konkret; deshalb
|
||||
mit Punkt und Dauer, nicht als Lob.
|
||||
3. WAS LIEGT — erst an dritter Stelle. Schwächenfokus
|
||||
senkt die Leistung messbar.
|
||||
4. WAS NIEMAND ANSIEHT — die einzige Zeile, die von UNS handelt.
|
||||
5. WAS ALS NÄCHSTES KOMMT— der Weg nach vorn, nicht die Lücke.
|
||||
|
||||
JEDER ABSATZ NENNT DEN PUNKT, auf den er sich bezieht. Ein Satz wie
|
||||
„es gibt Bewegung“ ist keine Auskunft, sondern eine Behauptung.
|
||||
===================================================================== */
|
||||
|
||||
const zaehl = (n, eins, viele) => (n === 1 ? eins : viele.replace("%n", n));
|
||||
|
||||
export function texteVon({ spur, beurteilerId, stand, person }) {
|
||||
const texte = [];
|
||||
const meine = spur.filter((z) => z.von_id === beurteilerId);
|
||||
const titel = (s) => ENTWICKLUNG_NACH_SCHLUESSEL.get(s)?.titel || s;
|
||||
|
||||
/* ---- 1. Was sich bewegt hat ------------------------------------- */
|
||||
const bewegt = meine.filter((z) => {
|
||||
const art = artVon(z.vorher, z.stufe, z.nachgetragen);
|
||||
return art === "vor" || art === "zurueck" || art === "geklaert";
|
||||
});
|
||||
const vor = bewegt.filter((z) => artVon(z.vorher, z.stufe, z.nachgetragen) === "vor");
|
||||
const zurueck = bewegt.filter((z) => artVon(z.vorher, z.stufe, z.nachgetragen) === "zurueck");
|
||||
|
||||
if (vor.length) {
|
||||
texte.push({
|
||||
art: "vor",
|
||||
titel: "Was sich bewegt hat",
|
||||
satz: zaehl(vor.length,
|
||||
`Ein Punkt ist weitergekommen: „${titel(vor[0].schluessel)}“.`,
|
||||
`%n Punkte sind weitergekommen.`)
|
||||
+ " Das ist die Sorte Veränderung, die man aussprechen sollte –"
|
||||
+ " sichtbarer Fortschritt hält Menschen bei der Sache, stärker"
|
||||
+ " als Lob es je könnte.",
|
||||
punkte: vor.slice(-5).reverse().map((z) => ({
|
||||
titel: titel(z.schluessel), von: z.vorher, zu: z.stufe, am: z.am,
|
||||
})),
|
||||
});
|
||||
}
|
||||
if (zurueck.length) {
|
||||
texte.push({
|
||||
art: "zurueck",
|
||||
titel: "Wo du es anders siehst als vorher",
|
||||
satz: zaehl(zurueck.length,
|
||||
"Bei einem Punkt hast du zurückgesetzt.",
|
||||
"Bei %n Punkten hast du zurückgesetzt.")
|
||||
+ " Das ist kein Rückschritt dieser Person – es heißt zuerst,"
|
||||
+ " dass du genauer hingesehen hast als beim letzten Mal."
|
||||
+ " Beides gehört ins Gespräch, und zwar in dieser Reihenfolge.",
|
||||
punkte: zurueck.slice(-5).reverse().map((z) => ({
|
||||
titel: titel(z.schluessel), von: z.vorher, zu: z.stufe, am: z.am,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
/* ---- 2. Was trägt ------------------------------------------------ */
|
||||
const traegt = stand
|
||||
.filter((s) => s.stufe === "laeuft" && (tageSeit(s.am) ?? 0) >= TRAEGT_AB_TAGEN)
|
||||
.sort((a, b) => Date.parse(a.am) - Date.parse(b.am));
|
||||
if (traegt.length) {
|
||||
const t = traegt[0];
|
||||
texte.push({
|
||||
art: "traegt",
|
||||
titel: "Was seit Längerem trägt",
|
||||
satz: zaehl(traegt.length,
|
||||
`„${titel(t.schluessel)}“ steht seit ${tageSeit(t.am)} Tagen auf „Läuft“ und ist nie gekippt.`,
|
||||
`%n Punkte stehen seit über ${Math.round(TRAEGT_AB_TAGEN / 30)} Monaten auf „Läuft“ und sind nie gekippt.`)
|
||||
+ " Genau so etwas wird nie gesagt, weil nichts passiert –"
|
||||
+ " und fehlende Anerkennung ist der am häufigsten genannte"
|
||||
+ " Grund, warum Leute in dieser Arbeit aufhören.",
|
||||
punkte: traegt.slice(0, 5).map((s) => ({
|
||||
titel: titel(s.schluessel), zu: "laeuft", am: s.am, tage: tageSeit(s.am),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
/* ---- 3. Was liegt ------------------------------------------------ */
|
||||
const liegt = stand
|
||||
.filter((s) => s.stufe === "hakt" && (tageSeit(s.am) ?? 0) >= LIEGT_AB_TAGEN)
|
||||
.sort((a, b) => Date.parse(a.am) - Date.parse(b.am));
|
||||
if (liegt.length) {
|
||||
texte.push({
|
||||
art: "liegt",
|
||||
titel: "Was seit Längerem unverändert steht",
|
||||
satz: zaehl(liegt.length,
|
||||
`„${titel(liegt[0].schluessel)}“ steht seit ${tageSeit(liegt[0].am)} Tagen unverändert auf „Da hakt es“.`,
|
||||
`%n Punkte stehen seit über ${LIEGT_AB_TAGEN} Tagen unverändert auf „Da hakt es“.`)
|
||||
+ " Ein „da hakt es“, das nirgends hinführt, ist nach einer Weile"
|
||||
+ " keine Beobachtung mehr, sondern eine Notiz über einen Menschen."
|
||||
+ " Auf der Karte steht neben jedem Punkt „Daraus einen Schritt machen“.",
|
||||
punkte: liegt.slice(0, 5).map((s) => ({
|
||||
titel: titel(s.schluessel), zu: "hakt", am: s.am, tage: tageSeit(s.am),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
/* ---- 4. Was niemand mehr angesehen hat --------------------------- */
|
||||
const alt = stand.filter((s) => (tageSeit(s.am) ?? 0) >= VERBLASST_NACH_TAGEN);
|
||||
if (alt.length) {
|
||||
texte.push({
|
||||
art: "alt",
|
||||
titel: "Das sagt etwas über uns, nicht über sie",
|
||||
satz: zaehl(alt.length,
|
||||
`Eine Einschätzung ist älter als ${VERBLASST_NACH_TAGEN} Tage.`,
|
||||
`%n Einschätzungen sind älter als ${VERBLASST_NACH_TAGEN} Tage.`)
|
||||
+ " Sie zählen in der Auswertung nicht mehr mit. Eine Einschätzung"
|
||||
+ " vom Frühjahr, heute unverändert angezeigt, wäre eine Lüge mit"
|
||||
+ " Zeitstempel – deshalb verblasst sie, statt zu verschwinden.",
|
||||
punkte: alt.slice(0, 5).map((s) => ({
|
||||
titel: titel(s.schluessel), zu: s.stufe, am: s.am, tage: tageSeit(s.am),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
/* ---- 5. Was als Nächstes kommt ----------------------------------- */
|
||||
const gesetzt = new Set(stand.map((s) => s.schluessel));
|
||||
const offen = ENTWICKLUNG_FREMD.filter((p) => !gesetzt.has(p.schluessel));
|
||||
if (offen.length) {
|
||||
/* NACH ERWARTUNGSSTUFE SORTIERT, niedrigste zuerst: Was ab Tag eins
|
||||
gilt, ist die sinnvollere nächste Frage als das, wofür man
|
||||
jemanden fragt. */
|
||||
const rang = new Map(ENTWICKLUNG_ERWARTUNG.map((e) => [e.schluessel, e.rang]));
|
||||
const sortiert = [...offen].sort(
|
||||
(a, b) => (rang.get(a.stufe) ?? 9) - (rang.get(b.stufe) ?? 9));
|
||||
texte.push({
|
||||
art: "offen",
|
||||
titel: "Wozu du noch nichts gesagt hast",
|
||||
satz: zaehl(offen.length,
|
||||
"Ein Punkt des Katalogs ist von dir noch unbeantwortet.",
|
||||
"%n Punkte des Katalogs sind von dir noch unbeantwortet.")
|
||||
+ " Das ist kein Versäumnis – „Kann ich nicht sagen“ ist eine"
|
||||
+ " gültige Antwort und ehrlicher als zu raten. Es steht hier,"
|
||||
+ " damit man weiß, worüber die Auswertung schweigt.",
|
||||
punkte: sortiert.slice(0, 5).map((p) => ({
|
||||
titel: p.titel,
|
||||
/* DER NAME, NICHT DER SCHLUESSEL -- und schon gar nicht der
|
||||
`rang` daneben. ENTWICKLUNG_ERWARTUNG traegt eine Zahl je
|
||||
Stufe ("rang"), die es nur zum Sortieren gibt. Sie mitzuliefern
|
||||
hiesse, eine Zahl ueber einen Menschen auszuliefern, die
|
||||
irgendwann jemand addiert. Aufgefallen ist das der eigenen
|
||||
Pruefung, nicht dem Lesen. */
|
||||
erwartung: ENTWICKLUNG_ERWARTUNG.find((e) => e.schluessel === p.stufe)?.name
|
||||
|| p.stufe,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
return texte;
|
||||
}
|
||||
|
||||
/** Der eine Satz oben — die ganze Auswertung in einer Zeile.
|
||||
*
|
||||
* WER NUR IHN LIEST, SOLL RICHTIG INFORMIERT SEIN. Dieselbe Regel wie
|
||||
* bei der Befinden-Analyse: Der Satz ist kein Aufhänger, er ist die
|
||||
* Kurzfassung. */
|
||||
export function satzVon({ bewegung, stand, texte, spalten }) {
|
||||
const vor = bewegung.reduce((s, m) => s + m.vor, 0);
|
||||
const zurueck = bewegung.reduce((s, m) => s + m.zurueck, 0);
|
||||
const neu = bewegung.reduce((s, m) => s + m.neu + m.geklaert, 0);
|
||||
|
||||
if (!stand.length) {
|
||||
return "Zu dieser Person hast du noch nichts eingetragen. Die Auswertung "
|
||||
+ "beginnt mit der ersten Einschätzung – vorher stünde hier eine Grafik "
|
||||
+ "über nichts.";
|
||||
}
|
||||
if (vor + zurueck + neu === 0) {
|
||||
const alt = texte.find((t) => t.art === "alt");
|
||||
return alt
|
||||
? `In den letzten ${spalten.length} Monaten hat sich an dieser Karte nichts `
|
||||
+ "bewegt – und ein Teil davon ist inzwischen älter als drei Monate. "
|
||||
+ "Das ist eine Auskunft über uns, nicht über sie."
|
||||
: `In den letzten ${spalten.length} Monaten hat sich an dieser Karte nichts `
|
||||
+ "bewegt. Das kann heißen, dass alles ruhig läuft – oder dass "
|
||||
+ "länger niemand hingesehen hat. Die Karte selbst kann das nicht "
|
||||
+ "unterscheiden.";
|
||||
}
|
||||
const teile = [];
|
||||
if (vor) teile.push(zaehl(vor, "ein Punkt nach vorn", "%n Punkte nach vorn"));
|
||||
if (zurueck) teile.push(zaehl(zurueck, "einer zurück", "%n zurück"));
|
||||
if (neu) teile.push(zaehl(neu, "einer zum ersten Mal beantwortet",
|
||||
"%n zum ersten Mal beantwortet"));
|
||||
return `In den letzten ${spalten.length} Monaten: ${teile.join(", ")}. `
|
||||
+ (vor >= zurueck
|
||||
? "Das Erste gehört ausgesprochen – es ist der Teil, den sonst niemand bemerkt."
|
||||
: "Das ist kein schlechtes Zeichen: Wer zurücksetzt, hat hingesehen.");
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIE WEGE
|
||||
===================================================================== */
|
||||
|
||||
function angemeldet(req, res, next) {
|
||||
const person = sitzungLesen(req);
|
||||
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||||
req.person = person;
|
||||
try { tabellen(); } catch (f) {
|
||||
console.error("[werdegang] Tabellen:", f?.message);
|
||||
return res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
/** Nur DogFather und die rechte Hand. 404 statt 403 — wer „kein
|
||||
* Zugriff“ liest, fragt als Nächstes, wer denn dann. */
|
||||
function nurLeitung(req, res, next) {
|
||||
if (fuehrtTeamDogi(req.person)) return next();
|
||||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
}
|
||||
|
||||
werdegangRouter.use("/workspace/api/werdegang", angemeldet);
|
||||
|
||||
/* ---- Die Liste ------------------------------------------------------
|
||||
Wer eine Karte hat, und was sich dort zuletzt getan hat.
|
||||
|
||||
AUSDRÜCKLICH OHNE SORTIERUNG NACH „AKTIVITÄT“. Eine Liste, die die
|
||||
mit der meisten Bewegung nach oben stellt, ist eine Rangliste mit
|
||||
einem anderen Namen. Sortiert wird nach Namen.
|
||||
------------------------------------------------------------------- */
|
||||
werdegangRouter.get("/workspace/api/werdegang/liste", nurLeitung, (req, res) => {
|
||||
try {
|
||||
const leute = db().prepare(
|
||||
`SELECT id, name, rolle FROM personen
|
||||
WHERE aktiv = 1 AND rolle IN (${KARTEN_ROLLEN.map(() => "?").join(",")})
|
||||
ORDER BY name COLLATE NOCASE`).all(...KARTEN_ROLLEN);
|
||||
|
||||
const spalten = monatsspalten();
|
||||
const liste = leute.map((p) => {
|
||||
const spur = spurVon(p.id);
|
||||
const bewegung = bewegungVon(spur.filter((z) => z.von_id === req.person.id), spalten);
|
||||
const stand = db().prepare(
|
||||
"SELECT schluessel, stufe, am FROM entwicklung_stand WHERE person_id = ? AND von_id = ?")
|
||||
.all(p.id, req.person.id)
|
||||
.filter((s) => ENTWICKLUNG_FREMD.some((x) => x.schluessel === s.schluessel));
|
||||
const juengste = stand.map((s) => s.am).sort().at(-1) || null;
|
||||
return {
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
rolle_name: ROLLEN_NAME[p.rolle] ?? p.rolle,
|
||||
gesetzt: stand.length,
|
||||
gesamt: ENTWICKLUNG_FREMD.length,
|
||||
bewegt: bewegung.reduce((s, m) => s + m.vor + m.zurueck + m.neu + m.geklaert, 0),
|
||||
vor: bewegung.reduce((s, m) => s + m.vor, 0),
|
||||
zuletzt: juengste,
|
||||
zuletzt_tage: juengste ? tageSeit(juengste) : null,
|
||||
};
|
||||
});
|
||||
|
||||
res.json({
|
||||
liste,
|
||||
monate: spalten.length,
|
||||
/* WAS DIESE SEITE NICHT TUT, steht in der Antwort und nicht nur
|
||||
im Kopf des Entwicklers: Die Oberfläche zeigt den Satz. */
|
||||
hinweis: "Keine Punktzahl, kein Vergleich zwischen Personen. "
|
||||
+ "Jede Karte wird nur mit sich selbst über die Zeit verglichen.",
|
||||
});
|
||||
} catch (f) {
|
||||
console.error("[werdegang] Liste:", f?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/* ---- Eine Person ---------------------------------------------------- */
|
||||
werdegangRouter.get("/workspace/api/werdegang/person/:id", nurLeitung, (req, res) => {
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
const p = Number.isInteger(id) && id > 0
|
||||
&& db().prepare("SELECT id, name, rolle FROM personen WHERE id = ?").get(id);
|
||||
if (!p || !KARTEN_ROLLEN.includes(p.rolle)) {
|
||||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
}
|
||||
|
||||
const spur = spurVon(p.id);
|
||||
const spalten = monatsspalten();
|
||||
const meine = spur.filter((z) => z.von_id === req.person.id);
|
||||
const bewegung = bewegungVon(meine, spalten);
|
||||
const streifen = streifenVon(spur, spalten, req.person.id);
|
||||
const stand = db().prepare(
|
||||
"SELECT schluessel, stufe, am FROM entwicklung_stand WHERE person_id = ? AND von_id = ?")
|
||||
.all(p.id, req.person.id)
|
||||
.filter((s) => ENTWICKLUNG_FREMD.some((x) => x.schluessel === s.schluessel));
|
||||
|
||||
const texte = texteVon({ spur, beurteilerId: req.person.id, stand, person: p });
|
||||
|
||||
/* MONATE MIT INHALT, nicht Monate überhaupt. Sechs leere Spalten
|
||||
sind kein Verlauf — sie sehen nur aus wie einer. */
|
||||
const gefuellt = streifen.length
|
||||
? spalten.filter((m) => streifen.some(
|
||||
(z) => z.zellen.find((c) => c.monat === m)?.stufe)).length
|
||||
: 0;
|
||||
|
||||
res.json({
|
||||
person: { id: p.id, name: p.name, rolle_name: ROLLEN_NAME[p.rolle] ?? p.rolle },
|
||||
stufen: ENTWICKLUNG_STUFEN,
|
||||
/* ENTWICKLUNG_ERWARTUNG wird NICHT mitgeschickt. Die Seite braucht
|
||||
die Liste nicht, und sie traegt ein Feld `rang` -- eine Zahl je
|
||||
Erwartungsstufe, die es nur zum Sortieren gibt. Auf einer Seite
|
||||
ueber Menschen hat keine Zahl etwas verloren, die man addieren
|
||||
koennte. Der Name der Stufe steht dort, wo er gebraucht wird. */
|
||||
bloecke: ENTWICKLUNG_BLOECKE.filter((b) => !b.nurSelbst)
|
||||
.map((b) => ({ schluessel: b.schluessel, name: b.name })),
|
||||
spalten,
|
||||
bewegung,
|
||||
streifen,
|
||||
texte,
|
||||
satz: satzVon({ bewegung, stand, texte, spalten }),
|
||||
gesetzt: stand.length,
|
||||
gesamt: ENTWICKLUNG_FREMD.length,
|
||||
/* DER DRITTE AUSGANG. Nicht „in Ordnung“ und nicht „nicht in
|
||||
Ordnung“, sondern: konnte noch nicht nachsehen — mit Grund.
|
||||
Die Oberfläche zeichnet dann KEIN Diagramm, sondern schreibt
|
||||
den Grund hin. */
|
||||
verlauf_moeglich: gefuellt >= VERLAUF_AB,
|
||||
verlauf_ab: VERLAUF_AB,
|
||||
monate_mit_inhalt: gefuellt,
|
||||
verblasst_nach_tagen: VERBLASST_NACH_TAGEN,
|
||||
});
|
||||
} catch (f) {
|
||||
console.error("[werdegang] Person:", f?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/* ---- Die eigene Entwicklung -----------------------------------------
|
||||
|
||||
Der Teil, der den Menschen selbst angeht — und der nach der
|
||||
Forschung der wirksamste ist: Wer seinen eigenen Fortschritt sieht,
|
||||
bleibt dabei.
|
||||
|
||||
OHNE NAMEN, OHNE ANLASS, OHNE WER-HAT-WAS. Genau wie bei
|
||||
`meine-karte`: Sie sieht, DASS ein Punkt sich bewegt hat und wann —
|
||||
nicht, wer ihn gesetzt hat und was danebensteht. Der Anlass ist für
|
||||
ein Gespräch aufgeschrieben, nicht zum Lesen ohne den Menschen dazu.
|
||||
|
||||
UND ERST, WENN ÜBERHAUPT JEMAND GESETZT HAT. Eine leere eigene Seite
|
||||
würde wie ein Fehler aussehen; ein Satz ist eine Auskunft.
|
||||
------------------------------------------------------------------- */
|
||||
werdegangRouter.get("/workspace/api/werdegang/mich", (req, res) => {
|
||||
try {
|
||||
if (!KARTEN_ROLLEN.includes(req.person.rolle)) {
|
||||
return res.json({ hat_karte: false });
|
||||
}
|
||||
const spur = spurVon(req.person.id).filter((z) => z.von_id !== req.person.id);
|
||||
const spalten = monatsspalten();
|
||||
|
||||
/* ZUSAMMENGEFASST ÜBER ALLE BEURTEILER. Hier geht es nicht darum,
|
||||
wer was gesagt hat — sondern darum, ob sich etwas bewegt.
|
||||
Deshalb ist eine Bewegung eine Bewegung, egal von wem. */
|
||||
const bewegung = bewegungVon(spur, spalten);
|
||||
const vor = bewegung.reduce((s, m) => s + m.vor, 0);
|
||||
const zurueck = bewegung.reduce((s, m) => s + m.zurueck, 0);
|
||||
|
||||
let satz;
|
||||
if (!spur.length) {
|
||||
satz = "Zu dir wurde noch nichts eingetragen. Sobald das passiert ist, "
|
||||
+ "steht hier, was sich über die Zeit bewegt hat.";
|
||||
} else if (!vor && !zurueck) {
|
||||
satz = `In den letzten ${spalten.length} Monaten hat sich an den `
|
||||
+ "Einschätzungen zu dir nichts geändert.";
|
||||
} else if (vor) {
|
||||
satz = vor === 1
|
||||
? "Seit dem letzten Mal ist ein Punkt weitergekommen."
|
||||
: `Seit dem letzten Mal sind ${vor} Punkte weitergekommen.`;
|
||||
} else {
|
||||
satz = "Seit dem letzten Mal wurde ein Punkt anders eingeschätzt als vorher. "
|
||||
+ "Das ist kein Urteil – frag danach, dann weißt du, worum es geht.";
|
||||
}
|
||||
|
||||
res.json({
|
||||
hat_karte: true,
|
||||
spalten,
|
||||
bewegung,
|
||||
satz,
|
||||
/* KEIN STREIFEN. Er würde zeigen, WELCHER Punkt wann gewackelt
|
||||
hat — und damit mittelbar, was jemand über sie gesagt hat,
|
||||
ohne dass derjenige dabei ist. Die Zahl reicht; für alles
|
||||
Weitere ist das Gespräch da, und der Satz sagt das auch. */
|
||||
hinweis: "Welche Punkte das sind, steht nicht hier. Frag danach – "
|
||||
+ "eine Einschätzung ohne den Menschen dazu ist eine Behauptung.",
|
||||
});
|
||||
} catch (f) {
|
||||
console.error("[werdegang] Mich:", f?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
+44
-1
@@ -750,6 +750,48 @@ const ENTWICKLUNG_KACHEL = {
|
||||
zeichen: "steckbrief", ton: 26, ziel: "entwicklung.html", szene: "studio",
|
||||
};
|
||||
|
||||
/* DIE KACHEL, FUER DIE DER NAME FREI GEMACHT WURDE (19.09.2026).
|
||||
|
||||
Filipe: "dan perfektionierst du eine neue wo diagramme texte und so
|
||||
zu jedem modi gemacht wird und diese kategorie kannst du entwicklung
|
||||
nenen."
|
||||
|
||||
SIE ZEIGT AUF werdegang.html UND NICHT AUF entwicklung.html. Das ist
|
||||
Absicht und sieht beim ersten Hinsehen verdreht aus:
|
||||
|
||||
entwicklung.html = der KATALOG, Kachel "Eure Aufgaben"
|
||||
werdegang.html = die AUSWERTUNG, Kachel "Entwicklung"
|
||||
|
||||
Die saubere Endform waere, die alte Datei umzubenennen. Das waeren
|
||||
25 Fundstellen in 11 Dateien -- und damit zwei Aenderungen in einer:
|
||||
Wenn danach etwas klemmt, weiss niemand, welche der beiden es war.
|
||||
Deshalb steht es hier als Kommentar und in `pruef-werdegang` als
|
||||
Pruefung, die rot wird, wenn die Kachel woanders hinzeigt.
|
||||
|
||||
EIGENER TON 40: GEMESSEN, NICHT GERATEN -- und der erste Anlauf lag
|
||||
falsch. 38 sah frei aus, weil 37 die hoechste Nummer in DIESER Datei
|
||||
ist. Gezaehlt in start.css sind es aber 39, alle vergeben: 38 ist
|
||||
"Unsere Seiten", 39 "Vertraulich melden" -- beide kamen am 19.09.
|
||||
frueh dazu und stehen nicht als `ton:` in workspace.js.
|
||||
|
||||
Genau davor warnt das Farbwerkzeug in seinem eigenen Kopf ("Zweiter
|
||||
Anlauf war Ton 22, weil er frei AUSSAH"). Gezaehlt wird deshalb in
|
||||
der Datei, die die Farben wirklich enthaelt.
|
||||
|
||||
Nach dem Hinzufuegen laeuft `node tools/kachel-farben.mjs --schreiben`
|
||||
-- es rechnet alle 40 Toene neu gegeneinander, damit sich keine zwei
|
||||
aehneln.
|
||||
|
||||
EIGENES ZEICHEN "werdegang": Die vorhandenen 22 sind entweder
|
||||
vergeben (steckbrief viermal) oder sagen etwas anderes -- "zahlen"
|
||||
ist eine Messkurve, "teamlage" sind zwei Koepfe. Hier geht es um
|
||||
EINEN Menschen und seinen Weg. */
|
||||
const WERDEGANG_KACHEL = {
|
||||
...FUEHRUNG_GRUPPE,
|
||||
name: "Entwicklung", unter: "Wie sich jemand über die Zeit bewegt – ohne Note",
|
||||
zeichen: "werdegang", ton: 40, ziel: "werdegang.html", szene: "wald",
|
||||
};
|
||||
|
||||
const TALENTE_KACHEL = {
|
||||
...FUEHRUNG_GRUPPE,
|
||||
name: "Talente", unter: "Zuschauer, die ins Team passen könnten",
|
||||
@@ -875,7 +917,7 @@ const BEFINDEN_KACHEL = {
|
||||
const HAND_BEREICHE = vorGruppe(
|
||||
[...MODI_BEREICHE, EINGANG_KACHEL, PERSONEN_KACHEL],
|
||||
"Rund ums Live",
|
||||
ENTWICKLUNG_KACHEL, TALENTE_KACHEL,
|
||||
ENTWICKLUNG_KACHEL, WERDEGANG_KACHEL, TALENTE_KACHEL,
|
||||
).concat(BEFINDEN_KACHEL, {
|
||||
gruppe: "Für dich", gruppeUnter: "Dein Platz im Team",
|
||||
...RECHTE_KACHEL,
|
||||
@@ -1640,6 +1682,7 @@ const ZUSATZ_BEREICHE = {
|
||||
gruppe: TEAM_GRUPPE.gruppe, gruppeUnter: TEAM_GRUPPE.gruppeUnter,
|
||||
},
|
||||
{ ...ENTWICKLUNG_KACHEL, gruppeNach: TEAM_GRUPPE.gruppe },
|
||||
{ ...WERDEGANG_KACHEL, gruppeNach: TEAM_GRUPPE.gruppe },
|
||||
{ ...TALENTE_KACHEL, gruppeNach: TEAM_GRUPPE.gruppe },
|
||||
/* DER TREFF -- GANZ UNTEN, UND ZWAR ABSICHTLICH.
|
||||
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
/* =====================================================================
|
||||
EINE KACHELFARBE NACHZIEHEN — ohne die anderen anzufassen (19.09.2026)
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WOZU ES DAS BRAUCHT, OBWOHL kachel-farben.mjs EXISTIERT
|
||||
|
||||
Das große Werkzeug rechnet ALLE Töne gegeneinander neu. Das ist die
|
||||
richtige Rechnung und das falsche Mittel, wenn eine einzige Kachel
|
||||
dazukommt: Am 19.09.2026 nachgemessen hätte ein Lauf für die 40.
|
||||
Farbe alle 39 anderen verändert — jede Kachel im Haus bekäme eine
|
||||
neue Farbe, ohne dass jemand danach gefragt hat.
|
||||
|
||||
Dieses Werkzeug macht das Gegenteil: Es hält alle vorhandenen Töne
|
||||
fest und sucht für EINEN die Farbe, die am weitesten von allen
|
||||
anderen entfernt ist.
|
||||
|
||||
Gemessen am selben Tag: Für die neue Kachel „Entwicklung“ fand es
|
||||
einen Abstand von 0,0988 — fast das Dreifache des engsten Paares,
|
||||
das im Haus ohnehin schon stand (0,0362 zwischen Ton 15 und 39).
|
||||
Ein Gesamtneuanstrich war also gar nicht nötig.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
DIE VIER BEDINGUNGEN sind dieselben wie im großen Werkzeug:
|
||||
|
||||
1. LESBAR. Mindestens 4,5:1 gegen den dunklen Grund (#0a121e).
|
||||
Nicht verhandelbar — eine Kachelfarbe trägt auch Text.
|
||||
2. SPECIELL. Buntheit bis 0,30. Augenschonend heißt dunkles
|
||||
Schema, kein Flackern — NICHT blass.
|
||||
3. TRENNBAR. Der Abstand zur nächstliegenden vorhandenen Farbe
|
||||
wird so groß wie möglich gemacht. Gerechnet in OKLab, weil
|
||||
dort der Zahlenabstand dem entspricht, was das Auge sieht.
|
||||
4. NICHT SCHLECHTER ALS VORHER. Findet sich nichts Besseres,
|
||||
bleibt die alte Farbe stehen — und das Werkzeug sagt es.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
UND DIE FÜNFTE, DIE HIER DAZUKOMMT: ES MUSS ALLES LESEN KÖNNEN
|
||||
|
||||
Der erste Anlauf dieser Datei las 31 von 40 Tönen und rechnete
|
||||
fröhlich weiter. Der Grund: Einstellige Nummern stehen in start.css
|
||||
mit ZWEI Leerzeichen da (`[data-ton="1"] { …`), zur Ausrichtung.
|
||||
Das Ergebnis war eine plausible, falsche Farbe — sie hätte mit einer
|
||||
der neun ungelesenen kollidieren können, ohne dass es auffällt.
|
||||
|
||||
Deshalb bricht es jetzt ab, wenn die Zahl der gelesenen Töne nicht
|
||||
zur Zahl der vorhandenen passt. Das ist der dritte Ausgang:
|
||||
in Ordnung / nicht in Ordnung / KONNTE NICHT NACHSEHEN, mit Grund.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
AUFRUF
|
||||
|
||||
node tools/kachel-farbe-einzeln.mjs 40 nur zeigen
|
||||
node tools/kachel-farbe-einzeln.mjs 40 --schreiben eintragen
|
||||
node tools/kachel-farbe-einzeln.mjs --pruefen nur nachmessen
|
||||
|
||||
Mehrere Nummern gehen auch: `… 38 39 --schreiben` zieht sie
|
||||
NACHEINANDER nach, jede gegen den Stand INKLUSIVE der vorherigen.
|
||||
===================================================================== */
|
||||
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
const HIER = dirname(fileURLToPath(import.meta.url));
|
||||
const CSS = join(HIER, "..", "workspace", "assets", "css", "start.css");
|
||||
const GRUND = "#0a121e";
|
||||
|
||||
/* ---------- Farbrechnung (gleich wie in kachel-farben.mjs) ---------- */
|
||||
const linear = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
||||
const zurueck = (c) => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);
|
||||
const srgb = (hex) => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);
|
||||
|
||||
function oklab(hex) {
|
||||
const [r, g, b] = srgb(hex).map(linear);
|
||||
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
||||
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
||||
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
||||
return [
|
||||
0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
||||
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
||||
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,
|
||||
];
|
||||
}
|
||||
|
||||
function ausOklch(L, C, Hgrad) {
|
||||
const h = (Hgrad * Math.PI) / 180;
|
||||
const a = C * Math.cos(h), b2 = C * Math.sin(h);
|
||||
const l_ = (L + 0.3963377774 * a + 0.2158037573 * b2) ** 3;
|
||||
const m_ = (L - 0.1055613458 * a - 0.0638541728 * b2) ** 3;
|
||||
const s_ = (L - 0.0894841775 * a - 1.2914855480 * b2) ** 3;
|
||||
const r = +4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_;
|
||||
const g = -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_;
|
||||
const b = -0.0041960863 * l_ - 0.7034186147 * m_ + 1.7076147010 * s_;
|
||||
const kanal = (x) => {
|
||||
const v = zurueck(x);
|
||||
if (v < -0.001 || v > 1.001) return null;
|
||||
return Math.round(Math.min(1, Math.max(0, v)) * 255);
|
||||
};
|
||||
const [R, G, B] = [kanal(r), kanal(g), kanal(b)];
|
||||
if (R === null || G === null || B === null) return null;
|
||||
return "#" + [R, G, B].map((x) => x.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
|
||||
const lum = (hex) => {
|
||||
const [r, g, b] = srgb(hex).map(linear);
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
};
|
||||
const kontrast = (hex) => (lum(hex) + 0.05) / (lum(GRUND) + 0.05);
|
||||
const abstand = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
|
||||
|
||||
/* ---------- Lesen, und zwar vollstaendig ---------------------------- */
|
||||
|
||||
/** Alle Toene als {nummer, hex, lab, zeile}.
|
||||
*
|
||||
* `\s*` UND NICHT EIN LEERZEICHEN: Einstellige Nummern stehen mit zwei
|
||||
* Leerzeichen da. Genau daran ist der erste Anlauf gescheitert. */
|
||||
function lies(css) {
|
||||
const aus = [];
|
||||
for (const zeile of css.split("\n")) {
|
||||
const m = zeile.match(/\[data-ton="(\d+)"\]\s*\{\s*--ton:\s*(#[0-9a-fA-F]{6})/);
|
||||
if (!m) continue;
|
||||
const wofuer = (zeile.match(/\/\*\s*([^*]+?)\s*\*\//) || [])[1] || "";
|
||||
aus.push({ n: Number(m[1]), hex: m[2].toLowerCase(), lab: oklab(m[2]), zeile, wofuer });
|
||||
}
|
||||
return aus;
|
||||
}
|
||||
|
||||
/** Bricht ab, wenn nicht jeder vorhandene Ton auch gelesen wurde. */
|
||||
function vollstaendigOderAbbruch(css) {
|
||||
const roh = new Set([...css.matchAll(/\[data-ton="(\d+)"\]/g)].map((m) => Number(m[1])));
|
||||
const gelesen = lies(css);
|
||||
const nummern = gelesen.map((t) => t.n);
|
||||
if (roh.size !== nummern.length) {
|
||||
console.error(`ABBRUCH: ${roh.size} Toene stehen in der Datei, gelesen wurden ${nummern.length}.`);
|
||||
console.error(" nicht gelesen: " + [...roh].filter((n) => !nummern.includes(n)).join(", "));
|
||||
console.error(" Wahrscheinlich hat sich die Schreibweise der Zeile geaendert.");
|
||||
process.exit(2);
|
||||
}
|
||||
return gelesen;
|
||||
}
|
||||
|
||||
function engstesPaar(liste) {
|
||||
let best = { d: Infinity, a: null, b: null };
|
||||
for (let i = 0; i < liste.length; i++) {
|
||||
for (let j = i + 1; j < liste.length; j++) {
|
||||
const d = abstand(liste[i].lab, liste[j].lab);
|
||||
if (d < best.d) best = { d, a: liste[i], b: liste[j] };
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/** Die Farbe mit dem groessten Abstand zu allen uebergebenen. */
|
||||
function beste(andere) {
|
||||
let treffer = null, wert = -1;
|
||||
for (let H = 0; H < 360; H += 1) {
|
||||
for (let L = 0.56; L <= 0.94; L += 0.01) {
|
||||
/* Von oben herunter, damit die Farbe so satt wie erlaubt wird
|
||||
und nicht blass -- Filipe: „richtig geil und speziell“. */
|
||||
for (let C = 0.30; C >= 0.04; C -= 0.004) {
|
||||
const hex = ausOklch(L, C, H);
|
||||
if (!hex) continue;
|
||||
if (kontrast(hex) < 4.5) break;
|
||||
const lab = oklab(hex);
|
||||
let naechster = Infinity;
|
||||
for (const v of andere) naechster = Math.min(naechster, abstand(lab, v.lab));
|
||||
if (naechster > wert) { wert = naechster; treffer = { hex, lab, L, C, H }; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { ...treffer, abstand: wert };
|
||||
}
|
||||
|
||||
/* ---------- Ablauf --------------------------------------------------- */
|
||||
|
||||
const argumente = process.argv.slice(2);
|
||||
const schreiben = argumente.includes("--schreiben");
|
||||
const nurPruefen = argumente.includes("--pruefen");
|
||||
const nummern = argumente.filter((a) => /^\d+$/.test(a)).map(Number);
|
||||
|
||||
let css = readFileSync(CSS, "utf8");
|
||||
const anfang = vollstaendigOderAbbruch(css);
|
||||
console.log(`${anfang.length} Toene gelesen — vollstaendig.`);
|
||||
|
||||
{
|
||||
const p = engstesPaar(anfang);
|
||||
console.log(`Engstes Paar jetzt: ${p.d.toFixed(4)} `
|
||||
+ `Ton ${p.a.n} ${p.a.hex} (${p.a.wofuer}) <-> Ton ${p.b.n} ${p.b.hex} (${p.b.wofuer})`);
|
||||
}
|
||||
|
||||
if (nurPruefen || !nummern.length) {
|
||||
if (!nummern.length && !nurPruefen) {
|
||||
console.log("\nKeine Nummer angegeben. Aufruf: node tools/kachel-farbe-einzeln.mjs <nr> [--schreiben]");
|
||||
}
|
||||
const sortiert = [];
|
||||
for (let i = 0; i < anfang.length; i++) {
|
||||
for (let j = i + 1; j < anfang.length; j++) {
|
||||
sortiert.push({ d: abstand(anfang[i].lab, anfang[j].lab), a: anfang[i], b: anfang[j] });
|
||||
}
|
||||
}
|
||||
sortiert.sort((x, y) => x.d - y.d);
|
||||
console.log("\nDie fuenf engsten Paare:");
|
||||
for (const p of sortiert.slice(0, 5)) {
|
||||
console.log(` ${p.d.toFixed(4)} Ton ${p.a.n} (${p.a.wofuer}) <-> Ton ${p.b.n} (${p.b.wofuer})`);
|
||||
}
|
||||
const schwach = anfang.filter((t) => kontrast(t.hex) < 4.5);
|
||||
console.log(schwach.length
|
||||
? `\nZU DUNKEL (< 4,5:1): ${schwach.map((t) => `Ton ${t.n}`).join(", ")}`
|
||||
: "\nAlle Toene erreichen 4,5:1 gegen den Grund.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
for (const ziel of nummern) {
|
||||
/* JEDES MAL NEU LESEN: Wer zwei Nummern nacheinander nachzieht, soll
|
||||
die zweite gegen den Stand INKLUSIVE der ersten rechnen. Sonst
|
||||
bekommen beide dieselbe Farbe -- genau das ist beim ersten Anlauf
|
||||
passiert. */
|
||||
const alle = lies(css);
|
||||
const vorher = alle.find((t) => t.n === ziel);
|
||||
if (!vorher) { console.error(`Ton ${ziel} gibt es nicht.`); process.exit(2); }
|
||||
const andere = alle.filter((t) => t.n !== ziel);
|
||||
|
||||
const alt = Math.min(...andere.map((t) => abstand(vorher.lab, t.lab)));
|
||||
const neu = beste(andere);
|
||||
|
||||
if (neu.abstand <= alt) {
|
||||
console.log(`\nTon ${ziel} (${vorher.wofuer}): bleibt ${vorher.hex} — `
|
||||
+ `nichts Besseres gefunden (${alt.toFixed(4)}).`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`\nTon ${ziel} (${vorher.wofuer})`);
|
||||
console.log(` vorher ${vorher.hex} Abstand zum Nachbarn ${alt.toFixed(4)}`);
|
||||
console.log(` nachher ${neu.hex} Abstand zum Nachbarn ${neu.abstand.toFixed(4)}`
|
||||
+ ` (${(neu.abstand / alt).toFixed(1)}-fach)`);
|
||||
console.log(` Kontrast ${kontrast(neu.hex).toFixed(2)}:1, Buntheit ${neu.C.toFixed(3)}`);
|
||||
|
||||
if (!schreiben) { console.log(" (nur gezeigt — mit --schreiben eintragen)"); continue; }
|
||||
|
||||
const neueZeile = vorher.zeile.replace(vorher.hex, neu.hex);
|
||||
if (neueZeile === vorher.zeile) {
|
||||
console.error(" ABBRUCH: Die Zeile liess sich nicht ersetzen.");
|
||||
process.exit(2);
|
||||
}
|
||||
css = css.replace(vorher.zeile, neueZeile);
|
||||
}
|
||||
|
||||
if (schreiben) {
|
||||
writeFileSync(CSS, css);
|
||||
const nachher = vollstaendigOderAbbruch(readFileSync(CSS, "utf8"));
|
||||
const p = engstesPaar(nachher);
|
||||
console.log(`\nGeschrieben. ${nachher.length} Toene, engstes Paar jetzt ${p.d.toFixed(4)} `
|
||||
+ `(Ton ${p.a.n} <-> Ton ${p.b.n}).`);
|
||||
const schwach = nachher.filter((t) => kontrast(t.hex) < 4.5);
|
||||
if (schwach.length) {
|
||||
console.error(`WARNUNG: ${schwach.map((t) => `Ton ${t.n}`).join(", ")} unter 4,5:1.`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,26 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>Anruf-Probe · Team Dogi</title>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="stylesheet" href="assets/css/basis.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/kopf.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<!-- KORRIGIERT AM 19.09.2026. Hier standen `basis.css` und `kopf.css` --
|
||||
zwei Dateien, die es im Haus gar nicht gibt. Die Seite lud sie bei
|
||||
jedem Aufruf ins Leere und stand damit OHNE die Hausfarben da; die
|
||||
Angaben darunter (var(--text-leise) usw.) liefen ins Nichts.
|
||||
|
||||
Ausgerechnet auf dieser Seite faellt das nicht auf: Sie hat ihre
|
||||
eigene Stilangabe im Kopf, damit sie auch dann noch etwas anzeigt,
|
||||
wenn die Stilvorlagen klemmen -- und genau die hat den Mangel
|
||||
verdeckt. Gefunden hat es pruef-struktur.
|
||||
|
||||
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
<!-- Ohne diese drei nimmt ein iPhone einen Bildschirmausschnitt als
|
||||
Symbol, Android zeichnet die Leiste weiss, und die Seite oeffnet
|
||||
ausserhalb der App -- auf einer Seite, die genau erklaeren soll,
|
||||
WARUM das Telefon nicht klingelt, ist das besonders unpassend. -->
|
||||
<link rel="manifest" href="/workspace/crew.webmanifest?v=202609191018" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<style>
|
||||
/* Bewusst in der Seite und nicht in einer eigenen Datei: Diese Seite
|
||||
soll auch dann noch etwas anzeigen, wenn genau das schiefgeht,
|
||||
@@ -58,6 +75,6 @@
|
||||
<p class="probe__ende" id="rat"></p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/anruf-probe.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/anruf-probe.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -22,25 +22,25 @@
|
||||
},
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609190208",
|
||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609191018",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609190208",
|
||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609191018",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609190208",
|
||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609191018",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609190208",
|
||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609191018",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
|
||||
@@ -1027,7 +1027,7 @@ body.start > main { position: relative; z-index: 1; }
|
||||
Wenn eine achtundzwanzigste Kachel kommt, ist das der Zeitpunkt, an
|
||||
dem die Kacheln Gruppenfarben brauchen statt Einzelfarben -- nicht
|
||||
der, an dem man den naechsten Ton dazwischenquetscht. */
|
||||
[data-ton="26"] { --ton: #07fff1; } /* Entwicklung */
|
||||
[data-ton="26"] { --ton: #07fff1; } /* Eure Aufgaben (bis 19.09. "Entwicklung") */
|
||||
[data-ton="27"] { --ton: #ff6bfa; } /* Talente */
|
||||
|
||||
/* =====================================================================
|
||||
@@ -1115,7 +1115,7 @@ body.start > main { position: relative; z-index: 1; }
|
||||
Ergebnis: OKLCH L=0.59 C=0.185 H=254. Abstand 0.310 zum nächsten
|
||||
(Ton 33), Kontrast 4.51:1. Blau ist die einzige Ecke, die im Treff
|
||||
noch frei war — das ist kein Zufall, sondern das Ergebnis. */
|
||||
[data-ton="38"] { --ton: #087ce7; } /* Unsere Seiten */
|
||||
[data-ton="38"] { --ton: #0286e5; } /* Unsere Seiten */
|
||||
/* VERTRAULICH MELDEN (19.09.2026) -- ebenfalls abgesucht, nicht gewaehlt.
|
||||
140 973 lesbare Kandidaten, gewonnen hat der mit dem groessten
|
||||
Mindestabstand zu allen neun belegten Toenen: OKLCH L=0.77 C=0.130
|
||||
@@ -1126,7 +1126,8 @@ body.start > main { position: relative; z-index: 1; }
|
||||
sind. Mehr ist im lesbaren Bereich nicht zu holen; wer den naechsten
|
||||
hinzufuegt, sollte das wissen und notfalls einen alten verschieben,
|
||||
statt die Schwelle stillschweigend zu senken. */
|
||||
[data-ton="39"] { --ton: #1acae2; } /* Vertraulich melden */
|
||||
[data-ton="39"] { --ton: #ff57ba; } /* Vertraulich melden */
|
||||
[data-ton="40"] { --ton: #feb9b8; } /* Entwicklung (die Auswertung) */
|
||||
|
||||
/* Rückfallwert für alles, was einen Ton braucht, aber keine Nummer hat.
|
||||
Steht bewusst in :where() -- damit zählt die Zeile beim Wettstreit der
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
/* =====================================================================
|
||||
ENTWICKLUNG — die Auswertung je Person (19.09.2026)
|
||||
|
||||
Sie erbt die Farben von `entwicklung.css` und hält sich an dessen
|
||||
wichtigste Entscheidung: ES GIBT HIER KEIN ROT.
|
||||
|
||||
kühl-grün #7fb08a läuft · nach vorn
|
||||
babyblau #8ec9ff wächst gerade
|
||||
bernstein #d8a76a da hakt es · zurückgesetzt
|
||||
grau kann ich nicht sagen · noch nichts
|
||||
|
||||
„Da hakt es" ist eine Beobachtung, kein Alarm. Ein Rückschritt heißt
|
||||
oft nur, dass jemand genauer hingesehen hat als beim letzten Mal.
|
||||
Rot würde aus einem Gesprächsanlass ein Urteil machen.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
NACHGERECHNET, NICHT GESCHÄTZT (19.09.2026)
|
||||
|
||||
Die vier Töne wurden gegen die Fläche dieser Seite geprüft
|
||||
(#14161c — das ist `color-mix(var(--tinte) 94%, #ffffff 6%)`
|
||||
ausgerechnet):
|
||||
|
||||
Kontrast gegen die Fläche alle vier ≥ 3:1 bestanden
|
||||
Unterscheidbarkeit schlechtestes Paar ΔE 16,6 (normal)
|
||||
bei Farbsehschwäche ΔE 14,9 (Protan)
|
||||
ΔE 11,2 (Tritan)
|
||||
|
||||
Sie liegen bewusst UNTER der Sättigung, die ein Diagrammwerkzeug
|
||||
vorschlagen würde. Das ist die Hausregel „augenschonend, gedeckt
|
||||
statt grell" — und sie ist hier vertretbar, weil die Farbe nie
|
||||
allein trägt: Jede Zelle hat ihr Zeichen, jeder Balken seine Zahl,
|
||||
und unter dem Diagramm steht eine Legende.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
UND WAS ES WEITERHIN NICHT GIBT
|
||||
|
||||
Keinen Fortschrittsbalken je Person, keinen Prozentwert, keine
|
||||
Punktzahl. Eine Zahl über einen Menschen wird gelesen, verglichen und
|
||||
weitererzählt.
|
||||
|
||||
Die Balken hier messen etwas anderes: WIE VIEL SICH BEWEGT HAT. Das
|
||||
ist keine Eigenschaft der Person, sondern ein Ereignis zwischen zwei
|
||||
Zeitpunkten — und es kann nicht „hoch" oder „niedrig" gut sein.
|
||||
===================================================================== */
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
Gemeinsam
|
||||
------------------------------------------------------------------- */
|
||||
.w-hinweis {
|
||||
margin: 0 0 18px; max-width: 70ch;
|
||||
font-size: .84rem; line-height: 1.6; color: var(--text-still);
|
||||
}
|
||||
.w-satz {
|
||||
margin: 0 0 20px; max-width: 72ch;
|
||||
font-size: .95rem; line-height: 1.65; color: var(--text-leise);
|
||||
}
|
||||
.w-teil { margin: 30px 0 0; }
|
||||
.w-teil__satz {
|
||||
margin: 0 0 14px; max-width: 72ch;
|
||||
font-size: .83rem; line-height: 1.6; color: var(--text-still);
|
||||
}
|
||||
.w-fuss {
|
||||
margin: 34px 0 0; max-width: 72ch;
|
||||
font-size: .8rem; line-height: 1.6; color: var(--text-still);
|
||||
}
|
||||
.w-fuss a { color: var(--text-leise); }
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
DIE LISTE
|
||||
|
||||
Kacheln statt Tabelle: Eine Tabelle mit Zahlenspalten lädt dazu ein,
|
||||
die Spalte zu sortieren — und eine sortierte Spalte über Menschen ist
|
||||
eine Rangliste, egal wie sie überschrieben ist.
|
||||
------------------------------------------------------------------- */
|
||||
.w-liste__gitter {
|
||||
display: grid; gap: 12px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
|
||||
}
|
||||
.w-karte {
|
||||
display: grid; gap: 3px; text-align: left;
|
||||
padding: 15px 17px;
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
border: 1px solid var(--rand);
|
||||
border-left: 3px solid var(--rand-hell, var(--rand));
|
||||
border-radius: var(--radius);
|
||||
color: inherit; font: inherit; cursor: pointer;
|
||||
transition: border-color .15s ease, background .15s ease;
|
||||
}
|
||||
.w-karte:hover,
|
||||
.w-karte:focus-visible {
|
||||
background: color-mix(in srgb, var(--tinte) 90%, #ffffff 10%);
|
||||
border-left-color: color-mix(in srgb, var(--ton, #8ec9ff) 60%, transparent);
|
||||
}
|
||||
.w-karte__name { font-size: 1rem; font-weight: 640; color: var(--text); }
|
||||
.w-karte__rolle {
|
||||
font-size: .72rem; letter-spacing: .05em; text-transform: uppercase;
|
||||
color: var(--text-still);
|
||||
}
|
||||
.w-karte__zeilen { display: grid; gap: 2px; margin-top: 7px; }
|
||||
.w-karte__zeile { font-size: .82rem; line-height: 1.5; color: var(--text-leise); }
|
||||
/* Die einzige Farbe in der Liste — und sie sagt etwas über UNS. */
|
||||
.w-karte__zeile[data-alt="ja"] { color: #d8a76a; }
|
||||
|
||||
.w-name {
|
||||
margin: 16px 0 6px; font-size: 1.32rem; font-weight: 650; color: var(--text);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
DIAGRAMM 1 — BEWEGUNG
|
||||
|
||||
Eine Nulllinie, darüber „nach vorn", darunter „zurück". Beide Hälften
|
||||
teilen sich denselben Maßstab: Sonst sähe ein einzelner Rückschritt
|
||||
so groß aus wie fünf Fortschritte.
|
||||
------------------------------------------------------------------- */
|
||||
.w-bewegung {
|
||||
padding: 16px 16px 12px;
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
border: 1px solid var(--rand);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
.w-bewegung__gitter {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--spalten, 6), 1fr);
|
||||
gap: 8px;
|
||||
align-items: end;
|
||||
}
|
||||
.w-bewegung__spalte { display: grid; grid-template-rows: 5.2rem auto 3.4rem auto; }
|
||||
|
||||
.w-bewegung__halb { display: flex; flex-direction: column; min-height: 0; }
|
||||
.w-bewegung__halb--oben { justify-content: flex-end; align-items: center; }
|
||||
.w-bewegung__halb--unten { justify-content: flex-start; align-items: center; }
|
||||
|
||||
/* Die Nulllinie. Zurückhaltend — sie ordnet, sie behauptet nichts. */
|
||||
.w-bewegung__null {
|
||||
height: 1px; background: var(--rand);
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.w-bewegung__balken {
|
||||
width: min(100%, 2.4rem);
|
||||
min-height: 3px;
|
||||
/* 4px runde Enden am ABGEWANDTEN Ende, das Ende an der Nulllinie
|
||||
bleibt eckig: Ein Balken, der an der Achse rund ist, sieht aus, als
|
||||
schwebe er. */
|
||||
border-radius: 4px 4px 0 0;
|
||||
background: color-mix(in srgb, #7fb08a 78%, transparent);
|
||||
}
|
||||
.w-bewegung__halb--unten .w-bewegung__balken { border-radius: 0 0 4px 4px; }
|
||||
.w-bewegung__balken[data-richtung="zurueck"] {
|
||||
background: color-mix(in srgb, #d8a76a 78%, transparent);
|
||||
}
|
||||
.w-bewegung__zahl {
|
||||
font-size: .78rem; font-weight: 650; font-variant-numeric: tabular-nums;
|
||||
color: var(--text-leise);
|
||||
padding: 2px 0;
|
||||
}
|
||||
.w-bewegung__monat {
|
||||
margin-top: 8px; text-align: center;
|
||||
font-size: .72rem; color: var(--text-still);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Die Legende ist Pflicht, nicht Zierde: ohne sie trägt allein die
|
||||
Farbe die Bedeutung. */
|
||||
.w-legende {
|
||||
display: flex; flex-wrap: wrap; gap: 8px 20px;
|
||||
margin-top: 14px; padding-top: 12px;
|
||||
border-top: 1px solid var(--rand);
|
||||
}
|
||||
.w-legende__punkt { display: inline-flex; align-items: center; gap: 7px; }
|
||||
.w-legende__fleck {
|
||||
width: 11px; height: 11px; border-radius: 3px; flex: none;
|
||||
background: color-mix(in srgb, #7fb08a 78%, transparent);
|
||||
}
|
||||
.w-legende__fleck[data-richtung="zurueck"] {
|
||||
background: color-mix(in srgb, #d8a76a 78%, transparent);
|
||||
}
|
||||
.w-legende__text { font-size: .76rem; color: var(--text-still); }
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
DIAGRAMM 2 — DER STREIFEN
|
||||
|
||||
Dieselbe Bauform wie der Befinden-Verlauf. Wer die eine kennt, liest
|
||||
die andere ohne Erklärung.
|
||||
------------------------------------------------------------------- */
|
||||
.w-streifen__gitter {
|
||||
display: grid; gap: 4px;
|
||||
padding: 14px 16px;
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
border: 1px solid var(--rand);
|
||||
border-radius: var(--radius);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.w-streifen__zeile {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(14ch, 1fr) repeat(auto-fit, minmax(2.4rem, 2.4rem));
|
||||
align-items: center; gap: 6px;
|
||||
}
|
||||
.w-streifen__zeile--kopf { margin-bottom: 4px; }
|
||||
.w-streifen__punkt-name {
|
||||
font-size: .79rem; line-height: 1.35; color: var(--text-leise);
|
||||
padding-right: 8px;
|
||||
}
|
||||
.w-streifen__monat {
|
||||
font-size: .71rem; color: var(--text-still); text-align: center;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.w-streifen__zelle {
|
||||
display: grid; place-items: center;
|
||||
width: 2.2rem; height: 1.9rem;
|
||||
font-size: .9rem; font-weight: 700;
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-still);
|
||||
background: rgba(0, 0, 0, .25);
|
||||
border: 1px solid var(--rand);
|
||||
}
|
||||
.w-streifen__zelle[data-stufe="laeuft"] { color: #7fb08a; border-color: color-mix(in srgb, #668e6e 55%, transparent); }
|
||||
.w-streifen__zelle[data-stufe="waechst"] { color: #8ec9ff; border-color: color-mix(in srgb, #8ec9ff 45%, transparent); }
|
||||
.w-streifen__zelle[data-stufe="hakt"] { color: #d8a76a; border-color: color-mix(in srgb, #cc9451 55%, transparent); }
|
||||
.w-streifen__zelle[data-stufe="leer"] { opacity: .32; }
|
||||
/* Älter als 90 Tage: sichtbar, aber sichtbar alt. Nicht weg — man soll
|
||||
sehen, DASS etwas alt ist, statt dass es verschwindet. */
|
||||
.w-streifen__zelle[data-alt="ja"] { opacity: .45; border-style: dashed; }
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
DIE TEXTE
|
||||
------------------------------------------------------------------- */
|
||||
.w-texte { display: grid; gap: 12px; }
|
||||
.w-text {
|
||||
padding: 14px 16px;
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
border: 1px solid var(--rand);
|
||||
border-left: 3px solid var(--rand);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
/* Nur zwei Arten bekommen eine Farbe, und beide sind ruhig. Alles
|
||||
einzufärben hieße, dass keine Farbe mehr etwas bedeutet. */
|
||||
.w-text[data-art="vor"] { border-left-color: color-mix(in srgb, #7fb08a 70%, transparent); }
|
||||
.w-text[data-art="traegt"] { border-left-color: color-mix(in srgb, #7fb08a 70%, transparent); }
|
||||
.w-text[data-art="liegt"] { border-left-color: color-mix(in srgb, #cc9451 70%, transparent); }
|
||||
.w-text[data-art="alt"] { border-left-color: color-mix(in srgb, #cc9451 70%, transparent); }
|
||||
|
||||
.w-text__titel {
|
||||
margin: 0 0 6px; font-size: .95rem; font-weight: 640; color: var(--text);
|
||||
}
|
||||
.w-text__satz {
|
||||
margin: 0; max-width: 72ch;
|
||||
font-size: .86rem; line-height: 1.65; color: var(--text-leise);
|
||||
}
|
||||
.w-text__liste { margin: 11px 0 0; padding: 0; list-style: none; display: grid; gap: 4px; }
|
||||
.w-text__zeile {
|
||||
display: flex; flex-wrap: wrap; align-items: baseline;
|
||||
justify-content: space-between; gap: 6px 14px;
|
||||
padding: 5px 0;
|
||||
border-top: 1px solid color-mix(in srgb, var(--rand) 60%, transparent);
|
||||
}
|
||||
.w-text__punkt { font-size: .83rem; color: var(--text); flex: 1 1 18ch; }
|
||||
.w-text__rechts { display: inline-flex; align-items: baseline; gap: 7px; flex: none; }
|
||||
.w-text__stufe { font-size: .76rem; color: var(--text-still); }
|
||||
.w-text__stufe[data-stufe="laeuft"] { color: #7fb08a; }
|
||||
.w-text__stufe[data-stufe="waechst"] { color: #8ec9ff; }
|
||||
.w-text__stufe[data-stufe="hakt"] { color: #d8a76a; }
|
||||
.w-text__pfeil { font-size: .76rem; color: var(--text-still); }
|
||||
.w-text__tage {
|
||||
font-size: .72rem; color: var(--text-still); font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
Handy
|
||||
|
||||
Der Streifen darf schmaler werden, das Bewegungsdiagramm nicht: Unter
|
||||
rund 2 rem je Spalte sind sechs Balken nicht mehr unterscheidbar. Er
|
||||
rollt dann seitlich, statt unleserlich zu werden.
|
||||
------------------------------------------------------------------- */
|
||||
@media (max-width: 560px) {
|
||||
.w-streifen__zeile {
|
||||
grid-template-columns: minmax(10ch, 1fr) repeat(auto-fit, minmax(2rem, 2rem));
|
||||
}
|
||||
.w-streifen__zelle { width: 1.9rem; }
|
||||
.w-bewegung__gitter { gap: 5px; }
|
||||
.w-bewegung__spalte { grid-template-rows: 4.4rem auto 2.8rem auto; }
|
||||
.w-bewegung__balken { width: min(100%, 1.9rem); }
|
||||
.w-text__zeile { justify-content: flex-start; }
|
||||
}
|
||||
|
||||
/* Wer weniger Bewegung möchte, bekommt weniger Bewegung. Hier gibt es
|
||||
ohnehin keine Animation — der Übergang an der Karte ist alles, und
|
||||
auch der fällt weg. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.w-karte { transition: none; }
|
||||
}
|
||||
|
||||
/* Erzwungene Farben (Windows-Kontrastmodus): Dort gilt die
|
||||
Systemfarbe, und die Zeichen in den Zellen tragen die Auskunft
|
||||
allein — genau dafür sind sie da. */
|
||||
@media (forced-colors: active) {
|
||||
.w-bewegung__balken { background: CanvasText; }
|
||||
.w-legende__fleck { background: CanvasText; }
|
||||
.w-streifen__zelle { border-color: CanvasText; }
|
||||
}
|
||||
@@ -263,6 +263,22 @@
|
||||
fuellung: ['M3.5 5.5h17l-6.6 7.6h-3.8z'],
|
||||
linien: ['M3.5 5.5h17l-6.6 7.6v6.4l-3.8-2.1v-4.3z'],
|
||||
},
|
||||
werdegang: {
|
||||
/* EIN Mensch und sein Weg -- nicht zwei Koepfe wie bei
|
||||
"teamlage", nicht eine Messkurve wie bei "zahlen". Der
|
||||
Unterschied ist der ganze Punkt dieser Kachel: Hier wird
|
||||
niemand mit jemandem verglichen.
|
||||
|
||||
Drei Stufen, die ansteigen, und ein gefuellter Punkt auf der
|
||||
obersten -- das Jetzt. Gefuellt ist NUR der Punkt: Wuerden die
|
||||
Stufen mitgefuellt, saehe es aus wie ein Balkendiagramm, und
|
||||
genau das ist es ausdruecklich nicht. */
|
||||
fuellung: ['M17.8 6.6a1.7 1.7 0 1 0 0 3.4 1.7 1.7 0 0 0 0-3.4z'],
|
||||
linien: ['M3.4 19.6h4.2v-3.4H3.4z', 'M9.9 19.6h4.2v-6.6H9.9z',
|
||||
'M16.4 19.6h4.2v-6.2h-4.2z',
|
||||
'M17.8 6.6a1.7 1.7 0 1 0 0 3.4 1.7 1.7 0 0 0 0-3.4z',
|
||||
'M5.5 16.2 12 13l5.8-2.7'],
|
||||
},
|
||||
wissen: {
|
||||
/* Die linke Seite gefüllt: ein aufgeschlagenes Buch hat eine
|
||||
Seite im Licht und eine im Schatten. */
|
||||
|
||||
@@ -663,7 +663,37 @@
|
||||
Die Zuordnung kommt aus assets/js/bereiche.js, also aus derselben
|
||||
Liste wie die Kachel selbst. Wer dort eine Farbe ändert, ändert
|
||||
Kachel UND Seite -- sie können gar nicht auseinanderlaufen. */
|
||||
(function seiteEinfaerben() {
|
||||
/* ==== GEFUNDEN AM 19.09.2026: DIE HAELFTE DER SEITEN BEKAM NICHTS =====
|
||||
|
||||
Der Umbau von heute Nacht heisst "Jede Seite traegt die Farbe ihrer
|
||||
Kachel". Nachgemessen traf das auf 18 von 34 Seiten zu. Elf
|
||||
Seiten mit echter Kachel gingen leer aus:
|
||||
|
||||
befinden, bewerben, bewerbungen, entwicklung, hilfe, rechte,
|
||||
talente, teamlage, teilen, treff-moderation, treff-regeln,
|
||||
unsere-seiten
|
||||
|
||||
Es sind ausgerechnet die von Team Dogi -- also die, die Filipe
|
||||
taeglich benutzt.
|
||||
|
||||
DER GRUND: `B.zuSeite()` sucht nur in `Bereiche.GRUPPEN`, und dort
|
||||
stehen ausschliesslich die Kacheln der AGENTUR. Die Kacheln von
|
||||
Team Dogi und vom Treff liefert der SERVER (`ich.bereiche`,
|
||||
`ich.bereiche_zusatz`) -- die kannte diese Stelle nicht.
|
||||
|
||||
Auffallen konnte das nicht: Eine Seite ohne Farbe sieht aus wie
|
||||
eine Seite, die eben keine hat. Dieselbe Sorte Fehler wie bei
|
||||
`chat.html` am 09.09. -- im Quelltext sieht die Zeile richtig aus,
|
||||
und nur das Nachzaehlen findet sie.
|
||||
|
||||
JETZT ZWEIMAL, UND DAS IST ABSICHT:
|
||||
1. sofort beim Laden aus GRUPPEN -- die Agenturseiten haben ihre
|
||||
Farbe damit ohne ein einziges Flackern, wie bisher.
|
||||
2. noch einmal, sobald der Server gesagt hat, wer man ist. Nur
|
||||
wenn beim ersten Mal nichts gefunden wurde.
|
||||
Zweimal zeichnen verhindert `kopf.dataset.gezeichnet`.
|
||||
================================================================= */
|
||||
function seiteEinfaerben(zusatz) {
|
||||
const B = window.Bereiche;
|
||||
/* Der Kalender hat einen eigenen Kopf (.k-kopf), weil dort der
|
||||
Zeitraum die Überschrift ist. Er soll sein Zeichen trotzdem
|
||||
@@ -671,8 +701,11 @@
|
||||
einzige ohne. */
|
||||
if (!B) return;
|
||||
|
||||
const bereich = B.zuSeite(location.pathname, location.search);
|
||||
if (!bereich) return;
|
||||
/* Erst die Kacheln, die der Server geschickt hat -- sie sind die
|
||||
genauere Auskunft: Wer diese Seite oeffnen darf, hat sie auch als
|
||||
Kachel bekommen. Danach die Agenturliste aus dem Browser. */
|
||||
const bereich = ausListe(zusatz) || B.zuSeite(location.pathname, location.search);
|
||||
if (!bereich) return false;
|
||||
|
||||
/* ==== ERST DIE SEITE, DANN DIE PLAKETTE ==========================
|
||||
|
||||
@@ -721,9 +754,12 @@
|
||||
ohne ihn anzufassen. Dadurch funktioniert das auf allen dreizehn
|
||||
Seiten, ohne dreizehn HTML-Dateien zu ändern. */
|
||||
const kopf = document.querySelector('.kopf-zeile, .k-kopf');
|
||||
if (!kopf) return;
|
||||
/* `true` und nicht `false`: Ton und Buehne SIND gesetzt. Nur die
|
||||
Plakette faellt aus, weil es keinen Kopf gibt -- das ist kein
|
||||
Grund, es beim zweiten Anlauf noch einmal zu versuchen. */
|
||||
if (!kopf || kopf.dataset.gezeichnet === 'ja') return true;
|
||||
const erstes = kopf.firstElementChild;
|
||||
if (!erstes) return;
|
||||
if (!erstes) return true;
|
||||
|
||||
const plakette = document.createElement('span');
|
||||
plakette.className = 'kopf-zeichen';
|
||||
@@ -745,7 +781,43 @@
|
||||
schatten.append(B.zeichenBauen(bereich.zeichen, 'kopf-wasserzeichen__svg'));
|
||||
kopf.append(schatten);
|
||||
kopf.dataset.gezeichnet = 'ja';
|
||||
})();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Die Kachel zu dieser Seite in einer Liste vom Server suchen.
|
||||
*
|
||||
* UEBER DAS ZIEL UND NICHT UEBER DEN NAMEN. Namen aendern sich --
|
||||
* am 19.09. hiess eine Kachel morgens noch "Entwicklung" und
|
||||
* mittags "Eure Aufgaben". Das Ziel ist die Angabe, die bleibt. */
|
||||
function ausListe(listen) {
|
||||
if (!Array.isArray(listen) || !listen.length) return null;
|
||||
const datei = String(location.pathname).split('/').pop() || 'start.html';
|
||||
const gesucht = datei + (location.search || '');
|
||||
let treffer = null;
|
||||
for (const k of listen) {
|
||||
if (!k || !k.ziel) continue;
|
||||
if (k.ziel === gesucht) return k;
|
||||
if (!treffer && String(k.ziel).split('?')[0] === datei) treffer = k;
|
||||
}
|
||||
return treffer;
|
||||
}
|
||||
|
||||
const schonEingefaerbt = seiteEinfaerben(null);
|
||||
|
||||
/* Der zweite Anlauf. `werZeigen` ruft jede Seite auf, sobald der
|
||||
Server geantwortet hat -- es ist die einzige Stelle, an der die
|
||||
Kacheln von Team Dogi im Browser ankommen. */
|
||||
if (!schonEingefaerbt) {
|
||||
const vorherWer = window.werZeigen;
|
||||
window.werZeigen = function (ich) {
|
||||
try { if (typeof vorherWer === 'function') vorherWer.apply(this, arguments); }
|
||||
finally {
|
||||
seiteEinfaerben([
|
||||
...(ich?.bereiche || []), ...(ich?.bereiche_zusatz || []),
|
||||
]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* ---------- Der Schriftzug oben links -----------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
/* =====================================================================
|
||||
ENTWICKLUNG — die Auswertung je Person (19.09.2026)
|
||||
|
||||
Zwei Gesichter, und welches man sieht, entscheidet der Server:
|
||||
Wer das Team führt, bekommt die Karten; wer dazugehört, bekommt
|
||||
ausschließlich die Bewegung in der eigenen.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
DIE DIAGRAMME SIND SELBST GEZEICHNET
|
||||
|
||||
Kein Diagrammpaket. Zwei Formen, beide aus wenigen Zeilen:
|
||||
|
||||
BEWEGUNG Balken um eine Nulllinie, nach oben „nach vorn“, nach
|
||||
unten „zurück“. Sechs Monate, mehr passt auf ein Handy
|
||||
nicht und sagt auch nichts.
|
||||
STREIFEN Ein Raster: Zeile = Punkt, Spalte = Monat, Zelle = die
|
||||
Stufe, die damals galt. Dieselbe Bauform wie der
|
||||
Befinden-Verlauf — wer die eine kennt, liest die andere.
|
||||
|
||||
Ein Paket dafür wären 60 kB über eine Leitung, auf der jemand mit dem
|
||||
Handy im Stream steht. Und es brächte seine eigenen Farben mit, die
|
||||
dann neben den Hausfarben stünden.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
DIE FARBE IST NIE DIE AUSKUNFT
|
||||
|
||||
Jede Zelle trägt das Zeichen ihrer Stufe als Text (✓ ↗ ! –), jeder
|
||||
Balken seine Zahl. Wer Farben schlecht unterscheidet, liest genau
|
||||
dasselbe. Das ist im Haus seit dem Befinden-Verlauf so.
|
||||
|
||||
Und es gibt hier kein Rot. „Da hakt es“ ist eine Beobachtung, kein
|
||||
Alarm; ein Rückschritt heißt oft nur, dass jemand genauer hingesehen
|
||||
hat als beim letzten Mal.
|
||||
===================================================================== */
|
||||
(() => {
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const el = (t, k, text) => {
|
||||
const n = document.createElement(t);
|
||||
if (k) n.className = k;
|
||||
if (text !== undefined) n.textContent = text;
|
||||
return n;
|
||||
};
|
||||
const melde = (t) => { const f = $('fehler'); if (f) { f.textContent = t; f.hidden = !t; } };
|
||||
|
||||
async function hole(pfad) {
|
||||
const a = await fetch(pfad, { credentials: 'same-origin' });
|
||||
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Die vier Stufen, wie der Server sie schickt — Zeichen inklusive.
|
||||
Eine eigene Liste hier wäre die, die beim nächsten Nachschärfen
|
||||
auseinanderläuft. */
|
||||
let stufen = [];
|
||||
const zeichenVon = (s) => stufen.find((x) => x.schluessel === s)?.zeichen || '·';
|
||||
const namenVon = (s) => stufen.find((x) => x.schluessel === s)?.name || s;
|
||||
|
||||
const MONATSNAME = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];
|
||||
function monatKurz(m) {
|
||||
const [j, mm] = String(m).split('-').map(Number);
|
||||
if (!j || !mm) return m;
|
||||
return `${MONATSNAME[mm - 1]}${mm === 1 ? ' ' + String(j).slice(2) : ''}`;
|
||||
}
|
||||
function datumKurz(iso) {
|
||||
const t = Date.parse(iso || '');
|
||||
if (!Number.isFinite(t)) return '';
|
||||
return new Date(t).toLocaleDateString('de-DE',
|
||||
{ day: '2-digit', month: '2-digit', year: '2-digit' });
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIAGRAMM 1 — BEWEGUNG
|
||||
|
||||
ZWEI RICHTUNGEN UM EINE NULLLINIE. „Nach vorn“ und „zurück“ sind
|
||||
dieselbe Größe mit verschiedenem Vorzeichen; zwei getrennte
|
||||
Diagramme würden dazu verleiten, sie wie zwei Themen zu lesen.
|
||||
|
||||
EINE ACHSE, EIN MASSSTAB. Der höchste Balken bestimmt ihn, und er
|
||||
gilt für oben und unten gleichermaßen — sonst sähe ein einzelner
|
||||
Rückschritt so groß aus wie fünf Fortschritte.
|
||||
|
||||
KEINE ZAHL AN JEDEM BALKEN. Nur dort, wo etwas steht. Eine Null an
|
||||
jedem leeren Monat wäre Lärm; die leere Spalte sagt dasselbe.
|
||||
===================================================================== */
|
||||
function bewegungBauen(bewegung, spalten) {
|
||||
const wurzel = el('div', 'w-bewegung');
|
||||
|
||||
const hoechst = Math.max(1, ...bewegung.map(
|
||||
(m) => Math.max(m.vor + m.geklaert + m.neu, m.zurueck + m.offen)));
|
||||
|
||||
const gitter = el('div', 'w-bewegung__gitter');
|
||||
gitter.style.setProperty('--spalten', String(spalten.length));
|
||||
|
||||
for (const m of bewegung) {
|
||||
const spalte = el('div', 'w-bewegung__spalte');
|
||||
|
||||
const oben = el('div', 'w-bewegung__halb w-bewegung__halb--oben');
|
||||
const obenWert = m.vor + m.geklaert + m.neu;
|
||||
if (obenWert) {
|
||||
const b = el('div', 'w-bewegung__balken');
|
||||
b.dataset.richtung = 'vor';
|
||||
b.style.height = `${Math.round((obenWert / hoechst) * 100)}%`;
|
||||
b.title = [
|
||||
m.vor ? `${m.vor}× nach vorn` : null,
|
||||
m.geklaert ? `${m.geklaert}× geklärt` : null,
|
||||
m.neu ? `${m.neu}× zum ersten Mal` : null,
|
||||
].filter(Boolean).join(', ');
|
||||
oben.append(el('span', 'w-bewegung__zahl', String(obenWert)), b);
|
||||
}
|
||||
spalte.append(oben);
|
||||
|
||||
spalte.append(el('div', 'w-bewegung__null'));
|
||||
|
||||
const unten = el('div', 'w-bewegung__halb w-bewegung__halb--unten');
|
||||
const untenWert = m.zurueck + m.offen;
|
||||
if (untenWert) {
|
||||
const b = el('div', 'w-bewegung__balken');
|
||||
b.dataset.richtung = 'zurueck';
|
||||
b.style.height = `${Math.round((untenWert / hoechst) * 100)}%`;
|
||||
b.title = [
|
||||
m.zurueck ? `${m.zurueck}× zurückgesetzt` : null,
|
||||
m.offen ? `${m.offen}× auf „Kann ich nicht sagen“` : null,
|
||||
].filter(Boolean).join(', ');
|
||||
unten.append(b, el('span', 'w-bewegung__zahl', String(untenWert)));
|
||||
}
|
||||
spalte.append(unten);
|
||||
|
||||
spalte.append(el('div', 'w-bewegung__monat', monatKurz(m.monat)));
|
||||
gitter.append(spalte);
|
||||
}
|
||||
wurzel.append(gitter);
|
||||
|
||||
/* DIE LEGENDE IST PFLICHT, nicht Zierde: Ohne sie trägt allein die
|
||||
Farbe die Bedeutung, und das ist genau die Stelle, an der ein
|
||||
Diagramm für manche Leute aufhört zu funktionieren. */
|
||||
const legende = el('div', 'w-legende');
|
||||
for (const [richtung, text] of [
|
||||
['vor', 'nach vorn · geklärt · zum ersten Mal'],
|
||||
['zurueck', 'zurückgesetzt · wieder offen'],
|
||||
]) {
|
||||
const p = el('span', 'w-legende__punkt');
|
||||
const fleck = el('span', 'w-legende__fleck');
|
||||
fleck.dataset.richtung = richtung;
|
||||
p.append(fleck, el('span', 'w-legende__text', text));
|
||||
legende.append(p);
|
||||
}
|
||||
wurzel.append(legende);
|
||||
return wurzel;
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIAGRAMM 2 — DER STREIFEN
|
||||
===================================================================== */
|
||||
function streifenBauen(streifen, spalten) {
|
||||
const gitter = el('div', 'w-streifen__gitter');
|
||||
|
||||
const kopf = el('div', 'w-streifen__zeile w-streifen__zeile--kopf');
|
||||
kopf.append(el('span', 'w-streifen__punkt-name', ''));
|
||||
for (const m of spalten) kopf.append(el('span', 'w-streifen__monat', monatKurz(m)));
|
||||
gitter.append(kopf);
|
||||
|
||||
for (const z of streifen) {
|
||||
const zeile = el('div', 'w-streifen__zeile');
|
||||
zeile.append(el('span', 'w-streifen__punkt-name', z.titel));
|
||||
for (const c of z.zellen) {
|
||||
const zelle = el('span', 'w-streifen__zelle',
|
||||
c.stufe ? zeichenVon(c.stufe) : '·');
|
||||
if (c.stufe) {
|
||||
zelle.dataset.stufe = c.stufe;
|
||||
if (c.alt) zelle.dataset.alt = 'ja';
|
||||
zelle.title = `${monatKurz(c.monat)}: ${namenVon(c.stufe)}`
|
||||
+ (c.seit ? ` (gesetzt am ${datumKurz(c.seit)})` : '')
|
||||
+ (c.alt ? ' – älter als drei Monate' : '');
|
||||
} else {
|
||||
zelle.dataset.stufe = 'leer';
|
||||
zelle.title = `${monatKurz(c.monat)}: noch keine Einschätzung`;
|
||||
}
|
||||
zeile.append(zelle);
|
||||
}
|
||||
gitter.append(zeile);
|
||||
}
|
||||
return gitter;
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIE TEXTE
|
||||
===================================================================== */
|
||||
function texteBauen(texte) {
|
||||
const wurzel = el('div', 'w-texte');
|
||||
for (const t of texte) {
|
||||
const kasten = el('section', 'w-text');
|
||||
kasten.dataset.art = t.art;
|
||||
kasten.append(el('h4', 'w-text__titel', t.titel));
|
||||
kasten.append(el('p', 'w-text__satz', t.satz));
|
||||
if (t.punkte?.length) {
|
||||
const liste = el('ul', 'w-text__liste');
|
||||
for (const p of t.punkte) {
|
||||
const zeile = el('li', 'w-text__zeile');
|
||||
zeile.append(el('span', 'w-text__punkt', p.titel));
|
||||
const rechts = el('span', 'w-text__rechts');
|
||||
if (p.von && p.zu) {
|
||||
rechts.append(el('span', 'w-text__stufe', namenVon(p.von)));
|
||||
rechts.append(el('span', 'w-text__pfeil', '→'));
|
||||
}
|
||||
if (p.zu) {
|
||||
const s = el('span', 'w-text__stufe', namenVon(p.zu));
|
||||
s.dataset.stufe = p.zu;
|
||||
rechts.append(s);
|
||||
}
|
||||
if (p.erwartung) rechts.append(el('span', 'w-text__stufe', p.erwartung));
|
||||
if (p.tage != null) rechts.append(el('span', 'w-text__tage', `seit ${p.tage} T.`));
|
||||
else if (p.am) rechts.append(el('span', 'w-text__tage', datumKurz(p.am)));
|
||||
zeile.append(rechts);
|
||||
liste.append(zeile);
|
||||
}
|
||||
kasten.append(liste);
|
||||
}
|
||||
wurzel.append(kasten);
|
||||
}
|
||||
return wurzel;
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIE KARTE EINER PERSON
|
||||
===================================================================== */
|
||||
async function karteZeigen(id) {
|
||||
const a = await hole(`/workspace/api/werdegang/person/${id}`);
|
||||
if (!a.ok) { melde('Diese Karte lässt sich gerade nicht öffnen.'); return; }
|
||||
const d = await a.json();
|
||||
stufen = d.stufen || [];
|
||||
|
||||
$('liste-block').hidden = true;
|
||||
$('mich-block').hidden = true;
|
||||
$('karte-block').hidden = false;
|
||||
$('karte-name').textContent = `${d.person.name} · ${d.person.rolle_name}`;
|
||||
$('karte-satz').textContent = d.satz;
|
||||
|
||||
/* ==== DER DRITTE AUSGANG ========================================
|
||||
|
||||
Nicht „in Ordnung“ und nicht „nicht in Ordnung“, sondern:
|
||||
KONNTE NOCH NICHT NACHSEHEN — mit Grund. Ein Diagramm über
|
||||
einen einzigen Zeitpunkt wäre kein Diagramm, sondern ein
|
||||
Balken, und er sähe aus wie eine Aussage.
|
||||
============================================================= */
|
||||
const bBlock = $('bewegung-block'), sBlock = $('streifen-block');
|
||||
if (!d.verlauf_moeglich) {
|
||||
bBlock.hidden = false; sBlock.hidden = true;
|
||||
$('bewegung').replaceChildren();
|
||||
$('bewegung-satz').textContent = d.gesetzt === 0
|
||||
? 'Zu dieser Person ist noch nichts eingetragen. Sobald die erste '
|
||||
+ 'Einschätzung steht, beginnt hier der Verlauf.'
|
||||
: `Es gibt bisher ${d.monate_mit_inhalt === 1 ? 'einen Monat' : d.monate_mit_inhalt + ' Monate'} `
|
||||
+ `mit Einschätzungen. Ein Verlauf braucht mindestens ${d.verlauf_ab} – `
|
||||
+ 'vorher stünde hier eine Linie, die nichts bedeutet.';
|
||||
} else {
|
||||
bBlock.hidden = false; sBlock.hidden = false;
|
||||
$('bewegung-satz').textContent =
|
||||
`Die letzten ${d.spalten.length} Monate. Nach oben: weitergekommen, `
|
||||
+ 'geklärt oder zum ersten Mal beantwortet. Nach unten: zurückgesetzt '
|
||||
+ 'oder wieder offen. Beides ist eine Auskunft, keines ist eine Note.';
|
||||
$('bewegung').replaceChildren(bewegungBauen(d.bewegung, d.spalten));
|
||||
|
||||
$('streifen-satz').textContent =
|
||||
'Was in einem Monat galt, gilt auch im nächsten, solange niemand etwas '
|
||||
+ `ändert – deshalb sind die Zellen durchgezogen. Blasse Zellen sind älter `
|
||||
+ `als ${d.verblasst_nach_tagen} Tage und zählen nicht mehr mit.`;
|
||||
$('streifen').replaceChildren(streifenBauen(d.streifen, d.spalten));
|
||||
}
|
||||
|
||||
const tBlock = $('texte-block');
|
||||
if (d.texte?.length) {
|
||||
tBlock.hidden = false;
|
||||
$('texte').replaceChildren(texteBauen(d.texte));
|
||||
} else {
|
||||
tBlock.hidden = true;
|
||||
}
|
||||
|
||||
/* Die Adresse merkt sich, welche Karte offen ist — damit „Zurück“
|
||||
auf die Liste führt und nicht aus der Seite heraus. */
|
||||
history.replaceState(null, '', `?p=${id}`);
|
||||
window.scrollTo({ top: 0, behavior: 'instant' });
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIE LISTE
|
||||
|
||||
KEINE SORTIERUNG NACH AKTIVITÄT. Eine Liste, die die mit der
|
||||
meisten Bewegung nach oben stellt, ist eine Rangliste mit einem
|
||||
anderen Namen. Der Server sortiert nach Namen, und dabei bleibt es.
|
||||
===================================================================== */
|
||||
function listeBauen(d) {
|
||||
const wurzel = el('div', 'w-liste__gitter');
|
||||
for (const p of d.liste) {
|
||||
const karte = el('button', 'w-karte');
|
||||
karte.type = 'button';
|
||||
karte.append(el('span', 'w-karte__name', p.name));
|
||||
karte.append(el('span', 'w-karte__rolle', p.rolle_name));
|
||||
|
||||
const zeilen = el('span', 'w-karte__zeilen');
|
||||
zeilen.append(el('span', 'w-karte__zeile',
|
||||
p.gesetzt === 0
|
||||
? 'Noch nichts eingetragen'
|
||||
: `${p.gesetzt} von ${p.gesamt} Punkten beantwortet`));
|
||||
zeilen.append(el('span', 'w-karte__zeile',
|
||||
p.bewegt === 0
|
||||
? `In ${d.monate} Monaten keine Änderung`
|
||||
: `${p.bewegt} Änderung${p.bewegt === 1 ? '' : 'en'} in ${d.monate} Monaten`
|
||||
+ (p.vor ? ` · ${p.vor} nach vorn` : '')));
|
||||
if (p.zuletzt_tage != null) {
|
||||
const z = el('span', 'w-karte__zeile',
|
||||
p.zuletzt_tage === 0 ? 'Zuletzt heute angesehen'
|
||||
: `Zuletzt vor ${p.zuletzt_tage} Tag${p.zuletzt_tage === 1 ? '' : 'en'} angesehen`);
|
||||
/* DIE EINZIGE FARBE IN DER LISTE, und sie sagt etwas über UNS:
|
||||
Wer seit über drei Monaten nicht hingesehen hat, hat keine
|
||||
Auskunft über diesen Menschen, sondern über sich. */
|
||||
if (p.zuletzt_tage >= 90) z.dataset.alt = 'ja';
|
||||
zeilen.append(z);
|
||||
}
|
||||
karte.append(zeilen);
|
||||
karte.addEventListener('click', () => karteZeigen(p.id).catch(() => {}));
|
||||
wurzel.append(karte);
|
||||
}
|
||||
return wurzel;
|
||||
}
|
||||
|
||||
async function listeZeigen() {
|
||||
const a = await hole('/workspace/api/werdegang/liste');
|
||||
if (a.status === 404) return false; /* kein Leitungszugang */
|
||||
if (!a.ok) { melde('Die Karten lassen sich gerade nicht laden.'); return true; }
|
||||
const d = await a.json();
|
||||
|
||||
$('karte-block').hidden = true;
|
||||
$('mich-block').hidden = true;
|
||||
$('liste-block').hidden = false;
|
||||
$('liste').removeAttribute('aria-busy');
|
||||
$('liste-hinweis').textContent = d.hinweis;
|
||||
|
||||
if (!d.liste.length) {
|
||||
$('liste').replaceChildren(el('p', 'leise',
|
||||
'Es gibt noch niemanden mit einer Karte. Sobald jemand ins Team kommt, '
|
||||
+ 'steht er hier.'));
|
||||
} else {
|
||||
$('liste').replaceChildren(listeBauen(d));
|
||||
}
|
||||
history.replaceState(null, '', location.pathname);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DIE EIGENE SICHT
|
||||
|
||||
OHNE NAMEN, OHNE ANLASS, OHNE STREIFEN. Sie sieht, DASS sich etwas
|
||||
bewegt hat und wann — nicht, wer was gesetzt hat und was
|
||||
danebensteht. Der Anlass ist für ein Gespräch aufgeschrieben, nicht
|
||||
zum Lesen ohne den Menschen dazu.
|
||||
===================================================================== */
|
||||
async function michZeigen() {
|
||||
const a = await hole('/workspace/api/werdegang/mich');
|
||||
if (!a.ok) { melde('Das lässt sich gerade nicht laden.'); return; }
|
||||
const d = await a.json();
|
||||
|
||||
$('liste-block').hidden = true;
|
||||
$('karte-block').hidden = true;
|
||||
$('mich-block').hidden = false;
|
||||
$('titel').textContent = 'Deine Entwicklung';
|
||||
$('unterzeile').textContent =
|
||||
'Was sich über die Zeit bewegt hat. Keine Note, keine Punktzahl.';
|
||||
|
||||
if (!d.hat_karte) {
|
||||
$('mich-satz').textContent =
|
||||
'Für deine Rolle wird keine Entwicklungskarte geführt. Das ist keine '
|
||||
+ 'Lücke – es gibt schlicht nichts, was hier stehen müsste.';
|
||||
$('mich-hinweis').textContent = '';
|
||||
return;
|
||||
}
|
||||
stufen = d.stufen || stufen;
|
||||
$('mich-satz').textContent = d.satz;
|
||||
$('mich-hinweis').textContent = d.hinweis;
|
||||
const etwas = d.bewegung?.some((m) => m.vor || m.zurueck || m.neu || m.geklaert);
|
||||
$('mich-bewegung').replaceChildren(
|
||||
etwas ? bewegungBauen(d.bewegung, d.spalten) : el('span'));
|
||||
}
|
||||
|
||||
/* ---- Start ---------------------------------------------------------- */
|
||||
$('zur-liste').addEventListener('click', () => { listeZeigen().catch(() => {}); });
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
/* ERST DIE LISTE FRAGEN. Antwortet der Server mit 404, führt diese
|
||||
Person das Team nicht — dann ist die eigene Sicht die richtige.
|
||||
Die Rolle hier selbst zu prüfen hieße, dieselbe Entscheidung an
|
||||
zwei Stellen zu treffen, und die zweite ist immer die, die
|
||||
irgendwann etwas durchlässt. */
|
||||
const leitung = await listeZeigen();
|
||||
if (!leitung) { await michZeigen(); return; }
|
||||
|
||||
const p = new URLSearchParams(location.search).get('p');
|
||||
if (p) await karteZeigen(Number(p));
|
||||
} catch (f) {
|
||||
if (String(f?.message || '') !== 'abgemeldet') {
|
||||
melde('Die Seite lässt sich gerade nicht laden.');
|
||||
}
|
||||
}
|
||||
})();
|
||||
})();
|
||||
+12
-12
@@ -12,19 +12,19 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -291,10 +291,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -168,10 +168,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -7,14 +7,14 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -105,9 +105,9 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/befinden.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/befinden.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+18
-18
@@ -12,24 +12,24 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191018" />
|
||||
<!-- Wegen des Blocks „Willst du dazugehoeren?" auf dem Brett
|
||||
„Mitmachen". Eine eigene Kopie der Regel hier waere die zweite
|
||||
Stelle, an der dieselbe Sache anders aussieht. -->
|
||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -251,14 +251,14 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+15
-15
@@ -7,17 +7,17 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -108,10 +108,10 @@
|
||||
</main>
|
||||
|
||||
<!-- Der Fuß gehört zu jeder Seite - siehe pruef-css-klassen. -->
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/auskunft.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bewerben.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/auskunft.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bewerben.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -7,16 +7,16 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -80,10 +80,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/auskunft.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bewerbungen.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/auskunft.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bewerbungen.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -89,11 +89,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,26 +12,26 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
||||
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
||||
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
||||
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
||||
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
||||
Dialog zu sehen bekam. -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -341,15 +341,15 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609190208"></script>
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208"></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018"></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018"></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018"></script>
|
||||
<!-- ANRUF VOR CHAT (18.09.2026): anruf.js stellt `window.anrufStarten`
|
||||
bereit, und chat.js baut die Knoepfe nur, wenn es das gibt.
|
||||
Andersherum waere die Reihenfolge eine stille Abhaengigkeit --
|
||||
die Knoepfe fehlten, und niemand wuesste warum. -->
|
||||
<script src="assets/js/anruf.js?v=202609190208"></script>
|
||||
<script src="assets/js/chat.js?v=202609190208"></script>
|
||||
<script src="assets/js/anruf.js?v=202609191018"></script>
|
||||
<script src="assets/js/chat.js?v=202609191018"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+15
-15
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -179,12 +179,12 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/content.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/content.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -36,15 +36,15 @@
|
||||
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
|
||||
type="image/webp" media="(max-width: 899px)" />
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<!-- Diese Wand steht NUR auf der Adresse von Team Dogi (ueberall sonst
|
||||
404, siehe crewWeiche). Sie darf das Haus deshalb direkt nennen
|
||||
statt ueber den Platzhalter haus.css zu gehen -- und sie MUSS es:
|
||||
Eine Zugangswand, die sich erst nach dem Umbiegen faerbt, waere
|
||||
die eine Seite, auf der man das Umfaerben sieht. -->
|
||||
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="gate gate--crew">
|
||||
@@ -339,6 +339,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/gate.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/gate.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -22,25 +22,25 @@
|
||||
},
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/img/app-symbole/crew-192.png?v=202609190208",
|
||||
"src": "/assets/img/app-symbole/crew-192.png?v=202609191018",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/crew-512.png?v=202609190208",
|
||||
"src": "/assets/img/app-symbole/crew-512.png?v=202609191018",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609190208",
|
||||
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609191018",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609190208",
|
||||
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609191018",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
|
||||
+13
-13
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -108,10 +108,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -7,14 +7,14 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -106,9 +106,9 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/entwicklung.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/entwicklung.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -7,13 +7,13 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/hilfe.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/hilfe.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -141,9 +141,9 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/hilfe.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/hilfe.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
|
||||
type="image/webp" media="(max-width: 899px)" />
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="gate">
|
||||
@@ -401,6 +401,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/gate.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/gate.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -377,11 +377,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -444,10 +444,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208"></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208"></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208"></script>
|
||||
<script src="assets/js/leistung.js?v=202609190208"></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018"></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018"></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018"></script>
|
||||
<script src="assets/js/leistung.js?v=202609191018"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -423,10 +423,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -214,11 +214,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -7,14 +7,14 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -134,9 +134,9 @@
|
||||
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
|
||||
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
|
||||
die seit dem 07.09. jede Seite danach absucht. -->
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/rechte.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/rechte.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -106,11 +106,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/report.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/report.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -173,10 +173,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,21 +12,21 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
||||
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -678,10 +678,10 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/start.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/start.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -85,11 +85,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -150,11 +150,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/auskunft.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/auskunft.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -7,18 +7,18 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191018" />
|
||||
<!-- Wegen des Kastens „Anfragen“ ueber der Liste. Eine eigene Kopie
|
||||
der Regel hier waere die zweite Stelle, an der dieselbe Sache
|
||||
anders aussieht. -->
|
||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -111,9 +111,9 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/talente.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/talente.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -7,20 +7,20 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -110,10 +110,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/team.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/team.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -7,20 +7,20 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<!-- `data-buehne` steht hier fest und nicht im Skript: Die Kachel zu
|
||||
@@ -84,9 +84,9 @@
|
||||
Kaputt ist dabei nichts -- er funktioniert. Genau deshalb faellt
|
||||
es niemandem auf, und deshalb prueft pruef-css-klassen die
|
||||
ABHAENGIGKEIT statt der Optik. Hier hat sie angeschlagen. -->
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/teamlage.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/teamlage.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -7,13 +7,13 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -112,9 +112,9 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/teilen.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/teilen.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -126,9 +126,9 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/treff-moderation.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/treff-moderation.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -7,18 +7,18 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein. Auf der Adresse des
|
||||
Treffs biegt der Server genau diesen Dateinamen auf crew-haus.css
|
||||
um, und die Seite kommt schon beim ERSTEN Abruf in Lila und
|
||||
Babyblau an -- ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -221,10 +221,10 @@
|
||||
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
|
||||
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
|
||||
die seit dem 07.09. jede Seite danach absucht. -->
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/auskunft.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/treff-regeln.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/auskunft.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/treff-regeln.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,22 +12,22 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||
Dialog "Creator anlegen". -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -149,11 +149,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/unsere-seiten.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/unsere-seiten.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -217,9 +217,9 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/unsere-seiten.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/unsere-seiten.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>Entwicklung · Spicy & Dogi</title>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/werdegang.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
|
||||
<div class="schleier" aria-hidden="true"></div>
|
||||
|
||||
<header class="kopfleiste">
|
||||
<button type="button" class="zurueck-knopf" id="zurueck" hidden>
|
||||
<svg class="zurueck-knopf__pfeil" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M14.5 5.5 8 12l6.5 6.5" />
|
||||
</svg>
|
||||
<span class="zurueck-knopf__text">Zurück</span>
|
||||
</button>
|
||||
<p class="marke"><span class="marke__text"><a class="zurueck" href="start.html">Team Dogi</a> · Entwicklung</span></p>
|
||||
<div class="kopfleiste__rechts">
|
||||
<span class="wer" id="wer">…</span>
|
||||
<button type="button" class="abmelden" id="abmelden">Abmelden</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="inhalt">
|
||||
|
||||
<!-- ==================================================================
|
||||
ENTWICKLUNG — die Auswertung je Person (19.09.2026)
|
||||
|
||||
Filipe: „dan perfektionierst du eine neue wo diagramme texte und
|
||||
so zu jedem modi gemacht wird und diese kategorie kannst du
|
||||
entwicklung nenen."
|
||||
|
||||
DIESE SEITE HAT ZWEI GESICHTER, und welches man sieht, entscheidet
|
||||
der Server:
|
||||
|
||||
Leitung -> die Karten aller, die eine haben
|
||||
Ein Modi -> ausschliesslich die Bewegung in der EIGENEN Karte,
|
||||
ohne Namen, ohne Anlass, ohne wer-hat-was
|
||||
|
||||
WAS HIER NICHT STEHT UND NIE STEHEN WIRD: eine Punktzahl, ein
|
||||
Prozentwert, ein Durchschnitt, eine Rangliste, ein Vergleich
|
||||
zwischen zwei Menschen. Eine Zahl ueber einen Menschen wird
|
||||
gelesen, verglichen und weitererzaehlt — gebraucht wird ein
|
||||
Gespraechsanlass.
|
||||
============================================================== -->
|
||||
|
||||
<section class="kopf-zeile">
|
||||
<div>
|
||||
<p class="marke">Entwicklung & Nachwuchs</p>
|
||||
<h1 class="titel" id="titel">Entwicklung</h1>
|
||||
<p class="unterzeile" id="unterzeile">
|
||||
Wie sich jemand über die Zeit bewegt. Keine Note, kein Vergleich –
|
||||
jede Karte nur mit sich selbst.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
|
||||
|
||||
<!-- ---- Die Liste (nur Leitung) ---------------------------------- -->
|
||||
<section id="liste-block" hidden>
|
||||
<p class="w-hinweis" id="liste-hinweis"></p>
|
||||
<div id="liste" class="w-liste" aria-busy="true">
|
||||
<p class="leise">wird geladen …</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ---- Eine Karte (nur Leitung) --------------------------------- -->
|
||||
<section id="karte-block" hidden>
|
||||
<button type="button" class="knopf knopf--still knopf--klein" id="zur-liste">
|
||||
← Alle Karten
|
||||
</button>
|
||||
|
||||
<h2 class="w-name" id="karte-name">…</h2>
|
||||
<p class="w-satz" id="karte-satz">…</p>
|
||||
|
||||
<!-- Diagramm 1: Bewegung. Zwei Richtungen um eine Nulllinie —
|
||||
vorwaerts und rueckwaerts sind dieselbe Groesse mit
|
||||
verschiedenem Vorzeichen, nicht zwei Themen. -->
|
||||
<section class="w-teil" id="bewegung-block">
|
||||
<h3 class="e-abschnitt">Was sich bewegt hat</h3>
|
||||
<p class="w-teil__satz" id="bewegung-satz"></p>
|
||||
<div id="bewegung"></div>
|
||||
</section>
|
||||
|
||||
<!-- Diagramm 2: der Streifen. Eine Zeile je Punkt, eine Spalte je
|
||||
Monat. Das Zeichen sagt die Stufe, die Farbe verstaerkt nur. -->
|
||||
<section class="w-teil" id="streifen-block">
|
||||
<h3 class="e-abschnitt">Punkt für Punkt, Monat für Monat</h3>
|
||||
<p class="w-teil__satz" id="streifen-satz"></p>
|
||||
<div id="streifen" class="w-streifen"></div>
|
||||
</section>
|
||||
|
||||
<!-- Die Texte. Reihenfolge ist Absicht: erst was sich bewegt hat,
|
||||
dann was traegt, erst danach was liegt. -->
|
||||
<section class="w-teil" id="texte-block">
|
||||
<h3 class="e-abschnitt">Was dazu zu sagen ist</h3>
|
||||
<div id="texte"></div>
|
||||
</section>
|
||||
|
||||
<p class="w-fuss">
|
||||
Auf dieser Seite gibt es keine Punktzahl und keinen Vergleich zwischen
|
||||
Personen. Was hier steht, ist ein Gesprächsanlass – kein Zeugnis.
|
||||
Einschätzungen setzt und ändert man im Katalog:
|
||||
<a href="entwicklung.html">Eure Aufgaben</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- ---- Die eigene Sicht (Modi) ---------------------------------- -->
|
||||
<section id="mich-block" hidden>
|
||||
<h2 class="e-abschnitt">Deine Entwicklung</h2>
|
||||
<p class="w-satz" id="mich-satz">…</p>
|
||||
<div id="mich-bewegung"></div>
|
||||
<p class="w-fuss" id="mich-hinweis"></p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/werdegang.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
+13
-13
@@ -12,20 +12,20 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609190208" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609190208" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191018" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609191018" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609191018" />
|
||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609190208" />
|
||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609191018" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -190,10 +190,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609190208" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609191018" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609191018" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user