diff --git a/server/pruef-meldungen.mjs b/server/pruef-meldungen.mjs new file mode 100644 index 00000000..1ce4a932 --- /dev/null +++ b/server/pruef-meldungen.mjs @@ -0,0 +1,219 @@ +/* ===================================================================== + pruef-meldungen.mjs — Kein Benutzer liest je eine Maschinenkennung. + + Filipe, 19.09.2026: „keine kryptischen oder technischen + Fehlermeldungen, sondern klare Aussagen darüber, was passiert ist + und wie man das Problem lösen kann." + + --------------------------------------------------------------------- + WARUM DIESE PRÜFUNG DIE EIGENTLICHE ARBEIT IST + + Die Übersetzungstabelle in `workspace/assets/js/meldung.js` zu + schreiben war eine Stunde Arbeit. Sie in einem halben Jahr noch + vollständig zu haben, ist die schwierigere Aufgabe — und zwar aus + einem Grund, den dieses Haus teuer gelernt hat: + + „Eine abgeschriebene Liste ist dieselbe Falle wie eine feste Zahl." + (11.09.2026, als die Umstellung drei Spalten verlor, obwohl ein + Kommentar direkt daneben davor warnte.) + + Wer morgen `fehler: "zu_frueh"` erfindet, wird nicht daran denken, + hier einen Satz nachzutragen. Er merkt es auch nicht: Die Oberfläche + zeigt dann klaglos den Ersatzsatz. Es sieht aus, als funktioniere es. + + Deshalb rechnet diese Prüfung die Liste der Kennungen AUS DEM SERVER + aus, statt sie zu kennen. Eine neue Kennung ohne Satz macht sie rot. + Man KANN es nicht mehr vergessen. + + --------------------------------------------------------------------- + DREI DINGE WERDEN GEPRÜFT + + 1. VOLLSTÄNDIG — jede Kennung, die der Server schicken kann, hat + einen Satz. + 2. ANGESCHLOSSEN — keine Seite zeigt `.fehler` noch roh an, und + jede Seite, deren Skript `sagWas` benutzt, lädt auch + `meldung.js`. (Der zweite Teil ist der wichtigere: Ohne die + Datei wäre `sagWas` nicht definiert, und aus einer Fehlermeldung + würde ein Absturz — schlimmer als vorher.) + 3. LESBAR — kein Satz enthält eine Kennung, eine nackte Zahl oder + ein englisches Wort. + + Dazu drei GEGENPROBEN. Eine Prüfung, die immer bestätigt, bestätigt + nichts (01.09.2026): Jede der drei muss beweisen, dass sie auch + „nicht in Ordnung" sagen kann. + + AUFRUF: node server/pruef-meldungen.mjs + Läuft in unter einer Sekunde, ohne Server, ohne Browser. + ===================================================================== */ + +import { readFileSync, readdirSync, existsSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const HIER = dirname(fileURLToPath(import.meta.url)); +const WURZEL = join(HIER, ".."); +const JS = join(WURZEL, "workspace", "assets", "js"); +const SEITEN = join(WURZEL, "workspace"); + +let ok = 0, fehl = 0; +const pruef = (name, bedingung, zusatz = "") => { + if (bedingung) { ok++; console.log(` OK ${name}`); } + else { fehl++; console.log(` FEHL ${name}${zusatz ? " — " + zusatz : ""}`); } +}; + +/* --------------------------------------------------------------------- + Die Tabelle aus meldung.js lesen — ohne einen Browser zu bauen. + + `meldung.js` hängt sich an `window`. Statt eine halbe Browserwelt + nachzustellen, wird der Block zwischen `var SAETZE = {` und der + schließenden Klammer ausgeschnitten und als JavaScript ausgewertet. + Das ist ehrlicher als ein zweiter Auszug der Namen: Es ist dieselbe + Quelle, kein abgeschriebener Zwilling. + ------------------------------------------------------------------- */ +function saetzeLesen() { + const quelle = readFileSync(join(JS, "meldung.js"), "utf8"); + const start = quelle.indexOf("var SAETZE = {"); + if (start < 0) return null; + const auf = quelle.indexOf("{", start); + let tiefe = 0, ende = -1; + for (let i = auf; i < quelle.length; i++) { + if (quelle[i] === "{") tiefe++; + else if (quelle[i] === "}") { tiefe--; if (tiefe === 0) { ende = i; break; } } + } + if (ende < 0) return null; + // eslint-disable-next-line no-new-func + return Function(`"use strict"; return (${quelle.slice(auf, ende + 1)});`)(); +} + +/* --------------------------------------------------------------------- + Alle Kennungen, die der Server schicken kann. + + ABGELEITET, NICHT ABGESCHRIEBEN. Gesucht wird `fehler: "…"` in allen + Server-Dateien; als Kennung gilt, was nur aus Kleinbuchstaben, + Ziffern und Unterstrichen besteht. Alles andere ist ein fertiger + Satz und braucht keine Übersetzung. + ------------------------------------------------------------------- */ +function kennungenAusServer() { + const gefunden = new Map(); + for (const datei of readdirSync(HIER)) { + if (!datei.endsWith(".js")) continue; + if (datei.startsWith("pruef-") || datei.startsWith("test-")) continue; + const text = readFileSync(join(HIER, datei), "utf8"); + for (const m of text.matchAll(/fehler:\s*"([a-z0-9_]+)"/g)) { + if (!gefunden.has(m[1])) gefunden.set(m[1], datei); + } + } + return gefunden; +} + +console.log("\n=== Meldungen: sagt das Haus ganze Sätze? ===\n"); + +const SAETZE = saetzeLesen(); +/* DER DRITTE AUSGANG. Lässt sich die Tabelle nicht lesen, ist das + weder „in Ordnung" noch „ein Satz fehlt" — es heißt, die Prüfung + kann ihre Frage nicht beantworten. Dann bricht sie ab, statt grün + zu melden. Genau dieser Fall hat am 03.09. wochenlang eine + Pflichtprüfung wertlos gemacht. */ +if (!SAETZE) { + console.log(" KONNTE NICHT NACHSEHEN — in meldung.js ist kein Block `var SAETZE = {` zu finden."); + console.log(" Grund: entweder wurde die Datei umgebaut oder sie fehlt."); + console.log(" Ohne sie lässt sich nicht sagen, ob Sätze fehlen. Abbruch.\n"); + process.exit(2); +} + +const kennungen = kennungenAusServer(); +console.log(` (${kennungen.size} Kennungen im Server, ${Object.keys(SAETZE).length} Sätze in der Tabelle)\n`); + +/* ---- 1. Vollständig ------------------------------------------------- */ +const ohneSatz = [...kennungen.keys()].filter((k) => !SAETZE[k]); +pruef("Jede Server-Kennung hat einen Satz", + kennungen.size > 0 && ohneSatz.length === 0, + ohneSatz.length ? `ohne Satz: ${ohneSatz.map((k) => `${k} (${kennungen.get(k)})`).join(", ")}` : ""); + +/* Die Zahl gehört in die BEDINGUNG, nicht nur in den Meldetext. + `every()` auf einem leeren Feld liefert `true` — eine Prüfung, die + nichts gefunden hat, wäre sonst grün (01.09.2026, Shop). */ +pruef("Es wurden überhaupt Kennungen gefunden", kennungen.size >= 30, + `nur ${kennungen.size} — findet der Suchausdruck noch, was er soll?`); + +/* ---- 2. Angeschlossen ----------------------------------------------- */ +const skripte = readdirSync(JS).filter((f) => f.endsWith(".js")); + +/* 2a: Keine Rohanzeige mehr. Gesucht wird `.fehler` in derselben Zeile + wie eine Ausgabe — aber nur dort, wo KEIN `sagWas` und kein + Vergleich (`===`) steht. Ein Vergleich übersetzt selbst. */ +const roh = []; +for (const f of skripte) { + if (f === "meldung.js") continue; + const zeilen = readFileSync(join(JS, f), "utf8").split("\n"); + zeilen.forEach((z, i) => { + if (!/\.fehler/.test(z)) return; + if (!/(textContent\s*=|melde[n]?\(|alert\(|innerHTML\s*=)/.test(z)) return; + if (/sagWas/.test(z) || /\.fehler\s*===/.test(z) || /\[\s*\w+\.fehler\s*\]/.test(z)) return; + roh.push(`${f}:${i + 1}`); + }); +} +pruef("Keine Seite zeigt eine Kennung roh an", roh.length === 0, + roh.length ? roh.join(", ") : ""); + +/* 2b: Wer sagWas benutzt, muss meldung.js laden. DAS IST DIE + WICHTIGERE HÄLFTE: Fehlt die Datei, ist `sagWas` nicht definiert, + und aus einer Fehlermeldung wird ein Absturz. */ +const benutzt = new Set(skripte.filter( + (f) => f !== "meldung.js" && /\bsagWas\(/.test(readFileSync(join(JS, f), "utf8")))); +const luecken = []; +for (const seite of readdirSync(SEITEN).filter((f) => f.endsWith(".html"))) { + const html = readFileSync(join(SEITEN, seite), "utf8"); + const geladen = [...html.matchAll(/src="assets\/js\/([a-z0-9-]+)\.js/g)].map((m) => m[1] + ".js"); + const braucht = geladen.filter((g) => benutzt.has(g)); + if (braucht.length && !geladen.includes("meldung.js")) { + luecken.push(`${seite} (braucht es wegen ${braucht.join(", ")})`); + } +} +pruef("Jede Seite, die sagWas braucht, lädt meldung.js", + benutzt.size > 0 && luecken.length === 0, + luecken.length ? luecken.join("; ") : (benutzt.size ? "" : "keine Datei benutzt sagWas — stimmt das?")); + +/* ---- 3. Lesbar ------------------------------------------------------ */ +const schlecht = []; +for (const [k, satz] of Object.entries(SAETZE)) { + if (/[a-z]+_[a-z]+/.test(satz)) schlecht.push(`${k}: enthält eine Kennung`); + else if (/\b(?:400|401|403|404|409|429|500|503)\b/.test(satz)) schlecht.push(`${k}: enthält eine Fehlernummer`); + else if (/\b(error|failed|invalid|denied|request|server error)\b/i.test(satz)) schlecht.push(`${k}: englisches Wort`); + else if (satz.length < 15) schlecht.push(`${k}: zu kurz ("${satz}")`); +} +pruef("Jeder Satz ist deutsch, ohne Kennung und ohne Zahl", + Object.keys(SAETZE).length > 0 && schlecht.length === 0, + schlecht.length ? schlecht.join("; ") : ""); + +/* ===================================================================== + DIE GEGENPROBEN + + Zu jeder Prüfung, die „alles in Ordnung" sagen kann, gehört der + Beweis, dass sie auch „nicht in Ordnung" sagen kann. Sonst weiß + niemand, ob sie überhaupt hinsieht. + ===================================================================== */ +console.log("\n --- Gegenproben (jede MUSS anschlagen) ---"); + +const g1 = (() => { const t = { ...SAETZE }; delete t.nicht_verfuegbar; + return [...kennungen.keys()].filter((k) => !t[k]).length > 0; })(); +pruef("Gegenprobe: fehlt ein Satz, schlägt Prüfung 1 an", g1); + +const g2 = (() => { + const z = " $('x').textContent = d.fehler || 'Ging nicht.';"; + return /\.fehler/.test(z) + && /(textContent\s*=|melde[n]?\(|alert\(|innerHTML\s*=)/.test(z) + && !/sagWas/.test(z) && !/\.fehler\s*===/.test(z); +})(); +pruef("Gegenprobe: eine Rohanzeige würde Prüfung 2a finden", g2); + +const g3 = (() => { + const s = "Fehler 503: request denied"; + return /[a-z]+_[a-z]+/.test(s) + || /\b(?:400|401|403|404|409|429|500|503)\b/.test(s) + || /\b(error|failed|invalid|denied|request)\b/i.test(s); +})(); +pruef("Gegenprobe: ein unlesbarer Satz würde Prüfung 3 finden", g3); + +console.log(`\n ${ok} bestanden, ${fehl} fehlgeschlagen\n`); +process.exit(fehl ? 1 : 0); diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index df57f60a..242ec87b 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -75,6 +75,8 @@

+ + diff --git a/workspace/assets/js/ampel.js b/workspace/assets/js/ampel.js index 1d060165..ba0a18a1 100644 --- a/workspace/assets/js/ampel.js +++ b/workspace/assets/js/ampel.js @@ -122,7 +122,7 @@ body: JSON.stringify({ stufe: wert, begruendung: grund }), }); if (!a.ok) { - alert((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + alert(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); k.disabled = false; return; } @@ -169,7 +169,7 @@ body: JSON.stringify({ text: feld.value }), }); if (!a.ok) { - alert((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + alert(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } const { nachrichten } = await a.json(); diff --git a/workspace/assets/js/anruf.js b/workspace/assets/js/anruf.js index 38eb819c..a548e9ba 100644 --- a/workspace/assets/js/anruf.js +++ b/workspace/assets/js/anruf.js @@ -297,7 +297,7 @@ }).catch(() => null); if (!a || !a.ok) { const d = a ? await a.json().catch(() => ({})) : {}; - lage(d.fehler || 'Der Anruf ließ sich nicht starten.'); + lage(sagWas(d.fehler, 'Der Anruf ließ sich nicht starten.')); setTimeout(() => auflegen(false), 5000); return; } @@ -315,7 +315,7 @@ }).catch(() => null); if (!a || !a.ok) { const d = a ? await a.json().catch(() => ({})) : {}; - window.anrufMeldung?.(d.fehler || 'Der Anruf ist schon vorbei.'); + window.anrufMeldung?.(sagWas(d.fehler, 'Der Anruf ist schon vorbei.')); klingelnAus(); return; } diff --git a/workspace/assets/js/aufgaben.js b/workspace/assets/js/aufgaben.js index dd7468a2..ce9a7c9a 100644 --- a/workspace/assets/js/aufgaben.js +++ b/workspace/assets/js/aufgaben.js @@ -160,7 +160,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text }), }); - if (!r.ok) { melde((await r.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!r.ok) { melde(sagWas((await r.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await laden(); } catch { /* umgeleitet */ } finally { senden.disabled = false; } }); @@ -346,7 +346,7 @@ }); if (!antwort.ok) { const d = await antwort.json().catch(() => ({})); - $('abbruch-fehler').textContent = d.fehler || 'Hat nicht geklappt.'; + $('abbruch-fehler').textContent = sagWas(d.fehler, 'Hat nicht geklappt.'); return; } dlg.close(); @@ -362,7 +362,7 @@ { method: 'POST' }); if (!antwort.ok) { const d = await antwort.json().catch(() => ({})); - melde(d.fehler || 'Hat nicht geklappt.'); + melde(sagWas(d.fehler, 'Hat nicht geklappt.')); b.disabled = false; return; } @@ -921,7 +921,7 @@ }); if (!antwort.ok) { const d = await antwort.json().catch(() => ({})); - $('neu-fehler').textContent = d.fehler || 'Anlegen hat nicht geklappt.'; + $('neu-fehler').textContent = sagWas(d.fehler, 'Anlegen hat nicht geklappt.'); return; } $('neu').reset(); @@ -988,7 +988,7 @@ body: JSON.stringify(daten), }); if (!a.ok) { - $('b-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Speichern ging nicht.'; + $('b-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Speichern ging nicht.'); return; } dialog.close(); @@ -1624,7 +1624,7 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe }); if (!a.ok) { const f = await a.json().catch(() => ({})); - melde(f.fehler || 'Das hat nicht geklappt.'); + melde(sagWas(f.fehler, 'Das hat nicht geklappt.')); knopf.disabled = false; knopf.textContent = vorher; return; } @@ -1729,7 +1729,7 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe const j = await a.json().catch(() => ({})); sagen(j.fehler === 'zu_gross' ? 'Die Datei ist zu groß.' - : (j.fehler || 'Das hat nicht geklappt.')); + : (sagWas(j.fehler, 'Das hat nicht geklappt.'))); return; } sagen('Angehängt.'); diff --git a/workspace/assets/js/automation.js b/workspace/assets/js/automation.js index c79ee5d6..50a62b3e 100644 --- a/workspace/assets/js/automation.js +++ b/workspace/assets/js/automation.js @@ -112,7 +112,7 @@ body: JSON.stringify({ an }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); $('ki-schalter').checked = !an; return; } @@ -305,7 +305,7 @@ try { const a = await hole('/workspace/api/zustand/sichern', { method: 'POST' }); const d = await a.json().catch(() => ({})); - if (!a.ok) { melde(d.fehler || 'Die Sicherung ist fehlgeschlagen.'); return; } + if (!a.ok) { melde(sagWas(d.fehler, 'Die Sicherung ist fehlgeschlagen.')); return; } melde(''); await zustand(); } catch { /* umgeleitet */ } finally { diff --git a/workspace/assets/js/bereich.js b/workspace/assets/js/bereich.js index a9509d7b..b26c445a 100644 --- a/workspace/assets/js/bereich.js +++ b/workspace/assets/js/bereich.js @@ -509,7 +509,7 @@ const a = await hole(`/workspace/api/bereich/entwicklung/${e.id}/senden`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); b.disabled = false; return; } @@ -874,7 +874,7 @@ body: JSON.stringify({ grund }), }); melde(a.ok ? 'Danke – wir sehen uns das an.' - : ((await a.json().catch(() => ({}))).fehler || 'Ging nicht.')); + : (sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'))); })); } @@ -889,8 +889,8 @@ ? { method: 'DELETE' } : { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: '{}' }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler - || ('Es hängen schon ' + lage.anschlag_max + ' Ansagen.')); + melde(sagWas((await a.json().catch(() => ({}))).fehler, + 'Es hängen schon ' + lage.anschlag_max + ' Ansagen.')); return; } await laden(); @@ -931,7 +931,7 @@ const j = await a.json().catch(() => ({})); melde(a.ok ? 'Steht jetzt bei den Talenten.' : (j.fehler === 'schon_uebernommen' ? 'Steht dort schon.' - : (j.fehler || 'Ging nicht.'))); + : (sagWas(j.fehler, 'Ging nicht.')))); })); } @@ -951,7 +951,7 @@ const a = await hole('/workspace/api/bereich/' + bereich + '/' + e.id + '?grund=' + encodeURIComponent(grund), { method: 'DELETE' }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await laden(); @@ -1031,7 +1031,7 @@ body: JSON.stringify({ text }), }); if (!r.ok) { - melde((await r.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await r.json().catch(() => ({}))).fehler, 'Ging nicht.')); ab.disabled = false; return; } feld.value = ''; @@ -1167,7 +1167,7 @@ if (!a.ok) { melde(j.fehler === 'schon_uebernommen' ? 'Daraus gibt es schon eine Aufgabe.' - : (j.fehler || 'Ging nicht.')); + : (sagWas(j.fehler, 'Ging nicht.'))); return; } melde('Steht jetzt im Aufgabenbrett – bei dir.'); @@ -1513,7 +1513,7 @@ if (!a.ok) { /* Der Grund des Servers, nicht „Ging nicht" -- er weiss, ob der Account fremd war oder die Adresse keine ist. */ - sagt.textContent = r.fehler || 'Ging nicht.'; + sagt.textContent = sagWas(r.fehler, 'Ging nicht.'); senden.disabled = false; return; } @@ -1561,7 +1561,7 @@ }); const r = await a.json().catch(() => ({})); if (!a.ok) { - melde(r.fehler || 'Ging nicht.'); + melde(sagWas(r.fehler, 'Ging nicht.')); if (hinweis) hinweis.textContent = alterText; knopf.disabled = false; return; @@ -2081,7 +2081,7 @@ body: JSON.stringify(daten), }); if (!a.ok) { - $('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } $('neu').reset(); umschalten(false); diff --git a/workspace/assets/js/bewerben.js b/workspace/assets/js/bewerben.js index bec85866..87f16055 100644 --- a/workspace/assets/js/bewerben.js +++ b/workspace/assets/js/bewerben.js @@ -246,7 +246,7 @@ }); const d = await a.json().catch(() => ({})); if (!a.ok) { - melde(d.fehler || 'Das hat nicht geklappt.'); + melde(sagWas(d.fehler, 'Das hat nicht geklappt.')); knopf.disabled = false; knopf.textContent = 'Anfrage abschicken'; return; diff --git a/workspace/assets/js/bewerbungen.js b/workspace/assets/js/bewerbungen.js index 7e808884..d8711db3 100644 --- a/workspace/assets/js/bewerbungen.js +++ b/workspace/assets/js/bewerbungen.js @@ -152,7 +152,7 @@ }); if (!a2.ok) { const d = await a2.json().catch(() => ({})); - melde(d.fehler || 'Ging nicht.'); + melde(sagWas(d.fehler, 'Ging nicht.')); knopf.disabled = false; return; } @@ -268,7 +268,7 @@ }); const d = await a.json().catch(() => ({})); if (!a.ok) { - melde(d.fehler || 'Das hat nicht geklappt.'); + melde(sagWas(d.fehler, 'Das hat nicht geklappt.')); senden.disabled = false; senden.textContent = gewaehlt.name + ' – übernehmen'; return; diff --git a/workspace/assets/js/calls.js b/workspace/assets/js/calls.js index 1c2607d0..ad719794 100644 --- a/workspace/assets/js/calls.js +++ b/workspace/assets/js/calls.js @@ -227,7 +227,7 @@ }), }); if (!a.ok) { - meldung.textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + meldung.textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } const r = await a.json(); @@ -375,7 +375,7 @@ + 'Bereits daraus entstandene Aufgaben bleiben ebenfalls stehen.')) return; const a = await hole('/workspace/api/calls/' + c.id + '/protokoll', { method: 'DELETE' }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Löschen ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Löschen ging nicht.')); return; } offen.delete(c.id); @@ -763,7 +763,7 @@ body: JSON.stringify({ link: $('call-raum-feld').value.trim() }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Speichern ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Speichern ging nicht.')); return; } $('call-raum-form').hidden = true; diff --git a/workspace/assets/js/chat.js b/workspace/assets/js/chat.js index 25d500da..5a2dbfeb 100644 --- a/workspace/assets/js/chat.js +++ b/workspace/assets/js/chat.js @@ -674,7 +674,7 @@ body: JSON.stringify({ an }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } /* Neu holen statt hier nachzurechnen: Der Server kennt die Grenze @@ -746,7 +746,7 @@ if (!a.ok) { /* DIE ABSAGE STEHT IN DER TAFEL, nicht irgendwo anders: Wer gerade Favoriten festlegt, schaut genau dorthin. */ - if (h) h.textContent = d.fehler || 'Ging nicht.'; + if (h) h.textContent = sagWas(d.fehler, 'Ging nicht.'); return; } auswahlDaten.favoriten = d.favoriten; @@ -1391,7 +1391,7 @@ }); if (!a.ok) { const d = await a.json().catch(() => ({})); - markiereAlsGescheitert(vorlaeufig.id, d.fehler || 'Nicht angekommen.'); + markiereAlsGescheitert(vorlaeufig.id, sagWas(d.fehler, 'Nicht angekommen.')); return; } const { nachricht } = await a.json(); @@ -1749,7 +1749,7 @@ body: JSON.stringify({ mit: ids }), }); if (!a.ok) { - $('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } const raumId = kanalUmbau; @@ -1765,7 +1765,7 @@ body: JSON.stringify({ kategorie, mit: ids }), }); if (!a.ok) { - $('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } const raumId = (await a.json()).raum_id; @@ -1787,7 +1787,7 @@ body: JSON.stringify({ mit: ids[0] }), }); if (!a.ok) { - $('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } raumId = (await a.json()).raum_id; @@ -1803,7 +1803,7 @@ body: JSON.stringify({ name, mit: ids }), }); if (!a.ok) { - $('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } raumId = (await a.json()).raum_id; diff --git a/workspace/assets/js/checkliste.js b/workspace/assets/js/checkliste.js index 7bedb51a..60cc5598 100644 --- a/workspace/assets/js/checkliste.js +++ b/workspace/assets/js/checkliste.js @@ -436,7 +436,7 @@ creator_id: daten.creator_id }), }); if (!a.ok) { - alert((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + alert(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); b.disabled = false; return; } @@ -503,7 +503,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: feld.value, creator_id: daten.creator_id }), }); - if (!a.ok) { alert((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!a.ok) { alert(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } const r = await a.json(); feld.value = ''; zeichne(verlauf, r.nachrichten, n.ich); diff --git a/workspace/assets/js/content.js b/workspace/assets/js/content.js index 37563535..301c5af1 100644 --- a/workspace/assets/js/content.js +++ b/workspace/assets/js/content.js @@ -244,7 +244,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(daten), }); - if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } melde(''); await laden(); } @@ -256,7 +256,7 @@ body: JSON.stringify({ ziel }), }); const d = await a.json().catch(() => ({})); - if (!a.ok) { melde(d.fehler || 'Ging nicht.'); return; } + if (!a.ok) { melde(sagWas(d.fehler, 'Ging nicht.')); return; } melde(''); /* Sagen, wohin es gewandert ist -- und den Weg dorthin anbieten. Eine stille Uebernahme laesst einen ratlos zurueck. */ @@ -510,7 +510,7 @@ body: JSON.stringify(daten), }); if (!a.ok) { - $('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } $('neu').reset(); umschalten(false); @@ -532,7 +532,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(daten), }); - if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } melde(''); $('saeule-neu').reset(); await laden(); diff --git a/workspace/assets/js/dateien.js b/workspace/assets/js/dateien.js index 7ecc77cb..59433697 100644 --- a/workspace/assets/js/dateien.js +++ b/workspace/assets/js/dateien.js @@ -122,7 +122,7 @@ const a = await hole('/workspace/api/dateien', { method: 'POST', headers: kopf, body: datei }); if (!a.ok) { const d = await a.json().catch(() => ({})); - melde(d.fehler || 'Hochladen hat nicht geklappt.'); + melde(sagWas(d.fehler, 'Hochladen hat nicht geklappt.')); return; } /* Auswahl nach dem Hochladen loeschen -- sonst bekaeme die @@ -179,7 +179,7 @@ body: JSON.stringify({ status: neu }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ändern hat nicht geklappt.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ändern hat nicht geklappt.')); return; } await laden(); @@ -330,7 +330,7 @@ body: JSON.stringify({ freigaben: ids }), }); speichern.disabled = false; - if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await laden(); }); const abbrechen = el('button', 'knopf-still', 'Abbrechen'); diff --git a/workspace/assets/js/entwicklung.js b/workspace/assets/js/entwicklung.js index 2f637b37..c713a11b 100644 --- a/workspace/assets/js/entwicklung.js +++ b/workspace/assets/js/entwicklung.js @@ -597,7 +597,7 @@ }); if (!a.ok) { const j = await a.json().catch(() => ({})); - melde(j.fehler || 'Ging nicht.'); + melde(sagWas(j.fehler, 'Ging nicht.')); feld.focus(); return; } diff --git a/workspace/assets/js/kalender.js b/workspace/assets/js/kalender.js index 73a74209..d2fb6da3 100644 --- a/workspace/assets/js/kalender.js +++ b/workspace/assets/js/kalender.js @@ -1195,7 +1195,7 @@ }); if (!a.ok) { const f = await a.json().catch(() => ({})); - melde(f.fehler || 'Das hat nicht geklappt.'); + melde(sagWas(f.fehler, 'Das hat nicht geklappt.')); return; } const d = await a.json().catch(() => ({})); @@ -1896,7 +1896,7 @@ body: JSON.stringify(koerper), }); if (!a.ok) { - $('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } $('neu').reset(); diff --git a/workspace/assets/js/ki.js b/workspace/assets/js/ki.js index 2add25e6..0392071e 100644 --- a/workspace/assets/js/ki.js +++ b/workspace/assets/js/ki.js @@ -39,7 +39,7 @@ window.KI = (() => { }); if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); } const d = await a.json().catch(() => ({})); - if (!a.ok) throw new Error(d.fehler || 'Die KI konnte nicht antworten.'); + if (!a.ok) throw new Error(sagWas(d.fehler, 'Die KI konnte nicht antworten.')); return d; } diff --git a/workspace/assets/js/leistung.js b/workspace/assets/js/leistung.js index 6abcbe9e..6517e302 100644 --- a/workspace/assets/js/leistung.js +++ b/workspace/assets/js/leistung.js @@ -394,7 +394,7 @@ }), }); if (!a.ok) { - $('tag-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('tag-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } d.close(); @@ -506,7 +506,7 @@ }); const d = await a.json().catch(() => ({})); if (!a.ok) { - $('import-fehler').textContent = d.fehler || 'Die Excel-Datei ließ sich nicht lesen.'; + $('import-fehler').textContent = sagWas(d.fehler, 'Die Excel-Datei ließ sich nicht lesen.'); $('datei').value = ''; return; } @@ -555,7 +555,7 @@ }); const v = await a.json(); if (!a.ok) { - $('import-fehler').textContent = v.fehler || 'Die Datei konnte nicht gelesen werden.'; + $('import-fehler').textContent = sagWas(v.fehler, 'Die Datei konnte nicht gelesen werden.'); return; } importSpalten = v.spalten; @@ -621,7 +621,7 @@ body: JSON.stringify({ text: importText, spalten: importSpalten }), }); const r = await a.json(); - if (!a.ok) { $('import-fehler').textContent = r.fehler || 'Ging nicht.'; return; } + if (!a.ok) { $('import-fehler').textContent = sagWas(r.fehler, 'Ging nicht.'); return; } $('import-dialog').close(); $('datei').value = ''; importText = ''; @@ -882,7 +882,7 @@ if (!a.ok) { $('netz-vorschau').hidden = true; $('netz-los').disabled = true; - $('netz-fehler').textContent = d.fehler || 'Die Tabelle konnte nicht gelesen werden.'; + $('netz-fehler').textContent = sagWas(d.fehler, 'Die Tabelle konnte nicht gelesen werden.'); /* Fehlt nur der Tag, ist das kein Fehler, sondern eine Frage -- also wird das Feld hervorgehoben statt nur gemeckert. */ if (d.braucht_tag) $('netz-tag').focus(); @@ -960,7 +960,7 @@ if (!a) { $('netz-los').disabled = false; return; } const d = await a.json().catch(() => ({})); if (!a.ok) { - $('netz-fehler').textContent = d.fehler || 'Übernehmen ging nicht.'; + $('netz-fehler').textContent = sagWas(d.fehler, 'Übernehmen ging nicht.'); $('netz-los').disabled = false; return; } @@ -1016,7 +1016,7 @@ body: await datei.arrayBuffer(), }); const d = await a.json().catch(() => ({})); - if (!a.ok) { $('netz-fehler').textContent = d.fehler || 'Ging nicht.'; return; } + if (!a.ok) { $('netz-fehler').textContent = sagWas(d.fehler, 'Ging nicht.'); return; } text = d.text; ausExcel = d; } else if (roh[0] === 0xd0 && roh[1] === 0xcf) { diff --git a/workspace/assets/js/meldung.js b/workspace/assets/js/meldung.js new file mode 100644 index 00000000..eb06ce4c --- /dev/null +++ b/workspace/assets/js/meldung.js @@ -0,0 +1,182 @@ +/* ===================================================================== + WAS DER SERVER SAGT, IN SÄTZEN — 19.09.2026 + + Filipe: „keine kryptischen oder technischen Fehlermeldungen, sondern + klare Aussagen darüber, was passiert ist und wie man das Problem + lösen kann." + + --------------------------------------------------------------------- + DAS PROBLEM, NACHGEMESSEN + + Der Server antwortet im Fehlerfall mit `{ fehler: "…" }`. Darin + stehen ZWEI verschiedene Dinge, und sie sehen von außen gleich aus: + + 1. MASCHINENKENNUNGEN — `nicht_verfuegbar`, `nicht_gefunden`, + `ungueltig`. Sie sind fürs Protokoll gedacht, nicht für Augen. + Gezählt am 19.09.2026: 43 verschiedene, an 1 300 Stellen. + 2. FERTIGE SÄTZE — z. B. „Woran hast du das gemerkt? Ein Satz + genügt – ohne ihn ist es beim Gespräch nur noch ein Gefühl." + Die sind genau richtig so und sollen unverändert durch. + + An 75 Stellen in der Oberfläche stand `textContent = d.fehler` — + ungefiltert. Wer beim Hochladen einer Datei Pech hatte, las also + wörtlich `nicht_verfuegbar` auf dem Bildschirm. Ein Mensch ohne + Vorwissen weiß dann nicht einmal, ob er selbst etwas falsch gemacht + hat. + + --------------------------------------------------------------------- + DREI AUSGÄNGE, NICHT ZWEI + + Das ist dieselbe Regel wie überall im Haus — eine Auskunft, die ihre + Frage nicht beantworten kann, muss das SAGEN und darf nicht schweigen: + + BEKANNTE KENNUNG → ihr Satz aus der Tabelle unten. + UNBEKANNTE KENNUNG → der Ersatzsatz des Aufrufers. Sie wird NIE + angezeigt. Eine neue Kennung, die noch + niemand übersetzt hat, ist ein Fehler im + Haus — nicht etwas, das der Benutzer + ausbaden soll. + FERTIGER SATZ → unverändert durch. + + Woran „Kennung" erkannt wird: nur Kleinbuchstaben, Ziffern und + Unterstriche, kein Leerzeichen. Ein deutscher Satz hat immer + Leerzeichen und Großbuchstaben; eine Kennung nie. Die Unterscheidung + ist damit nicht geraten, sondern entschieden. + + --------------------------------------------------------------------- + UND WARUM DIE TABELLE NICHT ALTERN KANN + + Eine von Hand gepflegte Liste ist im Haus schon zweimal teuer + geworden (die abgeschriebene Spaltenliste, die feste Umbruchschwelle). + Deshalb steht daneben `pruef-meldungen.mjs`: Sie liest ALLE + Kennungen aus dem Server und wird rot, sobald eine hier fehlt. + Wer morgen eine neue erfindet, kann sie nicht vergessen. + ===================================================================== */ +(function () { + 'use strict'; + + /* Zu jeder Kennung ein Satz, der zwei Dinge tut: sagen, WAS ist — + und, wo es etwas zu tun gibt, was der nächste Schritt ist. + + KEIN „Fehler 400". Keine Zahl, kein englisches Wort, keine + Entschuldigung ohne Inhalt. */ + var SAETZE = { + /* ---- Die drei häufigsten ------------------------------------- */ + nicht_verfuegbar: + 'Der Server antwortet gerade nicht. Nichts wurde gespeichert – ' + + 'versuch es in einer Minute noch einmal.', + nicht_gefunden: + 'Das gibt es nicht mehr. Wahrscheinlich hat es jemand gelöscht – ' + + 'lade die Seite neu.', + ungueltig: + 'Mit der Eingabe stimmt etwas nicht. Sieh die Felder noch einmal durch.', + + /* ---- Anmeldung und Zugang ------------------------------------ */ + nicht_angemeldet: + 'Du bist nicht mehr angemeldet. Melde dich noch einmal an.', + zu_viele_versuche: + 'Zu viele Versuche hintereinander. Warte einen Moment, dann geht es wieder.', + /* Nicht „etwas Älteres ist offen" — es geht um die Bestätigung des + eigenen ALTERS beim ersten Anmelden. Beim ersten Hinsehen hatte + ich es falsch verstanden; der Blick in workspace.js:4864 hat es + geklärt. */ + alter_offen: + 'Bitte bestätige zuerst dein Alter, dann geht es weiter.', + eigener_code: + 'Das ist dein eigener Zugang – den kannst du hier nicht neu vergeben.', + fremde_herkunft: + 'Das wurde aus Sicherheitsgründen abgelehnt. Lade die Seite neu ' + + 'und versuch es noch einmal.', + + /* ---- Darf ich das? ------------------------------------------- */ + nicht_erlaubt: 'Das darfst du hier nicht ändern.', + nur_leitung: 'Das kann nur DogFather oder die rechte Hand.', + nur_gruender: 'Das kann nur, wer das Gespräch angelegt hat.', + hier_nicht: 'An dieser Stelle geht das nicht – abgestimmt wird auf der Wunschliste.', + gesperrt: 'Dafür ist die Abstimmung zu.', + geschlossen: 'Der Fall ist abgeschlossen. Hier kann nichts mehr dazukommen.', + nicht_dran: 'Du bist gerade nicht dran – warte auf die Antwort.', + leitung_meldet_nicht: + 'Diesen Weg gibt es für das Team. Du liest die Meldungen, du schreibst sie nicht.', + umgezogen: + 'Diese Seite ist umgezogen. Lade sie neu, dann bist du am richtigen Ort.', + + /* ---- Da fehlt oder stimmt etwas ------------------------------ */ + unvollstaendig: 'Da fehlt noch etwas. Die offenen Stellen sind markiert.', + leer: 'Da steht noch nichts drin.', + betreff_fehlt: 'Der Betreff fehlt noch.', + text_zu_kurz: 'Der Text ist zu kurz – ein ganzer Satz genügt.', + zu_lang: 'Das ist zu lang. Kürze es etwas.', + unbrauchbar: 'Damit kann der Server nichts anfangen. Prüf die Eingabe.', + unbekannte_stufe: + 'Diese Auswahl kennt der Server nicht. Lade die Seite neu.', + unbekanntes_zeichen: + 'Dieses Zeichen gibt es nicht. Lade die Seite neu.', + keine_datumsspalte: + 'In der Datei fehlt eine Spalte mit dem Datum. Ohne sie lässt sich nichts zuordnen.', + keine_zuordnung: + 'Die Zeilen lassen sich niemandem zuordnen. Prüf die Überschriften in der Datei.', + + /* ---- Gab es schon / gibt es nicht ---------------------------- */ + schon_da: 'Das gibt es schon.', + schon_uebernommen: 'Das hat schon jemand übernommen.', + schon_entschieden: 'Darüber wurde bereits entschieden.', + zurueckgenommen: 'Die Nachricht wurde zurückgenommen.', + nichts_zu_aendern: 'Es gab nichts zu ändern – es steht schon so da.', + nichts_zu_speichern:'Es gab nichts zu speichern.', + eintrag_nicht_gefunden: 'Den Eintrag gibt es nicht mehr. Lade die Seite neu.', + aufgabe_nicht_gefunden: 'Die Aufgabe gibt es nicht mehr. Lade die Seite neu.', + keine_fassung: 'Dazu gibt es noch keine gespeicherte Fassung.', + keine_daten: 'Dazu gibt es noch keine Daten.', + kein_protokoll: 'Dazu gibt es kein Protokoll.', + kein_block: 'Diesen Bereich gibt es nicht mehr. Lade die Seite neu.', + kein_hakt: + 'Daraus lässt sich kein Schritt machen – dafür müsste der Punkt auf ' + + '„Da hakt es" stehen.', + + /* ---- Erst das andere zu Ende -------------------------------- */ + uebergang_offen: + 'Es läuft schon ein Übergang. Schließ den erst ab.', + zu_viele_offen: + 'Du hast schon mehrere Meldungen offen. Warte, bis eine davon beantwortet ist.', + nur_gruppe_oder_kanal: + 'Das geht nur in einer Gruppe oder einem Kanal, nicht im Zweiergespräch.' + }; + + /* Sieht das wie eine Maschinenkennung aus? + Nur Kleinbuchstaben, Ziffern, Unterstriche — und kein Leerzeichen. + Ein Satz aus dem Server hat immer beides nicht. */ + function istKennung(t) { + return typeof t === 'string' && t.length > 0 && /^[a-z0-9_]+$/.test(t); + } + + /** Aus dem, was der Server schickt, einen Satz machen. + * + * @param {string|undefined} fehler was in `{ fehler: … }` stand + * @param {string} [ersatz] Satz für den Fall, dass nichts + * passt. Er soll zur Stelle + * passen („Anlegen hat nicht + * geklappt."), nicht allgemein + * sein. + * @returns {string} immer ein lesbarer Satz, nie eine Kennung. + */ + window.sagWas = function (fehler, ersatz) { + var standard = ersatz || 'Das hat nicht geklappt. Versuch es noch einmal.'; + if (!fehler || typeof fehler !== 'string') return standard; + if (SAETZE[fehler]) return SAETZE[fehler]; + /* UNBEKANNTE KENNUNG: der Ersatzsatz, niemals die Kennung selbst. + Sie taucht dafür in der Konsole auf — damit sie jemandem + auffällt, der sie beheben kann, statt dem, der sie nicht + versteht. */ + if (istKennung(fehler)) { + try { console.warn('[meldung] Kennung ohne Satz:', fehler); } catch (e) { /* egal */ } + return standard; + } + /* Ein fertiger Satz aus dem Server — unverändert durch. */ + return fehler; + }; + + /* Für die Prüfung, damit sie gegen DIESE Tabelle vergleicht und + nicht gegen eine abgeschriebene zweite. */ + window.sagWas.SAETZE = SAETZE; +})(); diff --git a/workspace/assets/js/personen.js b/workspace/assets/js/personen.js index 66358b5b..b03a7749 100644 --- a/workspace/assets/js/personen.js +++ b/workspace/assets/js/personen.js @@ -310,7 +310,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rolle: gewaehlt }), }); - if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } $('rolle-kasten').hidden = true; rolleFuer = null; await laden(); @@ -432,7 +432,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ betreuer_id: w.value || null }), }); - if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await laden(); } catch { /* umgeleitet */ } finally { w.disabled = false; } }); @@ -494,7 +494,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ manager_id: w2.value || null }), }); - if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await laden(); } catch { /* umgeleitet */ } finally { w2.disabled = false; } }); @@ -561,7 +561,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ aktiv: !p.aktiv }), }); - if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; } + if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await laden(); })); @@ -697,7 +697,7 @@ body: JSON.stringify({ name: eingabe.trim() }), }); const d = await a.json().catch(() => ({})); - if (!a.ok) { melde(d.fehler || 'Löschen hat nicht geklappt.'); return; } + if (!a.ok) { melde(sagWas(d.fehler, 'Löschen hat nicht geklappt.')); return; } /* Die Sicherung nennen. Wer gerade etwas Unumkehrbares getan hat, soll sofort wissen, woraus es sich zurückholen lässt. */ melde(`„${v.person.name}" wurde gelöscht. Sicherung davor: ${d.sicherung}`); @@ -1068,7 +1068,7 @@ : { name: $('f-name').value }), }); if (!a.ok) { - $('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } $('neu').reset(); umschalten(false); diff --git a/workspace/assets/js/profil.js b/workspace/assets/js/profil.js index 1d3dad2f..b2a7f5da 100644 --- a/workspace/assets/js/profil.js +++ b/workspace/assets/js/profil.js @@ -88,7 +88,7 @@ body: JSON.stringify(daten), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Speichern ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Speichern ging nicht.')); $('stand').textContent = ''; return; } diff --git a/workspace/assets/js/rechte.js b/workspace/assets/js/rechte.js index 86974074..e1862f05 100644 --- a/workspace/assets/js/rechte.js +++ b/workspace/assets/js/rechte.js @@ -121,7 +121,7 @@ body: JSON.stringify({ pfad: seite.pfad, rolle: rolle.schluessel, erlaubt }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } melde(`Gespeichert: „${rolle.name}" darf ${seite.datei} ` diff --git a/workspace/assets/js/report.js b/workspace/assets/js/report.js index df36548d..b4a1add6 100644 --- a/workspace/assets/js/report.js +++ b/workspace/assets/js/report.js @@ -321,7 +321,7 @@ }), }); if (!a.ok) { - $('e-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('e-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } $('entscheidung').reset(); diff --git a/workspace/assets/js/schulung.js b/workspace/assets/js/schulung.js index 232291d2..e42281e4 100644 --- a/workspace/assets/js/schulung.js +++ b/workspace/assets/js/schulung.js @@ -179,7 +179,7 @@ }); if (!a.ok) { const f = await a.json().catch(() => ({})); - alert(f.fehler || 'Ging nicht.'); + alert(sagWas(f.fehler, 'Ging nicht.')); b.disabled = false; b.textContent = 'Durchgegangen und bestätigt'; return; @@ -256,7 +256,7 @@ body: JSON.stringify({ titel: iTitel.value, wissen_id: Number(sDok.value) || null }), }); if (!a.ok) { - fehler.textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + fehler.textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } window.schulungBlock(behaelter); diff --git a/workspace/assets/js/scouting.js b/workspace/assets/js/scouting.js index 2f52b186..5251b70c 100644 --- a/workspace/assets/js/scouting.js +++ b/workspace/assets/js/scouting.js @@ -77,7 +77,7 @@ body: körper ? JSON.stringify(körper) : undefined, }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.')); return null; } melde(''); @@ -522,7 +522,7 @@ knopf.disabled = false; if (!a) return; if (!a.ok) { - $('n-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.'; + $('n-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'); return; } $('neu-zu').click(); diff --git a/workspace/assets/js/startcheck.js b/workspace/assets/js/startcheck.js index 0c3d76f8..a337e5ea 100644 --- a/workspace/assets/js/startcheck.js +++ b/workspace/assets/js/startcheck.js @@ -46,7 +46,7 @@ body: JSON.stringify({ feld, punkt, ...aenderung }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Konnte nicht gespeichert werden.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Konnte nicht gespeichert werden.')); return false; } melde(''); @@ -182,7 +182,7 @@ }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } const r = await a.json(); diff --git a/workspace/assets/js/steckbrief.js b/workspace/assets/js/steckbrief.js index 4bee61f8..6660186f 100644 --- a/workspace/assets/js/steckbrief.js +++ b/workspace/assets/js/steckbrief.js @@ -142,7 +142,7 @@ body: datei, }); const antwort = await a.json().catch(() => ({})); - if (!a.ok) { melde(antwort.fehler || 'Das Hochladen ging nicht.'); sage(''); return; } + if (!a.ok) { melde(sagWas(antwort.fehler, 'Das Hochladen ging nicht.')); sage(''); return; } bildZeigen(antwort.bild, stand?.name); sage('Bild gespeichert.'); /* Auch oben rechts in der Kopfleiste soll sofort das neue Bild @@ -187,7 +187,7 @@ body: JSON.stringify(koerper), }); const antwort = await a.json().catch(() => ({})); - if (!a.ok) { melde(antwort.fehler || 'Speichern ging nicht.'); return; } + if (!a.ok) { melde(sagWas(antwort.fehler, 'Speichern ging nicht.')); return; } /* Neu zeichnen, damit die "öffnen"-Verweise zu den frisch eingetragenen Namen sofort erscheinen. */ zeichne(antwort.steckbrief, KANAELE); diff --git a/workspace/assets/js/talente.js b/workspace/assets/js/talente.js index d5068dc1..3ebac53c 100644 --- a/workspace/assets/js/talente.js +++ b/workspace/assets/js/talente.js @@ -974,7 +974,7 @@ { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: '{}' }); if (!r.ok) { const d = await r.json().catch(() => ({})); - melde(d.fehler || absage(d)); + melde(sagWas(d.fehler, absage(d))); knopf.disabled = false; return; } diff --git a/workspace/assets/js/teamlage.js b/workspace/assets/js/teamlage.js index 82930991..c0a550eb 100644 --- a/workspace/assets/js/teamlage.js +++ b/workspace/assets/js/teamlage.js @@ -401,7 +401,7 @@ }), }); if (!a.ok) { - melden((await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.'); + melden(sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.')); ja.disabled = false; return; } melden(''); @@ -433,7 +433,7 @@ }), }); if (!a.ok) { - melden((await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.'); + melden(sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.')); nein.disabled = false; return; } melden(''); @@ -465,7 +465,7 @@ body: JSON.stringify(koerper), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.')); knopf.disabled = false; return; } const d = await a.json().catch(() => ({})); diff --git a/workspace/assets/js/teilen.js b/workspace/assets/js/teilen.js index 6443d11a..351dda39 100644 --- a/workspace/assets/js/teilen.js +++ b/workspace/assets/js/teilen.js @@ -115,7 +115,7 @@ }); const daten = await a.json().catch(() => null); if (!a.ok) { - melde(daten?.fehler || 'Das hat nicht geklappt.'); + melde(sagWas(daten?.fehler, 'Das hat nicht geklappt.')); knopf.disabled = false; knopf.textContent = alterText; return; diff --git a/workspace/assets/js/treff-moderation.js b/workspace/assets/js/treff-moderation.js index e85a9f04..6b4525fb 100644 --- a/workspace/assets/js/treff-moderation.js +++ b/workspace/assets/js/treff-moderation.js @@ -191,7 +191,7 @@ body: JSON.stringify({ status, entscheid }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await meldungen(); @@ -295,7 +295,7 @@ body: JSON.stringify({ person_id: m.id, art, grund, tage }), }); if (!a.ok) { - melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await mitglieder(); @@ -327,7 +327,7 @@ if (!confirm(`Maßnahme gegen ${m.person_name} aufheben?`)) return; const r = await hole('/workspace/api/treff/massnahme/' + m.id, { method: 'DELETE' }); if (!r.ok) { - melde((await r.json().catch(() => ({}))).fehler || 'Ging nicht.'); + melde(sagWas((await r.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; } await massnahmen(); diff --git a/workspace/assets/js/uebersicht.js b/workspace/assets/js/uebersicht.js index 1a3ecc53..a0b9cbb6 100644 --- a/workspace/assets/js/uebersicht.js +++ b/workspace/assets/js/uebersicht.js @@ -393,7 +393,7 @@ }); const antwort = await a.json().catch(() => ({})); if (!a.ok) { - f('cn-fehler').textContent = antwort.fehler || 'Ging nicht.'; + f('cn-fehler').textContent = sagWas(antwort.fehler, 'Ging nicht.'); return; } f('cn-code-name').textContent = antwort.name; diff --git a/workspace/assets/js/vorlagen.js b/workspace/assets/js/vorlagen.js index 2335b704..44edf97b 100644 --- a/workspace/assets/js/vorlagen.js +++ b/workspace/assets/js/vorlagen.js @@ -122,7 +122,7 @@ }); if (!a.ok) { const f = await a.json().catch(() => ({})); - nehmen.textContent = f.fehler || 'ging nicht'; + nehmen.textContent = sagWas(f.fehler, 'ging nicht'); nehmen.disabled = false; return; } diff --git a/workspace/assets/js/wissen.js b/workspace/assets/js/wissen.js index 329e9b1f..dd5e6035 100644 --- a/workspace/assets/js/wissen.js +++ b/workspace/assets/js/wissen.js @@ -601,7 +601,7 @@ a = await hole('/workspace/api/wissen', { method: 'POST', headers: kopf, body: datei }); } if (!a.ok) { - $('n-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.'; + $('n-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.'); return; } const kat = aufbau.kategorien.find((k) => k.schluessel === angaben.kategorie); diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 0ac0e2bd..81c23e95 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -291,6 +291,8 @@ + + diff --git a/workspace/automation.html b/workspace/automation.html index 7c4829db..7183a86e 100644 --- a/workspace/automation.html +++ b/workspace/automation.html @@ -168,6 +168,8 @@ + + diff --git a/workspace/befinden.html b/workspace/befinden.html index 2818d638..0d25fd8a 100644 --- a/workspace/befinden.html +++ b/workspace/befinden.html @@ -105,6 +105,8 @@ + + diff --git a/workspace/bereich.html b/workspace/bereich.html index 3f5a4ef2..d013229f 100644 --- a/workspace/bereich.html +++ b/workspace/bereich.html @@ -251,6 +251,8 @@ + + diff --git a/workspace/bewerben.html b/workspace/bewerben.html index 86a8d3c6..ed7531e6 100644 --- a/workspace/bewerben.html +++ b/workspace/bewerben.html @@ -108,6 +108,7 @@ + diff --git a/workspace/bewerbungen.html b/workspace/bewerbungen.html index 4f28ce1c..a0a57547 100644 --- a/workspace/bewerbungen.html +++ b/workspace/bewerbungen.html @@ -80,6 +80,8 @@ + + diff --git a/workspace/calls.html b/workspace/calls.html index 5320b7be..a5265770 100644 --- a/workspace/calls.html +++ b/workspace/calls.html @@ -89,6 +89,8 @@ + + diff --git a/workspace/chat.html b/workspace/chat.html index b72e58c0..ac127f92 100644 --- a/workspace/chat.html +++ b/workspace/chat.html @@ -368,6 +368,7 @@ + diff --git a/workspace/content.html b/workspace/content.html index 1c1f8205..e7091f74 100644 --- a/workspace/content.html +++ b/workspace/content.html @@ -179,6 +179,8 @@ + + diff --git a/workspace/crew-index.html b/workspace/crew-index.html index 007541c3..40c9ad52 100644 --- a/workspace/crew-index.html +++ b/workspace/crew-index.html @@ -339,6 +339,8 @@ + + diff --git a/workspace/dateien.html b/workspace/dateien.html index 8253c72c..62157fd0 100644 --- a/workspace/dateien.html +++ b/workspace/dateien.html @@ -108,6 +108,8 @@ + + diff --git a/workspace/entwicklung.html b/workspace/entwicklung.html index 2f7a545c..048287a9 100644 --- a/workspace/entwicklung.html +++ b/workspace/entwicklung.html @@ -106,6 +106,8 @@ + + diff --git a/workspace/hilfe.html b/workspace/hilfe.html index aad7545d..fa8b0335 100644 --- a/workspace/hilfe.html +++ b/workspace/hilfe.html @@ -141,6 +141,8 @@ + + diff --git a/workspace/index.html b/workspace/index.html index c81077b2..1b763eb2 100644 --- a/workspace/index.html +++ b/workspace/index.html @@ -401,6 +401,8 @@ + + diff --git a/workspace/kalender.html b/workspace/kalender.html index ecee74df..82f86ee5 100644 --- a/workspace/kalender.html +++ b/workspace/kalender.html @@ -377,6 +377,8 @@ + + diff --git a/workspace/leistung.html b/workspace/leistung.html index 7927635c..1ea3394a 100644 --- a/workspace/leistung.html +++ b/workspace/leistung.html @@ -444,6 +444,7 @@ + diff --git a/workspace/personen.html b/workspace/personen.html index 090c818f..d2af199f 100644 --- a/workspace/personen.html +++ b/workspace/personen.html @@ -423,6 +423,8 @@ + + diff --git a/workspace/profil.html b/workspace/profil.html index 0826eae0..d3e2ac46 100644 --- a/workspace/profil.html +++ b/workspace/profil.html @@ -214,6 +214,8 @@ + + diff --git a/workspace/rechte.html b/workspace/rechte.html index 4ef50d8b..11d3fb8f 100644 --- a/workspace/rechte.html +++ b/workspace/rechte.html @@ -134,6 +134,7 @@ Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen, die seit dem 07.09. jede Seite danach absucht. --> + diff --git a/workspace/report.html b/workspace/report.html index 116442db..84d710da 100644 --- a/workspace/report.html +++ b/workspace/report.html @@ -106,6 +106,8 @@ + + diff --git a/workspace/scouting.html b/workspace/scouting.html index a5289a18..809a6ca1 100644 --- a/workspace/scouting.html +++ b/workspace/scouting.html @@ -173,6 +173,8 @@ + + diff --git a/workspace/start.html b/workspace/start.html index eca56a99..3bc0ad67 100644 --- a/workspace/start.html +++ b/workspace/start.html @@ -678,6 +678,8 @@

