diff --git a/server/mess-anleitung-crew.mjs b/server/mess-anleitung-crew.mjs index 17df9dec..a4e089e0 100644 --- a/server/mess-anleitung-crew.mjs +++ b/server/mess-anleitung-crew.mjs @@ -242,6 +242,28 @@ for (const [rolle, wie] of ROLLEN) { console.log(` Querscrollen : ${lage.quer ? "JA -- Befund" : "nein"}`); console.log(` Tippziele unter 44 : ${lage.klein}`); console.log(` Stufen-Marken : ${lage.marken.join(" | ") || "keine"}`); + + /* ==== DER FILTER „MUSS" (Abnahme Kapitel 13) ===================== + „Filter ‚Muss' zeigt nur die Muss-Karten." Gemessen wird, was + nach dem Tippen SICHTBAR ist -- nicht, was im Baum steht. Ein + Kasten mit `hidden` enthaelt seine Karten weiterhin; wer sie + zaehlt, zaehlt auch die verborgenen und bekommt immer dieselbe + Zahl heraus. */ + const filter = await seite.evaluate(async () => { + const knopf = [...document.querySelectorAll(".an-filter__knopf")] + .find((k) => k.dataset.wert === "muss"); + if (!knopf) return { da: false }; + knopf.click(); + await new Promise((r) => setTimeout(r, 200)); + const sichtbar = [...document.querySelectorAll(".an-karte")] + .filter((k) => k.getBoundingClientRect().height > 0).length; + const offeneStufen = [...document.querySelectorAll(".an-stufe")] + .filter((b) => !b.hidden).map((b) => b.dataset.stufe); + return { da: true, sichtbar, stufen: offeneStufen.join(", ") }; + }); + console.log(` Filter „Muss“ : ${filter.da + ? `${filter.sichtbar} Karten sichtbar, Abschnitt(e): ${filter.stufen}` + : "KEIN FILTERKNOPF -- Befund"}`); console.log(` Browsermeldungen : ${klagen.length ? klagen.join(" | ") : "keine"}`); console.log(` Fehlgeschlagen : ${schlecht.length ? schlecht.slice(0, 6).join(" | ") : "nichts"}`); diff --git a/server/pruef-anleitung.mjs b/server/pruef-anleitung.mjs index e40cf475..2a41291f 100644 --- a/server/pruef-anleitung.mjs +++ b/server/pruef-anleitung.mjs @@ -1290,6 +1290,75 @@ melde("\n=== Seite sperren, Karte weg (Abnahme Kapitel 13) ==="); + `${vorher.length})`); } +/* ===================================================================== + DIE PFLEGE AUF DER CREW-ADRESSE (Bauplan Kapitel 11) + --------------------------------------------------------------------- + „Fuer DogFather gibt es keine Fassung und keinen Rundgang. Bei ihm + oeffnet die Kachel die Pflege-Ansicht: Rolle waehlen und Vorschau, + Einordnung aendern, Texte bearbeiten, Lueckenwarnung, Stand der + Ersten Schritte — nur fuer Team-Rollen, fuer die Community wird + nichts je Person angezeigt." + + DER LETZTE HALBSATZ IST DER, DER HIER GEMESSEN WIRD. Im Agenturhaus + konnte er nie falsch sein (dort ist jede Fassung eine Teamrolle); + auf der Crew-Adresse schon, seit die Community eine eigene Fassung + hat. Ein Mitglied hat sich zum Mitlesen angemeldet, nicht zu einer + Fortschrittsakte mit Namen und Datum. */ +melde("\n=== Die Pflege auf der Crew-Adresse (Kapitel 11) ==="); +{ + const dogiCrew = await holen("admin", CREW_HOST); + ok(dogiCrew.code === 200 && dogiCrew.j?.hat_fassung === false + && dogiCrew.j?.darf_pflegen === true, + `DogFather bekommt auf der Crew-Adresse die Pflege, keine Fassung ` + + `(HTTP ${dogiCrew.code}, Fassung ${dogiCrew.j?.hat_fassung}, ` + + `pflegen ${dogiCrew.j?.darf_pflegen})`); + /* Und zwar die VIER des Hauses, nicht die der Agentur. */ + const angeboten = (dogiCrew.j?.fassungen || []).join(","); + ok(angeboten === "gast,modi,linke,hand", + `…mit den vier Fassungen dieses Hauses (${angeboten || "keine"})`); + + const st = await roh("/workspace/api/anleitung/stand", CREW_HOST, null, kekse.admin); + let leute = []; + try { leute = JSON.parse(st.text)?.leute || []; } catch { /* bleibt leer */ } + ok(st.code === 200 && leute.length > 0, + `der Stand der Ersten Schritte oeffnet sich (HTTP ${st.code}, ` + + `${leute.length} Leute)`); + ok(!leute.some((p) => p.rolle === "gast"), + `…und kein einziges Mitglied steht darin (${ + leute.filter((p) => p.rolle === "gast").length} von ${leute.length})`); + /* GEGENPROBE: Es steht ueberhaupt jemand drin, und zwar aus dem + Team. Ohne diese Zeile waere die darueber auch dann gruen, wenn + die Liste aus einem ganz anderen Grund leer bliebe. */ + ok(leute.some((p) => ["modi", "linke", "hand"].includes(p.rolle)), + `Gegenprobe: die Teamrollen stehen sehr wohl darin (${ + leute.map((p) => p.rolle).join(", ") || "niemand"})`); + /* Und ein Modi kommt gar nicht erst an die Liste. */ + const stModi = await roh("/workspace/api/anleitung/stand", CREW_HOST, null, kekse.modi); + ok(stModi.code === 403, `ein Modi sieht den Stand nicht (${stModi.code})`); + + /* ---- „Rolle waehlen und Vorschau in der Sicht dieser Rolle" ------- + Gebaut ist das nicht als zweite Rollenauswahl, sondern ueber den + Sicht-Umschalter, den es im Haus schon gibt: kopf.js haengt jeder + LESENDEN Anfrage `sicht=` an. Genau das wird hier + nachgestellt -- nicht nachgebaut, sondern dieselbe Anfrage + gestellt, die der Browser stellt. */ + const modiId = (() => { + const d = new DatabaseSync(process.env.WORKSPACE_DB); + const z = d.prepare("SELECT id FROM personen WHERE rolle = 'modi'").get(); + d.close(); return z?.id || 0; + })(); + braucht(!!modiId, "kein Modi in der Probe-Datenbank"); + const vorschau = await roh(`/workspace/api/anleitung?sicht=${modiId}`, + CREW_HOST, null, kekse.admin); + let vj = null; try { vj = JSON.parse(vorschau.text); } catch { /* bleibt null */ } + ok(vj?.rolle === "modi" && (vj?.karten?.length ?? 0) === 29, + `in der Sicht auf einen Modi sieht er dessen Anleitung ` + + `(${vj?.rolle}, ${vj?.karten?.length ?? 0} Karten)`); + ok(vj?.darf_pflegen === true && vj?.darf_abhaken === false, + `…darf sie pflegen, aber nicht in fremdem Namen abhaken ` + + `(pflegen ${vj?.darf_pflegen}, abhaken ${vj?.darf_abhaken})`); +} + /* ---------- Abschluss ------------------------------------------------ */ try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } console.log(`\n${fehler ? "FEHLER: " + fehler : "Alles in Ordnung"} — ${geprueft} Prüfungen`); diff --git a/server/workspace-anleitung.js b/server/workspace-anleitung.js index 8ecd5fce..1f0b4693 100644 --- a/server/workspace-anleitung.js +++ b/server/workspace-anleitung.js @@ -615,8 +615,36 @@ anleitungRouter.put("/workspace/api/anleitung/rolle/:rolle", nurPflege, (req, re * Unterscheidung wie bei der Team-Lage. Hier steht, wer seine * Einweisung noch offen hat, damit jemand nachfragen kann. Nicht, * was er gelesen hat oder wie lange er gebraucht hat. */ +/** Welche Rollen stehen im „Stand der Ersten Schritte"? + * + * NUR TEAMROLLEN -- Bauplan Kapitel 11, woertlich: „Stand der Ersten + * Schritte: nur fuer Team-Rollen, ‚fertig / offen' mit Datum. Fuer die + * Community wird nichts je Person angezeigt." + * + * GEMESSEN AM 06.10.2026, UND ES STIMMTE NICHT: Auf der Crew-Adresse + * nahm die Liste schlicht alle Fassungen des Hauses -- und seit die + * Community eine eigene Fassung hat, stand jedes Mitglied mit Namen + * und Fortschritt darin. Das ist kein Schoenheitsfehler: Ein Mitglied + * hat sich zum Mitlesen angemeldet, nicht zu einer Fortschrittsakte. + * Im Agenturhaus fiel es nicht auf, weil dort jede Fassung eine + * Teamrolle ist. + * + * ABGELEITET AUS AUSSEN_ROLLEN und nicht aus einer zweiten Liste: + * Kommt eines Tages eine weitere Rolle von aussen dazu, ist sie damit + * automatisch heraus. Eine Liste hier waere die, die man beim + * Anlegen der neuen Rolle nicht anfasst. */ +function standRollen(haus) { + return fassungenFuer(haus).filter((r) => !AUSSEN_ROLLEN.has(r)); +} + anleitungRouter.get("/workspace/api/anleitung/stand", nurPflege, (req, res) => { try { + const rollen = standRollen(hausVon(req.person)); + /* Keine Teamrolle in diesem Haus? Dann eine leere Liste und kein + SQL mit leerem IN () -- das waere ein Syntaxfehler und damit ein + 500 an einer Stelle, an der „es gibt niemanden" die richtige + Antwort ist. */ + if (!rollen.length) return res.json({ leute: [] }); const leute = db().prepare( `SELECT p.id, p.name, p.rolle, (SELECT COUNT(*) FROM anl_zeile z @@ -625,8 +653,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 (${fassungenFuer(hausVon(req.person)).map(() => "?").join(",")}) - ORDER BY p.rolle, p.name`).all(hausVon(req.person), ...fassungenFuer(hausVon(req.person))); + WHERE p.aktiv = 1 AND p.rolle IN (${rollen.map(() => "?").join(",")}) + ORDER BY p.rolle, p.name`).all(hausVon(req.person), ...rollen); res.json({ leute: leute.map((p) => ({ name: p.name, rolle: p.rolle, diff --git a/workspace/anleitung.html b/workspace/anleitung.html index 635e85ae..0fa581e5 100644 --- a/workspace/anleitung.html +++ b/workspace/anleitung.html @@ -7,17 +7,17 @@ - - - - - + + + + + - - + + @@ -146,10 +146,10 @@ Bedarf sagen, was WICHTIG ist; Neu, Dabei und Stamm sagen, was jemand DARF. Deshalb heißt die Einordnung auf dieser Seite nirgends „Stufe". --> -