Wunsch Filipe (10.09.2026): *"diese kategorien mussen auf dieser seite auch noch auf die modis perfektionniert werden, da mussen lauter sachen sein die vanvan und ich danach verteilen können und da anpassen können ob gut oder nicht."* Und: *"da soll es eine kategorie geben für uns beide nur sonst keinen wo wir dass alles sehen und behandeln können."* === TEIL 1: SEIN EIGENER PUNKTESATZ === 101 Punkte in workspace-modi-punkte.js -- Live-Ablauf 40, Community 36, Technik 25. Die vorhandenen sagen "Upload messen", "Sendeplan festlegen", "Verweildauer vergleichen": Ein Modi sendet nicht, er moderiert. Ihm dieselbe Liste vorzulegen hiesse, ihn an Dingen zu messen, die nicht seine Arbeit sind. DIESELBE EINTEILUNG (Vor/Waehrend/Nach usw.), nur andere Punkte darin. Zwei verschiedene Einteilungen waeren zwei Dinge zum Lernen statt einem, und die Oberflaeche kaeme ohne Umbau nicht damit zurecht. DIE SCHLUESSEL BEGINNEN MIT "m-". Der Stand haengt an (bereich, schluessel, person) -- ohne Praefix koennte ein Modi-Punkt eines Tages denselben Namen tragen wie ein Creator-Punkt und dessen Bewertung erben. BEIDE SAETZE STEHEN IN GUELTIG. Stuenden dort nur die Creator-Punkte, kaeme die Liste an und jeder Klick darauf brachte einen 404 -- die Sorte Fehler, die man erst beim Benutzen merkt. Ein Modi sieht SEINE Liste, ohne jemanden auszuwaehlen (wie ein Creator), und bewertet sich nicht selbst. Ohne diese Zeile waere er in den Betreuer-Zweig gefallen, haette eine Auswahl fremder Creator vorgesetzt bekommen und seine eigene Liste gar nicht gesehen. DER HEIKELSTE PUNKT WAR EIN ANDERER: `darfCreator` sagt bei siehtAlles() pauschal ja -- und darin steckt auch Spicy Media. Wer die Modi-Auswahl daran haengt, oeffnet sie ihr nebenbei mit, ohne dass an der Stelle etwas davon steht. Deshalb eine eigene Regel (darfPerson), und die Pruefung versucht es ueber die Liste, ueber die Adresse UND ueber das Bewerten. Die Auswahl heisst bei der DogFather-Rolle jetzt "Person" statt "Creator" -- ein Feld namens "Creator", in dem ein Modi steht, ist falsch beschriftet. Das Wort kommt vom Server; ein Rollenvergleich im Browser waere die Stelle, an der der Name in einer ausgelieferten Datei landet. === TEIL 2: DIE GEMEINSAME SEITE === teamlage.html, nur fuer die DogFather-Rolle -- also Filipe und VanVan. Fuer alle anderen gibt es weder die Kachel noch die Seite noch die Schnittstelle (404, wie bei einer Adresse, die es nicht gibt). Zwei Schloesser, absichtlich: die Schranke und die Abfrage. Faellt eines weg, haelt das andere. Je Person: offene und ueberfaellige Aufgaben, beigetragene Ideen, Rueckmeldungen, und je Bereich gut/verbessern/offen. Jede Zeile fuehrt in den Bereich -- MIT DER PERSON VORAUSGEWAEHLT. Dafuer liest die Checkliste jetzt `creator_id` aus der Adresse; ohne das landet man auf der erstbesten Person und weiss beim zweiten Suchen nicht mehr, warum man hier war. SIE ZAEHLT, SIE BEWERTET NICHT. Bewertet wird dort, wo die Punkte stehen. Eine zweite Stelle dafuer waere eine zweite Stelle, an der es auseinanderlaeuft. UND SIE IST KEINE UEBERWACHUNG. Ueber team.html steht schon, dass eine Seite mit Zahlen ueber Kollegen als Kontrolle gelesen wird -- und dann arbeitet niemand mehr offen damit. "Offen" heisst hier deshalb ausdruecklich: darueber habt ihr noch nicht geredet. Eine Merkliste fuer euch beide, keine Note. ALLES IN VIER ABFRAGEN, nicht vier je Person -- bei zehn Modis waeren das vierzig. Denselben Fehler hat das Haus bei den Terminen schon gemacht und ihn dort vermerkt. KEINE NEUEN CSS-KLASSEN: Fuer genau diese Karten gibt es sie schon (tperson, tz, schritt -- aus team.html). Elf neue haetten gepflegt werden muessen fuer ein Aussehen, das bereits da ist. ZUSATZKACHELN sind ein neuer, kleiner Mechanismus: `bereiche` ERSETZT die Liste im Browser (fuer Rollen, die dort nicht vorkommen), `bereiche_zusatz` HAENGT an. Gebraucht fuer Kacheln, die nur eine einzige Rolle bekommt -- in bereiche.js duerften sie nicht stehen, weil eine Kachel, die nur bei einem erscheint, die Frage aufwirft, fuer wen sie ist. Findet sich die genannte Gruppe nicht, wird eine eigene angelegt: Sonst faellt die Kachel lautlos weg, sobald jemand eine Gruppe umbenennt. EHRLICH ZUM UMFANG: Technik ist mit 25 Punkten der duennste Bereich. Das ist Absicht -- der technische Spielraum eines Moderators ist kleiner als der eines Streamers. Auffuellen haette Fuellmaterial in eine Liste gebracht, die Filipe und VanVan durchgehen muessen. GEPRUEFT: pruef-modi-checkliste (39, neu), pruef-rollen (244 statt 243 -- die neue Kachel wird angeklickt und muss ankommen), pruef-modi-verborgen (75), pruef-workspace-seiten. Co-Authored-By: Claude Opus 5 <[email protected]>
154 lines
5.9 KiB
JavaScript
154 lines
5.9 KiB
JavaScript
/* =====================================================================
|
|
DIE LAGE DES TEAMS -- eine Seite fuer die, die sie sehen duerfen.
|
|
|
|
Wunsch Filipe (10.09.2026): eine Stelle, an der er und VanVan alles
|
|
sehen und behandeln koennen.
|
|
|
|
SIE ZAEHLT NUR, SIE BEWERTET NICHT. Bewertet wird dort, wo die
|
|
Punkte stehen -- je Bereich fuehrt ein Verweis dorthin, mit der
|
|
Person schon vorausgewaehlt. Eine zweite Stelle zum Bewerten waere
|
|
eine zweite Stelle, an der es auseinanderlaeuft.
|
|
|
|
KEINE NEUEN CSS-KLASSEN. Fuer genau diese Art Karte gibt es sie
|
|
schon (`tperson`, `tz`, `schritt` -- benutzt von team.html). Elf
|
|
neue haetten in mehreren Stilvorlagen gepflegt werden muessen, fuer
|
|
ein Aussehen, das es bereits gibt. Zwei Bauarten fuer dieselbe Sache
|
|
sind ausserdem zwei Dinge zum Lernen statt einem.
|
|
|
|
IN DIESER DATEI STEHT KEIN ROLLENNAME. Sie wird wie jede Datei unter
|
|
/workspace an JEDEN ausgeliefert, der die Seite oeffnet. Wer sie
|
|
bekommt, entscheiden der Server (404 fuer alle anderen) und die
|
|
Kachel, die ebenfalls vom Server kommt.
|
|
===================================================================== */
|
|
(() => {
|
|
const $ = (id) => document.getElementById(id);
|
|
const el = (tag, klasse, text) => {
|
|
const k = document.createElement(tag);
|
|
if (klasse) k.className = klasse;
|
|
if (text !== undefined) k.textContent = text;
|
|
return k;
|
|
};
|
|
const melde = (t) => { const f = $('fehler'); if (f) f.textContent = t || ''; };
|
|
|
|
async function hole(pfad, optionen = {}) {
|
|
const a = await fetch(pfad, { credentials: 'same-origin', ...optionen });
|
|
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
|
|
if (a.status === 404) { location.assign('/workspace/start.html'); throw new Error('nicht erlaubt'); }
|
|
return a;
|
|
}
|
|
|
|
const tageHer = (iso) => {
|
|
if (!iso) return null;
|
|
const d = new Date(iso);
|
|
if (Number.isNaN(d.getTime())) return null;
|
|
return Math.floor((Date.now() - d.getTime()) / 86400000);
|
|
};
|
|
|
|
/* Die drei Bereiche in derselben Reihenfolge wie auf der Startseite.
|
|
Die Namen stehen hier, weil die Seite sonst eine zweite Abfrage
|
|
braeuchte, nur um drei Woerter zu holen. */
|
|
const BEREICHE = [
|
|
{ schluessel: 'live', name: 'Live-Ablauf' },
|
|
{ schluessel: 'community', name: 'Community' },
|
|
{ schluessel: 'technik', name: 'Technik' },
|
|
];
|
|
|
|
function zahl(wert, schild) {
|
|
const k = el('div', 'tz');
|
|
k.append(el('strong', 'tz__wert', String(wert)));
|
|
k.append(el('span', 'tz__schild', schild));
|
|
return k;
|
|
}
|
|
|
|
function karte(m) {
|
|
const k = el('article', 'tperson');
|
|
|
|
const kopf = el('div', 'tperson__kopf');
|
|
const zeichen = el('span', 'tperson__zeichen');
|
|
if (m.bild) {
|
|
const b = document.createElement('img');
|
|
b.src = m.bild; b.alt = ''; b.loading = 'lazy';
|
|
zeichen.append(b);
|
|
} else {
|
|
zeichen.textContent = (m.name || '?').trim().charAt(0).toUpperCase();
|
|
}
|
|
kopf.append(zeichen);
|
|
|
|
const text = el('div', 'tperson__text');
|
|
text.append(el('p', 'tperson__namen', m.name));
|
|
/* WANN ZULETZT DA -- als Tatsache, nicht als Vorwurf. Wer zwei
|
|
Wochen nicht da war, hat vielleicht Urlaub; die Zahl sagt nur,
|
|
dass man nachfragen koennte. */
|
|
const her = tageHer(m.letzter_login);
|
|
text.append(el('span', 'tperson__zuletzt', her === null
|
|
? 'war noch nie angemeldet'
|
|
: her === 0 ? 'heute da gewesen'
|
|
: her === 1 ? 'gestern da gewesen' : `vor ${her} Tagen da gewesen`));
|
|
kopf.append(text);
|
|
k.append(kopf);
|
|
|
|
const zahlen = el('div', 'tperson__zahlen');
|
|
zahlen.append(zahl(m.aufgaben.offen, 'Aufgaben offen'));
|
|
zahlen.append(zahl(m.aufgaben.ueberfaellig, 'überfällig'));
|
|
zahlen.append(zahl(m.ideen, 'Ideen'));
|
|
zahlen.append(zahl(m.nachrichten, 'Rückmeldungen'));
|
|
for (const b of BEREICHE) {
|
|
const z = m.bereiche[b.schluessel];
|
|
if (z) zahlen.append(zahl(z.offen, `${b.name}: offen`));
|
|
}
|
|
k.append(zahlen);
|
|
|
|
/* UND HIER WIRD BEHANDELT. Ein Verweis je Bereich, der die Person
|
|
mitnimmt -- ohne sie muesste man dort noch einmal auswaehlen,
|
|
und beim zweiten Mal weiss man nicht mehr, warum man hier war. */
|
|
const wege = el('div', 'tpipe__reihe');
|
|
for (const b of BEREICHE) {
|
|
const z = m.bereiche[b.schluessel];
|
|
if (!z) continue;
|
|
const a = el('a', 'schritt',
|
|
`${b.name} ansehen (${z.gut} gut · ${z.verbessern} verbessern)`);
|
|
a.href = `bereich.html?b=${b.schluessel}&creator_id=${m.id}`;
|
|
wege.append(a);
|
|
}
|
|
k.append(wege);
|
|
return k;
|
|
}
|
|
|
|
async function laden() {
|
|
const ziel = $('lage');
|
|
try {
|
|
const a = await hole('/workspace/api/teamlage');
|
|
if (!a.ok) { melde('Die Lage konnte nicht geladen werden.'); return; }
|
|
const daten = await a.json();
|
|
ziel.textContent = '';
|
|
ziel.setAttribute('aria-busy', 'false');
|
|
|
|
const leute = daten.modis || [];
|
|
const offenGesamt = leute.reduce((s, m) =>
|
|
s + Object.values(m.bereiche).reduce((x, b) => x + b.offen, 0), 0);
|
|
/* Eine leere Seite ohne Erklaerung sieht aus wie ein Fehler --
|
|
deshalb steht hier auch, was zu tun waere. */
|
|
$('kopf-unter').textContent = leute.length
|
|
? `${leute.length} ${leute.length === 1 ? 'Person' : 'Personen'} · `
|
|
+ `${offenGesamt} Punkte noch nicht besprochen`
|
|
: 'Noch niemand im Team. Zugänge legst du unter „Personen & Zugänge“ an.';
|
|
|
|
for (const m of leute) ziel.append(karte(m));
|
|
} catch { /* umgeleitet */ }
|
|
}
|
|
|
|
(async () => {
|
|
try {
|
|
const a = await hole('/workspace/api/ich');
|
|
if (!a.ok) { location.assign('/workspace/'); return; }
|
|
window.werZeigen?.(await a.json());
|
|
} catch { return; }
|
|
$('abmelden')?.addEventListener('click', async () => {
|
|
try { await fetch('/workspace/api/abmelden', { method: 'POST', credentials: 'same-origin' }); }
|
|
catch { /* egal */ }
|
|
location.assign('/workspace/');
|
|
});
|
|
await laden();
|
|
})();
|
|
})();
|