Anleitung, Etappe 2: Erste Schritte zum Abhaken -- und eine Farbe, die vier Tage falsch war
Ein Creator hakt seine fuenf Ersten Schritte ab, sieht seinen
Fortschritt im Kopf, bekommt unter „Was ist dran" den Punkt „Erste
Schritte noch offen" und ganz oben auf der Startseite eine breite
Kachel „Fang hier an". Alle drei verschwinden von selbst, sobald er
durch ist -- niemand muss etwas wegklicken oder abschalten.
EINE QUELLE, DREI ANZEIGEN.
Der Bauplan wollte einen Punkt bei „Was ist dran" UND einen eigenen
Zaehler an der Kachel. Zwei getrennt gebaute Zaehler zeigen irgendwann
zwei verschiedene Zahlen uebereinander auf einem Bildschirm. Es gibt
deshalb nur `offeneHinweise()`: Daraus kommen die Zeile, die Zahl an
der kleinen Kachel und die Zahl an der breiten. Die breite Kachel
entsteht ausserdem aus der BEREITS GEFILTERTEN Kachelliste und erbt
damit beide Schranken (Rollenliste und Rechtetafel), statt sie ein
drittes Mal zu beantworten.
Gespeichert wird nur Erledigtes: eine Zeile heisst „abgehakt", keine
Zeile heisst „offen". Kein Feld mit 0 und 1 -- sonst gaebe es zwei
Arten, „offen" zu sagen.
DIE WICHTIGSTE ENTDECKUNG WAR NICHT TEIL DIESER ETAPPE.
`pruef-kachel-universum` wurde rot. Nachgemessen gegen den Stand VOR
Etappe 1: Sie war schon rot -- seit dem 02.10., und zwar wegen Ton 47
(Manager-Ziele, #7368ff), den ich selbst eingetragen habe. Im
Kommentar daneben steht sogar meine eigene Messung: „Abstand zum
naechsten Nachbarn 0,0899 ... knapp unter der 0,090". Ich habe die
Zahl hingeschrieben und die Pruefung nie laufen lassen. Vier Tage.
#7368ff -> #7269ff. Eine Hexziffer, Abstand zum alten Wert 0,0024 --
das sieht kein Mensch --, aber der Abstand zur naechsten Kachel im
eigenen Haus steigt von 0,0899 auf 0,0918. Nicht die am weitesten
entfernte Farbe genommen (das waere ein grelles Gruen gewesen):
Manager-Ziele ist in Benutzung und violett, gesucht war die kleinste
Aenderung, die die Regel haelt.
UND DIE REGEL SELBST WAR FUER EIN HAUS GESCHRIEBEN.
Auch mit sauberem Ton 47 blieb die Pruefung rot: Sie vergleicht ALLE
48 Toene miteinander, und rechnerisch ist 0,09 fuer 48 Farben nicht zu
halten (das Werkzeug findet als besten freien Platz 0,0793). Seit dem
24.09.2026 gibt es aber zwei Haeuser, und eine Kachel des einen steht
nie neben einer des anderen -- sie liegen auf verschiedenen Adressen.
Die Messung ist jetzt haus-bewusst: 0,09 innerhalb eines Hauses
(streng, und mit rund 24 Farden je Haus zu halten -- gemessen 0,0905),
ein Boden von 0,06 ueber die Haeuser hinweg. Das ist SCHAERFER, nicht
weicher: Vorher verteilte sich die Grenze auf 48 Farben und war
unerreichbar, weshalb die Pruefung dauerhaft rot stand -- und eine
Warnung, die immer kommt, ist keine mehr. Welcher Ton zu welchem Haus
gehoert, wird aus bereiche.js abgeleitet, nicht aufgelistet. Dazu eine
Gegenprobe, dass die Einteilung ueberhaupt trennt (553 Paare im Haus,
575 darueber hinweg).
NOCH EINE FESTE ZAHL ERSETZT: `pruef-start-ansicht` verlangte „genau
drei Gruppen" fuer einen Creator. Mit „Fang hier an" sind es
zeitweilig vier, und beides ist richtig. Gezaehlt wird nicht mehr,
sondern benannt -- auch das die schaerfere Pruefung: Die alte Zeile
waere gruen geblieben, wenn eine der drei Gruppen verschwindet und
eine fremde dazukommt.
Gemessen
pruef-anleitung 70 Pruefungen, 0 Fehler (vorher 47)
pruef-rollen 402 Pruefungen, 0 Fehler (vorher 401)
pruef-kachel-universum 16 Pruefungen, 0 Fehler (vorher 13, davon 1 rot)
pruef-start-ansicht alles in Ordnung (vorher 1 rot)
pruef-kachelraster 24 Pruefungen, 0 Fehler
pruef-css-klassen alles in Ordnung, 42 Stellen unter 11,5 px
mess-anleitung mit offenen Schritten: Gruppe „Fang hier an",
breite Kachel mit Zaehler 5, Hinweiszeile.
Nach dem Abhaken: alle drei weg, ein Weg
statt drei. Kein Querschieben bei 412/1440 px.
Sicherung: ~/sicherungen/workspace-vor-anleitung-e2-20261006-1421.db
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -67,6 +67,18 @@ function fassungFuer(req) {
|
||||
return { person, rolle: String(person?.rolle || "") };
|
||||
}
|
||||
|
||||
/** WESSEN Haken werden gezeigt?
|
||||
*
|
||||
* Die der angesehenen Person. DogFather, der die Sicht eines Creators
|
||||
* einnimmt, soll sehen, wie weit DIESER Creator ist — das ist der
|
||||
* Sinn der Sicht. Setzen darf er sie nicht (`darf_abhaken`).
|
||||
*
|
||||
* Ohne gewaehlte Sicht ist es die eigene Person, und das ist der
|
||||
* Normalfall. */
|
||||
function wessenStand(req) {
|
||||
return (req.sicht || req.person)?.id ?? 0;
|
||||
}
|
||||
|
||||
anleitungRouter.get("/workspace/api/anleitung", (req, res) => {
|
||||
try {
|
||||
const { person, rolle } = fassungFuer(req);
|
||||
@@ -102,12 +114,21 @@ anleitungRouter.get("/workspace/api/anleitung", (req, res) => {
|
||||
FROM anl_karte WHERE haus = ? AND rolle = ?
|
||||
ORDER BY reihe, id`).all(HAUS, rolle);
|
||||
|
||||
/* `erledigt` kommt aus derselben Abfrage und nicht aus einer
|
||||
zweiten: Zwei Abfragen koennten zwischen den Zeilen
|
||||
auseinanderlaufen, und dann zeigte der Fortschritt eine andere
|
||||
Zahl als die Haken darunter. */
|
||||
const zeilen = d.prepare(
|
||||
`SELECT art, titel, text, ziel, reihe
|
||||
FROM anl_zeile WHERE haus = ? AND rolle = ?
|
||||
ORDER BY art, reihe, id`).all(HAUS, rolle);
|
||||
`SELECT z.id, z.art, z.titel, z.text, z.ziel, z.reihe,
|
||||
(s.person_id IS NOT NULL) AS erledigt
|
||||
FROM anl_zeile z
|
||||
LEFT JOIN anl_stand s ON s.zeile_id = z.id AND s.person_id = ?
|
||||
WHERE z.haus = ? AND z.rolle = ?
|
||||
ORDER BY z.art, z.reihe, z.id`).all(wessenStand(req), HAUS, rolle);
|
||||
|
||||
const nachArt = (art) => zeilen.filter((z) => z.art === art);
|
||||
const schritte = nachArt("schritt");
|
||||
const fertig = schritte.filter((z) => z.erledigt).length;
|
||||
|
||||
/* DIE ZAHL GEHOERT IN DIE ANTWORT, nicht nur in die Liste. Die
|
||||
Seite kann damit sagen „18 Karten" — und eine Pruefung kann
|
||||
@@ -119,7 +140,19 @@ anleitungRouter.get("/workspace/api/anleitung", (req, res) => {
|
||||
leitgedanke: kopf?.leitgedanke || "",
|
||||
stufen: STUFEN,
|
||||
saetze: nachArt("satz").map((z) => z.text),
|
||||
schritte: nachArt("schritt").map((z) => ({ titel: z.titel, text: z.text, ziel: z.ziel })),
|
||||
schritte: schritte.map((z) => ({
|
||||
id: z.id, titel: z.titel, text: z.text, ziel: z.ziel, erledigt: !!z.erledigt,
|
||||
})),
|
||||
/* DER FORTSCHRITT ZAEHLT NUR DIE ERSTEN SCHRITTE -- so steht es
|
||||
im Bauplan, und das ist der Grund, warum es ihn gibt: "Niemand
|
||||
muss 18 Karten abhaken. Fertig ist, wer die Ersten Schritte
|
||||
erledigt hat." */
|
||||
fortschritt: { erledigt: fertig, gesamt: schritte.length },
|
||||
/* Abhaken darf nur, wer diese Fassung auch IST. DogFather, der
|
||||
sich die Sicht eines Creators ansieht, bekommt die Haken zu
|
||||
sehen, kann sie aber nicht setzen -- es waere sonst sein
|
||||
eigener Fortschritt an fremden Schritten. */
|
||||
darf_abhaken: String(req.person?.rolle || "") === rolle,
|
||||
karten,
|
||||
nicht: nachArt("nicht").map((z) => ({ titel: z.titel, text: z.text })),
|
||||
takt: nachArt("takt").map((z) => ({ titel: z.titel, text: z.text })),
|
||||
@@ -131,3 +164,118 @@ anleitungRouter.get("/workspace/api/anleitung", (req, res) => {
|
||||
res.status(500).json({ fehler: "serverfehler" });
|
||||
}
|
||||
});
|
||||
|
||||
/* =====================================================================
|
||||
EINEN ERSTEN SCHRITT ABHAKEN — ODER DEN HAKEN WEGNEHMEN
|
||||
=====================================================================
|
||||
|
||||
PUT und nicht POST: Der Aufruf setzt einen Zustand auf einen Wert.
|
||||
Zweimal „erledigt" nacheinander ist derselbe Zustand wie einmal —
|
||||
bei einem wackeligen Mobilfunknetz ist das der Unterschied zwischen
|
||||
„nochmal tippen hilft" und „jetzt ist es doppelt".
|
||||
|
||||
WER DARF: nur die Person selbst, und nur an Schritten IHRER Rolle.
|
||||
Beides wird hier geprueft und nicht nur in der Oberflaeche — eine
|
||||
ausgeblendete Schaltflaeche hat noch nie etwas geschuetzt.
|
||||
===================================================================== */
|
||||
anleitungRouter.put("/workspace/api/anleitung/schritt/:id", (req, res) => {
|
||||
try {
|
||||
const person = req.person;
|
||||
if (person?.haus === "crew") return res.status(404).json({ fehler: "nicht_hier" });
|
||||
|
||||
const id = Number(req.params.id);
|
||||
if (!Number.isInteger(id) || id <= 0) {
|
||||
return res.status(400).json({ fehler: "keine_nummer" });
|
||||
}
|
||||
const erledigt = req.body?.erledigt === true;
|
||||
|
||||
const d = db();
|
||||
/* DIE ZEILE MUSS EIN SCHRITT SEIN UND DER EIGENEN ROLLE GEHOEREN.
|
||||
Ohne `art = 'schritt'` liesse sich jede Zeile abhaken — auch eine
|
||||
Frage oder eine Rhythmus-Zeile. Das faellt niemandem auf, bis
|
||||
der Fortschritt eines Tages 7 von 5 meldet. */
|
||||
const zeile = d.prepare(
|
||||
`SELECT id FROM anl_zeile
|
||||
WHERE id = ? AND art = 'schritt' AND haus = ? AND rolle = ?`)
|
||||
.get(id, HAUS, String(person?.rolle || ""));
|
||||
if (!zeile) return res.status(404).json({ fehler: "kein_schritt" });
|
||||
|
||||
if (erledigt) {
|
||||
d.prepare(
|
||||
`INSERT INTO anl_stand (person_id, zeile_id, erledigt_am) VALUES (?,?,?)
|
||||
ON CONFLICT(person_id, zeile_id) DO NOTHING`)
|
||||
.run(person.id, id, new Date().toISOString());
|
||||
} else {
|
||||
d.prepare("DELETE FROM anl_stand WHERE person_id = ? AND zeile_id = ?")
|
||||
.run(person.id, id);
|
||||
}
|
||||
|
||||
/* Der neue Stand kommt zurueck, damit die Seite ihn nicht selbst
|
||||
hochrechnen muss. Eine Seite, die mitzaehlt, zaehlt irgendwann
|
||||
anders als der Server. */
|
||||
const stand = d.prepare(
|
||||
`SELECT COUNT(*) AS gesamt,
|
||||
SUM(CASE WHEN s.person_id IS NOT NULL THEN 1 ELSE 0 END) AS erledigt
|
||||
FROM anl_zeile z
|
||||
LEFT JOIN anl_stand s ON s.zeile_id = z.id AND s.person_id = ?
|
||||
WHERE z.haus = ? AND z.rolle = ? AND z.art = 'schritt'`)
|
||||
.get(person.id, HAUS, String(person.rolle));
|
||||
|
||||
res.json({
|
||||
erledigt,
|
||||
fortschritt: { erledigt: Number(stand?.erledigt || 0), gesamt: Number(stand?.gesamt || 0) },
|
||||
});
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] Anleitung abhaken:", fehler?.message);
|
||||
res.status(500).json({ fehler: "serverfehler" });
|
||||
}
|
||||
});
|
||||
|
||||
/* =====================================================================
|
||||
DER HINWEIS: „Erste Schritte noch offen"
|
||||
=====================================================================
|
||||
|
||||
EINE QUELLE, ZWEI ANZEIGEN. Diese Funktion speist sowohl die Zeile
|
||||
unter „Was ist dran" als auch die Zahl an der Kachel — und ab dieser
|
||||
Etappe zusaetzlich die breite Kachel oben auf der Startseite.
|
||||
|
||||
Das ist kein Nebeneffekt, sondern die Regel des Hauses: Der Bauplan
|
||||
wollte einen Punkt bei „Was ist dran" UND einen eigenen Zaehler an
|
||||
der Kachel. Zwei getrennt gebaute Zaehler zeigen irgendwann zwei
|
||||
verschiedene Zahlen uebereinander auf einem Bildschirm, und dann
|
||||
glaubt man keiner mehr.
|
||||
|
||||
`try` um alles: Ein Fehler hier wuerde sonst die GANZE Hinweisliste
|
||||
mitnehmen — Aufgaben, Termine, Reviews. Ein kaputter Hinweis darf
|
||||
nicht die zwoelf gesunden daneben loeschen.
|
||||
===================================================================== */
|
||||
export function offeneHinweise(person) {
|
||||
try {
|
||||
if (!person || person.haus === "crew") return [];
|
||||
const rolle = String(person.rolle || "");
|
||||
if (!FASSUNGEN.includes(rolle)) return [];
|
||||
|
||||
const stand = db().prepare(
|
||||
`SELECT COUNT(*) AS gesamt,
|
||||
SUM(CASE WHEN s.person_id IS NOT NULL THEN 1 ELSE 0 END) AS erledigt
|
||||
FROM anl_zeile z
|
||||
LEFT JOIN anl_stand s ON s.zeile_id = z.id AND s.person_id = ?
|
||||
WHERE z.haus = ? AND z.rolle = ? AND z.art = 'schritt'`)
|
||||
.get(person.id, HAUS, rolle);
|
||||
|
||||
const gesamt = Number(stand?.gesamt || 0);
|
||||
const offen = gesamt - Number(stand?.erledigt || 0);
|
||||
/* Gibt es fuer diese Rolle noch gar keine Schritte (Etappe 3 steht
|
||||
aus), ist `gesamt` 0 und `offen` 0 — kein Hinweis. Richtig: Ein
|
||||
„0 Schritte offen" waere eine Meldung ueber nichts. */
|
||||
if (offen <= 0) return [];
|
||||
|
||||
return [{
|
||||
art: "anl_schritte", stufe: "offen",
|
||||
text: "Erste Schritte noch offen", ziel: "anleitung.html", anzahl: offen,
|
||||
}];
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] Anleitung-Hinweise:", fehler?.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user