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 <[email protected]>
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user