From c64732b16d4f5bb8d6aef717e74ca44a907291da Mon Sep 17 00:00:00 2001 From: Dogfather Date: Fri, 25 Sep 2026 03:07:05 +0200 Subject: [PATCH] Neuer Stil "Nachtprisma", Gespraeche anheften, und unten wieder Luft Drei Sachen aus einem Bildschirmfoto-Satz. === 1. EIN ANDERER STIL, NICHT DIESELBE SPRACHE MIT NEUEN DETAILS === Filipe, zum dritten Mal an derselben Stelle: "du verstehst es wirklich nicht ... ich will eine komplette aenderung vom aussehen. vom hintergrund und von der grossen kachel. ich will einen ganz anderen stil ... die leute sollen morgen nichts mehr wieder erkennen vom aussehen her." WARUM MEINE ZWEI ANLAEUFE DAVOR NICHT GEREICHT HABEN -- und das ist kein Geschmacksstreit, sondern ein Fund: Der Umriss des Chats kommt gar nicht aus chat.css. Er steht in module.css, in einer Liste von 50 Klassen, und `.chat` ist eine davon: Fase oben links, Kantenlicht, Punktraster, drei goldene Eckwinkel. module.css wird NACH chat.css geladen -- und die Staerke des `:is(...)` dort ist (0,2,0), weil `.gruppe[data-gruppe]` mit in der Liste steht. Jede meiner Regeln war gleich stark und kam frueher. Deshalb stimmte beides: "ich habe den Rand geaendert" und "der Rand ist derselbe". Ich habe zweimal Details INNERHALB eines Rahmens geaendert, den ich nicht angefasst hatte -- und den erkennt man zuerst. Der neue Block steht als eine klar benannte Schicht am Ende von chat.css, mit `.inhalt.chat-seite` -- eine Klasse mehr als module.css, kein `!important` (das waere eine Tuer, die man nie wieder zubekommt). ALT NEU Fase oben links rundum 26 px weich drei goldene Eckwinkel keine -- der Koerper traegt sich selbst Punktraster drei weiche Lichter im Hintergrund 1-px-Rahmen ueberall kein Rahmen, Lichtkante innen Gold als Leitfarbe Lavendel/Violett, Blasen wie gehabt Kaesten nebeneinander Koerper mit Tiefe und farbigem Schatten DIE LEITFARBE WIRD AN EINER STELLE GETAUSCHT, nicht an zwanzig. Im ersten Anlauf habe ich zehn Regeln einzeln umgefaerbt und danach im Bild gesehen, dass Suchfeld, "Neu", Zaehler und Fokusrahmen weiter golden waren -- sie nehmen alle `--akzent` und `--rand`. Jetzt stehen beide am `
` der Chatseite. Uebersicht, Kalender und Aufgaben behalten ihr Gold; nur der Chat soll nicht wiederzuerkennen sein. #b9a7ff UND NICHT #7a5cff, und das ist gerechnet, nicht gewaehlt: Die Akzentfarbe ist hier auch FLAECHE unter dunkler Schrift (die Ungelesen-Marke). Das satte Violett kommt dort auf 3,7:1 -- zu wenig. Das helle auf 8,9:1, und als Schrift auf dunklem Grund genauso. WAS UNANGETASTET BLEIBT: `--blasengrund`, `--blase-text`, `--blase-leise`, `--namen-anteil`. An ihnen haengen die Messungen von pruef-chatkachel (12 Kacheln) und pruef-chat-neu (360 Ringtoene). Ein Stilwechsel darf eine Zusage nicht nebenbei aufheben. pruef-chat-optik hat sofort einen echten Schaden gemeldet: Der neue Stil nahm allen Blasen den Rahmen -- und damit auch den, mit dem eine NICHT ABGESCHICKTE Nachricht markiert ist ("der Unterschied ist auch zu SEHEN, nicht nur im Merkmal (Rand 0px)"). Genau dafuer steht die Zeile dort. Der Warnton sitzt jetzt zusaetzlich im inneren Saum. === 2. GESPRAECHE ANHEFTEN === Filipe: "ich will dass man auch individuel jeder fuer sich auch in der liste chats fixieren kann. auch mehrere nicht nur eins." Drei Aussagen, und jede wird einzeln geprueft: "fixieren" -> `fixiert_am` an der TEILNEHMER-Zeile; Angeheftetes steht oben, darunter geht die gewohnte Reihenfolge weiter. "individuell" -> die Spalte haengt an der Person, nicht am Raum. Eine Spalte an `chat_raeume` haette alles andere genauso erfuellt und jedem im Raum das Gespraech oben hingeklebt -- gemerkt haette man es erst, wenn sich jemand beschwert. Die Gegenprobe prueft deshalb ausdruecklich, dass es bei Luna weder markiert ist noch nach oben rutscht. "auch mehrere" -> keine Obergrenze. Ein Zeitstempel statt Ja/Nein kostet dasselbe und beantwortet die Frage mit, in welcher Reihenfolge mehrere stehen: zuletzt angeheftet oben. Die Nadel steht IMMER an der Zeile, nicht erst beim Ueberfahren -- am Handy gibt es kein Ueberfahren (dieselbe Entscheidung wie am 23.09. bei den Handgriffen), und eine Spalte, die mal da ist und mal nicht, laesst die Namen daneben wandern. Sie liegt schraeg, solange nichts angeheftet ist, und steht aufrecht, wenn doch -- das sieht man auch ohne Farbe. Der Zustand wird GESCHICKT, nicht errechnet (`an: true/false`): Ein Schalter, der den Gegenwert selbst ausrechnet, kippt bei zwei schnellen Klicks oder zwei offenen Fenstern in den falschen Zustand. Die Karte ist seit heute die ZEILE und nicht mehr der Knopf darin -- im ersten Anlauf sass die Nadel sichtbar ausserhalb der Flaeche, wie ein Knopf, der danebengefallen ist. === 3. UNTEN WIEDER LUFT === "schieb das bisschen hoeher bitte, weil das ist unten zu nah am rand." 14 px Polsterung. Sie geht nach INNEN (`border-box`), macht die Seite also nicht laenger -- sonst waere das Schreibfeld wieder unter den Bildrand gerutscht, und genau darum ging es am 09.09. schon einmal. Geprueft: pruef-chat (neuer Abschnitt Anheften, 14 Punkte, alle gruen), pruef-chat-optik, pruef-chatkachel (40), pruef-chat-neu (32), pruef-chat-ausbau (64), pruef-chat-kanaele (81), pruef-chat-aufloesen (126), pruef-chat-anhaenge (109), pruef-erwaehnung (129), pruef-css-klassen, pruef-tippziele (11), pruef-lesbarkeit. Co-Authored-By: Claude Opus 5 --- server/pruef-chat.mjs | 104 +++++++ server/workspace-chat.js | 66 ++++- server/workspace.js | 17 ++ workspace/anruf-probe.html | 10 +- workspace/app.webmanifest | 8 +- workspace/assets/css/chat.css | 491 ++++++++++++++++++++++++++++++++ workspace/assets/js/chat.js | 60 ++++ 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 +-- 46 files changed, 1366 insertions(+), 632 deletions(-) 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`. --> - - + + - +