Filipe: "ich will dass das viel schoener aussieht und nicht so ein
scheiss durcheinander, man wird ja bekloppt. das resume unten auf
jeder frage hab ich auch schon mehrmals gesagt muss weg ich will ein
ganzes gesamtresume was die leute animiert und pusht immer und mega
geile super team loesungen findet aber auch individuel so dass jeder
sich selber auch verbessert und steigert fuers team."
WAS WEG IST
Der Block "Was dein Verlauf sagt" -- eine Zeile JE FRAGE, jede mit
Titel, Marke, Einordnung aus der Forschung und einem "Was hilft". Bei
neun Fragen neun Kaesten untereinander. Genau das meint "man wird ja
bekloppt": Wenn alles gleich wichtig aussieht, ist nichts wichtig,
und man liest keinen davon.
WAS AN SEINE STELLE TRITT -- UND NICHTS VERLIERT
Die Erkenntnisse sind nicht weg, sie sind zusammengefasst:
DEIN NAECHSTER SCHRITT. Genau EINER, zu der Sache, die am meisten
haengt. Kernfragen zuerst -- sie werden in jeder Runde gestellt, ihr
"hakt" wiegt schwerer als das einer Zusatzfrage, die alle vier
Runden vorbeikommt. Haengt nichts, nimmt er die Sache, die noch
WAECHST: "nichts zu tun" ist nur fuer den richtig, bei dem alles
laeuft. Die Einordnung aus der Forschung steht daneben -- einmal
statt neunmal, als Grund, warum es dieser Schritt ist.
WAS DAS TEAM DAVON HAT. Abgeleitet aus der Lage, nicht erfunden.
Ein "gemeinsam schaffen wir das" an jemanden, bei dem fuenf von
neun Sachen haken, ist das Gegenteil von Hilfe -- in der schwersten
Lage ist der Team-Bezug deshalb eine Entlastung ("im Team ist
gerade nichts von dir gefragt"), keine Aufforderung. Vier Lagen,
vier Saetze. Die Pruefung verlangt ausdruecklich, dass es KEIN
Spruch ist, der immer passt.
ZWEI DINGE, DIE ERST DAS BILD GEZEIGT HAT
1. "Die Menge passt gerade" stand ZWEIMAL untereinander -- einmal im
Thema-Block, gleich darunter als Schritt. Kein Messwert meldet das;
im Bildschirmfoto sah es aus wie ein Fehler. Der Thema-Block nennt
die Titel jetzt nur noch, wenn es mehrere sind.
2. "Wenn du eine Sache angehst, dann die: Die Menge passt gerade" --
die Fragetitel im Katalog sind SAETZE, keine Substantive. Jetzt
"Fang hier an: 'Die Menge passt gerade'".
EIN FUND DER PRUEFUNG: Die TikTok-Quelle war beim Umzug ins Fazit
verlorengegangen. Filipe hatte sie ausdruecklich bestellt ("eine
professionelle auch mit infos und so aus aller welt was tiktok
angeht"). pruef-befinden hat es gemeldet.
Die Pruefung verlangte bis heute das GEGENTEIL -- eine Zeile je
Frage, zu jeder eine Einordnung. Sie ist beim Umbau rot geworden,
richtig so. Jetzt prueft sie, dass unter den Fragen KEIN Resuemee
mehr steht, dass es GENAU EINEN Schritt gibt (nicht "mindestens
einen" -- das waere auch bei neun gruen) und dass der Schritt Inhalt
hat, nicht nur einen Kasten.
pruef-befinden 116/0 (war 113), pruef-resuemee 35/0,
pruef-entwicklung 48/0, pruef-css-klassen 30/0.
Co-Authored-By: Claude Opus 5 <[email protected]>
969 lines
45 KiB
JavaScript
969 lines
45 KiB
JavaScript
/* =====================================================================
|
||
Wie geht's dir? (15.09.2026)
|
||
|
||
Filipe, mit dem Bildschirmfoto der Kachel: *„ich will dass du diese
|
||
seite perfektionnierst, denn gerade wenn ich drauf drücke geht die
|
||
entwicklungsseite auf."*
|
||
|
||
Er hat recht, und es war schlimmer als ein falscher Verweis: Die
|
||
Kachel verspricht „sechs Fragen – die Antworten sieht nur du" und
|
||
öffnete eine Seite mit Namen und Beobachtungen über andere Menschen.
|
||
Wer ihr glaubt und draufdrückt, sieht im ersten Moment das Gegenteil
|
||
dessen, was draufsteht.
|
||
|
||
---------------------------------------------------------------------
|
||
EINE SEITE, EIN ZWECK
|
||
|
||
`entwicklung.html` bediente zwei Fragen: „wie geht es dem Team?" und
|
||
„wie geht es mir?". Das ist eine Seite zu wenig. Die sechs Fragen
|
||
haben jetzt ihre eigene — und `entwicklung.html` wird dadurch selbst
|
||
einfacher, weil sie nur noch eine Sache tut.
|
||
|
||
---------------------------------------------------------------------
|
||
WAS DIE RECHERCHE ERGEBEN HAT (15.09.2026)
|
||
|
||
Zu Puls-/Befindensabfragen in kleinen Teams sagen die Quellen
|
||
übereinstimmend dreierlei:
|
||
|
||
1. KURZ. Fünf bis fünfzehn Fragen, unter zwei Minuten. Sechs sind
|
||
also richtig — daran ändert sich nichts.
|
||
|
||
2. WIEDERHOLT. Monatlich oder zweiwöchentlich. Genau das fehlte:
|
||
Bisher beantwortete man die sechs Fragen EINMAL, und die Antwort
|
||
stand für immer. Ein Befinden von vor drei Monaten ist keine
|
||
Auskunft mehr, sondern ein Andenken.
|
||
|
||
3. DER VERLAUF IST DIE AUSKUNFT, nicht der Einzelwert. „Niedrige
|
||
Energie über mehrere Wochen" ist das Warnzeichen — ein einzelnes
|
||
„da hakt es" ist ein schlechter Tag.
|
||
|
||
Deshalb zwei Dinge neu: ein RHYTHMUS (nach 14 Tagen wird wieder
|
||
gefragt) und ein VERLAUF, den nur die Person selbst sieht.
|
||
|
||
Quellen:
|
||
https://www.specific.app/blog/pulse-survey-questions-best-questions-for-burnout-to-detect-and-address-employee-engagement-risk
|
||
https://formbricks.com/blog/pulse-survey-questions
|
||
https://www.vantagecircle.com/en/blog/pulse-surveys/
|
||
|
||
---------------------------------------------------------------------
|
||
DER VERLAUF GEHÖRT IHR ALLEIN
|
||
|
||
Er wird in einer eigenen Tabelle geführt, und kein anderer Weg im
|
||
Haus liest sie. Die Ampel (wie viele es gerade schwer haben) zählt
|
||
weiterhin nur den AKTUELLEN Stand und kennt keine Namen — daran
|
||
ändert der Verlauf nichts.
|
||
|
||
Das ist kein Nebensatz: Eine Verlaufsakte über das Befinden eines
|
||
Menschen ist das Empfindlichste, was dieses Haus speichern kann. Sie
|
||
existiert nur, weil sie IHM hilft, sein eigenes Muster zu sehen.
|
||
===================================================================== */
|
||
|
||
import express from "express";
|
||
import { db, sitzungLesen, protokolliere, echteIp } from "./workspace.js";
|
||
import {
|
||
ENTWICKLUNG_BLOECKE, ENTWICKLUNG_PUNKTE, ENTWICKLUNG_STUFEN, BEFINDEN_FELDER,
|
||
} from "./workspace-entwicklungs-punkte.js";
|
||
import { tabellen as entwicklungTabellen } from "./workspace-entwicklung.js";
|
||
|
||
export const befindenRouter = express.Router();
|
||
|
||
/** Nach wie vielen Tagen wieder gefragt wird.
|
||
*
|
||
* 14 Tage — zweiwöchentlich, das untere Ende dessen, was die Quellen
|
||
* empfehlen (monatlich bis zweiwöchentlich). Häufiger wäre
|
||
* Umfragemüdigkeit: Wer jede Woche gefragt wird, klickt irgendwann
|
||
* dasselbe an wie beim letzten Mal, ohne nachzudenken — und dann misst
|
||
* die Abfrage nur noch sich selbst. */
|
||
export const RHYTHMUS_TAGE = 14;
|
||
|
||
/** Wie viele Zusatzfragen je Runde zum Kern dazukommen.
|
||
*
|
||
* VIER. Mit fünf Kernfragen macht das neun je Runde — die Quellen zu
|
||
* Puls-Abfragen sagen übereinstimmend fünf bis fünfzehn, unter zwei
|
||
* Minuten. Neun liegt mittig und lässt Platz: Der Katalog kann weiter
|
||
* wachsen, ohne dass eine einzelne Runde länger wird.
|
||
*
|
||
* Das ist der ganze Trick an Filipes zwei Wünschen, die sich zu
|
||
* widersprechen schienen — „viel mehr Fragen" und „kurz und knapp".
|
||
* Beides geht, nur nicht gleichzeitig in DERSELBEN Runde.
|
||
*
|
||
* WARUM VIER UND NICHT DREI (geändert 20.09.2026, mit den Feldern).
|
||
* Drei war richtig, solange es sieben wechselnde Fragen gab. Mit
|
||
* dreizehn dauerte es bei drei je Runde ZEHN Runden, bis jede einmal
|
||
* gestellt war — fast ein halbes Jahr, in dem eine Frage im Katalog
|
||
* steht und nie jemandem begegnet. Bei vier sind es vier Runden, also
|
||
* acht Wochen. Gemessen, nicht geschätzt; die Zahl steht in keiner
|
||
* Behauptung, sondern in pruef-befinden.mjs.
|
||
*
|
||
* Dieselbe Rechnung wieder anstellen, sobald der Katalog wächst:
|
||
* ceil(wechselnde / ZUSATZ_PRO_RUNDE) ist die Zahl der Runden, und
|
||
* mehr als fünf davon sind zu viele. */
|
||
export const ZUSATZ_PRO_RUNDE = 4;
|
||
|
||
/** So viele frühere Antworten bekommt die Person zu sehen.
|
||
*
|
||
* Sechs Stände sind drei Monate — lang genug, um ein Muster zu sehen,
|
||
* kurz genug, dass es kein Archiv wird. */
|
||
export const VERLAUF_MAX = 6;
|
||
|
||
const jetzt = () => new Date().toISOString();
|
||
|
||
let bereit = false;
|
||
function tabellen() {
|
||
if (bereit) return;
|
||
db().exec(`
|
||
/* DER VERLAUF DES EIGENEN BEFINDENS.
|
||
|
||
Eigene Tabelle und nicht „entwicklung_stand“: Dort steht der
|
||
AKTUELLE Stand, einer je Punkt und Beurteiler, und der wird
|
||
ueberschrieben. Genau das ist dort richtig -- eine Beobachtung
|
||
ueber einen Menschen soll aktuell sein, nicht historisch.
|
||
|
||
Hier ist es umgekehrt: Der Verlauf IST die Auskunft. Zwei
|
||
verschiedene Beduerfnisse, zwei Tabellen. Sie in eine zu zwingen
|
||
hiesse, eines von beiden falsch zu machen.
|
||
|
||
KEIN von_id: Was hier steht, hat die Person ueber sich selbst
|
||
gesagt. Eine Spalte fuer "wer hat das eingetragen" waere die
|
||
Einladung, eines Tages auch jemand anderen eintragen zu lassen. */
|
||
CREATE TABLE IF NOT EXISTS befinden_verlauf (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
person_id INTEGER NOT NULL REFERENCES personen(id) ON DELETE CASCADE,
|
||
runde TEXT NOT NULL, -- Zeitpunkt der Runde, alle sechs teilen ihn
|
||
schluessel TEXT NOT NULL,
|
||
stufe TEXT NOT NULL,
|
||
am TEXT NOT NULL
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_befinden_person
|
||
ON befinden_verlauf (person_id, runde);
|
||
`);
|
||
/* UND DIE TABELLE, AUS DER DIE ANTWORTEN KOMMEN.
|
||
|
||
Sie wird hier nicht angelegt, sondern angefordert: Die sechs
|
||
Antworten stehen in `entwicklung_stand`, und die legt die
|
||
Entwicklungsseite an -- beim ersten Aufruf, nicht beim Start.
|
||
Wer die neue Seite oeffnete, bevor irgendjemand die
|
||
Entwicklungsseite besucht hatte, bekam deshalb "no such table"
|
||
und eine 503. Gefunden hat das die Pruefung, nicht das Lesen. */
|
||
entwicklungTabellen();
|
||
bereit = true;
|
||
}
|
||
|
||
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("[befinden] Tabellen:", f?.message);
|
||
return res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||
}
|
||
next();
|
||
}
|
||
|
||
/** Der Block mit den sechs Fragen. Er ist als einziger `nurSelbst`. */
|
||
function block() {
|
||
return ENTWICKLUNG_BLOECKE.find((b) => b.nurSelbst) || null;
|
||
}
|
||
|
||
/** Wann war die letzte vollständige Runde — und ist wieder Zeit?
|
||
*
|
||
* EXPORTIERT SEIT DEM 17.09.2026, damit die automatische Erinnerung
|
||
* (workspace-push.js) DIESELBE Rechnung benutzt. Eine zweite Formel
|
||
* daneben ("älter als 14 Tage") wäre die Falle, die in diesem Haus
|
||
* schon mehrfach zugeschlagen hat: Sie stimmt am Tag des Schreibens
|
||
* und läuft danach auseinander -- und zwar still, weil beide Seiten
|
||
* plausible Zahlen liefern. */
|
||
export function rhythmus(personId) {
|
||
const kern = alleFragen().filter((p) => p.kern).length;
|
||
const letzte = db().prepare(
|
||
`SELECT runde, COUNT(*) AS n FROM befinden_verlauf
|
||
WHERE person_id = ? GROUP BY runde ORDER BY runde DESC LIMIT 1`).get(personId);
|
||
if (!letzte) return { letzte: null, faellig: true, tage: null, vollstaendig: false };
|
||
const tage = Math.floor((Date.now() - Date.parse(letzte.runde)) / 86400000);
|
||
return {
|
||
letzte: letzte.runde,
|
||
tage,
|
||
/* GEGEN DEN KERN GEZAEHLT, NICHT GEGEN DEN GANZEN KATALOG
|
||
(geaendert 19.09.2026, mit der Rotation).
|
||
|
||
Vorher stand hier `letzte.n >= gesamt` -- die Zahl ALLER Fragen.
|
||
Seit eine Runde nur Kern plus drei Zusatzfragen enthaelt, waere
|
||
das nie wieder erfuellt: Jede abgeschlossene Runde haette sich
|
||
selbst fuer unvollstaendig erklaert und waere sofort wieder
|
||
faellig gewesen. Die Seite haette jeden Tag gefragt, und genau
|
||
davor warnt die eigene Begruendung oben ("dann misst die Abfrage
|
||
nur noch sich selbst").
|
||
|
||
Der Kern ist in JEDER Runde enthalten -- daran laesst sich eine
|
||
Runde erkennen, ohne dass irgendwo stehen muss, wie viele Fragen
|
||
sie hatte. */
|
||
vollstaendig: letzte.n >= kern,
|
||
faellig: tage >= RHYTHMUS_TAGE || letzte.n < kern,
|
||
};
|
||
}
|
||
|
||
/** Alle Fragen des Blocks. */
|
||
function alleFragen() {
|
||
const b = block();
|
||
return b ? (ENTWICKLUNG_PUNKTE[b.schluessel] || []) : [];
|
||
}
|
||
|
||
/** Wie viele Runden diese Person schon abgeschlossen hat. */
|
||
function rundenBisher(personId) {
|
||
return db().prepare(
|
||
"SELECT COUNT(DISTINCT runde) AS n FROM befinden_verlauf WHERE person_id = ?")
|
||
.get(personId).n;
|
||
}
|
||
|
||
/* =====================================================================
|
||
WELCHE FRAGEN DIESE RUNDE — Kern plus Rotation (19.09.2026)
|
||
|
||
Filipe wollte "viel mehr Fragen" UND "kurz und knapp". Das geht, nur
|
||
nicht gleichzeitig in derselben Runde.
|
||
|
||
---------------------------------------------------------------------
|
||
DIE ROTATION WIRD GERECHNET, NICHT GEWUERFELT
|
||
|
||
Aus der Zahl der bisherigen Runden. Zwei Gruende, und beide sind
|
||
Hausregeln:
|
||
|
||
* Zufall hiesse, dass bei jedem Neuladen eine andere Liste
|
||
dasteht. Eine Seite, die sich unter den Fingern aendert,
|
||
beantwortet niemand zu Ende -- und die halbe Runde waere dann
|
||
auch noch eine andere als die angefangene.
|
||
* Gerechnet braucht es kein zusaetzliches Feld. Ein gespeicherter
|
||
Zeiger waere eine zweite Stelle, an der der Stand stimmen muss,
|
||
und die zweite ist immer die, die irgendwann danebenliegt.
|
||
|
||
---------------------------------------------------------------------
|
||
DER RAND, DEN MAN LEICHT UEBERSIEHT
|
||
|
||
Sieben Zusatzfragen, drei je Runde -- das geht nicht glatt auf.
|
||
Gerechnet wird deshalb mit Umlauf (`% laenge`), und die Auswahl
|
||
nimmt drei AUFEINANDERFOLGENDE aus dem Kreis. Ueber vier Runden
|
||
kommt damit jede Frage mindestens einmal dran; keine faellt dauerhaft
|
||
heraus, weil der Versatz um drei und die Laenge sieben teilerfremd
|
||
sind.
|
||
|
||
Waeren es einmal sechs Fragen bei drei je Runde, kaemen nur noch
|
||
zwei feste Haelften dran -- dieselben drei, immer abwechselnd. Der
|
||
Fall ist unten abgefangen: Bei einem gemeinsamen Teiler wird der
|
||
Versatz um eins erhoeht, damit der Kreis trotzdem vollstaendig
|
||
durchlaufen wird. Das ist kein Schoenheitsfehler -- eine Frage, die
|
||
nie drankommt, ist eine Frage, die man ebenso gut loeschen koennte.
|
||
===================================================================== */
|
||
|
||
/** Der groesste gemeinsame Teiler -- fuer den Rand oben. */
|
||
function ggt(a, b) { return b ? ggt(b, a % b) : a; }
|
||
|
||
/** Die Fragen dieser Runde: alle Kernfragen plus die Zusatzfragen, die
|
||
* turnusgemaess dran sind. */
|
||
export function fragenFuer(personId, runden = null) {
|
||
const alle = alleFragen();
|
||
const kern = alle.filter((p) => p.kern);
|
||
const rest = alle.filter((p) => !p.kern);
|
||
const n = runden == null ? rundenBisher(personId) : runden;
|
||
|
||
if (rest.length <= ZUSATZ_PRO_RUNDE) {
|
||
/* Weniger Zusatzfragen als Plaetze -- dann gibt es nichts zu
|
||
drehen, und alle kommen jede Runde dran. */
|
||
return { kern, zusatz: rest, alle: [...kern, ...rest], runden: n };
|
||
}
|
||
|
||
/* =====================================================================
|
||
DER RING IM REISSVERSCHLUSS (20.09.2026)
|
||
|
||
Seit die Fragen in sechs Felder gegliedert sind, reicht ein
|
||
einfacher Ring nicht mehr. Der Katalog ist nach Themen sortiert --
|
||
wer daraus drei AUFEINANDERFOLGENDE nimmt, bekommt meistens drei
|
||
aus demselben Feld. Eine Runde, die nur nach „Klarheit & Rueckhalt"
|
||
fragt, ist keine Befindensabfrage mehr, sondern ein Interview zu
|
||
einem Thema.
|
||
|
||
DER RING WIRD DESHALB VORHER VERZAHNT: erst je eine Frage aus
|
||
jedem Feld, dann je eine zweite, dann je eine dritte. Ein Fenster
|
||
von vier aufeinanderfolgenden Eintraegen trifft damit von selbst
|
||
vier verschiedene Felder -- ohne dass irgendwo eine Regel
|
||
nachrechnen muss, welche schon dran waren.
|
||
|
||
DIE FELDER OHNE KERNFRAGE STEHEN IM REISSVERSCHLUSS VORN. Sie sind
|
||
die einzigen, die eine Runde lang ganz fehlen KOENNTEN -- die
|
||
anderen sind ueber ihre Kernfrage ohnehin in jeder Runde dabei.
|
||
Gemessen ueber zwanzig Runden: 19 davon enthalten mindestens eine
|
||
Frage aus einem kernlosen Feld.
|
||
|
||
---------------------------------------------------------------------
|
||
WAS HIER VORHER STAND -- UND WARUM ES WEG IST
|
||
|
||
Der erste Anlauf reservierte JEDER Runde feste Plaetze fuer die
|
||
kernlosen Felder. Begruendet war das mit „sonst stuende in der
|
||
Auswertung nach Feldern ein leeres Kapitel, und das liest sich wie
|
||
Zustimmung".
|
||
|
||
Die Begruendung war falsch: Es gibt gar keine Auswertung je Feld.
|
||
Filipe wollte ausdruecklich EINE Zusammenfassung am Ende, nicht
|
||
eine je Kategorie -- und die sucht sich ihr Thema aus dem, was
|
||
tatsaechlich gefragt wurde. Ein Feld, das diese Runde nicht
|
||
drankam, behauptet also gar nichts.
|
||
|
||
Gekostet hat die Reservierung dafuer etwas Messbares: Zwei von
|
||
drei Plaetzen waren belegt, der Ring kam kaum voran, und bis jede
|
||
der 13 wechselnden Fragen einmal gestellt war, vergingen ZWOELF
|
||
Runden -- fast ein halbes Jahr. Gefunden hat das nicht das
|
||
Nachdenken, sondern `pruef-befinden.mjs`: Sie verlangt, dass ueber
|
||
wenige Runden jede Frage drankommt, und wurde rot.
|
||
|
||
Mit dem verzahnten Ring sind es vier Runden (acht Wochen), bei
|
||
gleichzeitig besserer Streuung. Beides gemessen, nicht geschaetzt.
|
||
===================================================================== */
|
||
const felderMitKern = new Set(kern.map((p2) => p2.feld).filter(Boolean));
|
||
|
||
/* Die Feldreihenfolge: kernlose zuerst, sonst wie im Katalog. Ein
|
||
stabiles Sortieren -- `sort` in JavaScript ist seit ES2019
|
||
garantiert stabil, die Katalogreihenfolge bleibt also innerhalb
|
||
der beiden Gruppen erhalten. */
|
||
const feldReihe = [...new Set(rest.map((p2) => p2.feld).filter(Boolean))]
|
||
.sort((a, b) => (felderMitKern.has(a) ? 1 : 0) - (felderMitKern.has(b) ? 1 : 0));
|
||
|
||
const koerbe = feldReihe.map((f) => rest.filter((p2) => p2.feld === f))
|
||
.filter((k) => k.length);
|
||
/* Fragen ganz ohne Feld kaemen sonst nie dran -- sie bekommen einen
|
||
eigenen Korb. Abgeleitet, nicht angenommen. */
|
||
const ohneFeld = rest.filter((p2) => !p2.feld);
|
||
if (ohneFeld.length) koerbe.push(ohneFeld);
|
||
|
||
const ring = [];
|
||
const tiefe = koerbe.reduce((m, k) => Math.max(m, k.length), 0);
|
||
for (let i = 0; i < tiefe; i++) {
|
||
for (const k of koerbe) if (k[i]) ring.push(k[i]);
|
||
}
|
||
|
||
/* Der Versatz muss zur Ringlaenge teilerfremd sein, sonst laeuft der
|
||
Kreis nie ganz durch -- dieselbe Ueberlegung wie vorher, nur jetzt
|
||
auf dem verzahnten Ring. */
|
||
let schritt = ZUSATZ_PRO_RUNDE;
|
||
while (ggt(schritt, ring.length) !== 1) schritt++;
|
||
|
||
const start = (n * schritt) % ring.length;
|
||
const zusatz = [];
|
||
const schonDrin = new Set();
|
||
for (let i = 0; i < ring.length && zusatz.length < ZUSATZ_PRO_RUNDE; i++) {
|
||
const w = ring[(start + i) % ring.length];
|
||
if (schonDrin.has(w.schluessel)) continue;
|
||
schonDrin.add(w.schluessel);
|
||
zusatz.push(w);
|
||
}
|
||
|
||
/* IN KATALOGREIHENFOLGE AUSGELIEFERT, nicht in Rotationsreihenfolge.
|
||
Der Katalog ist nach Thema sortiert; wer ihn jede Runde in einer
|
||
anderen Reihenfolge sieht, liest ihn jedes Mal neu statt ihn
|
||
wiederzuerkennen. */
|
||
const gewaehlt = new Set([...kern, ...zusatz].map((p) => p.schluessel));
|
||
return {
|
||
kern, zusatz,
|
||
alle: alle.filter((p) => gewaehlt.has(p.schluessel)),
|
||
runden: n,
|
||
};
|
||
}
|
||
|
||
/* =====================================================================
|
||
DIE SEITE
|
||
===================================================================== */
|
||
|
||
import { resuemeeVon } from "./workspace-befinden-resuemee.js";
|
||
|
||
befindenRouter.get("/workspace/api/befinden", angemeldet, (req, res) => {
|
||
try {
|
||
const b = block();
|
||
if (!b) return res.status(503).json({ fehler: "kein_block" });
|
||
|
||
const meine = new Map(db().prepare(
|
||
`SELECT schluessel, stufe, anlass, am FROM entwicklung_stand
|
||
WHERE person_id = ? AND von_id = ?`)
|
||
.all(req.person.id, req.person.id).map((z) => [z.schluessel, z]));
|
||
|
||
/* DER VERLAUF -- nach Runden gebuendelt, neueste zuerst. */
|
||
const runden = db().prepare(
|
||
`SELECT runde FROM befinden_verlauf WHERE person_id = ?
|
||
GROUP BY runde ORDER BY runde DESC LIMIT ?`).all(req.person.id, VERLAUF_MAX)
|
||
.map((z) => z.runde);
|
||
const zeilen = runden.length
|
||
? db().prepare(
|
||
`SELECT runde, schluessel, stufe FROM befinden_verlauf
|
||
WHERE person_id = ? AND runde IN (${runden.map(() => "?").join(",")})`)
|
||
.all(req.person.id, ...runden)
|
||
: [];
|
||
const verlauf = runden.map((r) => ({
|
||
runde: r,
|
||
antworten: Object.fromEntries(
|
||
zeilen.filter((z) => z.runde === r).map((z) => [z.schluessel, z.stufe])),
|
||
}));
|
||
|
||
const takt = rhythmus(req.person.id);
|
||
const diese = fragenFuer(req.person.id);
|
||
|
||
/* ==== WAS IST "FRISCH"? ==========================================
|
||
|
||
DER FEHLER, DEN DAS BEHEBT (gefunden 19.09.2026):
|
||
|
||
Eine Runde galt als vollstaendig, sobald zu jeder Frage IRGENDEIN
|
||
Stand in `entwicklung_stand` lag. Nach der ersten Runde ist das
|
||
immer der Fall -- die Antworten bleiben ja stehen. Ab da liess
|
||
sich jede weitere Runde abschliessen, OHNE eine einzige Frage
|
||
anzufassen: Die alten Antworten wanderten ein zweites Mal in den
|
||
Verlauf, mit neuem Datum.
|
||
|
||
Das ist schlimmer als eine fehlende Runde. Der Verlauf zeigte
|
||
dann eine gerade Linie, und die bedeutete nicht "es ist alles
|
||
gleich geblieben", sondern "niemand hat gefragt". Genau diese
|
||
Sorte Zahl ist im Haus verboten: eine, die etwas Falsches
|
||
behauptet, und der man glaubt.
|
||
|
||
Die vorhandene Pruefung hat den Fehler nicht gefunden, sondern
|
||
FESTGESCHRIEBEN -- sie aenderte eine Antwort und stellte
|
||
zufrieden fest, dass die zweite Runde durchgeht.
|
||
|
||
Frisch heisst ab jetzt: nach der letzten Runde gesetzt. Vor der
|
||
ersten Runde ist alles frisch. */
|
||
const grenze = takt.letzte ? Date.parse(takt.letzte) : null;
|
||
const istFrisch = (stand) => !stand ? false
|
||
: grenze === null ? true
|
||
: Date.parse(stand.am) > grenze;
|
||
|
||
const punkte = diese.alle.map((pt) => {
|
||
const m = meine.get(pt.schluessel) || null;
|
||
return {
|
||
...pt,
|
||
meins: m,
|
||
/* Die Antwort bleibt sichtbar -- sie ist ja nicht falsch, nur
|
||
alt. Sie zaehlt nur nicht mehr mit, und die Seite sagt das. */
|
||
frisch: istFrisch(m),
|
||
};
|
||
});
|
||
|
||
/* ==== DIE EINE ZUSAMMENFASSUNG AM ENDE (20.09.2026) ==============
|
||
|
||
Filipe: "am ende von allen fragen will ich eine gesamte
|
||
zusammenfassung, nicht bei jeder frage oder kategorie eine,
|
||
sondern EINE gesamte am ende. eine motivierende zusammenfassung
|
||
fuer die person."
|
||
|
||
SIE WIRD AUS DEN FRISCHEN ANTWORTEN GERECHNET, nicht aus allen
|
||
in `entwicklung_stand`. Der Unterschied ist derselbe wie eine
|
||
Bildschirmseite weiter oben: Eine Zusammenfassung ueber alte
|
||
Antworten sagt nicht "so geht es dir", sondern "so ging es dir
|
||
irgendwann mal", und man merkt es nicht.
|
||
|
||
UND SIE BRAUCHT KEINE ZWEITE RUNDE. Die Musteranalyse darunter
|
||
schon (ein Punkt ist keine Entwicklung) -- diese hier ist die
|
||
Antwort auf das, was gerade dasteht, und die gibt es beim ersten
|
||
Mal genauso.
|
||
|
||
DIE VORIGE RUNDE KOMMT MIT, wenn es eine gibt: Nur dann kann
|
||
"hat sich gebessert" ueberhaupt gesagt werden, und nur fuer
|
||
Fragen, die BEIDE Male dran waren. Seit die Fragen rotieren,
|
||
waere "war letztes Mal nicht dabei" sonst eine Verbesserung. */
|
||
const frischeAntworten = Object.fromEntries(
|
||
punkte.filter((pt) => pt.frisch && pt.meins)
|
||
.map((pt) => [pt.schluessel, pt.meins.stufe]));
|
||
|
||
/* WELCHE ANTWORTEN DIE ZUSAMMENFASSUNG MEINT -- und hier steckte
|
||
ein Fehler, den die Pruefung gefunden hat, nicht das Nachdenken.
|
||
|
||
Erster Anlauf: nur die FRISCHEN, also die nach der letzten Runde
|
||
gesetzten. Das ist waehrend einer laufenden Runde richtig -- und
|
||
ausgerechnet in dem Moment falsch, in dem man die Zusammenfassung
|
||
sehen will: DIREKT NACH dem Abschliessen einer Runde ist keine
|
||
einzige Antwort mehr frisch. Man hat sie ja Sekunden vorher
|
||
gesetzt, also VOR dem Abschluss. Die Seite zeigte nach dem
|
||
Abschliessen: nichts.
|
||
|
||
(Derselbe Sachverhalt steht schon beim Hinweis „das war deine
|
||
Antwort beim letzten Mal" in befinden.js -- dort wurde er
|
||
beruecksichtigt, hier nicht. Eine Erkenntnis, die an einer
|
||
Stelle notiert ist, wirkt nicht von selbst an der anderen.)
|
||
|
||
RICHTIG IST BEIDES, in dieser Rangfolge:
|
||
laeuft gerade eine Runde -> die frischen Antworten
|
||
gerade abgeschlossen -> die letzte Runde aus dem Verlauf
|
||
|
||
Der Vergleich rutscht entsprechend mit: Wird die laufende Runde
|
||
gezeigt, ist die letzte abgeschlossene der Vergleich; wird die
|
||
letzte abgeschlossene gezeigt, die vorletzte. Sonst verglichen
|
||
wir eine Runde mit sich selbst -- und "nichts hat sich
|
||
geaendert" waere dann keine Auskunft, sondern ein Rechenfehler. */
|
||
const laeuftRunde = Object.keys(frischeAntworten).length > 0;
|
||
const fazitAntworten = laeuftRunde
|
||
? frischeAntworten
|
||
: (verlauf[0]?.antworten || {});
|
||
const fazitVergleich = laeuftRunde
|
||
? (verlauf[0]?.antworten || null)
|
||
: (verlauf[1]?.antworten || null);
|
||
/* DIE FRAGEN DAZU aus dem KATALOG, nicht aus `diese.alle`: Eine
|
||
abgeschlossene Runde kann andere Fragen enthalten haben als die,
|
||
die gerade dran waeren -- die Zusatzfragen wechseln ja. Wer
|
||
stattdessen die aktuellen nimmt, fasst Antworten zusammen, die es
|
||
zu diesen Fragen gar nicht gibt. */
|
||
const fazitPunkte = (ENTWICKLUNG_PUNKTE[b.schluessel] || [])
|
||
.filter((pt) => fazitAntworten[pt.schluessel]);
|
||
|
||
res.json({
|
||
block: b,
|
||
stufen: ENTWICKLUNG_STUFEN,
|
||
/* DIE FELDER. Sie stehen im Katalog, nicht im Browser -- eine
|
||
zweite Liste dort waere die, die beim siebten Feld vergessen
|
||
wird. */
|
||
felder: BEFINDEN_FELDER,
|
||
/* DIE EINORDNUNGEN WANDERN INS GESAMTRESUEMEE (22.09.2026).
|
||
Vorher standen sie unter jeder einzelnen Frage -- bei neun
|
||
Fragen neun Ratschlaege. Jetzt geht genau EINER hinein, zu
|
||
der Sache, die am meisten haengt. Sie werden durchgereicht
|
||
statt importiert: workspace-befinden-resuemee.js haengt so
|
||
von nichts ab, und der Ringschluss bleibt aus. */
|
||
resuemee: resuemeeVon(fazitAntworten, fazitPunkte, BEFINDEN_FELDER,
|
||
fazitVergleich, EINORDNUNG),
|
||
/* WORAUF SIE SICH BEZIEHT -- die Seite sagt das dazu. „Was das
|
||
zusammen heisst" ueber eine Runde von vorgestern ist etwas
|
||
anderes als ueber die, die man gerade ausfuellt, und wer das
|
||
nicht weiss, bezieht den Satz auf den falschen Tag. */
|
||
resuemee_stand: laeuftRunde ? "laufend" : (verlauf[0] ? "letzte" : "keine"),
|
||
rhythmus_tage: RHYTHMUS_TAGE,
|
||
rhythmus: takt,
|
||
punkte,
|
||
/* WIE VIELE FRAGEN ES INSGESAMT GIBT und wie viele diese Runde
|
||
dran sind. Ohne diese beiden Zahlen saehe eine kurze Runde aus
|
||
wie ein Katalog, aus dem etwas verschwunden ist. */
|
||
fragen_gesamt: (ENTWICKLUNG_PUNKTE[b.schluessel] || []).length,
|
||
/* DER GANZE KATALOG -- nur Schluessel und Titel, fuer den Verlauf.
|
||
Der Verlauf zeigt jede Frage, zu der es Antworten gibt, nicht
|
||
nur die dieser Runde: Was vor vier Wochen beantwortet wurde,
|
||
hoert nicht auf zu gelten, weil die Frage gerade pausiert.
|
||
Ohne diese Liste kennte die Seite die Titel dazu nicht. */
|
||
katalog: (ENTWICKLUNG_PUNKTE[b.schluessel] || [])
|
||
.map((pt) => ({ schluessel: pt.schluessel, titel: pt.titel, kern: !!pt.kern })),
|
||
kern_anzahl: diese.kern.length,
|
||
zusatz_anzahl: diese.zusatz.length,
|
||
runden_bisher: diese.runden,
|
||
verlauf,
|
||
/* DIE ANALYSE KOMMT MIT, sie holt sich niemand extra.
|
||
|
||
Kein eigener Weg: Sie wird aus demselben Verlauf gerechnet, der
|
||
hier ohnehin schon steht. Ein zweiter Weg waere eine zweite
|
||
Stelle, an der die Rechte stimmen muessen -- und die zweite ist
|
||
immer die, die etwas durchlaesst. */
|
||
/* DIE ANALYSE SIEHT ALLE FRAGEN, nicht nur die dieser Runde.
|
||
Sie handelt vom Verlauf, und der Verlauf hoert nicht auf,
|
||
wenn eine Frage gerade nicht dran ist. Ihr nur die acht
|
||
aktuellen zu geben hiesse, vier Muster zu verschweigen --
|
||
darunter womoeglich das einzige, das zaehlt. */
|
||
analyse: analyseVon(verlauf, ENTWICKLUNG_PUNKTE[b.schluessel] || []),
|
||
});
|
||
} catch (f) {
|
||
console.error("[befinden] Lesen:", f?.message);
|
||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||
}
|
||
});
|
||
|
||
/** Eine Runde abschließen: alle sechs Antworten wandern in den Verlauf.
|
||
*
|
||
* ALS RUNDE UND NICHT JE KLICK: Ein Verlauf, der jeden einzelnen Klick
|
||
* festhält, zeigt kein Muster, sondern Rauschen — und er würde jedes
|
||
* Zögern mitschreiben. Was zählt, ist der Stand, wenn jemand fertig
|
||
* ist. */
|
||
befindenRouter.post("/workspace/api/befinden/runde", express.json({ limit: "8kb" }),
|
||
angemeldet, (req, res) => {
|
||
try {
|
||
const b = block();
|
||
const takt = rhythmus(req.person.id);
|
||
/* DIESELBE RECHNUNG WIE BEIM ANZEIGEN. Eine zweite hier waere die
|
||
Stelle, an der die Seite acht Fragen zeigt und der Server neun
|
||
verlangt -- und niemand faende heraus, welche. */
|
||
const punkte = fragenFuer(req.person.id).alle;
|
||
const meine = new Map(db().prepare(
|
||
`SELECT schluessel, stufe, am FROM entwicklung_stand
|
||
WHERE person_id = ? AND von_id = ?`)
|
||
.all(req.person.id, req.person.id).map((z) => [z.schluessel, z]));
|
||
|
||
/* NUR FRISCHE ANTWORTEN ZAEHLEN -- siehe die Begruendung beim
|
||
Anzeigen. Eine Runde, die alte Antworten ein zweites Mal
|
||
festhaelt, zeichnet eine gerade Linie, die nicht "gleich
|
||
geblieben" heisst, sondern "niemand hat gefragt". */
|
||
const grenze = takt.letzte ? Date.parse(takt.letzte) : null;
|
||
const fehlt = punkte.filter((p) => {
|
||
const m = meine.get(p.schluessel);
|
||
if (!m) return true;
|
||
return grenze !== null && Date.parse(m.am) <= grenze;
|
||
});
|
||
if (fehlt.length) {
|
||
/* EINE HALBE RUNDE IST KEINE RUNDE. Sie wuerde im Verlauf wie
|
||
ein vollstaendiger Stand aussehen und das Muster verfaelschen. */
|
||
return res.status(400).json({
|
||
fehler: "unvollstaendig", offen: fehlt.length, gesamt: punkte.length,
|
||
});
|
||
}
|
||
|
||
const runde = jetzt();
|
||
const rein = db().prepare(
|
||
`INSERT INTO befinden_verlauf (person_id, runde, schluessel, stufe, am)
|
||
VALUES (?,?,?,?,?)`);
|
||
for (const p of punkte) {
|
||
rein.run(req.person.id, runde, p.schluessel, meine.get(p.schluessel).stufe, runde);
|
||
}
|
||
|
||
/* IM PROTOKOLL STEHT NUR, DASS es passiert ist -- nicht was.
|
||
Das Protokoll liest DogFather; die Antworten gehen ihn nichts
|
||
an, und ein "detail" mit Stufen darin waere genau die
|
||
Hintertuer, die es hier nicht geben darf. */
|
||
protokolliere("befinden_runde", {
|
||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||
detail: `${punkte.length} Fragen`,
|
||
});
|
||
|
||
res.status(201).json({ runde, anzahl: punkte.length, rhythmus: rhythmus(req.person.id) });
|
||
} catch (f) {
|
||
console.error("[befinden] Runde:", f?.message);
|
||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||
}
|
||
});
|
||
|
||
/* =====================================================================
|
||
WAS DEIN VERLAUF SAGT (15.09.2026)
|
||
|
||
Filipe: „mach mir auch eine analyse, mach eine perfektion draus. eine
|
||
professionelle auch mit infos und so aus aller welt was tiktok
|
||
angeht. ich will dass es perfekt ist!!!!"
|
||
|
||
---------------------------------------------------------------------
|
||
SIE WIRD GERECHNET, NICHT GESPEICHERT
|
||
|
||
Kein Feld, keine Tabelle, keine Note. Die Analyse entsteht bei jedem
|
||
Abruf neu aus dem Verlauf dieser einen Person — und verlässt das
|
||
Haus damit genauso wenig wie er. DogFather sieht sie nicht; es gibt
|
||
keinen Weg, der sie für jemand anderen ausgibt.
|
||
|
||
---------------------------------------------------------------------
|
||
DREI DINGE, DIE SIE TUT — UND EINES, DAS SIE NICHT TUT
|
||
|
||
1. SIE NENNT DAS MUSTER, nicht den Einzelwert. „Da hakt es" in
|
||
einer Runde ist ein schlechter Tag. Dasselbe in zwei Runden
|
||
hintereinander ist etwas anderes, und genau das ist in der
|
||
Forschung das Warnzeichen.
|
||
|
||
2. SIE ORDNET EIN. Zu jeder der sechs Fragen gibt es einen
|
||
belegten Befund aus der Moderationsarbeit — damit niemand
|
||
denkt, er sei der Einzige, dem das so geht. Das ist der Teil,
|
||
den Filipe „Infos aus aller Welt" nennt.
|
||
|
||
3. SIE SAGT, WAS HILFT. Aus derselben Forschung, und zwar
|
||
Maßnahmen, die die Person oder das Team wirklich umsetzen kann
|
||
— nicht „mehr Selbstfürsorge".
|
||
|
||
4. SIE BEWERTET NICHT. Keine Punktzahl, kein Score, kein
|
||
Ampelgesicht. Eine Zahl über das eigene Befinden lädt dazu ein,
|
||
sie zu verbessern statt ehrlich zu antworten — und ab da misst
|
||
die Abfrage nur noch sich selbst.
|
||
|
||
---------------------------------------------------------------------
|
||
DIE QUELLEN
|
||
|
||
Zu freiwilligen Moderatoren (Reddit, Discord, Facebook):
|
||
„Zu wenig Zeit" und „Streit mit anderen Moderatoren" sind die zwei
|
||
meistgenannten Gründe fürs Aufhören. Burnout entsteht aus der
|
||
Menge, aus persönlichen Angriffen wegen Entscheidungen, aus der
|
||
unsichtbaren emotionalen Arbeit und aus fehlenden Werkzeugen.
|
||
https://news.umich.edu/online-content-moderators-likely-to-experience-burnout-u-m-study-suggests/
|
||
https://arxiv.org/pdf/2502.06985
|
||
|
||
Zu bezahlten Moderatoren bei TikTok und anderen Plattformen:
|
||
Über 80 % der Befragten sagen, ihr Arbeitgeber müsse mehr für ihre
|
||
psychische Gesundheit tun. In der Türkei berichteten TikTok-
|
||
Moderatoren von Traumatisierung, langen Schichten und fehlender
|
||
Begleitung; weltweit organisieren sich Moderatoren dagegen.
|
||
https://restofworld.org/2025/tiktok-moderators-turkey/
|
||
https://www.japantimes.co.jp/news/2025/07/04/world/science-health/content-moderators-mental-trauma/
|
||
https://uniglobalunion.org/news/tech-protocols/
|
||
|
||
Was schützt:
|
||
Pausen und Rotation (niemand ist dauerhaft zuständig), Anschluss
|
||
ans Team (der stärkste Einzelfaktor gegen Belastung), ausdrückliche
|
||
Anerkennung, und ein bekannter Weg für den Ernstfall.
|
||
https://discord.com/safety/understanding-and-avoiding-moderator-burnout
|
||
https://www.zevohealth.com/blog/7-tips-for-managing-content-moderator-wellbeing/
|
||
===================================================================== */
|
||
|
||
/** Ab wie vielen Runden hintereinander ein „hakt" ein Muster ist.
|
||
*
|
||
* ZWEI, nicht drei. Bei vierzehn Tagen Abstand sind zwei Runden ein
|
||
* Monat -- wer einen Monat lang dasselbe meldet, hat keinen schlechten
|
||
* Tag. Drei waeren zehn Wochen, und so lange soll niemand warten. */
|
||
export const MUSTER_AB = 2;
|
||
|
||
/** Zu jeder Frage: der belegte Befund und was dagegen hilft.
|
||
*
|
||
* KEINE ALLGEMEINPLAETZE. Jeder Eintrag gehoert zu genau dieser Frage
|
||
* und stammt aus den Quellen oben. Ein Satz, der zu allen sechs passen
|
||
* wuerde, gehoert hier nicht hinein. */
|
||
export const EINORDNUNG = {
|
||
/* ---- Die sechs vom 17.09.2026 ------------------------------------ */
|
||
"e-ich-abschalten": {
|
||
welt: "Nicht die Menge der Arbeit sagt am besten voraus, ob jemand aufhört, "
|
||
+ "sondern ob die Erholung dazwischen trägt. Wer abschalten kann, hält "
|
||
+ "deutlich mehr aus als jemand mit der halben Schicht und ohne Pause.",
|
||
hilft: "Ein klares Ende ist wirksamer als eine kürzere Schicht: Meldungen aus, "
|
||
+ "Chat zu, und niemand erwartet danach noch eine Antwort. Wer das nicht "
|
||
+ "für sich selbst festlegen kann, braucht jemanden, der es festlegt.",
|
||
},
|
||
"e-ich-sinn": {
|
||
welt: "Das Gefühl, etwas zu bewirken, gilt in Untersuchungen zu Ehrenamt als "
|
||
+ "der stärkste Grund zu bleiben – stärker als Anerkennung und stärker "
|
||
+ "als gute Stimmung im Team.",
|
||
hilft: "Es reicht oft, das Ergebnis überhaupt zu zeigen: Was ist durch die "
|
||
+ "Arbeit der letzten Wochen tatsächlich anders geworden? Wer nur "
|
||
+ "Aufgaben sieht und nie ein Ergebnis, arbeitet ins Leere.",
|
||
},
|
||
"e-ich-klar-was-erwartet": {
|
||
welt: "Unklare Erwartungen sind einer der am besten belegten Auslöser für "
|
||
+ "Erschöpfung – noch vor der Arbeitsmenge. Sie wirken leise, weil "
|
||
+ "niemand sie als Problem benennt.",
|
||
hilft: "Drei Sätze reichen meistens: Was gehört dir, was ausdrücklich nicht, "
|
||
+ "und wann ist es gut genug. Fehlt einer davon, füllt man ihn selbst – "
|
||
+ "und zwar immer mit zu viel.",
|
||
},
|
||
"e-ich-nicht-angefeindet": {
|
||
welt: "Anfeindungen außerhalb der Plattform sind bei Moderatoren verbreiteter "
|
||
+ "als in der Öffentlichkeit bekannt, und sie sind der Punkt, an dem aus "
|
||
+ "Belastung eine Gefahr wird.",
|
||
hilft: "Das ist kein Punkt für eine Umfrage, sondern für ein Gespräch – "
|
||
+ "sofort. Nachrichten sichern (Bildschirmfoto mit Uhrzeit), nichts "
|
||
+ "löschen, und niemand trägt das allein.",
|
||
},
|
||
"e-ich-lerne-dazu": {
|
||
welt: "Ehrenamtliche bleiben deutlich länger, wenn die Tätigkeit ihnen "
|
||
+ "erkennbar etwas mitgibt – eine Fähigkeit, eine Erfahrung, etwas, das "
|
||
+ "auch außerhalb zählt.",
|
||
hilft: "Das entsteht selten von allein. Eine neue Aufgabe, ein Bereich zum "
|
||
+ "Ausprobieren, ein Schnitt- oder Technikthema, das jemand übernimmt: "
|
||
+ "Es kostet nichts und wirkt lange.",
|
||
},
|
||
"e-ich-naechste-monate": {
|
||
welt: "Die Frage nach der eigenen Zukunft im Team ist der beste einzelne "
|
||
+ "Frühindikator, den Befindensabfragen kennen. Sie schlägt an, bevor "
|
||
+ "die anderen Fragen es tun.",
|
||
hilft: "Wenn hier etwas hakt, ist die Antwort nie eine Maßnahme, sondern ein "
|
||
+ "Gespräch – und zwar eines, das fragt statt überzeugt. Wer schon "
|
||
+ "innerlich gegangen ist, lässt sich nicht zurückargumentieren.",
|
||
},
|
||
|
||
"e-ich-menge-passt": {
|
||
welt: "„Zu wenig Zeit“ ist in Untersuchungen zu freiwilligen Moderatoren der "
|
||
+ "meistgenannte Grund fürs Aufhören – noch vor den Inhalten selbst.",
|
||
hilft: "Das ist fast nie eine Frage der Person, sondern der Verteilung: feste "
|
||
+ "Zeiten statt „immer erreichbar“, und eine Schicht, die wirklich endet. "
|
||
+ "Sag es weiter, bevor du es allein löst.",
|
||
},
|
||
"e-ich-niemand-schwierig": {
|
||
welt: "Streit im Team ist der zweithäufigste Grund – und der, über den am "
|
||
+ "wenigsten geredet wird. Er trifft gerade die Teams, die sonst gut laufen.",
|
||
hilft: "Je länger es liegt, desto größer wird es. Ein Gespräch zu dritt, mit "
|
||
+ "jemandem dabei, der nicht Partei ist, löst mehr als zwei Monate Abwarten.",
|
||
},
|
||
"e-ich-weiss-an-wen": {
|
||
welt: "Ein fehlender Eskalationsweg ist in der Forschung kein Wissensproblem, "
|
||
+ "sondern ein Belastungsproblem: Er fühlt sich nicht wie eine offene Frage "
|
||
+ "an, sondern wie Unsicherheit in jeder Schicht.",
|
||
hilft: "Ein Name, nicht „das Team“. Wer im Ernstfall überlegen muss, wen er "
|
||
+ "fragt, fragt niemanden.",
|
||
},
|
||
"e-ich-jemand-zum-reden": {
|
||
welt: "Anschluss ans Team gilt als der stärkste einzelne Schutzfaktor gegen "
|
||
+ "Belastung in der Moderationsarbeit. Umgekehrt arbeiten viele Moderatoren "
|
||
+ "faktisch allein – auch mitten in einem Team.",
|
||
hilft: "Nicht erst, wenn etwas passiert ist. Ein kurzer fester Austausch nach "
|
||
+ "harten Schichten wirkt nachweislich besser als ein Angebot, das man von "
|
||
+ "sich aus annehmen muss.",
|
||
},
|
||
"e-ich-nein-ohne-problem": {
|
||
welt: "Bei bezahlten Moderatoren – auch bei TikTok – sagen über 80 % der "
|
||
+ "Befragten, ihr Arbeitgeber müsse mehr für ihre psychische Gesundheit tun. "
|
||
+ "Wo Nein-Sagen teuer wirkt, bleibt Belastung unsichtbar, bis jemand geht.",
|
||
hilft: "Es geht nicht darum, ob du darfst – sondern wie es sich anfühlt. Wenn "
|
||
+ "es sich teuer anfühlt, ist das eine Auskunft über das Team, nicht über dich.",
|
||
},
|
||
"e-ich-faellt-auf": {
|
||
welt: "Fehlende Anerkennung ist der am häufigsten genannte emotionale Preis "
|
||
+ "dieser Arbeit – die eigentliche Arbeit ist die, die NICHT passiert ist, "
|
||
+ "und die sieht niemand.",
|
||
hilft: "Die billigste Maßnahme überhaupt und die mit der besten Wirkung: "
|
||
+ "ausdrücklich danken, für etwas Bestimmtes, nicht allgemein.",
|
||
},
|
||
|
||
/* ---- Die sechs vom 20.09.2026 ------------------------------------
|
||
Sie schließen drei Lücken, die die Liste vorher hatte: Schlaf,
|
||
Widersprechen und Mitreden. Jede bekommt hier ihre Einordnung --
|
||
eine Frage ohne Grundlage ist eine Frage, die jemand gut fand, und
|
||
davon gibt es in Befindensabfragen genug. */
|
||
"e-ich-schlaf": {
|
||
welt: "Schlafstörungen sind in der Erschöpfungsforschung das Zeichen, das "
|
||
+ "AM FRÜHESTEN kommt – vor dem Leistungsabfall, vor dem Zynismus, vor "
|
||
+ "dem Gedanken ans Aufhören. Wer nachts wach liegt und an eine Schicht "
|
||
+ "denkt, hat das Problem noch nicht bei der Arbeit, aber schon im Kopf.",
|
||
hilft: "Nicht weniger arbeiten, sondern klarer aufhören: ein festes Ende, "
|
||
+ "Meldungen aus, und etwas dazwischen, das nichts mit dem Stream zu tun "
|
||
+ "hat. Das Gehirn braucht ein Signal, dass die Schicht vorbei ist – die "
|
||
+ "Uhrzeit allein ist keins.",
|
||
},
|
||
"e-ich-widersprechen": {
|
||
welt: "Ob man widersprechen kann, ohne dass es unangenehm wird, ist der Kern "
|
||
+ "dessen, was die Forschung psychologische Sicherheit nennt – der am "
|
||
+ "besten belegte Einzelfaktor dafür, ob ein Team gut arbeitet. Ein Team, "
|
||
+ "in dem niemand widerspricht, ist selten einig; meistens ist es still.",
|
||
hilft: "Es liegt fast nie an der Person, die schweigt. Was hilft: dass die "
|
||
+ "Leitung selbst widerspricht und widersprochen wird, sichtbar, und dass "
|
||
+ "ein Einwand einmal ausdrücklich gedankt wird statt nur abgehakt.",
|
||
},
|
||
"e-ich-mitreden": {
|
||
welt: "Viel Anforderung bei wenig Einfluss ist die Kombination, aus der "
|
||
+ "Erschöpfung entsteht – nicht viel Arbeit allein. Das Modell dahinter "
|
||
+ "(Job Demands-Resources) ist seit Jahrzehnten eines der am besten "
|
||
+ "geprüften der Arbeitspsychologie, und Einfluss ist darin die "
|
||
+ "wirksamste einzelne Entlastung.",
|
||
hilft: "Es muss nicht Mitbestimmung über alles sein. Es reicht meistens, "
|
||
+ "über das Eigene zu entscheiden: wann, wie lange, in welcher "
|
||
+ "Reihenfolge. Wer das abgibt, gibt wenig ab und nimmt viel Last.",
|
||
},
|
||
"e-ich-fehler-ok": {
|
||
welt: "Was zählt, ist nicht, ob Fehler passieren, sondern ob man sie "
|
||
+ "erfährt. In Teams, in denen Fehler teuer sind, werden sie nicht "
|
||
+ "seltener – sie werden unsichtbar, und die Folgekosten trägt der, dem "
|
||
+ "er passiert ist, allein.",
|
||
hilft: "Der erste Fehler, den jemand aus der Leitung selbst erzählt, wirkt "
|
||
+ "mehr als jede Zusage. Und: beim nächsten gemeldeten Fehler zuerst "
|
||
+ "fragen, was er über den ABLAUF sagt, nicht über die Person.",
|
||
},
|
||
"e-ich-dazugehoeren": {
|
||
welt: "Anschluss ans Team ist in den Untersuchungen zu Moderatoren-Belastung "
|
||
+ "der STÄRKSTE einzelne Schutzfaktor – stärker als kürzere Schichten und "
|
||
+ "stärker als bessere Werkzeuge. Nützlich zu sein und dazuzugehören sind "
|
||
+ "dabei zwei verschiedene Dinge, und nur das zweite hält.",
|
||
hilft: "Es entsteht neben der Arbeit, nicht in ihr: ein Kanal, in dem es "
|
||
+ "nicht um Schichten geht, ein gemeinsamer Termin ohne Zweck. Wer nur "
|
||
+ "zum Arbeiten da ist, geht, sobald die Arbeit unangenehm wird.",
|
||
},
|
||
"e-ich-stolz": {
|
||
welt: "Die einzige Frage der Liste, die zurückschaut. Wer sich an nichts "
|
||
+ "erinnert, hat selten nichts geschafft – es hat nur niemand benannt. "
|
||
+ "Das ist die gleiche Lücke wie bei der Anerkennung, nur von innen: "
|
||
+ "Moderationsarbeit besteht zum großen Teil aus dem, was NICHT passiert "
|
||
+ "ist, und daran erinnert sich niemand von selbst.",
|
||
hilft: "Aufschreiben, sobald es passiert – eine Zeile reicht. Im Workspace "
|
||
+ "ist dafür das Highlight-Brett da. Was nirgends steht, ist nach zwei "
|
||
+ "Wochen weg, auch wenn es gut war.",
|
||
},
|
||
};
|
||
|
||
/** Das Muster einer Frage ueber die Runden hinweg -- neueste zuerst. */
|
||
function musterVon(antworten) {
|
||
if (!antworten.length) return { art: "offen", runden: 0 };
|
||
const jetzt = antworten[0];
|
||
let amStueck = 1;
|
||
while (amStueck < antworten.length && antworten[amStueck] === jetzt) amStueck++;
|
||
|
||
if (jetzt === "hakt") {
|
||
return amStueck >= MUSTER_AB
|
||
? { art: "dauerhaft", runden: amStueck }
|
||
: { art: "neu", runden: amStueck };
|
||
}
|
||
/* Vorher hakte es, jetzt nicht mehr -- das gehoert genauso gesagt.
|
||
Eine Auswertung, die nur Schlechtes findet, liest man zweimal. */
|
||
if (antworten.slice(1).includes("hakt")) return { art: "besser", runden: amStueck };
|
||
if (jetzt === "laeuft" && amStueck === antworten.length && antworten.length >= MUSTER_AB) {
|
||
return { art: "traegt", runden: amStueck };
|
||
}
|
||
return { art: "ruhig", runden: amStueck };
|
||
}
|
||
|
||
/** Die Analyse -- aus dem Verlauf gerechnet, fuer niemanden sonst. */
|
||
export function analyseVon(verlauf, punkte) {
|
||
/* EINE EINZIGE RUNDE IST KEINE ENTWICKLUNG. Dann steht hier nichts
|
||
ausser dem Hinweis, dass es beim naechsten Mal etwas zu sehen gibt
|
||
-- eine Auswertung ueber einen Punkt waere geraten. */
|
||
if (!Array.isArray(verlauf) || verlauf.length < MUSTER_AB) {
|
||
return { moeglich: false, runden: verlauf?.length || 0, ab: MUSTER_AB, zeilen: [] };
|
||
}
|
||
|
||
const zeilen = punkte.map((pt) => {
|
||
/* Neueste zuerst -- so kommt der Verlauf ohnehin. */
|
||
const antworten = verlauf.map((r) => r.antworten[pt.schluessel]).filter(Boolean);
|
||
const m = musterVon(antworten);
|
||
const e = EINORDNUNG[pt.schluessel] || {};
|
||
return {
|
||
schluessel: pt.schluessel,
|
||
titel: pt.titel,
|
||
art: m.art,
|
||
/* "MALE", NICHT "RUNDEN" (19.09.2026, mit der Rotation).
|
||
|
||
Vorher hiessen diese Zahlen Runden, und das stimmte, solange
|
||
jede Frage in jeder Runde drankam. Seit die Zusatzfragen
|
||
wechseln, ist "seit 2 Runden gleich" bei einer Frage, die nur
|
||
zweimal von vier Runden dran war, schlicht falsch -- sie waere
|
||
dazwischen vielleicht anders beantwortet worden.
|
||
|
||
Gezaehlt werden deshalb die MALE, die sie gestellt wurde. Das
|
||
ist die Zahl, die es wirklich gibt. */
|
||
male: m.runden || 0,
|
||
gefragt: verlauf.filter((r) => r.antworten[pt.schluessel]).length,
|
||
welt: e.welt || null,
|
||
/* WAS HILFT, STEHT NUR DA, WO ES ETWAS ZU TUN GIBT. Ein Rat an
|
||
einer Stelle, die laeuft, ist Laerm -- und er entwertet die
|
||
anderen fuenf. */
|
||
hilft: (m.art === "dauerhaft" || m.art === "neu") ? (e.hilft || null) : null,
|
||
};
|
||
});
|
||
|
||
const dauerhaft = zeilen.filter((z) => z.art === "dauerhaft");
|
||
const neu = zeilen.filter((z) => z.art === "neu");
|
||
const besser = zeilen.filter((z) => z.art === "besser");
|
||
const traegt = zeilen.filter((z) => z.art === "traegt");
|
||
|
||
/* DER SATZ OBEN IST DIE GANZE ANALYSE IN EINER ZEILE. Wer nur ihn
|
||
liest, soll richtig informiert sein. Vier Lagen, vier Saetze -- aus
|
||
demselben Grund, aus dem die Ampel drei hat. */
|
||
let satz;
|
||
if (dauerhaft.length) {
|
||
satz = dauerhaft.length === 1
|
||
? `Eine Sache hast du die letzten ${dauerhaft[0].male} Male gleich beantwortet. `
|
||
+ "Das ist kein schlechter Tag mehr – das ist das Muster, auf das es ankommt."
|
||
: `${dauerhaft.length} Sachen beantwortest du mehrmals hintereinander gleich. `
|
||
+ "Das ist selten Zufall und fast nie deine Schuld.";
|
||
} else if (neu.length) {
|
||
satz = neu.length === 1
|
||
? "Eine Sache hakt seit dem letzten Mal. Einmal ist noch kein Muster – "
|
||
+ "aber der richtige Moment, es anzusprechen, statt zu warten."
|
||
: `${neu.length} Sachen haken seit dem letzten Mal. Noch kein Muster – `
|
||
+ "aber zwei auf einmal sind einen Satz wert.";
|
||
} else if (besser.length) {
|
||
satz = "Nichts hakt gerade – und "
|
||
+ (besser.length === 1 ? "eine Sache hat sich" : `${besser.length} Sachen haben sich`)
|
||
+ " seit dem letzten Mal gelöst. Das gehört genauso festgehalten wie das Gegenteil.";
|
||
} else {
|
||
satz = "Nichts hakt, und daran hat sich über die letzten Male nichts geändert. "
|
||
+ "Das ist die Antwort, für die diese Seite gebaut ist – nicht die Ausnahme.";
|
||
}
|
||
|
||
return {
|
||
moeglich: true,
|
||
runden: verlauf.length,
|
||
ab: MUSTER_AB,
|
||
satz,
|
||
zeilen,
|
||
/* Abgeleitet, damit die Zahlen nicht neben den Zeilen danebenliegen
|
||
koennen. */
|
||
zahlen: {
|
||
dauerhaft: dauerhaft.length,
|
||
neu: neu.length,
|
||
besser: besser.length,
|
||
traegt: traegt.length,
|
||
},
|
||
};
|
||
}
|