diff --git a/server/pruef-chat.mjs b/server/pruef-chat.mjs index 38c64825..89549df3 100644 --- a/server/pruef-chat.mjs +++ b/server/pruef-chat.mjs @@ -402,6 +402,110 @@ console.log("\n=== Ohne Anmeldung ==="); d.close(); try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } +/* ===================================================================== + GESPRAECHE OBEN ANHEFTEN (25.09.2026) + ===================================================================== + + Filipe: „ich will dass man auch individuel jeder fuer sich auch in + der liste chats fixieren kann. auch mehrere nicht nur eins." + + DREI AUSSAGEN STECKEN IN DIESEM SATZ, und jede wird einzeln + geprueft: + + „fixieren" -> das Angeheftete steht OBEN, nicht irgendwo + „individuell" -> beim anderen aendert sich NICHTS + „auch mehrere" -> zwei gleichzeitig, und die Reihenfolge stimmt + + DIE ZWEITE IST DIE WICHTIGSTE und die, die am leichtesten kaputt + geht: Eine Spalte an `chat_raeume` haette alles andere genauso + erfuellt -- und jedem im Raum das Gespraech oben hingeklebt. Man + haette es erst gemerkt, wenn sich jemand beschwert. + + GEGENPROBEN: fremder Raum (404) und das Loesen (steht wieder + unten). Ohne sie waere „steht oben" ein Haken, der auch gruen + waere, wenn die Liste immer dieselbe Reihenfolge haette. + ===================================================================== */ +console.log("\n=== Anheften: oben, einzeln, mehrere ==="); +{ + /* Zwei eigene Raeume anlegen -- so haengt die Pruefung nicht davon + ab, wie viele Gespraeche zufaellig schon dastehen. Mit Luna + zusammen, denn an ihr wird gleich gezeigt, dass sich bei ihr + nichts aendert. */ + const machGruppe = async (name) => { + const a = await rufe(dogi.keks, "/workspace/api/chat/gruppe", + { name, mit: [idLuna, idPat] }, "POST"); + const j = await a.json().catch(() => ({})); + return j.raum_id; + }; + const raumA = await machGruppe("Anheft-Probe A"); + const raumB = await machGruppe("Anheft-Probe B"); + ok(Number.isInteger(raumA) && Number.isInteger(raumB) && raumA !== raumB, + `zwei Raeume zum Anheften (${raumA}, ${raumB})`); + + const liste = async (keks) => (await holen(keks, "/workspace/api/chat/raeume")).raeume || []; + const anheften = (keks, raum, an) => + rufe(keks, `/workspace/api/chat/raeume/${raum}/fixieren`, { an }, "PUT"); + + /* VORHER: nichts ist angeheftet. Ohne diese Zeile koennte alles + Folgende auch dann gruen sein, wenn die Spalte von sich aus + gefuellt waere. */ + const vorher = await liste(dogi.keks); + ok(vorher.length >= 2 && vorher.every((r) => r.fixiert === false), + `vorher ist nichts angeheftet (${vorher.length} Gespraeche)`); + + /* ---- 1. „fixieren": oben ---------------------------------------- */ + const wo = vorher.findIndex((r) => r.id === raumA); + const a1 = await anheften(dogi.keks, raumA, true); + const j1 = await a1.json().catch(() => ({})); + ok(a1.status === 200 && j1.fixiert === true, + `Raum A laesst sich anheften (HTTP ${a1.status}, fixiert ${j1.fixiert})`); + const nachA = await liste(dogi.keks); + ok(nachA[0]?.id === raumA, + `und steht danach ganz oben (vorher an Stelle ${wo + 1}, jetzt an 1)`); + ok(nachA[0]?.fixiert === true, " und traegt die Marke dafuer"); + + /* ---- 2. „individuell": bei Luna aendert sich nichts -------------- */ + const beiLuna = await liste(luna.keks); + const lunaA = beiLuna.find((r) => r.id === raumA); + ok(!!lunaA && lunaA.fixiert === false, + `Gegenprobe: bei Luna ist derselbe Raum NICHT angeheftet (${lunaA?.fixiert})`); + /* UND ER STEHT BEI IHR AUCH NICHT OBEN. Die Marke allein wuerde + nicht reichen: Sortiert der Server nach einer Spalte am RAUM, + stuende er bei ihr oben, obwohl `fixiert` false meldet. */ + ok(beiLuna.length < 2 || beiLuna[0].id !== raumA + || beiLuna.filter((r) => r.fixiert).length > 0, + ` und rutscht bei ihr auch nicht nach oben (oben steht ${beiLuna[0]?.id})`); + + /* ---- 3. „auch mehrere" ------------------------------------------ */ + await anheften(dogi.keks, raumB, true); + const nachB = await liste(dogi.keks); + ok(nachB.filter((r) => r.fixiert).length === 2, + `zwei gleichzeitig angeheftet (${nachB.filter((r) => r.fixiert).length})`); + ok(nachB[0]?.id === raumB && nachB[1]?.id === raumA, + `und das zuletzt angeheftete steht oben (${nachB[0]?.id}, dann ${nachB[1]?.id})`); + ok(nachB.slice(2).every((r) => r.fixiert === false), + " darunter geht die gewohnte Reihenfolge weiter"); + + /* ---- 4. Loesen --------------------------------------------------- */ + const a2 = await anheften(dogi.keks, raumB, false); + ok(a2.status === 200, `es laesst sich wieder loesen (HTTP ${a2.status})`); + const nachLos = await liste(dogi.keks); + ok(nachLos[0]?.id === raumA && nachLos.filter((r) => r.fixiert).length === 1, + `danach haengt nur noch A oben (oben ${nachLos[0]?.id}, ` + + `${nachLos.filter((r) => r.fixiert).length} angeheftet)`); + + /* ---- 5. Ein fremder Raum ---------------------------------------- */ + /* Nora sitzt in keinem der beiden. Kaeme hier etwas anderes als 404 + zurueck, liesse sich aus der Antwort ablesen, welche Raumnummern + es ueberhaupt gibt. */ + const fremd = await anheften(nora.keks, raumA, true); + ok(fremd.status === 404, + `Gegenprobe: ein fremder Raum laesst sich nicht anheften (${fremd.status})`); + const beiNora = await liste(nora.keks); + ok(beiNora.every((r) => r.fixiert === false), + " und bei ihr haengt danach auch nichts"); +} + console.log(`\n${fehler === 0 ? "ALLES IN ORDNUNG" : `${fehler} FEHLER`}`); /* HIER IST process.exit() RICHTIG -- anders als in pruef-push-weg. diff --git a/server/workspace-chat.js b/server/workspace-chat.js index 06c8e7ed..dc696de4 100644 --- a/server/workspace-chat.js +++ b/server/workspace-chat.js @@ -1110,7 +1110,7 @@ chatRouter.get("/workspace/api/chat/raeume", (req, res) => { angelegt hat. */ const reihen = db().prepare(` SELECT r.id, r.art, r.name, r.kategorie, r.letzte_am, r.erstellt_von, - t.gelesen_bis, t.leitung, t.geloescht_bis + t.gelesen_bis, t.leitung, t.geloescht_bis, t.fixiert_am FROM chat_raeume r JOIN chat_teilnehmer t ON t.raum_id = r.id AND t.person_id = ? WHERE t.raus_am IS NULL @@ -1118,7 +1118,19 @@ chatRouter.get("/workspace/api/chat/raeume", (req, res) => { AND (t.geloescht_am IS NULL OR EXISTS (SELECT 1 FROM chat_nachrichten n WHERE n.raum_id = r.id AND n.id > t.geloescht_bis)) - ORDER BY CASE WHEN r.letzte_am IS NULL THEN 1 ELSE 0 END, r.letzte_am DESC, r.id DESC`) + /* ANGEHEFTETES ZUERST (25.09.2026), und INNERHALB der beiden + Bloecke bleibt die gewohnte Ordnung: zuletzt gesprochen oben. + Wer etwas anheftet, will es oben haben -- er will aber nicht, + dass sich die Reihenfolge darunter aendert. + + ZULETZT ANGEHEFTET STEHT GANZ OBEN. Eine andere Regel gaebe + es nicht: Nach Name waere es Zufall, nach Datum der letzten + Nachricht waere das Anheften wirkungslos, sobald jemand + schreibt. */ + ORDER BY CASE WHEN t.fixiert_am IS NULL THEN 1 ELSE 0 END, + t.fixiert_am DESC, + CASE WHEN r.letzte_am IS NULL THEN 1 ELSE 0 END, + r.letzte_am DESC, r.id DESC`) .all(req.person.id); const raeume = reihen.map((r) => { @@ -1203,6 +1215,10 @@ chatRouter.get("/workspace/api/chat/raeume", (req, res) => { } : null, ungelesen, erwaehnt, + /* Als Ja/Nein an die Oberflaeche -- WANN jemand etwas + angeheftet hat, geht niemanden etwas an, auch ihn selbst + nicht. Der Zeitstempel ist Sortierung, keine Auskunft. */ + fixiert: !!r.fixiert_am, }; }); /* WELCHE KANAELE ES NOCH NICHT GIBT -- und nur fuer die, die sie @@ -2985,6 +3001,52 @@ chatRouter.put("/workspace/api/chat/raeume/:id/gelesen", gleicheHerkunft, } }); +/* ===================================================================== + EIN GESPRAECH OBEN ANHEFTEN (25.09.2026) + ===================================================================== + + Filipe: „ich will dass man auch individuel jeder fuer sich auch in + der liste chats fixieren kann. auch mehrere nicht nur eins." + + JEDER FUER SICH: Geschrieben wird in die TEILNEHMER-Zeile, nicht an + den Raum. Wer den Treff anheftet, heftet ihn nur bei sich an -- eine + Spalte am Raum waere eine Entscheidung fuer alle acht Leute darin. + + BELIEBIG VIELE: Es gibt keine Obergrenze und keine "erst das alte + loesen"-Logik. Er hat ausdruecklich „auch mehrere" gesagt, und eine + Grenze waere eine Zahl, die irgendwann jemand neu raten muesste. + + `istDrin` UND NICHT NUR "gibt es den Raum": Sonst liesse sich aus + der Antwort ablesen, welche Raumnummern es ueberhaupt gibt -- und + ein 404 fuer beides (nicht da / nicht meiner) sagt am wenigsten. + + DIE ANTWORT TRAEGT DEN NEUEN STAND und nicht nur „ok". Die + Oberflaeche zeichnet die Liste danach ohnehin neu; der Stand in der + Antwort ist die Sicherung dagegen, dass sie ihn selbst ausrechnet + und dabei danebenliegt. */ +chatRouter.put("/workspace/api/chat/raeume/:id/fixieren", gleicheHerkunft, + express.json({ limit: "4kb" }), (req, res) => { + try { + const raumId = Number(req.params.id); + if (!Number.isInteger(raumId)) return res.status(400).json({ fehler: "ungueltig" }); + if (!istDrin(raumId, req.person)) return res.status(404).json({ fehler: "nicht_gefunden" }); + + /* AUSDRUECKLICH `=== true` / `=== false` und kein Umschalten + ohne Angabe. Ein Schalter, der den Gegenwert selbst ausrechnet, + kippt bei zwei schnellen Klicks oder zwei offenen Fenstern in + den falschen Zustand -- und niemand sieht, warum. Die + Oberflaeche weiss, was sie will; sie sagt es. */ + const an = req.body?.an === true; + db().prepare(`UPDATE chat_teilnehmer SET fixiert_am = ? + WHERE raum_id = ? AND person_id = ?`) + .run(an ? new Date().toISOString() : null, raumId, req.person.id); + res.json({ ok: true, fixiert: an }); + } catch (fehler) { + console.error("[chat] Anheften:", fehler?.message); + res.status(503).json({ fehler: "nicht_verfuegbar" }); + } + }); + /* ===================================================================== ANKUENDIGUNGEN ANHEFTEN (10.09.2026, Kapitel 7.2) diff --git a/server/workspace.js b/server/workspace.js index b87bce57..63c9d498 100644 --- a/server/workspace.js +++ b/server/workspace.js @@ -3829,6 +3829,23 @@ function umstellungen(d) { ["chat_teilnehmer", "geloescht_bis", "INTEGER NOT NULL DEFAULT 0"], ["chat_teilnehmer", "geloescht_am", "TEXT"], + /* GESPRAECHE OBEN ANHEFTEN (25.09.2026). + + Filipe: „ich will dass man auch individuel jeder fuer sich auch + in der liste chats fixieren kann. auch mehrere nicht nur eins." + + AN DER TEILNEHMERZEILE UND NICHT AM RAUM -- das ist der ganze + Punkt seines Satzes: „individuell jeder fuer sich". Eine Spalte + an `chat_raeume` waere eine Entscheidung FUER ALLE; wer den + Treff anheftet, haette ihn jedem anderen oben hingeklebt. + + EIN ZEITSTEMPEL UND KEIN JA/NEIN. Er kostet dasselbe und + beantwortet eine Frage mehr: In welcher Reihenfolge stehen + mehrere angeheftete Gespraeche? Zuletzt angeheftet steht oben -- + das ist die Reihenfolge, die man selbst gebaut hat. Mit einer + 0/1-Spalte muesste man sie erfinden. */ + ["chat_teilnehmer", "fixiert_am", "TEXT"], + /* ANHAENGE: FOTOS UND PDF (09.09.2026). Filipe: "am besten waere es auch wenn man da auch im chat pdfs diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index adc5f77b..e154979a 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`. --> - - + + - +