From 8bf2f440d0fff0f8f9169487ff1c5ccdb850eae7 Mon Sep 17 00:00:00 2001 From: Dogfather Date: Fri, 25 Sep 2026 03:29:33 +0200 Subject: [PATCH] Bewerbungen der Modis bekommen eine eigene Spalte im Aufgabenbrett Filipe: "ich will in dieser seite auch eine eigene kategorie fuer die aufgaben wo die modis sich selbst bewerben. ich will dass man die getrennt sieht. mach es richtig uebersichtlich." WO SIE VORHER STANDEN, und warum das nicht reichte: nur auf dem Vorlagenbrett, an der jeweiligen Karte. Das ist der richtige Ort zum ENTSCHEIDEN -- man liest Aufgabe und Name nebeneinander. Es ist der falsche Ort zum SEHEN: Wer morgens das Aufgabenbrett oeffnet, sieht nicht, dass drei Leute auf eine Antwort warten, und das Vorlagenbrett ist eine Seite weiter. Fuer den Modi gab es ueberhaupt keine Stelle, an der stand "dein Wunsch ist angekommen". DIE SPALTE STEHT GANZ VORN. Sie ist die einzige, in der jemand auf eine ANTWORT wartet -- alle anderen zeigen Arbeit, die laeuft. Und sie VERSCHWINDET, wenn nichts wartet: Eine leere Spalte "Bewerbungen" stuende 360 Tage im Jahr im Weg, um an fuenf Tagen etwas zu sagen. Die vier festen Spalten haben auch leer eine Aussage ("hier landet, was fertig ist"); diese nicht. WAS AUF DER KARTE STEHT: der TITEL der Vorlage (nicht ihr Schluessel), wer sie uebernehmen moechte, und sein eigener Satz dazu -- als Zitat gesetzt, mit Strich davor. Er gehoert ihm, nicht dem Brett. Ohne ihn entscheidet man ueber einen Namen. DER TITEL WIRD NACHGESCHLAGEN, NICHT MITGESPEICHERT. Er gehoert dem Katalog; stuende er in der Bewerbungszeile, gaebe es zwei Wahrheiten, und die aeltere gewinnt still, sobald jemand eine Vorlage umbenennt. EIN EIGENER, KLEINER WEG statt eines Mitschleppens: `/workspace/api/vorlagen/bewerbungen` liefert nur die Bewerbungen -- nicht den ganzen Katalog, der an `/workspace/api/vorlagen` haengt. Das Brett zeichnet sich bei jedem Statuswechsel neu; der Katalog ist um ein Vielfaches groesser als die Handvoll Bewerbungen. WER ENTSCHEIDEN DARF, SAGT DER SERVER (`darf_entscheiden`) -- nicht der Rollenname im Browser. `assets/js` bekommt jeder, der die Seite oeffnet, und ein Rollenvergleich dort ist in diesem Haus allein diese Woche dreimal veraltet. Bis die Antwort da ist, gilt `false`: lieber einen Knopf zu spaet zeigen als einen, der eine Absage holt. DIE FARBE IST NEU IM BRETT. Die vier vorhandenen stehen fuer einen Arbeitsstand (grau, blau, gelb, gruen); hier wartet niemand auf Arbeit, sondern auf eine Entscheidung. Kein Rot ("kaputt"), kein Gelb (heisst schon "zur Freigabe") -- Violett, das im Chat seit gestern fuer "an dich gerichtet" steht. Eine Sprache im Haus. Geprueft: pruef-bewerbung-aufgaben 101 -> 111 Punkte. Darunter die drei Gegenproben, ohne die "die Spalte ist da" nichts bewiese: ohne Bewerbung gibt es sie NICHT, der Modi bekommt KEINE Entscheidungsknoepfe (sondern "wartet auf Antwort"), und die Spalte steht wirklich an erster Stelle. Dazu pruef-aufgabenbrett und pruef-vorlagen (24), beide unveraendert gruen. Co-Authored-By: Claude Opus 5 --- server/pruef-bewerbung-aufgaben.mjs | 115 +++++++++++++++++++ server/workspace-vorlagen.js | 63 +++++++++- workspace/anruf-probe.html | 10 +- workspace/app.webmanifest | 8 +- workspace/assets/css/aufgaben.css | 60 ++++++++++ workspace/assets/js/aufgaben.js | 172 ++++++++++++++++++++++++++++ workspace/aufgaben.html | 38 +++--- workspace/automation.html | 32 +++--- workspace/befinden.html | 34 +++--- workspace/bereich.html | 44 +++---- workspace/bewerben.html | 38 +++--- workspace/bewerbungen.html | 36 +++--- workspace/calls.html | 34 +++--- workspace/chat.html | 38 +++--- workspace/content.html | 34 +++--- workspace/crew-index.html | 12 +- workspace/crew.webmanifest | 8 +- workspace/dateien.html | 34 +++--- workspace/entwicklung.html | 36 +++--- workspace/hilfe.html | 30 ++--- workspace/index.html | 10 +- workspace/kalender.html | 34 +++--- workspace/leistung.html | 32 +++--- workspace/material.html | 32 +++--- workspace/personen.html | 32 +++--- workspace/profil.html | 36 +++--- workspace/rechte.html | 34 +++--- workspace/report.html | 36 +++--- workspace/scouting.html | 32 +++--- workspace/start.html | 32 +++--- workspace/startcheck.html | 34 +++--- workspace/steckbrief.html | 36 +++--- workspace/support.html | 28 ++--- workspace/talente.html | 36 +++--- workspace/team.html | 32 +++--- workspace/teamlage.html | 34 +++--- workspace/teilen.html | 30 ++--- workspace/treff-moderation.html | 32 +++--- workspace/treff-regeln.html | 32 +++--- workspace/uebersicht.html | 32 +++--- workspace/unsere-seiten.html | 30 ++--- workspace/unterstuetzen.html | 32 +++--- workspace/werdegang.html | 32 +++--- workspace/willkommen.html | 30 ++--- workspace/wissen.html | 34 +++--- 45 files changed, 1034 insertions(+), 636 deletions(-) diff --git a/server/pruef-bewerbung-aufgaben.mjs b/server/pruef-bewerbung-aufgaben.mjs index fd7fc487..8278da92 100644 --- a/server/pruef-bewerbung-aufgaben.mjs +++ b/server/pruef-bewerbung-aufgaben.mjs @@ -843,6 +843,121 @@ melde("=== 6. Am echten Bildschirm ==="); } finally { await m.ctx.close(); } } + /* ================================================================= + DIE SPALTE „BEWERBUNGEN" IM AUFGABENBRETT (25.09.2026) + ================================================================= + + Filipe: „ich will in dieser seite auch eine eigene kategorie fuer + die aufgaben wo die modis sich selbst bewerben. ich will dass man + die getrennt sieht. mach es richtig uebersichtlich." + + BISHER STANDEN BEWERBUNGEN NUR AUF DEM VORLAGENBRETT, an der + jeweiligen Karte. Wer morgens das Aufgabenbrett oeffnete, sah + nicht, dass jemand auf eine Antwort wartet. + + DREI DINGE WERDEN GEMESSEN, und das dritte ist das, was ohne + Pruefung irgendwann kippt: + 1. Wer entscheidet, sieht die Spalte MIT den zwei Knoepfen. + 2. Der Modi sieht seine eigene -- ohne Knoepfe, mit dem Satz + „wartet auf Antwort". Ein Knopf, der eine Absage holt, + waere schlimmer als keiner. + 3. Ohne Bewerbung gibt es die Spalte GAR NICHT. Eine leere + Spalte stuende 360 Tage im Jahr im Weg. + ================================================================= */ + { + /* ERST DER ZUSTAND OHNE: Ohne diese Messung waere „die Spalte ist + da" auch dann gruen, wenn sie immer da waere. */ + const vorher = await seiteAls("admin", "CODE-DOGI-0001", 1400); + let ohne = null; + try { + await vorher.s.goto(`${SEITE}/workspace/aufgaben.html`, { waitUntil: "networkidle" }); + await vorher.s.waitForTimeout(1700); + ohne = await vorher.s.evaluate(() => + document.querySelectorAll(".spalte--bewerbung").length); + } finally { await vorher.ctx.close(); } + ok(ohne === 0, `ohne Bewerbung gibt es die Spalte nicht (${ohne})`); + + /* JETZT BEWIRBT SICH EIN MODI -- ueber die echte Schnittstelle + der Seite, damit Sitzung und Herkunftspruefung mitlaufen. */ + const m = await seiteAls("modi", "CODE-MODI-0001", 1400); + let beworben = null, beiModi = null; + try { + await m.s.goto(`${SEITE}/workspace/entwicklung.html`, { waitUntil: "networkidle" }); + await m.s.waitForTimeout(1500); + beworben = await m.s.evaluate(async () => { + const k = await fetch("/workspace/api/vorlagen", { credentials: "same-origin" }); + const d = await k.json(); + const v = (d.katalog?.aufgaben || [])[0]; + if (!v) return { fehler: "kein Katalog" }; + const a = await fetch("/workspace/api/vorlagen/bewerben", { + method: "POST", credentials: "same-origin", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ vorlage: v.schluessel, text: "ich habe Freitag Zeit" }), + }); + return { code: a.status, titel: v.titel }; + }); + /* UND DER MODI SIEHT SEINE EIGENE -- auf dem Aufgabenbrett. */ + await m.s.goto(`${SEITE}/workspace/aufgaben.html`, { waitUntil: "networkidle" }); + await m.s.waitForTimeout(1800); + beiModi = await m.s.evaluate(() => { + const sp = document.querySelector(".spalte--bewerbung"); + if (!sp) return { spalte: false }; + return { + spalte: true, + karten: sp.querySelectorAll(".karte--bewerbung").length, + knoepfe: sp.querySelectorAll(".karte--bewerbung button").length, + warte: !!sp.querySelector(".karte__warte"), + titel: sp.querySelector(".karte__titel")?.textContent || "", + wort: sp.querySelector(".karte__bwort")?.textContent || "", + }; + }); + } finally { await m.ctx.close(); } + + ok(beworben?.code === 201, + `ein Modi bewirbt sich (HTTP ${beworben?.code} ${beworben?.fehler || ""})`); + ok(beiModi?.spalte === true && beiModi.karten === 1, + `er sieht sie im Aufgabenbrett in einer eigenen Spalte (${beiModi?.karten})`); + ok(beiModi?.titel === beworben?.titel, + `mit dem TITEL der Aufgabe, nicht dem Schluessel ("${beiModi?.titel}")`); + ok((beiModi?.wort || "").includes("Freitag"), + `und mit seinem eigenen Satz dazu ("${beiModi?.wort}")`); + ok(beiModi?.knoepfe === 0 && beiModi?.warte === true, + `Gegenprobe: er bekommt KEINE Entscheidungsknoepfe (${beiModi?.knoepfe}), ` + + `sondern „wartet auf Antwort" (${beiModi?.warte})`); + + /* UND DER, DER ENTSCHEIDET. */ + const e2 = await seiteAls("admin", "CODE-DOGI-0001", 1400); + let beiDogi = null; + try { + await e2.s.goto(`${SEITE}/workspace/aufgaben.html`, { waitUntil: "networkidle" }); + await e2.s.waitForTimeout(1800); + beiDogi = await e2.s.evaluate(() => { + const sp = document.querySelector(".spalte--bewerbung"); + if (!sp) return { spalte: false }; + return { + spalte: true, + karten: sp.querySelectorAll(".karte--bewerbung").length, + knoepfe: [...sp.querySelectorAll(".karte--bewerbung button")] + .map((b) => b.textContent.trim()), + /* Die Spalte muss die ERSTE sein: Was auf eine Antwort + wartet, gehoert an die Stelle, die man zuerst ansieht. */ + erste: document.querySelector("#brett .spalte")?.classList + .contains("spalte--bewerbung"), + wer: sp.querySelector(".karte__wer")?.textContent || "", + }; + }); + } finally { await e2.ctx.close(); } + + ok(beiDogi?.spalte === true && beiDogi.karten === 1, + `DogFather sieht dieselbe Bewerbung (${beiDogi?.karten})`); + ok(beiDogi?.erste === true, + `und die Spalte steht ganz vorn (${beiDogi?.erste})`); + ok(JSON.stringify(beiDogi?.knoepfe) === JSON.stringify(["Annehmen", "Ablehnen"]), + `mit beiden Knoepfen (${JSON.stringify(beiDogi?.knoepfe)})`); + ok((beiDogi?.wer || "").includes("möchte das übernehmen"), + `und dem Namen dessen, der wartet ("${beiDogi?.wer}")`); + } + ok(meckern.length === 0, meckern.length ? `der Browser meldet: ${meckern.slice(0, 3).join(" | ")}` : "kein Absturz und keine Fehlermeldung im Browser"); diff --git a/server/workspace-vorlagen.js b/server/workspace-vorlagen.js index 42655be7..41cf40ae 100644 --- a/server/workspace-vorlagen.js +++ b/server/workspace-vorlagen.js @@ -1542,12 +1542,30 @@ function vorlagenBewerbungenFuer(person) { LEFT JOIN personen p ON p.id = b.person_id WHERE b.zustand = 'beworben' ${darf ? "" : "AND b.person_id = ?"} ORDER BY b.id`).all(...(darf ? [] : [person.id])); - return zeilen.map((z) => ({ - id: z.id, vorlage: z.vorlage, kategorie: z.kategorie, - person_id: z.person_id, person_name: z.person_name || "Jemand", - text: z.text || null, erstellt: z.erstellt, - selbst: z.person_id === person.id, - })); + return zeilen.map((z) => { + /* DER TITEL KOMMT MIT (25.09.2026). + + Bis heute reichte der Schluessel: Die Bewerbung stand auf dem + Vorlagenbrett AN der Karte, und die Karte trug den Titel. Seit + sie auch im Aufgabenbrett steht (Filipes Wunsch, eigene + Kategorie), gibt es dort keine Karte daneben -- und ein + Schluessel wie "regeln_lesen" ist kein Satz, den jemand liest. + + NACHGESCHLAGEN, NICHT MITGESPEICHERT: Der Titel gehoert dem + Katalog. Wuerde er in der Bewerbungszeile stehen, haette man + zwei Wahrheiten, und die aeltere gewinnt still, sobald jemand + eine Vorlage umbenennt. */ + const v = vorlageZuSchluessel(z.vorlage); + return { + id: z.id, vorlage: z.vorlage, kategorie: z.kategorie, + titel: v?.titel || z.vorlage, + text_vorlage: v?.text || null, + tage: v?.tage ?? null, + person_id: z.person_id, person_name: z.person_name || "Jemand", + text: z.text || null, erstellt: z.erstellt, + selbst: z.person_id === person.id, + }; + }); } /* ===================================================================== @@ -1589,6 +1607,39 @@ function vorlageZuSchluessel(schluessel) { return MODI_AUFGABEN_FLACH.find((v) => v.schluessel === schluessel) || null; } +/* ==== NUR DIE BEWERBUNGEN, OHNE DEN GANZEN KATALOG (25.09.2026) ==== + + Filipe: „ich will in dieser seite auch eine eigene kategorie fuer + die aufgaben wo die modis sich selbst bewerben. ich will dass man + die getrennt sieht." + + Das Aufgabenbrett braucht die Bewerbungen -- aber nicht den Katalog + mit allen Vorlagen, der an `/workspace/api/vorlagen` haengt. Ein + eigener, kleiner Weg statt eines Mitschleppens: Das Brett laedt bei + jedem Zeichnen, und der Katalog ist um ein Vielfaches groesser als + die Handvoll Bewerbungen. + + WER WAS SIEHT, ENTSCHEIDET UNVERAENDERT `vorlagenBewerbungenFuer`: + Wer entscheidet, sieht alle offenen; alle anderen nur die eigenen. + Diese Route waehlt nichts selbst aus -- sonst gaebe es eine zweite + Stelle, an der die Sichtbarkeit falsch werden kann. */ +vorlagenRouter.get("/workspace/api/vorlagen/bewerbungen", (req, res) => { + try { + /* WER ENTSCHEIDET, KOMMT MIT. Die Oberflaeche soll die zwei + Knoepfe nur dort zeigen, wo der Server sie auch annimmt -- und + sie darf es nicht am Rollennamen ausrechnen: `assets/js` bekommt + jeder, der die Seite oeffnet, und ein Rollenvergleich im Browser + ist in diesem Haus diese Woche dreimal veraltet. */ + res.json({ + bewerbungen: vorlagenBewerbungenFuer(req.person), + darf_entscheiden: entscheidetUeberAufgaben(req.person), + }); + } catch (f) { + console.error("[vorlagen] Bewerbungen lesen:", f?.message); + res.status(503).json({ fehler: "nicht_verfuegbar" }); + } +}); + vorlagenRouter.post("/workspace/api/vorlagen/bewerben", gleicheHerkunft, express.json({ limit: "4kb" }), (req, res) => { try { diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 60292bd2..9e64136a 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -16,15 +16,15 @@ verdeckt. Gefunden hat es pruef-struktur. Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> - - + + - +