Files
dogfather-universe/workspace/assets/js/unterstuetzen.js
T
DogFatherGitandClaude Opus 5 aa2e1a4ae2 Eine Kachel "Unterstuetzen" -- und die Gespraechsliste neu gebaut
=== 1. UNTERSTUETZEN (neue Kachel im Community-Bereich) ===

Filipe: "ich brauch auch noch eine kachel im community bereich. wo mein
paypal und meine amazon liste sein wird. wo die leute alle supporten
koennen auf andere art anstatt nur tiktok. jeder soll diese kachel sehen
aber nur dogfather soll sie veraendern koennen oder vieles mehr sehen."

Neu: unterstuetzen.html + css + js, server/workspace-unterstuetzung.js,
server/unterstuetzung-tabellen.js. Ton 45 (#7567fe) ist gerechnet, das
Kachelzeichen ist ein Herz ueber zwei offenen Haenden.

DIE WEGE STEHEN IN DER DATENBANK, nicht im Quelltext -- ein Recht, das
man nur ueber einen Entwickler ausueben kann, ist keines. DogFather
schreibt Titel, Text, Knopf und Adresse selbst, blendet Wege aus und
nimmt neue dazu.

DER PAYPAL-LINK STEHT LEER UND UNSICHTBAR DA. Filipe schrieb "mein
paypal kennst du ja schon" -- gesucht im ganzen Haus und im Vault,
nirgends gefunden. Eine Zahlungsadresse zu RATEN waere der
gefaehrlichste Fehler dieser Seite: Geld an einen Fremden, und niemand
merkt es. Also steht dort nichts, der Weg ist ausgeblendet, und die
Seite sagt genau EINER Person, dass er fehlt.

DREI ARTEN, UND DIE DRITTE IST DIE WICHTIGSTE: geld, geschenk, frei.
"Kostet nichts" bekommt dieselbe Kartenform und dieselbe Groesse.
Waeren die freien Wege kleiner oder weiter unten, waere die Aussage
"das ist zweite Wahl" -- und die Mehrheit derer, die hier lesen, waere
damit zweite Wahl.

DIE ZAEHLUNG IST ANONYM, UND ZWAR VON DER TABELLE HER. DogFather sieht,
wie oft ein Weg geoeffnet wurde (7/30 Tage/gesamt). Was er NICHT sieht,
ist WER -- weil es in unterstuetzung_striche keine Spalte dafuer gibt.
Geprueft wird das ueber PRAGMA table_info, nicht ueber eine Abfrage:
eine Abfrage liesse sich morgen erweitern, eine fehlende Spalte nicht.

Schreiben haengt an istDogFather, nicht an istLeitung -- die rechte Hand
fuehrt dieses Team mit und kommt trotzdem nicht an diesen Link. Jede
Adresse wird beim SCHREIBEN geprueft (nur https:// oder eine Seite
dieses Hauses); javascript:, data:, http:// und // werden abgewiesen.

Neu: server/pruef-unterstuetzung.mjs -- 70 Pruefungen, alle gruen.
Sie misst ueber die CREW-ADRESSE: Beim ersten Lauf waren vier Rollen
gruen und der Zuschauer rot, und es sah nach einem Rechtefehler aus.
Es war ein Messfehler -- ueber 127.0.0.1 landet man still im
Agenturhaus, und dort gibt es die Rolle "gast" gar nicht.

=== 2. DIE GESPRAECHSLISTE (screen1 + screen2) ===

Filipe: "ich will dass die komplette kachel viel krasser und geiler
aussieht. der hintergrund der kachel soll gleich bleiben."

Der Hintergrund ist unangetastet. Zwei echte Fehler auf seinem Bild:

  - Bei "Das Rudel" stand das "@" rechts oben und die orange "6" eine
    ZEILE TIEFER. Der Knopf ist ein Raster mit DREI Spalten und bekam
    VIER Kinder -- das vierte fiel um. Ausgerechnet die wichtigste
    Auskunft der Liste landete an der unauffaelligsten Stelle.
  - Jedes Gespraech mit Profilbild haengte das Bild ZWEIMAL ein: ein
    Block stand Zeichen fuer Zeichen doppelt da. Zu sehen war nichts,
    gekostet hat es die doppelte Ladelast bei jedem Neuzeichnen.

Und eine tote Regel: `.chat-raum__knopf[data-an="ja"]` beschrieb die
Schiene am offenen Gespraech -- gesetzt wird aber `data-offen` am <li>.
Die Regel griff nie, und daneben stand eine zweite, blassere Fassung
derselben Sache. Jetzt steht alles einmal, und die Schiene ist da.

Dazu: Zeilen als Karten, 44px-Gesichter, ein Zaehler neben "Gespraeche",
Suchfeld als Pille mit Lupe, runder Farbfleck statt Quadrat, und der
leere Raum rechts bekommt eine Mitte statt eines Satzes in der Ecke.

=== 3. VIER FUNDE NEBENBEI ===

  - supportAufraeumen() war exportiert und wurde NIRGENDS gerufen. Die
    Meldungen samt Bildschirmfotos waeren fuer immer liegen geblieben,
    obwohl "90 Tage" dokumentiert ist. Jetzt im Loeschkonzept.
  - aufgaben_zuteilung fehlte ebenfalls im Loeschkonzept (seit 21.09.).
    pruef-aufbewahrung ist damit wieder gruen.
  - pruef-start-ansicht war rot, seit "Aufgaben" am 23.09. die silberne
    Kachel bekam -- die ueberschreibt ihr --ton absichtlich. Die
    Pruefung nimmt sie jetzt aus UND prueft die Ausnahme selbst.
  - pruef-buehne meldete auf entwicklung.html 1,79:1 Kontrast bei "Wen
    gehst du durch?" -- die Ueberschrift lag direkt auf dem Foto. Sie
    steht jetzt auf einer deckenden Flaeche.

OFFEN: pruef-buehne meldet treff-regeln.html mal "0 von 40", mal "3 von
40" -- dieselbe Seite, verschiedene Antworten. Eine Pruefung, die
schwankt, ist schlimmer als eine rote. Nicht in diesem Zug behoben.

Gemessen: pruef-unterstuetzung 70, pruef-start-ansicht, pruef-buehne
(bis auf den Wackler), pruef-workspace-seiten, pruef-aufbewahrung 45,
pruef-rechtetafel 19, pruef-css-klassen, pruef-chatkachel 36,
pruef-chat-ausbau, pruef-entwicklung-kacheln 31 -- gruen.

Neu: server/mess-chat-liste.mjs (zeigt die Liste mit sieben Gespraechen
verschiedener Art, prueft nichts).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-24 14:14:25 +02:00

477 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =====================================================================
UNTERSTÜTZEN (24.09.2026)
=====================================================================
Filipe: „jeder soll diese kachel sehen aber nur dogfather soll sie
verändern können oder vieles mehr sehen. alle anderen, rechte und
linke hand, modis und community sollen supporten können."
---------------------------------------------------------------------
DREI DINGE, DIE HIER ANDERS SIND ALS AUF DEN ÜBRIGEN SEITEN
1. DIE OBERFLÄCHE ENTSCHEIDET NICHTS. `darf_aendern` kommt vom
Server und wird hier nur GELESEN. Wer das Feld im Browser auf
„true" setzt, bekommt trotzdem 404 zurück — die Regel steht in
workspace-unterstuetzung.js. Ein verstecktes Feld ist eine
Bitte, keine Sperre.
2. DER ZÄHLER DARF DEN WEG NIE AUFHALTEN. Ein Klick auf „Bei PayPal
öffnen" muss PayPal öffnen, auch wenn der Server gerade nicht
antwortet. Deshalb `sendBeacon`: Er schickt den Strich los und
wartet auf nichts. Ein `await fetch()` vor der Weiterleitung
wäre ein Zähler, der im Zweifel eine Zahlung verhindert.
3. EINE ADRESSE WIRD NICHT AUS TEXT ZUSAMMENGESETZT. Das Ziel geht
über `href` in ein `<a>`, nie über `innerHTML` — und jeder Weg
nach draußen bekommt `rel="noopener noreferrer"`. Ohne
`noopener` kann die geöffnete Seite über `window.opener` auf
diese hier zurückgreifen.
===================================================================== */
(() => {
const $ = (id) => document.getElementById(id);
const el = (t, k, text) => {
const n = document.createElement(t);
if (k) n.className = k;
if (text !== undefined) n.textContent = text;
return n;
};
const hole = (pfad, optionen) => window.holen(pfad, optionen);
const melde = (text, gut) => {
const f = $('fehler');
if (!f) return;
f.textContent = text || '';
f.hidden = !text;
f.dataset.gut = gut ? 'ja' : 'nein';
};
let stand = null; // die Antwort des Servers
let aendern = false; // steht der Änderungsmodus an?
/* =====================================================================
DIE ZEICHEN
Eines je Weg, mit einem Rückfall je Art. Ein Weg, den DogFather
neu anlegt, hat keinen eigenen Schlüssel in dieser Liste — und
bekommt trotzdem ein Zeichen, statt ein Loch zu lassen.
`voll` markiert die Teile, die Fläche sind statt Linie. Auf 24
Pixeln verschwindet eine reine Strichzeichnung, und ein ganz
gefülltes Zeichen sieht aus wie ein Klecks — dieselbe Aufteilung
wie bei den Kachelzeichen in bereiche.js.
===================================================================== */
const ZEICHEN = {
paypal: [
['M3.6 8.2h16.8a1.4 1.4 0 0 1 1.4 1.4v8.2a1.4 1.4 0 0 1-1.4 1.4H3.6a1.4 1.4 0 0 1-1.4-1.4V9.6a1.4 1.4 0 0 1 1.4-1.4z'],
['M2.2 12.4h19.6'],
['M5.6 8.2 7.2 5a1.4 1.4 0 0 1 1.9-.6l6.9 3.8', 'voll-nein'],
],
amazon: [
['M4 11.2h16v7.2a1.4 1.4 0 0 1-1.4 1.4H5.4A1.4 1.4 0 0 1 4 18.4z'],
['M3 8.1h18v3.1H3z'],
['M12 8.1v11.7'],
['M12 8.1S10.9 4.2 8.8 4.2a2 2 0 0 0 0 3.9z'],
['M12 8.1s1.1-3.9 3.2-3.9a2 2 0 0 1 0 3.9z'],
],
dabei: [
['M9.2 11.4a3.1 3.1 0 1 0 0-6.2 3.1 3.1 0 0 0 0 6.2z'],
['M2.8 19.6c0-3.3 2.9-5.6 6.4-5.6s6.4 2.3 6.4 5.6'],
['M16.4 6a3 3 0 0 1 0 5.6'],
['M17.6 14.6c2.2.6 3.6 2.4 3.6 5'],
],
weitersagen: [
['M17.8 8.4a2.6 2.6 0 1 0 0-5.2 2.6 2.6 0 0 0 0 5.2z'],
['M5.6 14.6a2.6 2.6 0 1 0 0-5.2 2.6 2.6 0 0 0 0 5.2z'],
['M17.8 20.8a2.6 2.6 0 1 0 0-5.2 2.6 2.6 0 0 0 0 5.2z'],
['M7.9 10.7 15.5 7', 'M7.9 13.3l7.6 3.7'],
],
draussen: [
['M11 4.8H5.4A1.4 1.4 0 0 0 4 6.2v12.4a1.4 1.4 0 0 0 1.4 1.4h12.4a1.4 1.4 0 0 0 1.4-1.4V13'],
['M13.8 4H20v6.2'],
['M19.4 4.6 12 12'],
],
/* Rückfall je Art. */
geld: [['M3.6 8.2h16.8a1.4 1.4 0 0 1 1.4 1.4v8.2a1.4 1.4 0 0 1-1.4 1.4H3.6a1.4 1.4 0 0 1-1.4-1.4V9.6a1.4 1.4 0 0 1 1.4-1.4z'], ['M2.2 12.4h19.6']],
geschenk: [['M4 11.2h16v7.2a1.4 1.4 0 0 1-1.4 1.4H5.4A1.4 1.4 0 0 1 4 18.4z'], ['M3 8.1h18v3.1H3z'], ['M12 8.1v11.7']],
frei: [['M12 20.2 4.4 12.6a4.6 4.6 0 0 1 6.3-6.7l1.3 1.2 1.3-1.2a4.6 4.6 0 0 1 6.3 6.7z']],
};
function zeichenBauen(w) {
const pfade = ZEICHEN[w.schluessel] || ZEICHEN[w.art] || ZEICHEN.frei;
const NS = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(NS, 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('aria-hidden', 'true');
for (const gruppe of pfade) {
for (const d of gruppe) {
if (!d.startsWith('M')) continue;
const p = document.createElementNS(NS, 'path');
p.setAttribute('d', d);
svg.append(p);
}
}
return svg;
}
const PFEIL_RAUS = ['M13.8 4H20v6.2', 'M19.4 4.6 12 12',
'M11 4.8H5.4A1.4 1.4 0 0 0 4 6.2v12.4a1.4 1.4 0 0 0 1.4 1.4h12.4a1.4 1.4 0 0 0 1.4-1.4V13'];
function pfeilBauen() {
const NS = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(NS, 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('aria-hidden', 'true');
for (const d of PFEIL_RAUS) {
const p = document.createElementNS(NS, 'path');
p.setAttribute('d', d);
svg.append(p);
}
return svg;
}
/* =====================================================================
EINE KARTE
===================================================================== */
/** Führt die Adresse aus dem Haus hinaus?
*
* Entscheidet über `target` und `rel` — und darüber, ob überhaupt
* ein `rel` nötig ist. Eine Seite dieses Hauses öffnet im selben
* Fenster: Ein neuer Tab für „Draußen" wäre ein Fenster mehr, das
* niemand zumacht. */
const nachDraussen = (ziel) => /^https:\/\//i.test(ziel);
function karte(w) {
const k = el('article', 'u-weg');
k.dataset.art = w.art;
k.dataset.schluessel = w.schluessel;
if (!w.sichtbar) k.dataset.aus = 'ja';
const kopf = el('div', 'u-weg__kopf');
const zeichen = el('span', 'u-weg__zeichen');
zeichen.setAttribute('aria-hidden', 'true');
zeichen.append(zeichenBauen(w));
kopf.append(zeichen);
const namen = el('div', 'u-weg__namen');
namen.append(el('h3', 'u-weg__titel', w.titel));
namen.append(el('span', 'u-weg__marke', stand.arten[w.art] || w.art));
kopf.append(namen);
k.append(kopf);
if (w.text) k.append(el('p', 'u-weg__text', w.text));
if (aendern && stand.darf_aendern) {
k.append(formular(w));
return k;
}
/* NUR EIN KNOPF, WENN ES EINEN WEG GIBT. „Einfach da sein" hat
keinen — und ein Knopf, der nichts tut, lässt die Seite kaputt
aussehen statt den Weg selbsterklärend. */
if (w.ziel && w.knopf) {
const a = el('a', 'u-weg__knopf');
a.href = w.ziel;
a.append(document.createTextNode(w.knopf));
if (nachDraussen(w.ziel)) {
a.target = '_blank';
/* `noopener` ist die Sicherung: Ohne ihn kann die geöffnete
Seite über `window.opener` auf diese hier zugreifen. */
a.rel = 'noopener noreferrer';
a.append(pfeilBauen());
}
a.addEventListener('click', () => strich(w.id));
k.append(a);
}
/* WAS NUR DOGFATHER SIEHT. Ohne Änderungsmodus, damit er im Alltag
dieselbe Seite sieht wie alle anderen — und nur dann mehr, wenn
er danach fragt. */
if (stand.darf_aendern) {
if (!w.sichtbar) k.append(el('span', 'u-weg__aus', 'steht für niemanden da'));
const z = stand.zahlen?.[w.id];
k.append(el('span', 'u-weg__zahl', z
? `${z.gesamt}× geöffnet · ${z.tage30} in 30 Tagen · ${z.tage7} in 7 Tagen`
: 'noch nicht geöffnet'));
}
return k;
}
/* =====================================================================
DER STRICH
Kein Name, keine Nummer — der Server zählt Weg und Tag (siehe
unterstuetzung-tabellen.js).
`sendBeacon` und nicht `fetch`: Der Browser folgt dem Link im
selben Augenblick, und eine laufende Anfrage würde dabei
abgebrochen. Ein Strich, der nur gezählt wird, wenn die Seite noch
offen ist, zählt ausgerechnet die erfolgreichen Klicks nicht.
===================================================================== */
function strich(id) {
const weg = `/workspace/api/unterstuetzung/${id}/strich`;
try {
if (navigator.sendBeacon && navigator.sendBeacon(weg, new Blob())) return;
} catch { /* dann eben der Rückfall darunter */ }
/* RÜCKFALL, und er darf ebenfalls nichts aufhalten: `keepalive`
lässt die Anfrage weiterlaufen, während die Seite wechselt. */
try {
fetch(weg, { method: 'POST', credentials: 'same-origin', keepalive: true })
.catch(() => {});
} catch { /* der Weg nach draußen zählt, nicht der Strich */ }
}
/* =====================================================================
DAS FORMULAR — nur im Änderungsmodus
===================================================================== */
function feld(art, name, wert, hinweis) {
const l = el('label');
l.append(el('span', null, hinweis));
const i = document.createElement(art === 'lang' ? 'textarea' : 'input');
if (art !== 'lang') i.type = 'text';
i.value = wert ?? '';
i.dataset.feld = name;
l.append(i);
return l;
}
function formular(w) {
const f = el('div', 'u-form');
f.append(feld('kurz', 'titel', w.titel, 'Name'));
f.append(feld('lang', 'text', w.text, 'Was dahintersteht'));
const reihe = el('div', 'u-form__reihe');
const artFeld = el('label');
artFeld.append(el('span', null, 'Art'));
const auswahl = document.createElement('select');
auswahl.dataset.feld = 'art';
for (const [wert, name] of Object.entries(stand.arten)) {
const o = document.createElement('option');
o.value = wert; o.textContent = name;
if (wert === w.art) o.selected = true;
auswahl.append(o);
}
artFeld.append(auswahl);
reihe.append(artFeld);
const an = el('label', 'u-form__an');
const haken = document.createElement('input');
haken.type = 'checkbox';
haken.dataset.feld = 'sichtbar';
haken.checked = !!w.sichtbar;
an.append(haken, document.createTextNode('steht für alle da'));
reihe.append(an);
f.append(reihe);
f.append(feld('kurz', 'knopf', w.knopf, 'Aufschrift des Knopfes (leer = kein Knopf)'));
const zielFeld = feld('kurz', 'ziel', w.ziel, 'Adresse (https://… oder seite.html)');
f.append(zielFeld);
const knoepfe = el('div', 'u-form__knoepfe');
const sichern = el('button', 'knopf knopf--klein', 'Speichern');
sichern.type = 'button';
sichern.addEventListener('click', () => sichere(w.id, f));
knoepfe.append(sichern);
const weg = el('button', 'abmelden u-form__weg', 'Entfernen');
weg.type = 'button';
weg.addEventListener('click', () => entferne(w));
knoepfe.append(weg);
f.append(knoepfe);
return f;
}
const lies = (f, name) => {
const e = f.querySelector(`[data-feld="${name}"]`);
if (!e) return undefined;
return e.type === 'checkbox' ? e.checked : e.value;
};
async function sichere(id, f) {
melde('');
const koerper = {
titel: lies(f, 'titel'), text: lies(f, 'text'),
knopf: lies(f, 'knopf'), ziel: lies(f, 'ziel'),
art: lies(f, 'art'), sichtbar: lies(f, 'sichtbar'),
};
try {
const a = await hole(`/workspace/api/unterstuetzung/${id}`, {
method: 'PUT', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(koerper),
});
if (!a.ok) {
const r = await a.json().catch(() => ({}));
melde(r.fehler || 'Das ließ sich nicht speichern.');
return;
}
melde('Gespeichert.', true);
await laden();
} catch { melde('Das ließ sich nicht speichern.'); }
}
async function entferne(w) {
/* GEFRAGT WIRD ÜBER DIE HAUS-NACHFRAGE und nicht über `confirm`.
Sie sieht aus wie der Rest des Hauses und lässt sich mit der
Tastatur bedienen — `confirm` sperrt die ganze Seite. */
const ja = window.bestaetige
? await window.bestaetige({
titel: `„${w.titel}" entfernen?`,
text: 'Der Weg verschwindet für alle, und die Striche der letzten '
+ 'Monate gehen mit. Das lässt sich nicht zurückholen.',
ja: 'Entfernen', nein: 'Behalten',
})
: true;
if (!ja) return;
try {
const a = await hole(`/workspace/api/unterstuetzung/${w.id}`, { method: 'DELETE' });
if (!a.ok) { melde('Das ließ sich nicht entfernen.'); return; }
melde('Entfernt.', true);
await laden();
} catch { melde('Das ließ sich nicht entfernen.'); }
}
async function neuerWeg() {
melde('');
try {
const a = await hole('/workspace/api/unterstuetzung', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ titel: 'Neuer Weg', art: 'frei', text: '' }),
});
if (!a.ok) {
const r = await a.json().catch(() => ({}));
melde(r.fehler || 'Das ließ sich nicht anlegen.');
return;
}
await laden();
} catch { melde('Das ließ sich nicht anlegen.'); }
}
/* =====================================================================
ZEICHNEN
===================================================================== */
function gruppe(titel, satz, wege) {
if (!wege.length) return null;
const s = el('section', 'u-gruppe');
const kopf = el('div', 'u-gruppe__kopf');
kopf.append(el('h2', 'u-gruppe__titel', titel));
if (satz) kopf.append(el('p', 'u-gruppe__satz', satz));
s.append(kopf);
const gitter = el('div', 'u-gitter');
for (const w of wege) gitter.append(karte(w));
s.append(gitter);
return s;
}
function zeichnen() {
const ziel = $('wege');
ziel.textContent = '';
const geben = stand.wege.filter((w) => w.art !== 'frei');
const frei = stand.wege.filter((w) => w.art === 'frei');
/* ZWEI GRUPPEN, UND DIE ZWEITE IST NICHT DIE ABTEILUNG FÜR ARME.
Sie trägt dieselbe Kartenform und dieselbe Größe; nur die
Überschrift sortiert. Ohne Überschriften stünden fünf Karten
nebeneinander und man müsste jede lesen, um zu wissen, was
Geld kostet. */
const a = gruppe('Direkt etwas geben',
'Geht an Dogi selbst – ohne Umweg über TikTok.', geben);
const b = gruppe('Kostet nichts',
'Genauso viel wert. Wirklich.', frei);
if (a) ziel.append(a);
if (b) ziel.append(b);
if (aendern && stand.darf_aendern) {
const neu = el('button', 'u-neu');
neu.type = 'button';
neu.append(el('span', 'u-neu__plus', '+'));
neu.append(el('span', null, 'Noch einen Weg dazunehmen'));
neu.addEventListener('click', neuerWeg);
const gitter = el('div', 'u-gitter');
gitter.append(neu);
const s = el('section', 'u-gruppe');
s.append(gitter);
ziel.append(s);
}
if (!stand.wege.length) {
ziel.append(el('p', 'u-laedt', stand.darf_aendern
? 'Noch kein Weg eingetragen. Leg oben einen an.'
: 'Hier steht gerade nichts. Schau später noch einmal vorbei.'));
}
}
function leitungZeichnen() {
const block = $('leitung');
if (!stand.darf_aendern) { block.hidden = true; return; }
block.hidden = false;
/* DER HINWEIS AUF DEN FEHLENDEN PAYPAL-LINK.
Er steht hier und nicht als Platzhalter in der Datenbank: Eine
geratene Zahlungsadresse wäre der gefährlichste Fehler dieser
ganzen Seite — Geld an einen Fremden, und niemand merkt es.
Also steht dort nichts, der Weg ist unsichtbar, und genau EINE
Person erfährt, dass er fehlt. */
const ohne = stand.wege.filter((w) => w.knopf === '' && w.ziel === '' && w.art !== 'frei');
const unsichtbar = stand.wege.filter((w) => !w.sichtbar);
const satz = $('leitung-satz');
if (ohne.length || unsichtbar.length) {
const namen = [...new Set([...ohne, ...unsichtbar].map((w) => w.titel))].join(', ');
satz.textContent = `Noch ohne Adresse und deshalb für niemanden sichtbar: ${namen}. `
+ 'Drück auf „Ändern", trag die Adresse ein und setz den Haken bei '
+ '„steht für alle da".';
satz.dataset.warnung = 'ja';
} else {
satz.textContent = 'Du kannst hier jeden Weg umschreiben, ausblenden oder '
+ 'einen neuen dazunehmen. Alle anderen sehen nur, was sichtbar ist.';
delete satz.dataset.warnung;
}
/* DIE SUMME ÜBER ALLE WEGE. Eine Zahl je Karte steht unten an der
Karte; hier oben steht, ob die Seite überhaupt benutzt wird. */
const kasten = $('zahlen');
kasten.textContent = '';
const z = Object.values(stand.zahlen || {});
if (!z.length) { kasten.hidden = true; return; }
kasten.hidden = false;
const summe = (feld) => z.reduce((n, x) => n + (x[feld] || 0), 0);
for (const [wert, text] of [
[summe('tage7'), 'geöffnet in 7 Tagen'],
[summe('tage30'), 'in 30 Tagen'],
[summe('gesamt'), 'insgesamt'],
]) {
const kachel = el('div', 'u-zahl');
kachel.append(el('span', 'u-zahl__n', String(wert)));
kachel.append(el('span', 'u-zahl__text', text));
kasten.append(kachel);
}
/* KEIN NAME, NIRGENDS. Die Tabelle hat gar keine Spalte dafür —
das steht so im Schema und ist nicht nur eine Abfrage, die
jemand später ändern könnte. */
}
async function laden() {
try {
const a = await hole('/workspace/api/unterstuetzung');
if (!a.ok) { melde('Die Seite ließ sich nicht laden.'); return; }
stand = await a.json();
$('laedt')?.remove();
leitungZeichnen();
zeichnen();
} catch {
window.ladefehler?.();
}
}
$('aendern-an')?.addEventListener('click', () => {
aendern = !aendern;
$('aendern-an').textContent = aendern ? 'Fertig' : 'Ändern';
melde('');
zeichnen();
});
laden();
})();