Rueckmeldung in beide Richtungen (Kapitel 5 und 6)
Filipe: "Nicht nur ich soll meine Modis bewerten oder ihnen Feedback geben koennen. Auch die Modis sollen mir Feedback geben koennen. Sie sollen mir beispielsweise sagen koennen: Was koennte ich verbessern?" Und der Schlusssatz seines Pflichtenhefts: "Es soll nicht nur dazu dienen, Leistungen zu kontrollieren. Es soll vor allem dabei helfen, als Team besser zu werden." EIN BRETT FUER BEIDE KAPITEL, NICHT ZWEI. Kapitel 5 (gegenseitiges Feedback) und Kapitel 6 (gemeinsame Reflexion) stellen dieselben Fragen -- "was laeuft gut, was laeuft schlecht, was fehlt" --, einmal an eine Person und einmal an das Team. Zwei Bretter haetten bedeutet, dass man beim Schreiben zuerst entscheiden muss, an WEN es geht, bevor man weiss, WAS man sagen will. Hier ist es umgekehrt: erst die Sache, dann die Richtung. DIE NEUN FRAGEN SIND SEINE, wortwoertlich aus dem Pflichtenheft zusammengezogen: laeuft gut · laeuft nicht gut · unbedingt behalten · an DogFather · was dem Team fehlt · Regel aendern · besser organisieren · Idee · Wunsch fuer spaeter. Sie stehen als feste Faecher da und nicht als freies Feld -- genau das ist der Unterschied zwischen einer Sammlung und einem Haufen: Neun Faecher kann man auswerten, tausend Formulierungen nicht. KEIN NEUER BAUKASTEN. Die Rueckmeldung ist ein BEREICH wie das Ideen-Board: dieselbe Tabelle, dieselbe Seite, dieselben Regeln fuers Anlegen, Aendern und Loeschen, dieselbe Zugangssperre. Ein eigenes Modul haette all das ein zweites Mal gebraucht -- und die zweite Fassung waere die gewesen, in der eine Regel fehlt. DIE EINE NEUE SACHE IST DIE RICHTUNG. Beim Schreiben waehlt man zwischen "fuers Team" (Vorgabe) und "nur an DogFather". Ohne diese Wahl haette man eines von beidem verloren: Wer "was koenntest du besser machen" vor versammelter Mannschaft sagen muss, sagt es nicht -- wer alles nur unter vier Augen sagen kann, hat kein Team-Gespraech. UND "NUR DOGFATHER" HEISST NUR DOGFATHER -- die rechte Hand ausdruecklich nicht. Sie sieht sonst ueberall dasselbe wie er; hier nicht, weil das Etikett sonst nicht stimmen wuerde. Eine Zusage mit einer Ausnahme im Kleingedruckten ist keine. Sie schreibt selbst genauso -- auch ueber ihn. DIE ZUSAGE STEHT IN sichtbarEintrag(), also in derselben Funktion, durch die auch das Lesen einer einzelnen Zeile, das Aendern und das Loeschen gehen. Eine Regel, die nur die Liste filtert, laesst die Zeile ueber ihre Nummer trotzdem heraus; die Pruefung klopft deshalb auch von hinten (PATCH und DELETE auf die vertrauliche Zeile: 404, auf die offene: 200). `COALESCE(nur_leitung, 0)`: Jede Zeile, die es vor heute gab, hat dort NULL, und in SQL ist `NULL = 0` nicht falsch, sondern UNBEKANNT. Ohne den Ersatzwert waere der gesamte alte Bestand von einer Minute auf die andere unsichtbar gewesen -- und niemand haette es gemeldet, denn ein leeres Brett sieht nicht nach Fehler aus. Beide Richtungen sind gemessen. KEINE ANONYMITAET, und das ist eine Entscheidung, keine Luecke. In einem Team dieser Groesse waere sie ohnehin keine: An drei Saetzen erkennt jeder jeden. Ein Versprechen, das nicht haelt, ist schlimmer als keines. DER FARBTON DER KACHEL IST AUSGERECHNET, NICHT AUSGESUCHT. Bei 24 vorhandenen Toenen landet ein neuer fast zwangslaeufig neben einem alten, und zwei Kacheln in FAST derselben Farbe sind schlimmer als in derselben -- bei gleicher merkt man den Fehler, bei fast gleicher sucht man ihn. Alle 24 wurden in Farbwinkel umgerechnet; die groesste Luecke liegt zwischen 90 und 160 Grad und ist 70 Grad breit, mehr als doppelt so viel wie die naechste. Der neue Ton sitzt in ihrer Mitte, 8,9 zu 1 auf dunklem Grund. pruef-rueckmeldung 34 (neu) · pruef-rollen 274 -> 277 · pruef-start-ansicht zaehlt jetzt 25 Kacheln und 25 Farben (sie zaehlt selbst, statt eine Zahl festzuhalten -- deshalb blieb sie gruen) · pruef-modi-ideen 30 · pruef-modi-verborgen 78 · pruef-css-klassen gruen · pruef-modi-wortleck 5. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -146,6 +146,62 @@ export const BEREICHE = {
|
||||
DIE ARTEN SIND DIE DREI AUS DEM DOKUMENT, dazu "Sonstiges": Eine
|
||||
Idee, die in keine der drei passt, soll nicht ungeschrieben
|
||||
bleiben, weil das Formular sie nicht kennt. */
|
||||
/* =====================================================================
|
||||
DIE RUECKMELDUNG (10.09.2026, Kapitel 5 und 6 des Pflichtenhefts)
|
||||
|
||||
Filipe: "Nicht nur ich soll meine Modis bewerten oder ihnen Feedback
|
||||
geben koennen. Auch die Modis sollen mir Feedback geben koennen."
|
||||
Und: "Es soll nicht nur dazu dienen, Leistungen zu kontrollieren. Es
|
||||
soll vor allem dabei helfen, als Team besser zu werden."
|
||||
|
||||
EIN BEREICH FUER BEIDE KAPITEL, NICHT ZWEI. Kapitel 5 (gegenseitiges
|
||||
Feedback) und Kapitel 6 (gemeinsame Reflexion) stellen dieselben
|
||||
Fragen -- "Was laeuft gut? Was laeuft schlecht? Was fehlt?" --, nur
|
||||
einmal an eine Person und einmal an das Team. Zwei Bretter dafuer
|
||||
haetten bedeutet, dass man beim Schreiben zuerst entscheiden muss,
|
||||
an WEN es geht, bevor man weiss, WAS man sagen will. Hier ist es
|
||||
umgekehrt: erst die Sache, dann die Richtung (siehe nur_leitung).
|
||||
|
||||
DIE NEUN FRAGEN SIND SEINE, wortwoertlich aus dem Pflichtenheft
|
||||
zusammengezogen. Sie stehen als "Arten" da und nicht als freies
|
||||
Textfeld, weil genau das der Unterschied zwischen einer Sammlung
|
||||
und einem Haufen ist: Neun Faecher kann man auswerten, tausend
|
||||
Formulierungen nicht.
|
||||
|
||||
KEINE ANONYMITAET, und das ist eine Entscheidung, keine Luecke. In
|
||||
einem Team dieser Groesse waere sie ohnehin keine -- an drei
|
||||
Saetzen erkennt jeder jeden. Ein Versprechen, das nicht haelt, ist
|
||||
schlimmer als keines. Was es stattdessen gibt, ist die Wahl
|
||||
zwischen "fuers Team" und "nur an DogFather". */
|
||||
rueckmeldung: {
|
||||
name: "Rückmeldung",
|
||||
/* Wie beim Ideen-Board: Das hier ist keine Akte ueber jemanden,
|
||||
sondern etwas, das allen gehoert. */
|
||||
ober: "Gemeinsam besser werden",
|
||||
arten: {
|
||||
gut: "Läuft gut",
|
||||
haken: "Läuft nicht gut",
|
||||
behalten: "Unbedingt behalten",
|
||||
dogfather: "An DogFather",
|
||||
team: "Was dem Team fehlt",
|
||||
regeln: "Regel ändern",
|
||||
ablauf: "Besser organisieren",
|
||||
idee: "Idee",
|
||||
zukunft: "Wunsch für später",
|
||||
},
|
||||
bewertung: false,
|
||||
/* Dringlichkeit ja: "Der Ton war gestern kaputt" ist etwas anderes
|
||||
als "waere schoen, irgendwann". Eine Sammlung ohne Rangfolge
|
||||
wird nach vier Wochen nicht mehr gelesen. */
|
||||
dringlichkeit: true,
|
||||
ohneCreatorBezug: true,
|
||||
/* Schaltet das Feld "nur an DogFather" frei -- siehe pruefe() und
|
||||
sichtbarEintrag(). Ohne dieses Merkmal wird es in jedem anderen
|
||||
Bereich stillschweigend verworfen, wie Bewertung und
|
||||
Dringlichkeit auch. */
|
||||
vertraulich: true,
|
||||
},
|
||||
|
||||
ideen: {
|
||||
name: "Ideen-Board",
|
||||
/* DAS WORT UEBER DEM TITEL. Ueberall sonst steht dort "Betreuung",
|
||||
@@ -276,7 +332,7 @@ bereicheRouter.use("/workspace/api/bereich", angemeldet);
|
||||
ZWEI SCHLOESSER, ABSICHTLICH: Hier haengt der ZUGANG zur Seite,
|
||||
sichtbar() haengt die Bedingung an die ZEILEN. Faellt eines weg,
|
||||
haelt das andere. */
|
||||
for (const geschuetzt of ["ideen", "angebote"]) {
|
||||
for (const geschuetzt of ["ideen", "angebote", "rueckmeldung"]) {
|
||||
bereicheRouter.use(`/workspace/api/bereich/${geschuetzt}`, (req, res, next) => {
|
||||
if (siehtModis(req.person)) return next();
|
||||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
@@ -482,11 +538,41 @@ function sichtbarRoh(person) {
|
||||
`praefix` ist da, weil die Suche dieselbe Tabelle unter demselben
|
||||
Kuerzel "e" fuehrt -- geht das eines Tages auseinander, faellt es
|
||||
beim Aufruf auf, nicht erst im Betrieb. */
|
||||
/* VERTRAULICHE RUECKMELDUNGEN SIEHT NUR DOGFATHER -- UND DER SCHREIBER
|
||||
(10.09.2026).
|
||||
|
||||
Wer beim Schreiben "nur an DogFather" waehlt, muss sich darauf
|
||||
verlassen koennen. Deshalb steht die Bedingung HIER, in derselben
|
||||
Funktion, durch die auch das Lesen einer einzelnen Zeile, das Aendern
|
||||
und das Loeschen gehen -- nicht in der Listenabfrage. Eine Regel, die
|
||||
nur die Liste filtert, laesst die Zeile ueber ihre Nummer trotzdem
|
||||
heraus.
|
||||
|
||||
"NUR DOGFATHER" HEISST AUCH NICHT DIE RECHTE HAND. Sie sieht sonst
|
||||
ueberall dasselbe wie er. Hier nicht, und zwar deshalb, weil das
|
||||
Etikett sonst nicht stimmte: Eine Zusage mit einer Ausnahme im
|
||||
Kleingedruckten ist keine. Sie kann selbst genauso schreiben -- auch
|
||||
ueber ihn, und dann sieht er es nicht anders als jeder andere.
|
||||
|
||||
`COALESCE(..., 0)`: Alle Zeilen, die es vor heute gab, haben in
|
||||
dieser Spalte NULL. Ohne den Ersatzwert waere `nur_leitung = 0`
|
||||
fuer sie UNBEKANNT statt wahr -- und der gesamte alte Bestand waere
|
||||
von einer Minute auf die andere unsichtbar gewesen. Ein Loch, das
|
||||
niemand meldet: Ein leeres Brett sieht nicht nach Fehler aus. */
|
||||
function ohneVertrauliche(regel, person, praefix) {
|
||||
if (!regel || istDogFather(person)) return regel;
|
||||
return {
|
||||
wo: `(${regel.wo}) AND (COALESCE(${praefix}.nur_leitung, 0) = 0`
|
||||
+ ` OR ${praefix}.erstellt_von = ?)`,
|
||||
werte: [...regel.werte, person.id],
|
||||
};
|
||||
}
|
||||
|
||||
export function sichtbarEintrag(person, praefix = "e") {
|
||||
const regel = sichtbar(person);
|
||||
if (!regel) return regel;
|
||||
if (regel.wo === "1=1") return regel; // DogFather sieht ohnehin alles
|
||||
if (!BEREICHE_FUER_ALLE.length) return regel;
|
||||
if (!BEREICHE_FUER_ALLE.length) return ohneVertrauliche(regel, person, praefix);
|
||||
const liste = BEREICHE_FUER_ALLE.map(() => "?").join(", ");
|
||||
const mitAllen = {
|
||||
wo: `(${praefix}.bereich IN (${liste}) OR ${regel.wo})`,
|
||||
@@ -499,11 +585,11 @@ export function sichtbarEintrag(person, praefix = "e") {
|
||||
kaeme durch, egal wem er gehoert. Heute hat kein Modi eine Kachel
|
||||
dorthin; ein Aufruf an der Oberflaeche vorbei braucht sie aber
|
||||
nicht. Eine Regel, die nur im Formular gilt, ist keine Regel. */
|
||||
if (siehtModis(person)) return mitAllen;
|
||||
return {
|
||||
if (siehtModis(person)) return ohneVertrauliche(mitAllen, person, praefix);
|
||||
return ohneVertrauliche({
|
||||
wo: `(${mitAllen.wo}) AND ${ohneTeamDogi(praefix)}`,
|
||||
werte: mitAllen.werte,
|
||||
};
|
||||
}, person, praefix);
|
||||
}
|
||||
|
||||
const SPALTEN = `
|
||||
@@ -515,6 +601,13 @@ const SPALTEN = `
|
||||
/* Was DogFather dafuer tun muesste -- nur bei den Angeboten
|
||||
gefuellt, siehe BEREICHE.angebote. */
|
||||
e.einsatz,
|
||||
/* 1 = nur DogFather darf das lesen. Die Spalte MUSS mitkommen, auch
|
||||
wenn die Regel unten schon dafuer sorgt, dass niemand Fremdes die
|
||||
Zeile bekommt: Der Schreibende soll an seiner eigenen Zeile sehen,
|
||||
dass sie vertraulich ist. Ohne diese Angabe saehe sie aus wie jede
|
||||
andere -- und beim naechsten Mal schriebe er offen, was er
|
||||
vertraulich meinte. */
|
||||
e.nur_leitung,
|
||||
${externSql("pc.name", "e.creator_extern")} AS creator_name,
|
||||
pe.name AS erstellt_name,
|
||||
(SELECT name FROM content_saeulen s WHERE s.id = e.saeule_id) AS saeule_name,
|
||||
@@ -609,6 +702,13 @@ function pruefe(bereich, körper, { neu }) {
|
||||
if (!DRINGLICHKEITEN.includes(körper.dringlichkeit)) fehler.push("Unbekannte Dringlichkeit.");
|
||||
else aus.dringlichkeit = körper.dringlichkeit;
|
||||
}
|
||||
/* Nur dort, wo der Bereich es vorsieht -- sonst koennte man einen
|
||||
Schutzvorfall oder eine Content-Idee als "vertraulich" markieren
|
||||
und damit vor den eigenen Leuten verstecken. */
|
||||
if (einstellung.vertraulich && körper.nur_leitung !== undefined) {
|
||||
aus.nur_leitung = (körper.nur_leitung === true || körper.nur_leitung === 1
|
||||
|| körper.nur_leitung === "1") ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Die vier Content-Felder gibt es nur in der Content-Planung. In jedem
|
||||
anderen Bereich werden sie stillschweigend verworfen -- wie Bewertung
|
||||
@@ -771,14 +871,19 @@ bereicheRouter.post("/workspace/api/bereich/:bereich", gleicheHerkunft, (req, re
|
||||
INSERT INTO eintraege
|
||||
(bereich, art, titel, text, datum, bewertung, dringlichkeit, status,
|
||||
creator_id, creator_extern, erstellt, erstellt_von, hook, format, saeule_id, geplant,
|
||||
event_ende, event_aufgaben, event_regeln, einsatz)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(
|
||||
event_ende, event_aufgaben, event_regeln, einsatz, nur_leitung)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(
|
||||
bereich, aus.art, aus.titel, aus.text ?? null, aus.datum,
|
||||
aus.bewertung ?? null, aus.dringlichkeit ?? "mittel", aus.status ?? "offen",
|
||||
aus.creator_id ?? null, aus.creator_extern ?? null, jetzt(), req.person.id,
|
||||
aus.hook ?? null, aus.format ?? null, aus.saeule_id ?? null, aus.geplant ?? null,
|
||||
aus.event_ende ?? null, aus.event_aufgaben ?? null, aus.event_regeln ?? null,
|
||||
aus.einsatz ?? null);
|
||||
aus.einsatz ?? null,
|
||||
/* 0 und nicht NULL als Vorgabe: NULL hiesse "unbekannt", und
|
||||
unbekannt ist bei einer Vertraulichkeit die falsche Antwort.
|
||||
Wer nichts waehlt, schreibt fuers Team -- das ist der offene
|
||||
Fall, und der soll der Normalfall sein. */
|
||||
aus.nur_leitung ?? 0);
|
||||
|
||||
protokolliere("eintrag_angelegt", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
|
||||
Reference in New Issue
Block a user