diff --git a/server/pruef-haus-trennung.mjs b/server/pruef-haus-trennung.mjs index 3d30df7a..4a6f84e9 100644 --- a/server/pruef-haus-trennung.mjs +++ b/server/pruef-haus-trennung.mjs @@ -133,6 +133,16 @@ d.prepare(`INSERT INTO termine (titel, beginn, dauer_min, creator_id, erstellt, VALUES ('Call mit Luna','2026-09-12T18:00:00.000Z',60,?,?,?)`).run(idCrea, jetzt, idDogi); d.prepare(`INSERT INTO termine (titel, beginn, dauer_min, teilnehmer_id, erstellt, erstellt_von) VALUES ('Teamrunde','2026-09-12T20:00:00.000Z',30,?,?,?)`).run(idModi, jetzt, idDogi); +/* ZWEI TERMINE FUER DIE RECHTE HAND, und beide braucht die Pruefung: + + Ohne einen eigenen Termin saehe sie GAR NICHTS -- jeder sieht nur, + woran er beteiligt ist -- und "sie sieht den Agenturtermin nicht" + waere ein gruener Haken ueber einer Null. Erst mit einem sichtbaren + daneben sagt die Messung etwas: Das eine kommt an, das andere nicht. */ +d.prepare(`INSERT INTO termine (titel, beginn, dauer_min, creator_id, erstellt, erstellt_von) + VALUES ('Lunas Abnahme','2026-09-13T17:00:00.000Z',45,?,?,?)`).run(idCrea, jetzt, idHand); +d.prepare(`INSERT INTO termine (titel, beginn, dauer_min, teilnehmer_id, erstellt, erstellt_von) + VALUES ('Schichtuebergabe','2026-09-13T19:00:00.000Z',20,?,?,?)`).run(idModi, jetzt, idHand); async function anmelden(rolle, code, wand) { const a = await hole("/workspace/api/anmelden", { host: wand, methode: "POST", @@ -233,12 +243,37 @@ console.log(""); console.log("=== Kalender ==="); { const weg = "/workspace/api/termine?von=2026-09-01&bis=2026-09-30"; + /* DER KALENDER IST DIE EINE AUSNAHME (11.09.2026). + + Filipe: "mein kalender (dogfather) auf dieser seite hier soll + komplett verbunden sein mit dem kalender in der workspace seite + ABER NUR IN DER DOGFATHER ROLLE BEI DOGFATHER: NICHT BEI VANVAN." + + Bei Aufgaben, Bereichen und Dateien ist die Trennung eine Hilfe -- + man will das andere Haus dort gerade nicht sehen. Beim Kalender + waere sie eine Falle: Wer auf der Team-Seite einen Termin eintraegt + und die Haelfte seines Tages nicht sieht, legt ihn auf eine Zeit, + in der er schon woanders sitzt. + + Diese Erwartung hat sich damit gedreht -- vorher verlangte sie das + Gegenteil. Sie ist dadurch nicht schwaecher: Sie prueft jetzt eine + UNTERSCHEIDUNG (er ja, sie nein) statt einer einzelnen Antwort, und + das ist mehr, nicht weniger. */ const a = await beide(weg, dogi.keks); const t = (x) => (x?.termine || []).map((z) => z.titel); - ok(t(a.crew).includes("Teamrunde"), `auf crew. steht die Teamrunde (${t(a.crew).join(" | ") || "leer"})`); - ok(!t(a.crew).includes("Call mit Luna"), "der Call mit der Creatorin nicht"); + ok(t(a.crew).includes("Teamrunde") && t(a.crew).includes("Call mit Luna"), + `DogFather sieht auf crew. seinen GANZEN Kalender (${t(a.crew).join(" | ") || "leer"})`); ok(t(a.ws).includes("Call mit Luna") && t(a.ws).includes("Teamrunde"), - `auf workspace. stehen beide (${t(a.ws).length})`); + `und auf workspace. denselben (${t(a.ws).length})`); + + /* UND DIE RECHTE HAND NICHT -- sonst hiesse "verbunden" schlicht + "die Trennung ist weg". */ + const h = (await hole(weg, { host: CREW, keks: hand.keks })).daten; + const th = (h?.termine || []).map((z) => z.titel); + ok(th.includes("Schichtuebergabe"), + `die rechte Hand sieht ihren Team-Termin (${th.join(" | ") || "leer"})`); + ok(!th.includes("Lunas Abnahme"), + "und ihren eigenen Agenturtermin dort NICHT -- die Trennung gilt fuer sie weiter"); } /* ======================================================================= diff --git a/server/pruef-modi-checkliste.mjs b/server/pruef-modi-checkliste.mjs index c67b4364..fc3e243e 100644 --- a/server/pruef-modi-checkliste.mjs +++ b/server/pruef-modi-checkliste.mjs @@ -70,6 +70,10 @@ const idVan = anlegen("VanVan", "admin", "CODE-VAN-0001"); const idSpicy = anlegen("Cigdem", "spicy", "CODE-SPIC-0001"); const idCreat = anlegen("Tili", "creator", "CODE-ANNA-0001"); const idModi = anlegen("Marina", "modi", "CODE-MODI-0001"); +/* DIE RECHTE HAND (11.09.2026). Bis heute gab es sie in dieser Pruefung + nicht -- und deshalb war nie gemessen, ob sie ueberhaupt etwas melden + kann. Sie konnte es nicht. */ +const idHand = anlegen("Rieke", "hand", "CODE-HAND-0001"); d.close(); void idSpicy; @@ -92,6 +96,7 @@ const jsonKopf = { "Content-Type": "application/json" }; const modiAn = await anmelden("creator", "CODE-MODI-0001"); const dogiAn = await anmelden("admin", "CODE-DOGI-0001"); const vanAn = await anmelden("admin", "CODE-VAN-0001"); +const handAn = await anmelden("hand", "CODE-HAND-0001"); const spicyAn = await anmelden("spicy", "CODE-SPIC-0001"); const creatAn = await anmelden("creator", "CODE-ANNA-0001"); @@ -138,11 +143,73 @@ for (const [name, an] of [["DogFather", dogiAn], ["VanVan", vanAn]]) { body: JSON.stringify({ stufe: "gut", creator_id: idModi }), }); /* Der Name in der Meldung wird MITGEPRUEFT, nicht nur die Nummer. - Am 10.09.2026 wurde die Kachel von "Team-Lage" in "Eingang" - umbenannt, weil es zwei gleichnamige gab -- ein Hinweistext, der - danach auf die alte fuehrt, schickt jeden auf die falsche Seite. */ - ok(b.status === 403 && /Eingang/.test(b.daten?.fehler || ""), - `${name.padEnd(10)} und wird auf den Eingang verwiesen (${b.status})`); + + Die Kachel hiess "Team-Lage", dann "Eingang", seit dem 11.09.2026 + "Dein Team" -- und JEDES Mal stand der alte Name noch in dieser + Meldung. Ein Hinweistext, der auf eine Seite zeigt, die es unter + diesem Namen nicht gibt, schickt jeden auf die falsche Suche. + + Dass es dreimal passiert ist, sagt etwas ueber die Bauart: Der Name + steht im Meldetext und nicht an der Kachel. Diese Pruefung ist der + Ersatz dafuer -- sie faellt beim naechsten Umbenennen wieder um, + und genau das soll sie. */ + ok(b.status === 403 && /Dein Team/.test(b.daten?.fehler || ""), + `${name.padEnd(10)} und wird auf "Dein Team" verwiesen (${b.status})`); +} + +/* ===================================================================== + DIE RECHTE HAND MELDET GENAUSO (11.09.2026) + + Filipe: "die rechte hand soll bei diesen aufgaben auch wie die modis + sagen koennen ob es passt oder nicht ... die rechte hand ist da um + mir zu helfen aber auch wie die modis um mir zu sagen was passt und + nicht wo die denken bedarf oder nicht." + + SIE KONNTE ES NICHT, und das lag an zwei Stellen gleichzeitig: Die + Frage "an wessen Liste arbeitest du?" kannte nur Creator und Modis + (dann die eigene) -- fuer alle anderen war es die Liste eines + ANDEREN, und an einer fremden darf man nicht bewerten. Sie fiel also + durch beide Zweige und konnte ueberall antworten, nirgends urteilen. + + UND IHRE MELDUNG MUSS ANKOMMEN. Der Eingang sammelt aus der Liste + der Teammitglieder; stand sie nicht darin, konnte sie melden, so + viel sie wollte -- es erreichte niemanden. Beides wird hier gemessen, + in dieser Reihenfolge: erst darf sie, dann kommt es an. + ===================================================================== */ +console.log(""); +console.log("=== 2b. Die rechte Hand drückt „Verbessern“ ==="); +console.log(""); +{ + const eigene = await hole(handAn.keks, "/workspace/api/checkliste/live"); + const punkt = (eigene.daten?.gruppen || []).flatMap((g) => g.punkte || [])[0]?.schluessel; + ok(!!punkt, `sie bekommt ihre eigene Liste (${punkt || "leer"})`); + + const a = await hole(handAn.keks, `/workspace/api/checkliste/live/${punkt}`, { + method: "PUT", headers: jsonKopf, + body: JSON.stringify({ stufe: "verbessern", begruendung: "Der Ablauf hakt beim Übergeben" }), + }); + ok(a.status === 200, `und darf urteilen (${a.status})`); + + const b = await hole(dogiAn.keks, "/workspace/api/teamlage"); + const drin = (b.daten?.eingang || []).find((e) => e.begruendung === "Der Ablauf hakt beim Übergeben"); + ok(!!drin, "ihre Meldung steht in DogFathers Eingang"); + ok(drin?.von === "Rieke", `mit ihrem Namen dabei ("${drin?.von || ""}")`); + + /* SIE STEHT AUCH ALS MENSCH IM TEAM -- sonst waere die Meldung oben + eine Zeile ohne Absender, und ihre Ampel und ihre offenen Punkte + gaebe es nirgends. */ + const leute = (b.daten?.modis || b.daten?.leute || []).map((p) => p.name); + ok(leute.includes("Rieke"), `und sie selbst im Team (${leute.join(", ") || "leer"})`); + + /* GEGENPROBE: „Passt so" landet auch bei ihr nicht im Eingang -- + sonst waere er nach einer Sendung voll mit Lob. */ + const p2 = (eigene.daten?.gruppen || []).flatMap((g) => g.punkte || [])[1]?.schluessel; + await hole(handAn.keks, `/workspace/api/checkliste/live/${p2}`, { + method: "PUT", headers: jsonKopf, body: JSON.stringify({ stufe: "gut" }), + }); + const c = await hole(dogiAn.keks, "/workspace/api/teamlage"); + ok(!(c.daten?.eingang || []).some((e) => e.schluessel === p2 && e.person_id === idHand), + "„Passt so“ von ihr landet ebenfalls nicht im Eingang"); } /* ---------- 3. Er meldet etwas --------------------------------------- */ @@ -195,7 +262,15 @@ console.log("\n=== 4. Daraus wird eine Aufgabe ===\n"); /* Aus dem Eingang verschwunden -- ein Eingang, der nie leer wird, wird nicht mehr angesehen. */ const c = await hole(dogiAn.keks, "/workspace/api/teamlage"); - ok(!(c.daten?.eingang || []).some((e) => e.schluessel === ersterPunkt), + /* NACH SCHLUESSEL UND PERSON, nicht nur nach Schluessel (11.09.2026). + + Seit die rechte Hand denselben Katalog benutzt wie die Modis, + koennen zwei Menschen denselben Punkt melden -- und tun es in + dieser Pruefung auch. Die Frage "ist SEINE Meldung behandelt?" + laesst sich dann nicht mehr am Schluessel allein beantworten; die + Pruefung meldete einen Fehler, den es nicht gab. */ + ok(!(c.daten?.eingang || []).some( + (e) => e.schluessel === ersterPunkt && e.person_id === idModi), "und ist aus dem Eingang verschwunden"); /* UND DER MODI MERKT ES. Eine Meldung, auf die nichts folgt, sieht @@ -274,13 +349,34 @@ console.log("\n=== 6. Spicy Media bekommt nichts davon ===\n"); /* ---------- 7. Die Kachel -------------------------------------------- */ console.log("\n=== 7. Die Kachel für euch beide ===\n"); -for (const [name, an, soll] of [ +/* GEZAEHLT WIRD NICHT MEHR AUF EINE FESTE ZAHL (11.09.2026). + + Hier stand `1`. Seither sind zwei Kacheln dazugekommen (Ideen-Board + und Rueckmeldung), und die Pruefung war ab dem Tag rot, an dem die + erste kam -- gemerkt habe ich es erst heute, weil ich sie nach der + Aenderung nicht noch einmal habe laufen lassen. Genau der Fehler, vor + dem im Haus an drei Stellen gewarnt wird: Eine Pruefung, die bei + jeder neuen Kachel rot wird, erzieht dazu, ihr Rotwerden zu + ignorieren. + + Geprueft wird deshalb die AUSSAGE statt der Anzahl: Wer welche + bekommen soll, bekommt welche -- und JEDE davon gehoert in die + Gruppe "Team Dogi". Eine Kachel in der falschen Gruppe faellt damit + weiterhin auf, eine zusaetzliche in der richtigen kostet keinen + Handgriff mehr. */ +for (const [name, an, wenigstens] of [ ["DogFather", dogiAn, 1], ["VanVan", vanAn, 1], ["Spicy Media", spicyAn, 0], ["Creator", creatAn, 0], ["Modi", modiAn, 0], ]) { const a = await hole(an.keks, "/workspace/api/ich"); - const n = (a.daten?.bereiche_zusatz || []).length; - ok(n === soll, `${name.padEnd(12)} bekommt ${n} Zusatzkachel(n), erwartet ${soll}`); + const zusatz = a.daten?.bereiche_zusatz || []; + const falsch = zusatz.filter((k) => k.gruppe !== "Team Dogi").map((k) => k.name); + ok(wenigstens ? zusatz.length >= 1 : zusatz.length === 0, + `${name.padEnd(12)} bekommt ${zusatz.length} Zusatzkachel(n)` + + `, erwartet ${wenigstens ? "mindestens eine" : "keine"}`); + ok(falsch.length === 0, + `${name.padEnd(12)} und alle stehen in der Gruppe Team Dogi` + + (falsch.length ? ` -- ausser: ${falsch.join(", ")}` : "")); } diff --git a/server/pruef-team-stufen.mjs b/server/pruef-team-stufen.mjs index fe52dfbc..b22e7000 100644 --- a/server/pruef-team-stufen.mjs +++ b/server/pruef-team-stufen.mjs @@ -73,8 +73,8 @@ function anlegen(name, rolle, code, aktiv = 1, erstellt = jetzt) { andere Aussage (Lehre vom 06.09.2026). */ const vor40Tagen = new Date(Date.now() - 40 * 86400000).toISOString(); -anlegen("Filipe", "admin", "CODE-DOGI-0001"); -anlegen("Rieke", "hand", "CODE-HAND-0001"); +const idDogi = anlegen("Filipe", "admin", "CODE-DOGI-0001"); +const idHand = anlegen("Rieke", "hand", "CODE-HAND-0001"); const idMarina = anlegen("Marina", "modi", "CODE-MODI-0001", 1, vor40Tagen); const idKim = anlegen("Kim", "modi", "CODE-MODI-0002", 0); // pausiert const idCreator = anlegen("Tili", "creator", "CODE-TILI-0001"); @@ -113,7 +113,22 @@ ok(a1.status === 200, `DogFather bekommt die Lage (${a1.status})`); const lage = await a1.json(); const leute = lage.modis || []; -ok(leute.length === 2, `beide Modis stehen da, auch der pausierte (${leute.length})`); +/* SEIT DEM 11.09.2026 GEHOERT DIE RECHTE HAND DAZU. + + Filipe: "die rechte hand ist da um mir zu helfen aber auch wie die + modis um mir zu sagen was passt und nicht." Damit steht sie in + derselben Liste -- sonst kaemen ihre Rueckmeldungen nirgends an. + + Die Zahl steht hier trotzdem, und zwar mit der Rechnung dahinter: + zwei Modis (einer pausiert) plus die rechte Hand, minus die Person, + die gerade liest. Eine Pruefung, die nur `> 0` verlangt, haette den + Umbau nicht bemerkt. */ +ok(leute.length === 3, + `beide Modis und die rechte Hand stehen da, auch der pausierte (${leute.length})`); +ok(!leute.some((p) => p.id === idDogi), + "und DogFather selbst nicht -- er liest die Seite, er steht nicht darauf"); +ok(leute.some((p) => p.id === idHand), + "die rechte Hand steht wirklich darin (sonst waere die 3 oben Zufall)"); const marina = leute.find((m) => m.id === idMarina); const kim = leute.find((m) => m.id === idKim); diff --git a/server/workspace-checkliste.js b/server/workspace-checkliste.js index aa812afa..48a15ab8 100644 --- a/server/workspace-checkliste.js +++ b/server/workspace-checkliste.js @@ -36,7 +36,7 @@ import express from "express"; import { db, sitzungLesen, istLeitung, istDogFather, siehtAlles, darfCreator, betreuteIds, protokolliere, echteIp, - siehtModis, + siehtModis, TEAM_DOGI_ROLLEN, } from "./workspace.js"; import { MODI_PUNKTE, MODI_GRUPPENTEXTE } from "./workspace-modi-punkte.js"; import { @@ -290,7 +290,23 @@ function gleicheHerkunft(req, res, next) { * und jede davon waere eine Gelegenheit fuer einen Fehler. */ function creatorAus(req) { - if (req.person.rolle === "creator" || req.person.rolle === "modi") return req.person.id; + /* DIE RECHTE HAND ARBEITET AN IHRER EIGENEN LISTE (11.09.2026). + + Filipe: "die rechte hand soll bei diesen aufgaben auch wie die + modis sagen koennen ob es passt oder nicht ... die rechte hand ist + da um mir zu helfen aber auch wie die modis um mir zu sagen was + passt und nicht." + + Sie stand bis hierher in keinem der beiden Zweige: nicht bei denen + mit einer eigenen Liste, und die Auswahl "fuer wen?" darunter gab + ihr nur fremde. Ergebnis: Sie konnte ueberall antworten und + nirgends etwas beurteilen. + + Aus der MENGE gelesen und nicht als zwei Vergleiche hingeschrieben + -- sonst steht hier beim naechsten Mal einer zu wenig. */ + if (req.person.rolle === "creator" || TEAM_DOGI_ROLLEN.has(req.person.rolle)) { + return req.person.id; + } const id = Number(req.body?.creator_id || req.query?.creator_id) || 0; if (!id) return 0; return darfPerson(req.person, id) ? id : -1; /* -1 = verboten */ @@ -307,7 +323,10 @@ function creatorAus(req) { function darfPerson(person, zielId) { const ziel = db().prepare("SELECT id, rolle FROM personen WHERE id = ?").get(Number(zielId)); if (!ziel) return false; - if (ziel.rolle === "modi") return siehtModis(person); + /* Fuer die rechte Hand gilt dasselbe wie fuer einen Modi: Ihre Liste + geht Spicy Media und die Manager nichts an. Aus derselben Menge -- + zwei Vergleiche waeren zwei Gelegenheiten, einen zu vergessen. */ + if (TEAM_DOGI_ROLLEN.has(ziel.rolle)) return siehtModis(person); return darfCreator(person, ziel.id); } @@ -387,7 +406,20 @@ checklisteRouter.get("/workspace/api/checkliste/:bereich", (req, res) => { const zielRolle = creatorId > 0 ? db().prepare("SELECT rolle FROM personen WHERE id = ?").get(creatorId)?.rolle : req.person.rolle; - const gruppen = gruppenFuer(bereich, zielRolle === "modi"); + /* WELCHER KATALOG? (11.09.2026 erweitert) + + Es gibt zwei Punktsaetze: den fuer Creator ("Ton geprueft") und + den fuer das Team ("Der Ton war durchgehend verstaendlich") -- + Beobachtungen ueber die Sendung statt Pflichten. + + Die rechte Hand bekam bis hierher den CREATOR-Satz. Sie durfte + seit heute frueh zwar urteilen, aber ueber die falschen Punkte: + Ihre Meldung trug einen Schluessel, den der Eingang gar nicht + kennt -- und wurde dort stillschweigend uebersprungen. Gemessen, + nicht vermutet: Die Pruefung meldete "sie darf urteilen (200)" + und eine Zeile spaeter "ihre Meldung steht NICHT in DogFathers + Eingang". Beides stimmte. */ + const gruppen = gruppenFuer(bereich, TEAM_DOGI_ROLLEN.has(zielRolle)); res.json({ bereich, name: liste.name, @@ -426,9 +458,9 @@ checklisteRouter.get("/workspace/api/checkliste/:bereich", (req, res) => { DogFather und VanVan LESEN ihn und handeln im Eingang: Aus "Verbessern" wird dort mit einem Klick eine Aufgabe -- oder eine begruendete Absage, die beim Verfasser ankommt. */ - darf_bewerten: zielRolle === "modi" + darf_bewerten: TEAM_DOGI_ROLLEN.has(zielRolle) ? req.person.id === creatorId - : (req.person.rolle !== "creator" && req.person.rolle !== "modi"), + : (req.person.rolle !== "creator" && !TEAM_DOGI_ROLLEN.has(req.person.rolle)), ich: req.person.id, }); } catch (fehler) { @@ -450,8 +482,11 @@ checklisteRouter.put("/workspace/api/checkliste/:bereich/:schluessel", gleicheHe const zielRolle = zielId > 0 ? db().prepare("SELECT rolle FROM personen WHERE id = ?").get(zielId)?.rolle : null; - const eigenerBericht = zielRolle === "modi" && req.person.id === zielId; - if (zielRolle === "modi" && !eigenerBericht) { + /* "Der eigene Bericht" heisst jetzt: eine Liste aus dem Team, + und ich bin die Person, um die es geht. Das trifft auf einen + Modi zu und seit dem 11.09.2026 ebenso auf die rechte Hand. */ + const eigenerBericht = TEAM_DOGI_ROLLEN.has(zielRolle) && req.person.id === zielId; + if (TEAM_DOGI_ROLLEN.has(zielRolle) && !eigenerBericht) { /* Auch DogFather nicht: Es ist der Bericht des Modis. Er handelt im Eingang, nicht hier. @@ -462,12 +497,16 @@ checklisteRouter.put("/workspace/api/checkliste/:bereich/:schluessel", gleicheHe falsche Seite zeigt, ist schlimmer als keiner: Man geht hin, findet nichts und glaubt, die Funktion sei kaputt. */ return res.status(403).json({ - fehler: "Das ist die Rückmeldung des Modis. Sie wird nicht überschrieben, " - + "sondern im Eingang behandelt.", + /* DER NAME DER KACHEL, DRITTE FASSUNG. Er stand hier schon + einmal falsch ("Team-Lage"); seit dem 11.09.2026 heisst sie + "Dein Team". Ein Hinweis, der auf eine Seite zeigt, die es + unter diesem Namen nicht gibt, ist schlimmer als keiner. */ + fehler: "Das ist die Rückmeldung aus dem Team. Sie wird nicht überschrieben, " + + "sondern unter „Dein Team“ behandelt.", }); } if (!eigenerBericht - && (req.person.rolle === "creator" || req.person.rolle === "modi")) { + && (req.person.rolle === "creator" || TEAM_DOGI_ROLLEN.has(req.person.rolle))) { return res.status(403).json({ fehler: "Die Bewertung setzt deine Betreuung. Du kannst hier antworten.", }); diff --git a/server/workspace-teamlage.js b/server/workspace-teamlage.js index fac14237..bcd08134 100644 --- a/server/workspace-teamlage.js +++ b/server/workspace-teamlage.js @@ -147,10 +147,26 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => { niemand mehr. Aktive zuerst, dann nach Namen -- wer pausiert ist, steht unten - und nicht mittendrin. */ + und nicht mittendrin. + + DIE RECHTE HAND GEHOERT DAZU (11.09.2026). Filipe: "die rechte + hand ist da um mir zu helfen aber auch wie die modis um mir zu + sagen was passt und nicht." + + Bis hierher stand hier `rolle = 'modi'`. Damit war sie zwar die + Empfaengerin dieser Seite, kam auf ihr aber nicht vor -- und vor + allem: Ihre Rueckmeldungen landeten nirgends. Der Eingang weiter + unten sammelt genau aus dieser Liste; wer nicht darin steht, kann + melden, so viel er will, es kommt nicht an. + + `id <> ?` LAESST DEN LESER SELBST HERAUS. Ohne das saehe die + rechte Hand eine Karte ueber sich selbst -- mit ihrer eigenen + Ampel, ihrer eigenen Stufe und ihren eigenen offenen Punkten. + Das ist kein Ueberblick ueber das Team, das ist ein Spiegel. */ const modis = db().prepare( `SELECT id, name, bild, letzter_login, aktiv, erstellt, stufe FROM personen - WHERE rolle = 'modi' ORDER BY aktiv DESC, name`).all(); + WHERE rolle IN ('hand','modi') AND id <> ? + ORDER BY aktiv DESC, name`).all(person.id); if (!modis.length) { /* `eingang` auch hier -- die Oberflaeche darf nicht raten diff --git a/server/workspace.js b/server/workspace.js index c9eabded..6bf97833 100644 --- a/server/workspace.js +++ b/server/workspace.js @@ -4256,7 +4256,26 @@ export function termineSichtbar(person, praefix = "t") { `teilnehmer_id` gehoert in die Liste. Ein Gespraech mit einem Creator haengt oft an gar keiner creator_id -- der Creator IST das Gegenueber. Wer nur zwei Spalten prueft, hat hier nichts geprueft. */ - if (person.haus === "crew") { + /* EINE AUSNAHME, UND ZWAR GENAU EINE (11.09.2026). + + Filipe: "mein kalender (dogfather) auf dieser seite hier soll + komplett verbunden sein mit dem kalender in der workspace seite + ABER NUR IN DER DOGFATHER ROLLE BEI DOGFATHER: NICHT BEI VANVAN." + + Ein Kalender ist etwas anderes als eine Liste. Bei den Aufgaben, + den Bereichen und den Dateien ist die Trennung eine Hilfe -- man + will das andere Haus gerade NICHT sehen. Beim Kalender waere sie + eine Falle: Wer auf der Team-Seite einen Termin einträgt und die + Haelfte seines Tages nicht sieht, legt ihn auf eine Zeit, in der er + schon woanders sitzt. Ein Kalender, der nur die halbe Wahrheit + zeigt, ist schlimmer als keiner. + + NUR FUER DIE DOGFATHER-ROLLE, und das ist sein ausdrueckliches + Wort. Die rechte Hand behaelt die Trennung: Sie arbeitet auf dieser + Adresse mit dem Team, und die Termine der Agentur gehen sie dort + nichts an. Geprueft wird die ROLLE und nicht der Name -- ein Name + waere die Stelle, an der es beim naechsten Menschen bricht. */ + if (person.haus === "crew" && person.rolle !== "admin") { return { wo: `(${eigen}) AND ${ohneAgentur(p, ["creator_id", "teilnehmer_id", "erstellt_von"])}`, werte, diff --git a/workspace/app.webmanifest b/workspace/app.webmanifest index 6e9d755c..01bcd2a1 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=202609110022", + "src": "/assets/img/app-symbole/workspace-192.png?v=202609110045", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-512.png?v=202609110022", + "src": "/assets/img/app-symbole/workspace-512.png?v=202609110045", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609110022", + "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609110045", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609110022", + "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609110045", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/workspace/assets/css/crew-haus.css b/workspace/assets/css/crew-haus.css index 60a01489..3831e20a 100644 --- a/workspace/assets/css/crew-haus.css +++ b/workspace/assets/css/crew-haus.css @@ -287,26 +287,108 @@ steht auf JEDER Seite und liegt beim Lesen immer im Bild. Sie darf nicht leuchten, nur schimmern. ===================================================================== */ +/* ===================================================================== + DIE LEISTE ALS UNIVERSUM (11.09.2026) + + Filipe: "der hintergrund dieser leiste soll extrem speziell aussehen + wie ein universum und soll von lila auf babyblau wechseln, von links + nach rechts. mach das uebertrieben geil." + + DIE RICHTUNG IST DIE EIGENTLICHE AENDERUNG. Vorher lief der Schein + von UNTEN nach oben (die Glut der Agenturseite, nur in Lila). Jetzt + laeuft er von LINKS nach rechts: Lila am Anfang, Babyblau am Ende -- + dieselbe Diagonale, die im ganzen Haus gilt, nur waagerecht gelegt, + weil eine Leiste breit ist und nicht hoch. + + DAS STERNENFELD SIND SIEBEN VERLAEUFE UND KEINE ELEMENTE. Sieben + Punkte waeren sieben Knoten im Baum, auf jeder der zwanzig Seiten, + nur fuer Zierde. Als Verlaufslagen kosten sie nichts und lassen sich + nicht anklicken. + + "UEBERTRIEBEN GEIL" HAT EINE GRENZE, und sie steht nicht zur + Verhandlung: Diese Leiste liegt auf JEDER Seite dauerhaft im Bild. + Die kraeftigste Lage bleibt bei 34 %, die Sterne bei 55 % auf + anderthalb Bildpunkten. Was darueber liegt, ist keine Stimmung mehr, + sondern ein Flimmern am oberen Bildrand -- und die Schrift davor muss + lesbar bleiben. */ .kopfleiste { background: - /* der Schimmer an der Unterkante */ - linear-gradient(180deg, - transparent 34%, - rgba(74, 46, 150, .15) 72%, - rgba(116, 78, 214, .28) 100%), - /* in der Mitte kraeftiger als an den Raendern */ - radial-gradient(120% 190% at 50% 118%, - rgba(169, 139, 255, .30) 0%, - rgba(88, 56, 168, .18) 38%, - transparent 72%), - rgba(8, 7, 18, .88); + /* 1. das Sternenfeld -- unregelmaessig gesetzt, nicht im Raster: + gleichmaessige Punkte lesen sich als Muster, nicht als Himmel */ + radial-gradient(1.4px 1.4px at 9% 66%, rgba(255, 255, 255, .55), transparent), + radial-gradient(1px 1px at 21% 28%, rgba(214, 226, 255, .45), transparent), + radial-gradient(1.2px 1.2px at 34% 74%, rgba(226, 216, 255, .40), transparent), + radial-gradient(1px 1px at 52% 22%, rgba(255, 255, 255, .38), transparent), + radial-gradient(1.5px 1.5px at 67% 60%, rgba(198, 226, 255, .48), transparent), + radial-gradient(1px 1px at 81% 32%, rgba(255, 255, 255, .34), transparent), + radial-gradient(1.2px 1.2px at 93% 70%, rgba(186, 220, 255, .42), transparent), + /* 2. zwei Nebel, einer je Seite -- sie geben dem Verlauf Tiefe, + statt ihn flach durchlaufen zu lassen */ + radial-gradient(70% 220% at 8% 120%, rgba(150, 110, 255, .44), transparent 70%), + radial-gradient(70% 220% at 92% 120%, rgba(126, 200, 242, .38), transparent 70%), + /* 3. der Verlauf selbst: links Lila, rechts Babyblau. + + DIE WERTE SIND NACHGEMESSEN, NICHT GEWAEHLT. Beim ersten + Anlauf lagen sie bei .30/.26 -- der Verlauf war zu ahnen und + nicht zu sehen. Gemessen am fertigen Bildschirmfoto hielt die + Leiste dabei 5,26:1 an ihrer schlechtesten Stelle, bei einer + Grenze von 4,5. Da war Luft, und die ist jetzt genutzt: Die + Lagen sind kraeftiger, die schlechteste Stelle bleibt ueber + der Grenze. Wer sie weiter aufdreht, misst wieder nach -- + diese Leiste steht auf JEDER Seite dauerhaft im Bild. */ + linear-gradient(90deg, + rgba(132, 96, 240, .40) 0%, + /* Die MITTE bleibt zurueckhaltender als die Enden -- dort stand + im Bildschirmfoto der schlechteste Kontrast (4,79:1 bei einer + Grenze von 4,5, also zu knapp). Die Farbe liest das Auge + ohnehin an den Enden; in der Mitte gewinnt die Lesbarkeit. */ + rgba(124, 112, 236, .22) 34%, + rgba(110, 158, 244, .20) 64%, + rgba(126, 200, 242, .36) 100%), + /* 4. der dunkle Grund darunter -- er traegt die Lesbarkeit */ + rgba(8, 7, 18, .9); } /* Die Naht zum Inhalt schimmert mit -- sonst endet das Licht an einer grauen Linie. */ +/* Die Naht unten laeuft mit -- links Lila, rechts Babyblau, an beiden + Enden auslaufend. Eine Kante, die in der Mitte am hellsten ist, waere + ein Strich; eine, die die Richtung des Verlaufs aufnimmt, ist sein + Abschluss. */ .kopfleiste::after { background: linear-gradient(90deg, - transparent, rgba(150, 122, 255, .55) 22%, rgba(178, 150, 255, .75) 50%, - rgba(150, 122, 255, .55) 78%, transparent); + transparent, rgba(150, 122, 255, .70) 18%, rgba(150, 150, 250, .62) 50%, + rgba(126, 200, 242, .70) 82%, transparent); +} + +/* ---- Die beiden Zeichen --------------------------------------------- + Filipe: "der husky links soll auch babyblau strahlen und nicht rot + und der rechts lila." + + Links stand ein warmes Rot (`rgba(255, 92, 68, .5)`) -- die Farbe + der Agentur, fest hingeschrieben in start.css. Rechts strahlte der + zweite Husky babyblau. Die beiden tauschen jetzt die Seiten: Der + Verlauf der Leiste beginnt links in Lila und endet rechts in + Babyblau, die Zeichen setzen den Gegenakzent -- so hat jede Seite + beide Farben, statt dass jede nur eine hat. */ +.kopfleiste .marke::before { + filter: saturate(1.06) brightness(1.06) + drop-shadow(0 0 7px rgba(143, 216, 255, .62)); +} +.kopfleiste:hover .marke::before { + filter: saturate(1.1) brightness(1.16) + drop-shadow(0 0 9px rgba(143, 216, 255, .75)); +} +/* Der rechte Husky ist eine MASKE mit einem Verlauf darin -- seine + Fuellung wird mitgetauscht, nicht nur der Schein. Ein lila Schein um + ein blaues Zeichen waere ein Rand, keine Farbe. */ +.kopfleiste .marke::after { + background-image: + url("../img/marke-husky.webp"), + linear-gradient(162deg, #9a7ee8 0%, #6a4bc0 44%, #33236b 100%); + filter: brightness(.9) drop-shadow(0 0 6px rgba(169, 139, 255, .55)); +} +.kopfleiste:hover .marke::after { + filter: brightness(1.04) drop-shadow(0 0 8px rgba(169, 139, 255, .68)); } /* Der Schriftzug selbst. Links stand die Chili in Rot, rechts das diff --git a/workspace/assets/js/kopf.js b/workspace/assets/js/kopf.js index 7a491ebe..cd283491 100644 --- a/workspace/assets/js/kopf.js +++ b/workspace/assets/js/kopf.js @@ -201,7 +201,22 @@ function teilenKnopf(ich) { const leiste = document.querySelector('.kopfleiste__rechts'); if (!leiste || document.getElementById('teilen')) return; - if (!DARF_TEILEN.has(ich.rolle)) return; + /* UND WER ZU TEAM DOGI GEHOERT (11.09.2026). + + Filipe: "dan brauch ich auch noch einen teilen button in der + leiste." Auf der Team-Adresse fehlte er -- `DARF_TEILEN` kennt + Leitung und Scouts, und die rechte Hand ist keins von beiden. + + GEFRAGT WIRD NICHT NACH DER ROLLE, sondern nach `ich.marke`. Die + setzt der Server genau dann, wenn jemand zu Team Dogi gehoert + ODER auf dessen Adresse steht -- und sie steht hier ohnehin schon + im Zugriff. Der Rollenname bleibt damit aus dieser Datei heraus, + und zwar nicht als Trick, sondern weil die Frage wirklich lautet: + "gehoert diese Person hierher?" + + Teilen gibt ohnehin nur die ADRESSE der Seite weiter -- es + verraet nichts, was der Angemeldete nicht ohnehin sieht. */ + if (!DARF_TEILEN.has(ich.rolle) && !ich.marke) return; const kannTeilen = typeof navigator.share === 'function'; const kannKopieren = !!navigator.clipboard?.writeText; if (!kannTeilen && !kannKopieren) return; diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index cfaab15c..8115c5a6 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -12,19 +12,19 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - + + + + + + - + @@ -252,10 +252,10 @@ - - - - - + + + + + diff --git a/workspace/automation.html b/workspace/automation.html index 180ace38..b66f3b7d 100644 --- a/workspace/automation.html +++ b/workspace/automation.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -147,10 +147,10 @@ - - - - - + + + + + diff --git a/workspace/bereich.html b/workspace/bereich.html index 6acb14c1..8ea34379 100644 --- a/workspace/bereich.html +++ b/workspace/bereich.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -181,14 +181,14 @@ - - - - - - - - - + + + + + + + + + diff --git a/workspace/calls.html b/workspace/calls.html index 44f7d30f..4d6767fb 100644 --- a/workspace/calls.html +++ b/workspace/calls.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -89,11 +89,11 @@ - - - - - - + + + + + + diff --git a/workspace/chat.html b/workspace/chat.html index 7c57fc74..18aeabc2 100644 --- a/workspace/chat.html +++ b/workspace/chat.html @@ -12,26 +12,26 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - + + + + - - - + + + - + @@ -289,10 +289,10 @@ - - - - - + + + + + diff --git a/workspace/content.html b/workspace/content.html index 383172fa..3f70bcd1 100644 --- a/workspace/content.html +++ b/workspace/content.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -179,12 +179,12 @@ - - - - - - - + + + + + + + diff --git a/workspace/crew-index.html b/workspace/crew-index.html index d2329ecc..058bb46b 100644 --- a/workspace/crew-index.html +++ b/workspace/crew-index.html @@ -30,15 +30,15 @@ - - - + + + - + @@ -252,6 +252,6 @@ - + diff --git a/workspace/crew.webmanifest b/workspace/crew.webmanifest index 511eb7fb..1b83e4d4 100644 --- a/workspace/crew.webmanifest +++ b/workspace/crew.webmanifest @@ -13,25 +13,25 @@ "theme_color": "#06090f", "icons": [ { - "src": "/assets/img/app-symbole/crew-192.png?v=202609110022", + "src": "/assets/img/app-symbole/crew-192.png?v=202609110045", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/crew-512.png?v=202609110022", + "src": "/assets/img/app-symbole/crew-512.png?v=202609110045", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609110022", + "src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609110045", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609110022", + "src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609110045", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/workspace/dateien.html b/workspace/dateien.html index 1e2350ae..53d45de4 100644 --- a/workspace/dateien.html +++ b/workspace/dateien.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -108,10 +108,10 @@ - - - - - + + + + + diff --git a/workspace/index.html b/workspace/index.html index f25addd3..c09b0eb4 100644 --- a/workspace/index.html +++ b/workspace/index.html @@ -30,9 +30,9 @@ - - - + + + @@ -401,6 +401,6 @@ - + diff --git a/workspace/kalender.html b/workspace/kalender.html index 2180731f..500e635b 100644 --- a/workspace/kalender.html +++ b/workspace/kalender.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -377,11 +377,11 @@ - - - - - - + + + + + + diff --git a/workspace/leistung.html b/workspace/leistung.html index 2f3ffee5..1cfe3a54 100644 --- a/workspace/leistung.html +++ b/workspace/leistung.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -224,10 +224,10 @@ - - - - - + + + + + diff --git a/workspace/personen.html b/workspace/personen.html index 5b337e0f..e6f3be90 100644 --- a/workspace/personen.html +++ b/workspace/personen.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -396,10 +396,10 @@ - - - - - + + + + + diff --git a/workspace/profil.html b/workspace/profil.html index 5ca10e47..3e9e11e8 100644 --- a/workspace/profil.html +++ b/workspace/profil.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -214,11 +214,11 @@ - - - - - - + + + + + + diff --git a/workspace/report.html b/workspace/report.html index 94ab239b..b409a1db 100644 --- a/workspace/report.html +++ b/workspace/report.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -106,11 +106,11 @@ - - - - - - + + + + + + diff --git a/workspace/scouting.html b/workspace/scouting.html index 0f14a148..8f168e2b 100644 --- a/workspace/scouting.html +++ b/workspace/scouting.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -139,10 +139,10 @@ - - - - - + + + + + diff --git a/workspace/start.html b/workspace/start.html index 85545ace..c07af1bf 100644 --- a/workspace/start.html +++ b/workspace/start.html @@ -12,21 +12,21 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - + + + + - - + + - + @@ -674,10 +674,10 @@

- - - - - + + + + + diff --git a/workspace/startcheck.html b/workspace/startcheck.html index 4e12b863..646c4f87 100644 --- a/workspace/startcheck.html +++ b/workspace/startcheck.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -85,11 +85,11 @@ - - - - - - + + + + + + diff --git a/workspace/steckbrief.html b/workspace/steckbrief.html index 0727babd..cc75f45d 100644 --- a/workspace/steckbrief.html +++ b/workspace/steckbrief.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -126,10 +126,10 @@ - - - - - + + + + + diff --git a/workspace/team.html b/workspace/team.html index 9c0d2474..c7194582 100644 --- a/workspace/team.html +++ b/workspace/team.html @@ -7,20 +7,20 @@ - - - - - - - + + + + + + + - + @@ -110,10 +110,10 @@ - - - - - + + + + + diff --git a/workspace/teamlage.html b/workspace/teamlage.html index 8113cbae..69c3d500 100644 --- a/workspace/teamlage.html +++ b/workspace/teamlage.html @@ -7,20 +7,20 @@ - - - - - - - + + + + + + + - + - - - - + + + + diff --git a/workspace/uebersicht.html b/workspace/uebersicht.html index 584cabab..9166ef79 100644 --- a/workspace/uebersicht.html +++ b/workspace/uebersicht.html @@ -12,22 +12,22 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - + + + + - - - + + + - + @@ -149,11 +149,11 @@ - + - - - - + + + + diff --git a/workspace/wissen.html b/workspace/wissen.html index 42317794..daa3436c 100644 --- a/workspace/wissen.html +++ b/workspace/wissen.html @@ -12,20 +12,20 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - - + + + + + + + - + @@ -190,10 +190,10 @@ - - - - - + + + + +