+ + diff --git a/workspace/startcheck.html b/workspace/startcheck.html index 31426011..24cc37fa 100644 --- a/workspace/startcheck.html +++ b/workspace/startcheck.html @@ -85,6 +85,8 @@ + + diff --git a/workspace/steckbrief.html b/workspace/steckbrief.html index ed4bf99a..c2c428e6 100644 --- a/workspace/steckbrief.html +++ b/workspace/steckbrief.html @@ -150,6 +150,8 @@ + + diff --git a/workspace/talente.html b/workspace/talente.html index 685e223a..ccc401e3 100644 --- a/workspace/talente.html +++ b/workspace/talente.html @@ -111,6 +111,8 @@ + + diff --git a/workspace/team.html b/workspace/team.html index 92c3ca2c..07d45aa9 100644 --- a/workspace/team.html +++ b/workspace/team.html @@ -110,6 +110,8 @@ + + diff --git a/workspace/teamlage.html b/workspace/teamlage.html index 8021c04b..af94cfdd 100644 --- a/workspace/teamlage.html +++ b/workspace/teamlage.html @@ -84,6 +84,7 @@ Kaputt ist dabei nichts -- er funktioniert. Genau deshalb faellt es niemandem auf, und deshalb prueft pruef-css-klassen die ABHAENGIGKEIT statt der Optik. Hier hat sie angeschlagen. --> + diff --git a/workspace/teilen.html b/workspace/teilen.html index 5ce56e1f..cee18af2 100644 --- a/workspace/teilen.html +++ b/workspace/teilen.html @@ -112,6 +112,8 @@ + + diff --git a/workspace/treff-moderation.html b/workspace/treff-moderation.html index 5caaf9a7..76adfac6 100644 --- a/workspace/treff-moderation.html +++ b/workspace/treff-moderation.html @@ -126,6 +126,8 @@ + + diff --git a/workspace/treff-regeln.html b/workspace/treff-regeln.html index 2b168860..512a9f17 100644 --- a/workspace/treff-regeln.html +++ b/workspace/treff-regeln.html @@ -221,6 +221,7 @@ Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen, die seit dem 07.09. jede Seite danach absucht. --> + diff --git a/workspace/uebersicht.html b/workspace/uebersicht.html index 37d3392c..6de17391 100644 --- a/workspace/uebersicht.html +++ b/workspace/uebersicht.html @@ -149,6 +149,8 @@ + + diff --git a/workspace/unsere-seiten.html b/workspace/unsere-seiten.html index 98c3867e..bedb9ad1 100644 --- a/workspace/unsere-seiten.html +++ b/workspace/unsere-seiten.html @@ -217,6 +217,8 @@ + + diff --git a/workspace/werdegang.html b/workspace/werdegang.html index 399c1eb7..309855c7 100644 --- a/workspace/werdegang.html +++ b/workspace/werdegang.html @@ -131,6 +131,8 @@ + + diff --git a/workspace/wissen.html b/workspace/wissen.html index 78d5318d..8bdde20a 100644 --- a/workspace/wissen.html +++ b/workspace/wissen.html @@ -190,6 +190,8 @@ + +