Die Einweisung gibt es jetzt auch fuer Team Dogi -- vier Fassungen, 106 Karten
Filipe hat den Bauplan "Willkommen & Anleitung -- Einweisung je Rolle"
(27 Seiten, Stand 06.10.2026) geschickt: pruefen, perfektionieren und
auf der Crew-Seite umsetzen.
AUSGANGSMESSUNG, bevor etwas angefasst wurde:
agentur creator 18 · manager 22 · scout 21 · spicy 23 Karten
crew 0 Karten -- die Schnittstelle antwortete 404 "nicht_hier"
Die Seite lud also, war aber leer. Baulich vorbereitet war sie schon:
Alle drei Inhaltstabellen tragen `haus` mit UNIQUE (haus, rolle,
kachel), und die Aussaat nimmt das Haus als Parameter.
WAS JETZT DA IST
gast 14 Karten (5 Muss, 4 Regelmaessig, 5 Bedarf)
modi 29 Karten (7 / 7 / 15)
linke 29 Karten (7 / 7 / 15)
hand 34 Karten (8 / 7 / 19)
Jede Zahl entspricht Kapitel 13 des Bauplans. Dazu je Rolle
Leitgedanke, drei Saetze, fuenf Erste Schritte, "Nicht deins",
Rhythmus und Fragen -- woertlich aus Kapitel 06 bis 09.
VOR DEM SCHREIBEN ABGEGLICHEN, nicht danach: Ein Skript prueft jede
Karte gegen die echte Kachelliste der Rolle. Ergebnis fuer alle vier
Fassungen: Kartenzahl wie im Plan, jede Karte trifft eine Kachel,
jede Kachel hat eine Karte, keine doppelt.
ZWEI KACHELN AUF EINER SEITE. Auf der Crew-Adresse fuehren "Chat"
(das Team) und "Rudel-Chat" (`?raum=treff`) beide auf chat.html.
`schluesselVonZiel` behielt nur `b` -- beide ergaben denselben
Schluessel, und damit konnten sie keine eigenen Karten haben, obwohl
der Bauplan ihnen verschiedene Einordnungen gibt (fuer die rechte
Hand: Team-Chat Muss, Rudel-Chat nur bei Bedarf). Jetzt wird auch
`raum` unterschieden. Auf der Agenturadresse gibt es nur eine
Chat-Kachel; dort aendert sich nichts.
DAS HAUS KOMMT AUS DER ADRESSE, nicht aus der angesehenen Rolle.
`const HAUS = "agentur"` ist einer Funktion gewichen. Nimmt DogFather
die Sicht einer anderen Rolle ein, wechselt er die ROLLE, nicht die
ADRESSE -- wer hier `req.sicht` naehme, bekaeme auf der Crew-Adresse
Agenturinhalte, sobald eine Sicht gesetzt ist.
`FASSUNGEN` stand an zehn Stellen fest und haette die Crew ueberall
abgewiesen. Jetzt `fassungenFuer(haus)` -- eine Funktion statt zweier
Listen an zehn Stellen.
Neue Zeilenart `vorweg` fuer "Drei Dinge vorweg" (nur die Community,
Bauplan Kapitel 04). Fehlt die Liste bei einer Rolle, wird nichts
eingetragen -- kein Sonderfall noetig.
Eigene Datei fuer die Crew-Inhalte: anleitung-tabellen.js traegt
schon die Agenturfassungen und ist 856 Zeilen lang. Getrennte Dateien
sind hier dasselbe Prinzip wie getrennte Haeuser.
pruef-anleitung 125 -> 127. Fuenf Zeilen darin verteidigten den alten
Zustand ("ein Modi bekommt die Anleitung nicht, erwartet 404") -- sie
sind mitgewandert statt stehenzubleiben. Dazu eine neue Gegenprobe
zur Haustrennung: In der Crew-Fassung darf keine Agenturkachel
vorkommen.
Datenbank vorher gesichert und zurueckgelesen (integrity_check,
84 Karten).
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -35,12 +35,25 @@
|
||||
import express from "express";
|
||||
import { db, sitzungLesen } from "./workspace.js";
|
||||
import {
|
||||
STUFEN, FASSUNGEN, PFLEGE_ROLLEN,
|
||||
STUFEN, PFLEGE_ROLLEN, fassungenFuer,
|
||||
} from "./anleitung-tabellen.js";
|
||||
|
||||
export const anleitungRouter = express.Router();
|
||||
|
||||
const HAUS = "agentur";
|
||||
/** In welchem Haus sind wir?
|
||||
*
|
||||
* DIE ADRESSE ENTSCHEIDET, NICHT DIE ANGESEHENE ROLLE (06.10.2026).
|
||||
* Hier stand `const HAUS = "agentur"` -- die Anleitung gab es nur
|
||||
* im Agenturhaus, und die Crew bekam an fuenf Stellen ein 404
|
||||
* ("nicht_hier"). Seit heute hat auch Team Dogi ihre vier
|
||||
* Fassungen; das Haus kommt deshalb aus der Anmeldung.
|
||||
*
|
||||
* GEFRAGT WIRD IMMER `req.person`, NIE `req.sicht`: Nimmt DogFather
|
||||
* die Sicht einer anderen Rolle ein, wechselt er damit die ROLLE,
|
||||
* nicht die ADRESSE. Wer hier die angesehene Person naehme, bekaeme
|
||||
* auf der Crew-Adresse Agenturinhalte, sobald eine Sicht gesetzt
|
||||
* ist -- und genau das ist die Trennung, die seit dem 24.09. gilt. */
|
||||
const hausVon = (p) => (p?.haus === "crew" ? "crew" : "agentur");
|
||||
|
||||
function angemeldet(req, res, next) {
|
||||
const person = sitzungLesen(req);
|
||||
@@ -87,19 +100,18 @@ anleitungRouter.get("/workspace/api/anleitung", (req, res) => {
|
||||
Crew-Adresse gibt es diese Seite nicht, und „gibt es nicht" ist
|
||||
die richtige Auskunft — 403 waere der Hinweis, dass es sie
|
||||
woanders doch gibt. */
|
||||
if (person?.haus === "crew") return res.status(404).json({ fehler: "nicht_hier" });
|
||||
|
||||
const d = db();
|
||||
const darfPflegen = PFLEGE_ROLLEN.includes(String(req.person?.rolle || ""));
|
||||
|
||||
if (!FASSUNGEN.includes(rolle)) {
|
||||
if (!fassungenFuer(hausVon(req.person)).includes(rolle)) {
|
||||
/* DogFather ohne gewaehlte Sicht. Kein Fehler, sondern eine
|
||||
Auskunft: Er bekommt keine Einweisung, kann aber jede ansehen.
|
||||
Die Rollen stehen MIT Namen dabei, damit die Seite keine
|
||||
zweite Rollenliste braucht. */
|
||||
return res.json({
|
||||
rolle, hat_fassung: false, darf_pflegen: darfPflegen,
|
||||
fassungen: FASSUNGEN,
|
||||
fassungen: fassungenFuer(hausVon(req.person)),
|
||||
satz: "Für dich gibt es keine Einweisung – du kennst den Laden. "
|
||||
+ "Wähle oben unter „Meine Sicht“ eine Rolle, dann siehst du genau das, "
|
||||
+ "was diese Person zu lesen bekommt.",
|
||||
@@ -107,12 +119,12 @@ anleitungRouter.get("/workspace/api/anleitung", (req, res) => {
|
||||
}
|
||||
|
||||
const kopf = d.prepare(
|
||||
"SELECT leitgedanke FROM anl_rolle WHERE haus = ? AND rolle = ?").get(HAUS, rolle);
|
||||
"SELECT leitgedanke FROM anl_rolle WHERE haus = ? AND rolle = ?").get(hausVon(req.person), rolle);
|
||||
|
||||
const karten = d.prepare(
|
||||
`SELECT id, kachel, stufe, wozu, tust, merke, reihe, geaendert
|
||||
FROM anl_karte WHERE haus = ? AND rolle = ?
|
||||
ORDER BY reihe, id`).all(HAUS, rolle);
|
||||
ORDER BY reihe, id`).all(hausVon(req.person), rolle);
|
||||
|
||||
/* `erledigt` kommt aus derselben Abfrage und nicht aus einer
|
||||
zweiten: Zwei Abfragen koennten zwischen den Zeilen
|
||||
@@ -124,7 +136,7 @@ anleitungRouter.get("/workspace/api/anleitung", (req, res) => {
|
||||
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);
|
||||
ORDER BY z.art, z.reihe, z.id`).all(wessenStand(req), hausVon(req.person), rolle);
|
||||
|
||||
const nachArt = (art) => zeilen.filter((z) => z.art === art);
|
||||
const schritte = nachArt("schritt");
|
||||
@@ -217,7 +229,6 @@ anleitungRouter.get("/workspace/api/anleitung", (req, res) => {
|
||||
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) {
|
||||
@@ -233,7 +244,7 @@ anleitungRouter.put("/workspace/api/anleitung/schritt/:id", (req, res) => {
|
||||
const zeile = d.prepare(
|
||||
`SELECT id FROM anl_zeile
|
||||
WHERE id = ? AND art = 'schritt' AND haus = ? AND rolle = ?`)
|
||||
.get(id, HAUS, String(person?.rolle || ""));
|
||||
.get(id, hausVon(req.person), String(person?.rolle || ""));
|
||||
if (!zeile) return res.status(404).json({ fehler: "kein_schritt" });
|
||||
|
||||
if (erledigt) {
|
||||
@@ -255,7 +266,7 @@ anleitungRouter.put("/workspace/api/anleitung/schritt/:id", (req, res) => {
|
||||
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));
|
||||
.get(person.id, hausVon(req.person), String(person.rolle));
|
||||
|
||||
res.json({
|
||||
erledigt,
|
||||
@@ -287,9 +298,9 @@ anleitungRouter.put("/workspace/api/anleitung/schritt/:id", (req, res) => {
|
||||
===================================================================== */
|
||||
export function offeneHinweise(person) {
|
||||
try {
|
||||
if (!person || person.haus === "crew") return [];
|
||||
if (!person) return [];
|
||||
const rolle = String(person.rolle || "");
|
||||
if (!FASSUNGEN.includes(rolle)) return [];
|
||||
if (!fassungenFuer(hausVon(person)).includes(rolle)) return [];
|
||||
|
||||
const stand = db().prepare(
|
||||
`SELECT COUNT(*) AS gesamt,
|
||||
@@ -297,7 +308,7 @@ export function offeneHinweise(person) {
|
||||
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);
|
||||
.get(person.id, hausVon(person), rolle);
|
||||
|
||||
const gesamt = Number(stand?.gesamt || 0);
|
||||
const offen = gesamt - Number(stand?.erledigt || 0);
|
||||
@@ -337,7 +348,6 @@ export function offeneHinweise(person) {
|
||||
===================================================================== */
|
||||
|
||||
function nurPflege(req, res, next) {
|
||||
if (req.person?.haus === "crew") return res.status(404).json({ fehler: "nicht_hier" });
|
||||
if (!PFLEGE_ROLLEN.includes(String(req.person?.rolle || ""))) {
|
||||
return res.status(403).json({ fehler: "nicht_erlaubt" });
|
||||
}
|
||||
@@ -361,7 +371,7 @@ anleitungRouter.put("/workspace/api/anleitung/karte/:id", nurPflege, (req, res)
|
||||
geaendert = ?, geaendert_von = ?
|
||||
WHERE id = ? AND haus = ?`)
|
||||
.run(stufe, text(req.body?.wozu, 300), text(req.body?.tust, 400),
|
||||
text(req.body?.merke, 400), new Date().toISOString(), req.person.id, id, HAUS);
|
||||
text(req.body?.merke, 400), new Date().toISOString(), req.person.id, id, hausVon(req.person));
|
||||
if (!n.changes) return res.status(404).json({ fehler: "keine_karte" });
|
||||
res.json({ ok: true });
|
||||
} catch (fehler) {
|
||||
@@ -381,7 +391,7 @@ anleitungRouter.post("/workspace/api/anleitung/karte", nurPflege, (req, res) =>
|
||||
const rolle = String(req.body?.rolle ?? "");
|
||||
const kachel = text(req.body?.kachel, 80);
|
||||
const stufe = String(req.body?.stufe ?? "bedarf");
|
||||
if (!FASSUNGEN.includes(rolle)) return res.status(400).json({ fehler: "unbekannte_rolle" });
|
||||
if (!fassungenFuer(hausVon(req.person)).includes(rolle)) return res.status(400).json({ fehler: "unbekannte_rolle" });
|
||||
if (!kachel) return res.status(400).json({ fehler: "keine_kachel" });
|
||||
if (!STUFEN.some((s) => s.schluessel === stufe)) {
|
||||
return res.status(400).json({ fehler: "unbekannte_stufe" });
|
||||
@@ -389,13 +399,13 @@ anleitungRouter.post("/workspace/api/anleitung/karte", nurPflege, (req, res) =>
|
||||
const d = db();
|
||||
const platz = d.prepare(
|
||||
"SELECT COALESCE(MAX(reihe), -1) + 1 AS n FROM anl_karte WHERE haus = ? AND rolle = ?")
|
||||
.get(HAUS, rolle).n;
|
||||
.get(hausVon(req.person), rolle).n;
|
||||
try {
|
||||
d.prepare(
|
||||
`INSERT INTO anl_karte (haus, rolle, kachel, stufe, wozu, tust, merke,
|
||||
reihe, geaendert, geaendert_von)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?)`)
|
||||
.run(HAUS, rolle, kachel, stufe, text(req.body?.wozu, 300),
|
||||
.run(hausVon(req.person), rolle, kachel, stufe, text(req.body?.wozu, 300),
|
||||
text(req.body?.tust, 400), text(req.body?.merke, 400), platz,
|
||||
new Date().toISOString(), req.person.id);
|
||||
} catch {
|
||||
@@ -416,7 +426,7 @@ anleitungRouter.delete("/workspace/api/anleitung/karte/:id", nurPflege, (req, re
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
if (!Number.isInteger(id) || id <= 0) return res.status(400).json({ fehler: "keine_nummer" });
|
||||
const n = db().prepare("DELETE FROM anl_karte WHERE id = ? AND haus = ?").run(id, HAUS);
|
||||
const n = db().prepare("DELETE FROM anl_karte WHERE id = ? AND haus = ?").run(id, hausVon(req.person));
|
||||
if (!n.changes) return res.status(404).json({ fehler: "keine_karte" });
|
||||
res.json({ ok: true });
|
||||
} catch (fehler) {
|
||||
@@ -431,7 +441,7 @@ anleitungRouter.put("/workspace/api/anleitung/zeile/:id", nurPflege, (req, res)
|
||||
const id = Number(req.params.id);
|
||||
if (!Number.isInteger(id) || id <= 0) return res.status(400).json({ fehler: "keine_nummer" });
|
||||
const d = db();
|
||||
const alt = d.prepare("SELECT art FROM anl_zeile WHERE id = ? AND haus = ?").get(id, HAUS);
|
||||
const alt = d.prepare("SELECT art FROM anl_zeile WHERE id = ? AND haus = ?").get(id, hausVon(req.person));
|
||||
if (!alt) return res.status(404).json({ fehler: "keine_zeile" });
|
||||
/* Ein Ziel gibt es nur beim Schritt. Bei einer Frage waere es ein
|
||||
Feld, das nirgends angezeigt wird -- und beim naechsten Umbau
|
||||
@@ -441,7 +451,7 @@ anleitungRouter.put("/workspace/api/anleitung/zeile/:id", nurPflege, (req, res)
|
||||
`UPDATE anl_zeile SET titel = ?, text = ?, ziel = ?, geaendert = ?, geaendert_von = ?
|
||||
WHERE id = ? AND haus = ?`)
|
||||
.run(text(req.body?.titel, 200), text(req.body?.text, 600), ziel,
|
||||
new Date().toISOString(), req.person.id, id, HAUS);
|
||||
new Date().toISOString(), req.person.id, id, hausVon(req.person));
|
||||
res.json({ ok: true });
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] Anleitung Zeile:", fehler?.message);
|
||||
@@ -453,13 +463,13 @@ anleitungRouter.put("/workspace/api/anleitung/zeile/:id", nurPflege, (req, res)
|
||||
anleitungRouter.put("/workspace/api/anleitung/rolle/:rolle", nurPflege, (req, res) => {
|
||||
try {
|
||||
const rolle = String(req.params.rolle || "");
|
||||
if (!FASSUNGEN.includes(rolle)) return res.status(404).json({ fehler: "unbekannte_rolle" });
|
||||
if (!fassungenFuer(hausVon(req.person)).includes(rolle)) return res.status(404).json({ fehler: "unbekannte_rolle" });
|
||||
db().prepare(
|
||||
`INSERT INTO anl_rolle (haus, rolle, leitgedanke, geaendert, geaendert_von)
|
||||
VALUES (?,?,?,?,?)
|
||||
ON CONFLICT(haus, rolle) DO UPDATE SET leitgedanke = excluded.leitgedanke,
|
||||
geaendert = excluded.geaendert, geaendert_von = excluded.geaendert_von`)
|
||||
.run(HAUS, rolle, text(req.body?.leitgedanke, 600),
|
||||
.run(hausVon(req.person), rolle, text(req.body?.leitgedanke, 600),
|
||||
new Date().toISOString(), req.person.id);
|
||||
res.json({ ok: true });
|
||||
} catch (fehler) {
|
||||
@@ -484,8 +494,8 @@ anleitungRouter.get("/workspace/api/anleitung/stand", nurPflege, (req, res) => {
|
||||
WHERE s.person_id = p.id AND z2.art = 'schritt') AS erledigt,
|
||||
(SELECT MAX(s2.erledigt_am) FROM anl_stand s2 WHERE s2.person_id = p.id) AS zuletzt
|
||||
FROM personen p
|
||||
WHERE p.aktiv = 1 AND p.rolle IN (${FASSUNGEN.map(() => "?").join(",")})
|
||||
ORDER BY p.rolle, p.name`).all(HAUS, ...FASSUNGEN);
|
||||
WHERE p.aktiv = 1 AND p.rolle IN (${fassungenFuer(hausVon(req.person)).map(() => "?").join(",")})
|
||||
ORDER BY p.rolle, p.name`).all(hausVon(req.person), ...fassungenFuer(hausVon(req.person)));
|
||||
res.json({
|
||||
leute: leute.map((p) => ({
|
||||
name: p.name, rolle: p.rolle,
|
||||
@@ -525,7 +535,7 @@ anleitungRouter.get("/workspace/api/anleitung/einweisung", (req, res) => {
|
||||
recht nicht fuer ihn abhaken, dass sie gezeigt wurde. */
|
||||
const person = req.person;
|
||||
const rolle = String(person?.rolle || "");
|
||||
if (person?.haus === "crew" || !FASSUNGEN.includes(rolle)) {
|
||||
if (!fassungenFuer(hausVon(person)).includes(rolle)) {
|
||||
return res.json({ zeigen: false, rolle });
|
||||
}
|
||||
const d = db();
|
||||
@@ -539,7 +549,7 @@ anleitungRouter.get("/workspace/api/anleitung/einweisung", (req, res) => {
|
||||
const muss = d.prepare(
|
||||
`SELECT kachel FROM anl_karte
|
||||
WHERE haus = ? AND rolle = ? AND stufe = 'muss' ORDER BY reihe`)
|
||||
.all(HAUS, rolle).map((z) => z.kachel);
|
||||
.all(hausVon(req.person), rolle).map((z) => z.kachel);
|
||||
|
||||
res.json({
|
||||
zeigen: !schon,
|
||||
@@ -567,7 +577,7 @@ anleitungRouter.post("/workspace/api/anleitung/einweisung/gesehen", (req, res) =
|
||||
try {
|
||||
const person = req.person;
|
||||
const rolle = String(person?.rolle || "");
|
||||
if (person?.haus === "crew" || !FASSUNGEN.includes(rolle)) {
|
||||
if (!fassungenFuer(hausVon(person)).includes(rolle)) {
|
||||
return res.json({ ok: true });
|
||||
}
|
||||
db().prepare(
|
||||
|
||||
Reference in New Issue
Block a user