Verfuegbarkeits-Ampel im Eingang -- und eine eigene Gruppe fuer Team Dogi
Filipe: "ich will dass die kacheln von den modis bei der rolle dogfather eine eigene kategorie haben. damit ich nicht zwischen den kacheln suchen muss." DIE AMPEL (Blueprint 7.1). Der Eingang zeigt je Person sieben Tage: gruen frei, gelb belegt, rot voll ab 180 Minuten. Die Abfrage sammelt Termine ueber VIER Wege (creator_id, teilnehmer_id, erstellt_von und die Tabelle termin_teilnehmer) -- ueber nur einen davon waeren die meisten Termine unsichtbar geblieben und die Ampel dauerhaft gruen. Sie waehlt NIE titel, beschreibung oder ort: Filipe soll sehen, WANN jemand kann, nicht WAS die Person vorhat. Belegung ist Arbeitslage, Inhalt ist privat. DIE GRUPPE. Die zwei Team-Kacheln standen zwischen einundzwanzig anderen. Jetzt tragen sie `gruppe: "Team Dogi"` und `gruppeNach: "Täglich"`; start.js setzt eine so markierte Gruppe direkt HINTER die genannte statt ans Ende. Ohne das waere sie unten gelandet -- richtig gruppiert und trotzdem zum Suchen. Das Ideen-Board ist dabei aus workspace/assets/js/bereiche.js ausgezogen. Es stand dort mit `rollen: ['admin']` in einer Datei, die jeder Modi herunterlaedt: die Kachel war unsichtbar, ihr Name nicht. Jetzt liefert der Server sie, wie den Eingang auch. DREI FEHLER, DIE DER BILDSCHIRM GEZEIGT HAT, NICHT DER CODE: Das Profilbild sprengte die Karte. teamlage.js baute ein blankes <img> in `.tperson__zeichen` -- ohne die Klasse `tperson__bild`, die es auf 44 px begrenzt. Gemeldet hat es Filipe mit einem Bildschirm- foto, nicht eine Pruefung. Der Eingang hatte ueberhaupt keine Buehne. `zuSeite()` sucht nur in bereiche.js, und die Eingangs-Kachel kommt vom Server -- der Rueck- fall war ausgerechnet der Spicy-Wasserfall. Jetzt haengt das Bild an `data-buehne="eingang"` im CSS, wo kein Skript daran vorbeikommt. Pausierte Mitglieder verschwanden. `WHERE aktiv = 1` versteckte sie samt ihrer offenen Meldungen. Jetzt stehen sie hinten, sichtbar gekennzeichnet. DIE ERWARTUNG IN pruef-start-ansicht steht auf FUENF Gruppen, in ihrer Reihenfolge -- die Position ist hier die eigentliche Aussage. Eine Gruppe, die ans Ende rutscht, faellt auf dem Bildschirm kaum auf. Die Kachelzahl blieb bei 24: umgezogen, nichts hinzugefuegt. pruef-team-ampel 28, pruef-team-stufen 24, pruef-rollen 274, pruef-crew-adresse 123, pruef-modi-ideen 30, pruef-modi-wortleck 5, pruef-zwischenspeicher 21, pruef-start-ansicht gruen. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -232,6 +232,87 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
|
||||
WHERE e.bereich = 'angebote' AND e.status = 'offen'
|
||||
ORDER BY e.bewertung DESC, e.datum DESC`).all();
|
||||
|
||||
/* =================================================================
|
||||
DIE VERFUEGBARKEITS-AMPEL (Blueprint Kapitel 7.1, 10.09.2026)
|
||||
|
||||
"Persoenlicher Kalender (privat): Inhalt ausschliesslich fuer die
|
||||
jeweilige Person sichtbar; Owner/rechte Hand sehen nur die
|
||||
Verfuegbarkeits-Ampel, nicht den Klartext."
|
||||
|
||||
DIE ERSTE HAELFTE STAND SCHON: termineSichtbar() gibt jedem nur
|
||||
seine eigenen Termine -- ausdruecklich auch DogFather. Es fehlte
|
||||
genau die zweite: zu sehen, OB jemand kann, ohne zu sehen, WAS er
|
||||
vorhat.
|
||||
|
||||
DIESE ABFRAGE HOLT NIEMALS titel, beschreibung ODER ort. Nicht
|
||||
"sie zeigt sie nicht an" -- sie liest sie gar nicht erst. Was
|
||||
nicht abgefragt wird, kann auch nicht versehentlich in einer
|
||||
Antwort landen, wenn jemand spaeter ein Feld durchreicht.
|
||||
|
||||
WARUM DIE ZUGEHOERIGKEIT HIER NOCH EINMAL STEHT statt ueber
|
||||
termineSichtbar(): Jene Funktion beantwortet die Frage fuer EINE
|
||||
Person. Bei zehn Modis waeren das zehn Abfragen -- der Fehler,
|
||||
den diese Datei zwei Bildschirme weiter oben ausdruecklich
|
||||
vermeidet. Die Aufzaehlung unten ist dieselbe Regel als
|
||||
Mengenvereinigung: angelegt von mir, mir zugeordnet, ich bin das
|
||||
Gegenueber, oder ich stehe in der Teilnehmerliste.
|
||||
|
||||
DASS BEIDE DASSELBE SAGEN, IST GEMESSEN und nicht behauptet:
|
||||
pruef-team-ampel.mjs stellt beide Wege nebeneinander. Ohne diese
|
||||
Probe waere die Ampel eines Tages leise falsch -- und niemand
|
||||
haette einen Anhaltspunkt, denn sie zeigt ja nur eine Farbe. */
|
||||
const ampelTage = 7;
|
||||
const bisTag = new Date(Date.parse(`${heute}T12:00:00Z`) + (ampelTage - 1) * 86400000)
|
||||
.toISOString().slice(0, 10);
|
||||
|
||||
const belegt = new Map(); // person -> tag -> { termine, minuten }
|
||||
{
|
||||
const platzhalter = platz;
|
||||
const zeilen = db().prepare(`
|
||||
WITH meins AS (
|
||||
SELECT creator_id AS pid, id FROM termine WHERE creator_id IN (${platzhalter})
|
||||
UNION
|
||||
SELECT teilnehmer_id AS pid, id FROM termine WHERE teilnehmer_id IN (${platzhalter})
|
||||
UNION
|
||||
SELECT erstellt_von AS pid, id FROM termine WHERE erstellt_von IN (${platzhalter})
|
||||
UNION
|
||||
SELECT tn.person_id AS pid, tn.termin_id FROM termin_teilnehmer tn
|
||||
WHERE tn.person_id IN (${platzhalter})
|
||||
)
|
||||
SELECT m.pid AS p, date(t.beginn) AS tag,
|
||||
COUNT(*) AS termine, SUM(t.dauer_min) AS minuten
|
||||
FROM meins m JOIN termine t ON t.id = m.id
|
||||
WHERE date(t.beginn) BETWEEN ? AND ?
|
||||
GROUP BY m.pid, date(t.beginn)`)
|
||||
.all(...nummern, ...nummern, ...nummern, ...nummern, heute, bisTag);
|
||||
for (const z of zeilen) {
|
||||
if (!belegt.has(z.p)) belegt.set(z.p, {});
|
||||
belegt.get(z.p)[z.tag] = { termine: z.termine, minuten: z.minuten || 0 };
|
||||
}
|
||||
}
|
||||
|
||||
/* DIE DREI STUFEN.
|
||||
|
||||
Drei Stunden sind die Grenze zwischen "geht noch was" und "der
|
||||
Tag ist verplant" -- ein Sendeabend dauert etwa so lang. Der Wert
|
||||
ist ein Anfangswert und steht hier als Zahl mit Namen, damit man
|
||||
ihn an EINER Stelle aendern kann, wenn sich das im Alltag anders
|
||||
anfuehlt.
|
||||
|
||||
NULL MINUTEN HEISST "frei", nicht "unbekannt": Ein leerer
|
||||
Kalender ist eine Aussage. Wer nichts eingetragen hat, ist
|
||||
ansprechbar -- genau das will man wissen. */
|
||||
const VOLL_AB_MIN = 180;
|
||||
const ampelFuer = (eintrag) => {
|
||||
if (!eintrag || !eintrag.minuten) return "frei";
|
||||
return eintrag.minuten >= VOLL_AB_MIN ? "voll" : "teils";
|
||||
};
|
||||
const tagesliste = [];
|
||||
for (let i = 0; i < ampelTage; i++) {
|
||||
tagesliste.push(new Date(Date.parse(`${heute}T12:00:00Z`) + i * 86400000)
|
||||
.toISOString().slice(0, 10));
|
||||
}
|
||||
|
||||
/* Wie viele Rueckmeldungen an seinen Punkten haengen -- das ist
|
||||
das Gespraech, nicht die Note. */
|
||||
const nachrichten = new Map();
|
||||
@@ -271,6 +352,14 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => {
|
||||
aktiv: !!m.aktiv,
|
||||
seit: m.erstellt || null,
|
||||
stufe: stufeVon(m.stufe),
|
||||
/* NUR DIE AMPEL. Kein Titel, kein Ort, keine Beschreibung --
|
||||
die stehen nicht einmal in der Abfrage. Die Zahl der
|
||||
Termine und die belegten Minuten sagen, wie voll es ist,
|
||||
ohne zu sagen, womit. */
|
||||
verfuegbar: tagesliste.map((tag) => {
|
||||
const e = (belegt.get(m.id) || {})[tag];
|
||||
return { tag, ampel: ampelFuer(e), termine: e?.termine || 0, minuten: e?.minuten || 0 };
|
||||
}),
|
||||
aufgaben: aufgaben.get(m.id) || { offen: 0, ueberfaellig: 0 },
|
||||
ideen: ideen.get(m.id) || 0,
|
||||
nachrichten: nachrichten.get(m.id) || 0,
|
||||
|
||||
Reference in New Issue
Block a user