diff --git a/server/pruef-modi-katalog.mjs b/server/pruef-modi-katalog.mjs index fd34129a..86f7a449 100644 --- a/server/pruef-modi-katalog.mjs +++ b/server/pruef-modi-katalog.mjs @@ -344,6 +344,113 @@ console.log("\n=== 6. Das Brett im echten Browser ===\n"); } } +/* ======================================================================= + 6b. AN WEN? — die Auswahl steht dort, wo entschieden wird (16.09.2026) + + Filipe: „perfektionniere die kategorien wo ich aufgaben an die modis + verteile und so." + + Bis heute kam die Person aus dem Formular GANZ OBEN auf der Seite: + dort wählen, herunterscrollen, „Übernehmen" drücken. Wer das nicht + weiß, bekommt „Bitte zuerst eine Person wählen" und sucht. + + Und man sah beim Vergeben nicht, wer schon wie viel offen hat. Zu + wenig Zeit ist der meistgenannte Grund, warum Moderatoren aufhören; + die Person, die verteilt, ist die einzige, die das verhindern kann. + Dafür muss die Zahl dort stehen, wo entschieden wird — nicht auf + einer Auswertung, die man hinterher aufruft. + ======================================================================= */ +console.log(""); +console.log("=== 6b. An wen die Aufgabe geht ==="); +console.log(""); +{ + const { chromium } = await import( + "file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs"); + const browser = await chromium.launch(); + try { + const kontext = await browser.newContext({ viewport: { width: 1280, height: 1100 } }); + const seite = await kontext.newPage(); + const konsole = []; + seite.on("console", (e) => { if (e.type() === "error") konsole.push(e.text()); }); + seite.on("pageerror", (e) => konsole.push("Absturz: " + e.message)); + await seite.goto(BASIS + "/workspace/", { waitUntil: "domcontentloaded" }); + await seite.click('.rolle[data-rolle="admin"]'); + await seite.fill("#code", "CODE-DOGI-0001"); + await seite.click("button[type=submit]"); + await seite.waitForURL("**/start.html", { timeout: 15000 }); + await seite.goto(BASIS + "/workspace/aufgaben.html", { waitUntil: "networkidle" }); + await seite.waitForTimeout(1400); + const zu = await seite.evaluate(() => + document.getElementById("vorlagenbrett")?.dataset.auf !== "ja"); + if (zu) { + await seite.click(".vorlagenbrett__kopf").catch(() => {}); + await seite.waitForTimeout(800); + } + + const wahl = await seite.evaluate(() => { + const reihen = [...document.querySelectorAll(".stufenleiter--fein")]; + const letzte = reihen[reihen.length - 1]; + const zahl = /^[0-9]+$/; + return { + reihen: reihen.length, + namen: [...(letzte?.querySelectorAll(".stufenleiter__name") || [])] + .map((z) => z.textContent.trim()), + zahlenOk: [...(letzte?.querySelectorAll(".stufenleiter__zahl") || [])] + .every((z) => zahl.test(z.textContent.trim())), + zahlen: [...(letzte?.querySelectorAll(".stufenleiter__zahl") || [])] + .map((z) => z.textContent.trim()), + satz: [...document.querySelectorAll(".vorlagenbrett__stand")] + .map((z) => z.textContent.trim()) + .find((t) => t.includes("Formular oben") || t.includes("gehen an")) || "", + }; + }); + /* ZWEI FEINE REIHEN: die Stufen und die Personen. Eine einzelne + Zahl haette nicht gesagt, welche der beiden fehlt. */ + ok(wahl.reihen === 2, "zwei Auswahlreihen (Stufen und Personen): " + wahl.reihen); + ok(wahl.namen.includes("Marina"), + "der Modi steht zur Auswahl (" + wahl.namen.join(", ") + ")"); + ok(!wahl.namen.includes("Tili") && !wahl.namen.includes("Schulle"), + "ein Creator und eine Managerin nicht — sie arbeiten im anderen Haus"); + ok(wahl.zahlenOk && wahl.zahlen.length === wahl.namen.length, + "neben jedem Namen steht, wie viel offen ist (" + wahl.zahlen.join(", ") + ")"); + ok(wahl.satz.includes("Formular oben"), + "und solange niemand gewählt ist, sagt die Seite es"); + + /* JETZT WIRKLICH DRUECKEN -- und nachsehen, ob die Aufgabe bei + DIESEM Menschen landet. Ein Knopf, den niemand betaetigt hat, + ist kein geprueter Knopf. */ + await seite.evaluate(() => { + const reihen = [...document.querySelectorAll(".stufenleiter--fein")]; + const letzte = reihen[reihen.length - 1]; + [...letzte.querySelectorAll(".stufenleiter__knopf")] + .find((k) => k.textContent.includes("Marina"))?.click(); + }); + await seite.waitForTimeout(600); + const nachWahl = await seite.evaluate(() => + [...document.querySelectorAll(".vorlagenbrett__stand")] + .map((z) => z.textContent.trim()).find((t) => t.includes("gehen an")) || ""); + ok(nachWahl.includes("Marina"), "nach dem Klick steht es da: " + nachWahl); + + const vorher = await seite.evaluate(() => document.querySelectorAll(".karte").length); + await seite.evaluate(() => { + [...document.querySelectorAll(".vorlagenbrett__karte")][0] + ?.querySelector("button")?.click(); + }); + await seite.waitForTimeout(1800); + const danach = await seite.evaluate(() => ({ + karten: document.querySelectorAll(".karte").length, + fuerMarina: [...document.querySelectorAll(".karte")] + .filter((k) => k.textContent.includes("Marina")).length, + })); + ok(danach.karten === vorher + 1, + "die Aufgabe liegt auf dem Brett (" + vorher + " -> " + danach.karten + ")"); + ok(danach.fuerMarina > 0, "und zwar bei Marina (" + danach.fuerMarina + ")"); + ok(konsole.length === 0, + konsole.length ? "Fehler in der Konsole: " + konsole[0].slice(0, 80) + : "keine Fehler in der Konsole"); + } finally { await browser.close(); } +} + /* ---------- 7. Gegenprobe -------------------------------------------- */ console.log("\n=== 7. Gegenprobe: kann das hier ueberhaupt scheitern? ===\n"); { diff --git a/workspace/app.webmanifest b/workspace/app.webmanifest index e78c1a95..4ab7838b 100644 --- a/workspace/app.webmanifest +++ b/workspace/app.webmanifest @@ -13,25 +13,25 @@ "theme_color": "#06090f", "icons": [ { - "src": "/assets/img/app-symbole/workspace-192.png?v=202609161903", + "src": "/assets/img/app-symbole/workspace-192.png?v=202609161917", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-512.png?v=202609161903", + "src": "/assets/img/app-symbole/workspace-512.png?v=202609161917", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609161903", + "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609161917", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609161903", + "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609161917", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/workspace/assets/css/start.css b/workspace/assets/css/start.css index 0e70340f..4d204dbc 100644 --- a/workspace/assets/css/start.css +++ b/workspace/assets/css/start.css @@ -7620,6 +7620,22 @@ body.start .k-leiste .feldschild::after { .stufenleiter--fein { margin: -6px 0 14px; } .stufenleiter--fein .stufenleiter__knopf { padding: 5px 11px; font-size: .76rem; } +/* WER SCHON ETWAS LIEGEN HAT (16.09.2026). + + Beim Vergeben einer Aufgabe steht neben jedem Namen, wie viel offen + ist. Die Zahl allein bekommt KEINE Farbe: Was "zu viel" ist, haengt + vom Menschen ab, und eine feste Grenze waere geraten. + + Was nicht geraten ist: dass etwas ueberfaellig liegt. Nur das wird + markiert -- und zwar gedeckt, nicht grell. Ein Warnton an einem + Namen liest sich sonst wie ein Vorwurf gegen die Person, dabei ist + es eine Auskunft ueber die Verteilung. */ +.stufenleiter__knopf[data-spaet="ja"] { --sf: #cc9451; } +.stufenleiter__spaet { + font-size: .72rem; color: #d8a76a; + font-variant-numeric: tabular-nums; +} + .stufenleiter__knopf:hover { color: var(--text); box-shadow: diff --git a/workspace/assets/js/aufgaben.js b/workspace/assets/js/aufgaben.js index 2291ab19..70c03c1e 100644 --- a/workspace/assets/js/aufgaben.js +++ b/workspace/assets/js/aufgaben.js @@ -969,6 +969,11 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe $('b-feld-verant').hidden = false; try { const { personen } = await (await hole('/workspace/api/personen')).json(); + /* AUCH ALS DATEN BEHALTEN, nicht nur als