Fuenf Suchlaeufe parallel, ihre Funde selbst nachgemessen und behoben.
DER GROESSTE: 13 VON 21 SEITEN SCHLUCKTEN JEDEN NETZFEHLER.
Jede Seite beginnt mit `try { await hole('/api/ich') } catch { return; }`.
Faellt das Netz aus -- Aufzug, U-Bahn, Funkloch --, bricht der Block ab,
und die Seite bleibt fuer immer auf "wird geladen …" stehen. Gemessen:
0 von 32 Stellen setzten `aria-busy` im Fehlerfall zurueck, und es gab
KEINEN EINZIGEN "Nochmal versuchen" auf allen 21 Seiten.
Im Code stand der richtige Satz dazu -- an genau einer Stelle:
"Ein Platzhalter, der nie ersetzt wird, ist eine Luege mit
Fortschrittsanzeige." Er galt ueberall ausser dort.
Neu: window.ladefehler() in meldung.js. Sie ersetzt alles, was gerade
"laedt", durch drei Dinge: was los ist, was das bedeutet, und einen
Knopf, der es noch einmal versucht -- ohne die Seite neu zu laden.
21 Dateien angeschlossen. Neu: server/pruef-ladefehler.mjs, 26/0, mit
abgeklemmtem Netz im echten Browser.
UND DIE STARTSEITE WARF BEI EINEM FUNKLOCH AUF DIE ANMELDUNG.
`catch { location.assign('/workspace/') }` -- der Mensch glaubt, er sei
rausgeworfen, und tippt seinen Code neu. Dabei haelt seine Sitzung 12
Stunden; nur die Anfrage kam nicht durch. Umgeleitet wird jetzt nur
noch bei einer ANTWORT, die das sagt (401).
DER ANMELDEHINWEIS FUEHRTE MODIS IN DIE SPERRE.
Nach zwei Fehlversuchen stand: "Stimmt die Auswahl oben? Ein Code
gehoert immer zu genau einer davon." Auf der Crew-Wand ist das FALSCH
-- `stillerZugang()` sucht ueber die Codekennung, die angetippte
Kachel spielt fuer hand/modi keine Rolle. Sie probieren alle vier
durch, sammeln vier Fehlversuche, und nach acht in zehn Minuten ist
ihre Adresse gesperrt. Ein Hinweis, der die Sperre herbeifuehrt, gegen
die er helfen soll. Auf der Agenturwand stimmt der Satz weiter --
deshalb wird gefragt, auf welcher Wand man steht.
Dazu: Die Crew-Wand nannte nur der Community einen Weg ("Frag im Live
nach"). Wer zum Team gehoert und dessen Code nicht geht, fand dort
niemanden.
SACKGASSE AUF DER KERNSEITE EINES MODI.
treff-moderation sagte: "Zugaenge legst du in 'Personen & Zugaenge'
an" -- eine Seite, die ein Modi nicht oeffnen darf. Am ersten Tag, bei
leerer Community, war das der einzige Satz im Abschnitt. Jetzt fragt
die Seite ueber `window.__ich.seiten`, ob es den Weg fuer DIESEN
Menschen gibt. Und ein 404 wirft ihn nicht mehr wortlos auf die
Startseite.
TOTE KNOEPFE AN FREMDEN KARTEN.
"◀ zurueck" und "▶ In Arbeit" standen an JEDER Aufgabenkarte, auch an
fremden. Ein Modi sieht das Brett des ganzen Teams; er tippt, der
Server lehnt mit 403 ab, und die Meldung erscheint GANZ OBEN. Bei
einer Karte weiter unten sieht er nichts. Zwei Zeilen tiefer stand die
Regel im Klartext: "niemandem etwas anzubieten, das dann abgelehnt
wird." Der Server schickt jetzt `darf_aendern` mit.
13 STELLEN ROLLTEN GEGEN DEN WILLEN DES NUTZERS.
`scrollIntoView({ behavior: 'smooth' })` beachtet "Animationen
reduzieren" NICHT. Wer das eingestellt hat, hat es meist wegen
Schwindel getan. Zwei Stellen fragten vorher, dreizehn nicht.
Jetzt `window.sanft()`, einmal statt dreizehnmal.
DAS WORT UEBER DEN BRETTERN EINES MODI HIESS "BETREUUNG".
Fest im HTML, ueberschrieben nur bei Brettern mit eigenem `ober` --
sechs haben keines. Jetzt faellt es auf die GRUPPE der Kachel zurueck,
ueber die er hergekommen ist. Je Rolle richtig, ohne zweite Liste.
DER HINWEIS-ZU-KACHEL-WEG WAR DOPPELT KAPUTT.
`bereichZu` suchte nur in GRUPPEN -- der Liste der AGENTUR. Die
Kacheln von Team Dogi schickt der Server; fuer einen Modi fand die
Zeile entweder nichts oder eine fremde Kachel und uebernahm deren
Farbe. Und sie suchte ueber den NAMEN: "LIVE-Analyse" heisst auf der
Crew-Adresse "Live-Ablauf". Beim Beheben erst den Namen umgedreht --
und damit die Agenturseite kaputt gemacht (pruef-start-ansicht sofort
rot). Jetzt ueber das ZIEL, das in beiden Haeusern dasselbe ist.
UND EIN BRETT WAR SEIT GESTERN GESPERRT.
`TREFF_BRETTER` wird aus Kachelzielen abgeleitet. Als die Kachel
"Regeln & Hilfe" am 19.09. auf `treff-regeln.html` umgelenkt wurde,
fiel `regeln` heraus -- und `treff-regeln.html` verweist weiterhin
darauf ("Haeufige Fragen stehen auf dem Brett Regeln & Hilfe").
Gefunden hat es pruef-treff, die seit gestern rot war. 72/0.
NEBENBEI 7 SEITEN LEICHTER: meldung.js wird jetzt abgeleitet
eingebunden -- nur dort, wo sagWas/ladefehler/sanft wirklich
gebraucht werden. Die Anmeldewand traegt es nicht mehr.
Gruen: pruef-ladefehler 26/0, pruef-sackgassen 11/0 (582 Wege, 0 ins
Leere), pruef-start-ansicht, pruef-treff 72/0, pruef-treffchat,
pruef-community-sicht 10/0, pruef-aufgabenbrett, pruef-code 17/0,
pruef-chat-optik, pruef-nachfrage 49/0, pruef-meldungen 8/0,
pruef-css-klassen, pruef-tippziele 11/0, pruef-leerzustand 13/0,
pruef-rechtetafel.
396 lines
17 KiB
JavaScript
396 lines
17 KiB
JavaScript
/* ===================================================================
|
||
Reports & Review.
|
||
|
||
Die Fragen der Abschnitte stammen woertlich aus dem Konzept, Seite 16:
|
||
"Was wurde erledigt? Was blockiert? Was hat funktioniert? Was kommt
|
||
naechste Woche?"
|
||
=================================================================== */
|
||
(() => {
|
||
'use strict';
|
||
|
||
/* DogFather und Manager haben dieselben Rechte -- die Unterscheidung
|
||
macht nur die Personenverwaltung, und die prueft der Server. */
|
||
/* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun.
|
||
Sie stand hier frueher selbst, ohne 'spicy', und genau das war der
|
||
Fehler, den Filipe am 07.09.2026 gefunden hat. */
|
||
const LEITUNG = window.Bereiche.LEITUNG;
|
||
|
||
const $ = (id) => document.getElementById(id);
|
||
const BEREICHSNAME = {
|
||
live: 'LIVE', content: 'Content', technik: 'Technik',
|
||
community: 'Community', schutz: 'Schutz', agentur: 'Agentur',
|
||
};
|
||
|
||
let ich = null;
|
||
let kiDa = false;
|
||
|
||
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) => { $('fehler').textContent = t || ''; };
|
||
const datum = (iso) => (iso ? iso.slice(0, 10).split('-').reverse().join('.') : '—');
|
||
|
||
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('kein Zugriff'); }
|
||
return a;
|
||
}
|
||
|
||
/* Vorher/Nachher. `gut` sagt, ob ein Anstieg erfreulich ist -- bei
|
||
offenen Problemen ist er das nicht. */
|
||
function trend(jetzt, davor, gut = true) {
|
||
const t = el('span', 'trend');
|
||
t.dataset.gut = gut ? 'ja' : 'nein';
|
||
const diff = jetzt - davor;
|
||
if (!davor && !jetzt) { t.dataset.richtung = 'gleich'; t.textContent = '–'; return t; }
|
||
if (diff === 0) { t.dataset.richtung = 'gleich'; t.textContent = 'unverändert'; return t; }
|
||
t.dataset.richtung = diff > 0 ? 'hoch' : 'runter';
|
||
t.textContent = (diff > 0 ? '▲ +' : '▼ ') + diff + ' ggü. davor';
|
||
return t;
|
||
}
|
||
|
||
/* BEWUSST OHNE die Creator-Auswahl im Verweis. Der erste Entwurf hängte
|
||
?creator=<nr> an -- das sah hilfreich aus und wäre eine Lüge gewesen:
|
||
Keine der Zielseiten liest diesen Wert. Aufgabenbrett, Dateien und
|
||
Bereiche zeigen immer alles, was die angemeldete Person sehen darf.
|
||
Ein Parameter, der nichts bewirkt, wird beim nächsten Mal für
|
||
lebenden Code gehalten und mitgepflegt.
|
||
|
||
Wenn diese Seiten einmal nach Creator filtern können, gehört er
|
||
wieder dazu -- dann aber zusammen mit der Stelle, die ihn ausliest. */
|
||
|
||
/* Eine Kennzahl. `ziel` macht daraus einen Weg zu genau der Stelle,
|
||
an der die gezaehlten Dinge stehen (Wunsch 01.09.2026: "wenn man
|
||
auf die Kisten drueckt, sofort zu diesem Punkt weitergeschickt
|
||
werden").
|
||
|
||
Eine Zahl ohne Weg dahin ist eine Behauptung: Man kann sie nur
|
||
glauben oder von Hand nachsuchen. */
|
||
function kachel(zahl, name, { trendEl = null, warn = false, ziel = null } = {}) {
|
||
const k = el(ziel ? 'a' : 'div', 'kachel' + (ziel ? ' kachel--weg' : ''));
|
||
if (ziel) {
|
||
k.href = ziel;
|
||
/* Der ganze Satz fuer Vorleseprogramme -- "3" allein sagt nichts,
|
||
und ein Verweis ohne erkennbares Ziel ist fuer sie wertlos. */
|
||
k.setAttribute('aria-label', `${name}: ${zahl} – ansehen`);
|
||
}
|
||
if (warn) k.dataset.warn = 'ja';
|
||
const wert = el('div', 'kachel__wert');
|
||
wert.append(el('span', 'kachel__zahl', String(zahl)));
|
||
if (trendEl) wert.append(trendEl);
|
||
k.append(wert, el('span', 'kachel__name', name));
|
||
return k;
|
||
}
|
||
|
||
/* Wohin fuehrt ein Betreuungsbereich? Drei liegen auf bereich.html,
|
||
Content hat eine eigene Seite. Die Zuordnung steht EINMAL hier --
|
||
zweimal abgeschrieben waere sie beim naechsten neuen Bereich an
|
||
einer der beiden Stellen vergessen. */
|
||
const BEREICH_ZIEL = {
|
||
live: 'bereich.html?b=live',
|
||
community: 'bereich.html?b=community',
|
||
technik: 'bereich.html?b=technik',
|
||
schutz: 'bereich.html?b=schutz',
|
||
agentur: 'bereich.html?b=agentur',
|
||
content: 'content.html',
|
||
};
|
||
const bereichZiel = (b, zeigen) => {
|
||
const z = BEREICH_ZIEL[b];
|
||
if (!z) return null;
|
||
return zeigen ? z + (z.includes('?') ? '&' : '?') + 'zeigen=' + zeigen : z;
|
||
};
|
||
|
||
function block(titel, frage, inhalt) {
|
||
const b = el('section', 'block');
|
||
b.append(el('h2', 'block__titel', titel));
|
||
if (frage) b.append(el('p', 'block__frage', frage));
|
||
b.append(inhalt);
|
||
return b;
|
||
}
|
||
|
||
/* ---------- Bericht zeichnen ------------------------------------------- */
|
||
|
||
function zeichne(d) {
|
||
const ziel = $('bericht');
|
||
ziel.textContent = '';
|
||
ziel.setAttribute('aria-busy', 'false');
|
||
|
||
$('unterzeile').textContent =
|
||
`${d.creator_name ? d.creator_name + ' · ' : 'Alle Creator · '}`
|
||
+ `${datum(d.zeitraum.ab)} bis ${datum(d.zeitraum.bis)}`
|
||
+ ` · Vergleich mit den ${d.zeitraum.tage} Tagen davor`;
|
||
|
||
/* Was wurde erledigt? */
|
||
const k1 = el('div', 'kacheln');
|
||
k1.append(kachel(d.aufgaben.erledigt, 'Aufgaben erledigt',
|
||
{ trendEl: trend(d.aufgaben.erledigt, d.aufgaben.erledigt_davor),
|
||
ziel: 'aufgaben.html?zeigen=erledigt' }));
|
||
/* "neu angelegt" fuehrt auf das Brett OHNE Ausschnitt: Der Bericht
|
||
zaehlt hier einen Zeitraum, das Brett kennt keinen. Ein Sprung auf
|
||
"zeigen=angelegt" wuerde etwas hervorheben, das nicht dieselbe
|
||
Menge ist -- und eine Hervorhebung, die nicht stimmt, ist
|
||
schlimmer als keine. */
|
||
k1.append(kachel(d.aufgaben.angelegt, 'neu angelegt',
|
||
{ trendEl: trend(d.aufgaben.angelegt, d.aufgaben.angelegt_davor),
|
||
ziel: 'aufgaben.html' }));
|
||
k1.append(kachel(d.termine.gewesen, 'Termine gewesen',
|
||
{ ziel: 'kalender.html?zeigen=vergangen' }));
|
||
k1.append(kachel(d.dateien.freigegeben, 'Dateien freigegeben',
|
||
{ ziel: 'dateien.html?zeigen=frei' }));
|
||
ziel.append(block('Was wurde erledigt?', null, k1));
|
||
|
||
/* ---- WOMIT ANFANGEN? (15.09.2026, Stufe 9) ---------------------
|
||
|
||
Ein Brett mit dreissig offenen Karten sagt, WAS zu tun ist. Es
|
||
sagt nicht, WOMIT man anfaengt -- "wichtig" allein hilft dabei
|
||
nicht: Wenn acht Karten wichtig sind, ist keine davon die erste.
|
||
|
||
Die Verteilung kommt fertig vom Server. Der Browser rechnet hier
|
||
nichts nach; eine zweite Rechnung waere die, die irgendwann
|
||
anders antwortet als das Brett.
|
||
|
||
DIE GRUPPE "noch nicht eingeschaetzt" STEHT MIT DA, auch wenn sie
|
||
gross ist. Eine Uebersicht, die einen Teil der Arbeit unsichtbar
|
||
macht, ist schlimmer als keine -- man verlaesst sich darauf und
|
||
uebersieht genau das, was fehlt. */
|
||
if (Array.isArray(d.womit) && d.womit.some((f) => f.anzahl > 0)) {
|
||
const w = el('div', 'womit');
|
||
for (const f of d.womit) {
|
||
if (!f.anzahl) continue;
|
||
const k = el('section', 'womit__feld');
|
||
k.dataset.feld = f.schluessel;
|
||
|
||
const kopf = el('div', 'womit__kopf');
|
||
kopf.append(el('h3', 'womit__name', f.name));
|
||
kopf.append(el('span', 'womit__zahl', String(f.anzahl)));
|
||
k.append(kopf);
|
||
k.append(el('p', 'womit__satz', f.satz));
|
||
|
||
const liste = el('ul', 'womit__liste');
|
||
for (const a of f.aufgaben || []) {
|
||
const z = el('li', 'womit__eintrag');
|
||
const v = el('a', null, a.titel);
|
||
v.href = 'aufgaben.html#a' + a.id;
|
||
z.append(v);
|
||
liste.append(z);
|
||
}
|
||
k.append(liste);
|
||
if (f.gekuerzt) {
|
||
k.append(el('p', 'womit__mehr',
|
||
'und ' + (f.anzahl - (f.aufgaben || []).length) + ' weitere'));
|
||
}
|
||
w.append(k);
|
||
}
|
||
ziel.append(block('Womit anfangen?',
|
||
'Wichtig und Aufwand nebeneinander – daraus fällt die Entscheidung von selbst.', w));
|
||
}
|
||
|
||
/* Was blockiert? */
|
||
const k2 = el('div', 'kacheln');
|
||
k2.append(kachel(d.aufgaben.offen, 'Aufgaben offen',
|
||
{ ziel: 'aufgaben.html?zeigen=offen' }));
|
||
k2.append(kachel(d.aufgaben.ueberfaellig, 'überfällig',
|
||
{ warn: d.aufgaben.ueberfaellig > 0, ziel: 'aufgaben.html?zeigen=ueberfaellig' }));
|
||
const dringend = Object.values(d.bereiche).reduce((s, b) => s + b.dringend, 0);
|
||
/* Diese Zahl ist eine SUMME ueber alle Bereiche -- es gibt keine
|
||
einzelne Seite, auf der genau sie steht. Sie fuehrt deshalb in den
|
||
Bereich, in dem die meisten davon liegen, statt blind in die
|
||
LIVE-Analyse. Liegt nichts an, fuehrt sie nirgendwohin: ein Weg zu
|
||
null Dingen ist eine Enttaeuschung, kein Angebot. */
|
||
const meiste = Object.entries(d.bereiche)
|
||
.filter(([, w]) => w.dringend > 0)
|
||
.sort((a, b) => b[1].dringend - a[1].dringend)[0];
|
||
k2.append(kachel(dringend, 'dringende Punkte',
|
||
{ warn: dringend > 0, ziel: meiste ? bereichZiel(meiste[0], 'dringend') : null }));
|
||
k2.append(kachel(d.dateien.offen_review, 'Dateien im Review',
|
||
{ ziel: 'dateien.html?zeigen=review' }));
|
||
const b2 = block('Was blockiert?', 'Die drei Aufgaben, die am längsten offen liegen:', k2);
|
||
if (d.aufgaben.aeltester.length) {
|
||
const liste = el('ul', 'liste-klein');
|
||
for (const a of d.aufgaben.aeltester) {
|
||
const li = el('li');
|
||
li.append(document.createTextNode(a.titel));
|
||
li.append(el('span', 'klein',
|
||
`offen seit ${datum(a.erstellt)}${a.frist ? ' · Frist ' + datum(a.frist) : ''}`));
|
||
liste.append(li);
|
||
}
|
||
b2.append(liste);
|
||
} else {
|
||
b2.append(el('p', 'leer-hinweis', 'Nichts offen – sauber.'));
|
||
}
|
||
ziel.append(b2);
|
||
|
||
/* Was hat funktioniert? */
|
||
const k3 = el('div', 'kacheln');
|
||
const jetztS = d.live.jetzt.schnitt;
|
||
const davorS = d.live.davor.schnitt;
|
||
const kLive = kachel(jetztS === null ? '–' : String(jetztS).replace('.', ','),
|
||
`LIVE-Schnitt aus ${d.live.jetzt.anzahl} Bewertung(en)`,
|
||
{ ziel: d.live.jetzt.anzahl > 0 ? bereichZiel('live') : null });
|
||
if (jetztS !== null && davorS !== null) {
|
||
kLive.querySelector('.kachel__wert').append(
|
||
trend(Math.round(jetztS * 10), Math.round(davorS * 10)));
|
||
}
|
||
k3.append(kLive);
|
||
for (const [b, w] of Object.entries(d.bereiche)) {
|
||
k3.append(kachel(w.neu, BEREICHSNAME[b] + ': neue Einträge',
|
||
{ trendEl: trend(w.neu, w.neu_davor), ziel: bereichZiel(b) }));
|
||
}
|
||
ziel.append(block('Was hat funktioniert?',
|
||
'Bewegung in den Betreuungsbereichen im Vergleich zum Zeitraum davor:', k3));
|
||
|
||
/* Was kommt als Nächstes? */
|
||
const k4 = el('div', 'kacheln');
|
||
k4.append(kachel(d.termine.kommend, 'Termine stehen an',
|
||
{ ziel: 'kalender.html?zeigen=kommend' }));
|
||
for (const [b, w] of Object.entries(d.bereiche)) {
|
||
if (w.offen) k4.append(kachel(w.offen, BEREICHSNAME[b] + ': offen',
|
||
{ warn: w.dringend > 0, ziel: bereichZiel(b, 'offen') }));
|
||
}
|
||
const b4 = block('Was kommt als Nächstes?', null, k4);
|
||
if (d.profil && (d.profil.plan_prio1 || d.profil.plan_prio2 || d.profil.plan_prio3)) {
|
||
const liste = el('ul', 'liste-klein');
|
||
for (const [i, t] of [d.profil.plan_prio1, d.profil.plan_prio2, d.profil.plan_prio3].entries()) {
|
||
if (!t) continue;
|
||
const li = el('li');
|
||
li.append(document.createTextNode(t));
|
||
li.append(el('span', 'klein', `Priorität ${i + 1} aus dem 90-Tage-Plan`));
|
||
liste.append(li);
|
||
}
|
||
b4.append(liste);
|
||
}
|
||
if (d.profil?.naechster_review) {
|
||
b4.append(el('p', 'block__frage', 'Nächster Review-Termin: ' + datum(d.profil.naechster_review)));
|
||
}
|
||
ziel.append(b4);
|
||
|
||
$('entscheidung-block').hidden = false;
|
||
|
||
/* Zahlen in Worte -- erst auf Knopfdruck, nie von selbst. Der
|
||
Report ist ohne KI vollstaendig; das hier ist eine Zugabe. */
|
||
if (kiDa) {
|
||
const reihe = el('div', 'ki-reihe');
|
||
reihe.append(window.KI.knopf('In Worte fassen', async () => {
|
||
const zahlen = `Zeitraum ${d.zeitraum.tage} Tage`
|
||
+ `, ${d.aufgaben.erledigt} Aufgaben erledigt (davor ${d.aufgaben.erledigt_davor})`
|
||
+ `, ${d.aufgaben.angelegt} neu angelegt (davor ${d.aufgaben.angelegt_davor})`
|
||
+ `, ${d.aufgaben.offen} offen, ${d.aufgaben.ueberfaellig} überfällig`
|
||
+ `, ${Object.values(d.bereiche).reduce((s, b) => s + b.dringend, 0)} dringende Punkte`
|
||
+ `, LIVE-Schnitt ${d.live.jetzt.schnitt ?? '-'} aus ${d.live.jetzt.anzahl} Bewertungen`
|
||
+ `, ${d.termine.gewesen} Termine gewesen, ${d.termine.kommend} stehen an.`;
|
||
const r = await window.KI.frage('reporttext', { zahlen });
|
||
const alt = reihe.querySelector('.ki-text');
|
||
if (alt) alt.remove();
|
||
const text = el('p', 'ki-text', r.text);
|
||
reihe.append(text);
|
||
}));
|
||
ziel.append(reihe);
|
||
}
|
||
}
|
||
|
||
async function laden() {
|
||
melde('');
|
||
const creator = $('f-creator').value || 'alle';
|
||
const tage = $('f-tage').value;
|
||
try {
|
||
const a = await hole(`/workspace/api/report?creator=${encodeURIComponent(creator)}&tage=${tage}`);
|
||
if (!a.ok) { melde('Report konnte nicht erstellt werden.'); return; }
|
||
zeichne(await a.json());
|
||
} catch { /* umgeleitet */ }
|
||
}
|
||
|
||
/* ---------- Entscheidung ------------------------------------------------ */
|
||
|
||
$('entscheidung').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
$('e-fehler').textContent = '';
|
||
const knopf = $('e-speichern');
|
||
knopf.disabled = true;
|
||
try {
|
||
const a = await hole('/workspace/api/report/entscheidung', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
titel: $('e-titel').value,
|
||
frist: $('e-frist').value,
|
||
creator_id: $('f-creator').value || null,
|
||
}),
|
||
});
|
||
if (!a.ok) {
|
||
$('e-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
|
||
return;
|
||
}
|
||
$('entscheidung').reset();
|
||
$('e-stand').textContent = 'Als Aufgabe angelegt ✓';
|
||
setTimeout(() => { $('e-stand').textContent = ''; }, 3200);
|
||
await laden();
|
||
} catch { /* umgeleitet */ } finally { knopf.disabled = false; }
|
||
});
|
||
|
||
/* ---------- Start -------------------------------------------------------- */
|
||
|
||
(async () => {
|
||
try {
|
||
const a = await hole('/workspace/api/ich');
|
||
if (!a.ok) { location.assign('/workspace/'); return; }
|
||
ich = await a.json();
|
||
} catch {
|
||
/* NICHT MEHR STUMM (20.09.2026). Hier stand `catch { return; }`
|
||
-- und damit blieb die Seite bei jedem Netzaussetzer fuer
|
||
immer auf "wird geladen …" stehen. Gemessen auf 13 von 21
|
||
Seiten. `ladefehler()` sagt, was los ist, und bietet einen
|
||
Knopf an, der es noch einmal versucht. */
|
||
window.ladefehler?.();
|
||
return;
|
||
}
|
||
window.werZeigen?.(ich);
|
||
kiDa = await window.KI.verfuegbar();
|
||
|
||
try {
|
||
const { creator } = await (await hole('/workspace/api/report/creator')).json();
|
||
if (LEITUNG.has(ich.rolle)) {
|
||
$('creator-block').hidden = false;
|
||
const alle = document.createElement('option');
|
||
alle.value = ''; alle.textContent = 'Alle Creator';
|
||
$('f-creator').append(alle);
|
||
} else if (ich.rolle === 'scout') {
|
||
/* Ein Scout waehlt zwischen SEINEN Creator -- "alle" gibt es
|
||
fuer ihn nicht, das waere die Gesamtuebersicht. */
|
||
if (!creator.length) {
|
||
/* Nicht nur die Unterzeile beschriften: Der Berichtsbereich
|
||
stand danach für immer auf "Report wird erstellt …", weil
|
||
ihn nichts mehr anfasste. Für ein Vorleseprogramm ist eine
|
||
dauerhaft beschäftigte Fläche das Zeichen, hier nicht
|
||
weiterzulesen. Gefunden beim Rollen-Rundgang 02.09.2026. */
|
||
const satz = 'Dir ist noch kein Creator zugeteilt. '
|
||
+ 'DogFather legt fest, wen du betreust.';
|
||
$('unterzeile').textContent = satz;
|
||
const ziel = $('bericht');
|
||
ziel.setAttribute('aria-busy', 'false');
|
||
ziel.textContent = '';
|
||
const hinweis = document.createElement('p');
|
||
hinweis.className = 'leer-hinweis';
|
||
hinweis.textContent = satz;
|
||
ziel.append(hinweis);
|
||
return;
|
||
}
|
||
$('creator-block').hidden = false;
|
||
}
|
||
for (const c of creator) {
|
||
const o = document.createElement('option');
|
||
o.value = String(c.id); o.textContent = c.name;
|
||
$('f-creator').append(o);
|
||
}
|
||
if (ich.rolle === 'creator') $('f-creator').value = String(ich.id);
|
||
} catch { return; }
|
||
|
||
$('f-creator').addEventListener('change', laden);
|
||
$('f-tage').addEventListener('change', laden);
|
||
await laden();
|
||
})();
|
||
})();
|