/* ===================================================================== DIE LAGE DES MODI-TEAMS -- an einer Stelle (10.09.2026) Wunsch Filipe: *"dan will ich auch dass vanvan und ich diese sachen von den modis auch in der dogfather rolle sehen. da soll es eine kategorie geben für uns beide nur sonst keinen wo wir dass alles sehen und behandeln können. so wie mit dem ideen-board."* NUR DIE DOGFATHER-ROLLE. Das sind Filipe und VanVan -- sie tragen beide `admin`. Fuer alle anderen gibt es diese Seite nicht: 404 wie bei einer Adresse, die niemand kennt. Ein "kein Zugriff" waere die Auskunft, dass es sie gibt. SEHEN UND BEHANDELN, in dieser Reihenfolge: Die Seite zaehlt nur -- bewertet wird dort, wo die Punkte stehen. Jede Zahl ist deshalb ein Verweis in den jeweiligen Bereich, mit der Person schon vorausgewaehlt. Eine zweite Stelle zum Bewerten waere eine zweite Stelle, an der es auseinanderlaeuft. UND SIE IST KEINE UEBERWACHUNG. Dieselbe Ueberlegung steht schon ueber team.html: Eine Seite mit Zahlen ueber Kollegen wird als Kontrolle gelesen, und dann arbeitet niemand mehr offen damit. Deshalb zaehlt sie, was NOCH NICHT BESPROCHEN ist -- nicht, wer wie fleissig war. "Offen" heisst hier: Darueber habt ihr noch nicht geredet. Es ist eine Liste fuer euch, keine Note fuer ihn. ===================================================================== */ import express from "express"; import { db, sitzungLesen, istDogFather, heuteLokal, } from "./workspace.js"; import { MODI_PUNKTE } from "./workspace-modi-punkte.js"; export const teamlageRouter = express.Router(); /* Wie viele Punkte es je Bereich ueberhaupt gibt. Einmal gerechnet -- die Saetze aendern sich nicht zur Laufzeit. */ const GESAMT = Object.fromEntries( Object.entries(MODI_PUNKTE).map(([b, g]) => [b, Object.values(g).flat().length])); teamlageRouter.get("/workspace/api/teamlage", (req, res) => { try { const person = sitzungLesen(req); if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" }); /* 404 und nicht 403 -- siehe Kopf. */ if (!istDogFather(person)) return res.status(404).json({ fehler: "nicht_gefunden" }); const modis = db().prepare( `SELECT id, name, bild, letzter_login FROM personen WHERE rolle = 'modi' AND aktiv = 1 ORDER BY name`).all(); if (!modis.length) { return res.json({ modis: [], bereiche: GESAMT, heute: heuteLokal() }); } const platz = modis.map(() => "?").join(","); const nummern = modis.map((m) => m.id); const heute = heuteLokal(); /* ALLES IN VIER ABFRAGEN, nicht vier je Person. Bei zehn Modis waeren das vierzig -- den Fehler hat das Haus bei den Terminen schon einmal gemacht und ihn dort ausdruecklich vermerkt. */ const stand = new Map(); // person -> bereich -> {gut, verbessern} for (const z of db().prepare( `SELECT creator_id, bereich, stufe, COUNT(*) AS n FROM punkt_stand WHERE creator_id IN (${platz}) GROUP BY creator_id, bereich, stufe`).all(...nummern)) { if (!stand.has(z.creator_id)) stand.set(z.creator_id, {}); const b = stand.get(z.creator_id); b[z.bereich] = b[z.bereich] || { gut: 0, verbessern: 0 }; if (z.stufe === "gut" || z.stufe === "verbessern") b[z.bereich][z.stufe] = z.n; } const aufgaben = new Map(); for (const z of db().prepare( `SELECT verantwortlich_id AS p, COUNT(*) AS offen, SUM(CASE WHEN frist IS NOT NULL AND frist < ? THEN 1 ELSE 0 END) AS ueberfaellig FROM aufgaben WHERE verantwortlich_id IN (${platz}) AND status NOT IN ('erledigt', 'abgebrochen') GROUP BY verantwortlich_id`).all(heute, ...nummern)) { aufgaben.set(z.p, { offen: z.offen, ueberfaellig: z.ueberfaellig || 0 }); } const ideen = new Map(); for (const z of db().prepare( `SELECT erstellt_von AS p, COUNT(*) AS n FROM eintraege WHERE bereich = 'ideen' AND erstellt_von IN (${platz}) GROUP BY erstellt_von`) .all(...nummern)) { ideen.set(z.p, z.n); } /* Wie viele Rueckmeldungen an seinen Punkten haengen -- das ist das Gespraech, nicht die Note. */ const nachrichten = new Map(); for (const z of db().prepare( `SELECT creator_id AS p, COUNT(*) AS n FROM punkt_nachricht WHERE creator_id IN (${platz}) GROUP BY creator_id`).all(...nummern)) { nachrichten.set(z.p, z.n); } res.json({ heute, bereiche: GESAMT, modis: modis.map((m) => { const s = stand.get(m.id) || {}; const je = {}; for (const [b, gesamt] of Object.entries(GESAMT)) { const gut = s[b]?.gut || 0; const verbessern = s[b]?.verbessern || 0; je[b] = { gesamt, gut, verbessern, offen: Math.max(gesamt - gut - verbessern, 0) }; } return { id: m.id, name: m.name, bild: m.bild ? `/workspace/api/steckbrief/bild/${m.bild}` : null, letzter_login: m.letzter_login, aufgaben: aufgaben.get(m.id) || { offen: 0, ueberfaellig: 0 }, ideen: ideen.get(m.id) || 0, nachrichten: nachrichten.get(m.id) || 0, bereiche: je, }; }), }); } catch (fehler) { console.error("[workspace] Team-Lage:", fehler?.message); res.status(503).json({ fehler: "nicht_verfuegbar" }); } });