Filipe, 22.09.2026: "ersetze die zentrale durch, Die IrrenAnstalt.
genau so geschrieben bitte. und alles was treff heißt oder wo treff
steht soll durch Rudel ersetzt werden bitte."
Die Schreibweise "IrrenAnstalt" mit grossem A in der Mitte ist so
gewollt. Das steht als Hinweis daneben, damit sie beim naechsten Mal
niemand "korrigiert".
WAS UMBENANNT WURDE: alles, was jemand LIEST -- Kachelnamen,
Ueberschriften, Markenzeilen, Saetze, Meldungen, Aufgabenvorlagen.
116 Zeilen in 42 Dateien.
WAS BLEIBT: Adressen (treff-regeln.html), Bezeichner (TREFF_ROLLEN),
Datenbankwerte (bereich = 'treff'), Abfrageteile (b=treff). Eine
Adresse umzubenennen bricht jedes Lesezeichen, und ein Datenbankwert
umzuschreiben waere eine Umstellung ohne Gegenwert. Dieselbe
Entscheidung wie heute frueh bei Dogi-Media, wo material.html auch
material.html geblieben ist.
DREI GRAMMATIKFEHLER IM EIGENEN ENTWURF, alle vor dem Ausliefern
gefunden -- ein blindes Ersetzen reicht hier nicht:
1. "der Treff" ist maennlich, "das Rudel" saechlich. Ohne Tabelle
waere ueberall "Der Rudel" gestanden. (Und "Treffer" waere zu
"Rudeler" geworden, "Treffen" zu "Rudelen" -- deshalb greift die
Regel nur bei Wortgrenze und nie vor einem Kleinbuchstaben.)
2. BINDESTRICH-ZUSAMMENSETZUNGEN. In "der Treff-Chat" gehoert der
Artikel zu "Chat", nicht zu "Treff". Der erste Durchlauf machte
daraus "das Rudel-Chat", "das Rudel-Kacheln" und "ein Rudel-Raum".
Jetzt greift die Artikelregel nur, wenn das Wort allein steht.
3. GESCHUETZTE LEERZEICHEN. In den Markenzeilen steht
`Der Treff`, damit die zwei Woerter nicht umbrechen. Das
Muster hat daran vorbeigegriffen: "Der Rudel", auf fuenf
Seiten. Jetzt wird das Trennzeichen mitgefasst und unveraendert
wieder eingesetzt.
Gefunden wurden alle drei, weil jede einzelne der 116 Zeilen vor dem
Schreiben als ALT/NEU ausgegeben und gelesen wurde -- und danach
gezielt nach falschen Artikeln gesucht ("den Rudel", "der Rudel",
"einen Rudel"). Uebrig blieben sechs Treffer, und die sind alle
richtig: "einen Rudel-Raum", "der Rudel-Chat", "den Rudel-Regeln" --
Zusammensetzungen, bei denen der Artikel zum letzten Wort gehoert.
UND ZWEI PRUEFUNGEN, die die Umbenennung nicht mitbekommen haetten:
`/Treff/.test(...)` und `/Treff/i.test(...)`. Ein regulaerer Ausdruck
ist keine Zeichenkette -- sie haetten ab sofort nach einem Wort
gesucht, das die Seite nicht mehr sagt, und waeren still rot geworden,
ohne dass etwas kaputt ist.
Geprueft, alle gruen: pruef-treff, pruef-treffchat,
pruef-deutsche-texte, pruef-uebernahme, pruef-crew-adresse,
pruef-willkommen, pruef-wege-nach-draussen, pruef-vorlagen,
pruef-start-ansicht, pruef-struktur.
Co-Authored-By: Claude Opus 5 <[email protected]>
287 lines
11 KiB
JavaScript
287 lines
11 KiB
JavaScript
/* =====================================================================
|
||
DIE ANFRAGE AUS DER COMMUNITY -- der Fragebogen (17.09.2026)
|
||
|
||
Filipe: „ich will dass die leute sich da anonym bei mir melden können
|
||
und eine anfrage machen können ... wie ein fragebogen wieso die
|
||
person es werden will warum sie sollte, positiv negative sachen."
|
||
|
||
(Sein Satz nennt die Rolle beim Namen -- hier steht sie nicht: Diese
|
||
Datei ist ohne Anmeldung aus dem offenen Netz lesbar.)
|
||
|
||
DIE FRAGEN STEHEN NICHT HIER. Sie kommen vom Server, mit ihren
|
||
Gruppen, ihren Pflichtmarken und den Längengrenzen. Eine zweite Liste
|
||
im Browser wäre die, die beim nächsten Nachschärfen auseinanderläuft
|
||
-- und dann verlangt das Formular etwas, das der Server nicht prüft,
|
||
oder umgekehrt. Genau dieser Fehler hat heute Mittag die Rolle
|
||
„Community" zwei Stunden lang unanlegbar gemacht.
|
||
|
||
DER ENTWURF BLEIBT IM BROWSER, NICHT AUF DEM SERVER.
|
||
|
||
Fünfzehn Fragen sind nichts, was man zwischen zwei Streams tippt. Wer
|
||
aufhören und später weitermachen will, muss das können -- sonst wird
|
||
die Anfrage nie fertig, und das ist dieselbe Wirkung wie eine Absage.
|
||
Der Entwurf liegt deshalb auf dem Gerät.
|
||
|
||
Und er liegt AUSDRÜCKLICH NICHT auf dem Server: Ein halb ausgefülltes
|
||
Formular dort wäre bereits eine Anfrage, die niemand gestellt hat --
|
||
lesbar für DogFather, bevor die Person entschieden hat, ob sie sie
|
||
überhaupt schicken will. Beim Abschicken wird der Entwurf gelöscht.
|
||
===================================================================== */
|
||
(() => {
|
||
const $ = (id) => document.getElementById(id);
|
||
const ENTWURF = 'dogi-bewerbung-entwurf';
|
||
|
||
const melde = (t) => {
|
||
const f = $('fehler');
|
||
if (f) { f.textContent = t || ''; f.hidden = !t; }
|
||
};
|
||
|
||
const el = (art, klasse, text) => {
|
||
const k = document.createElement(art);
|
||
if (klasse) k.className = klasse;
|
||
if (text != null) k.textContent = text;
|
||
return k;
|
||
};
|
||
|
||
/* Der Entwurf. Ein kaputter Eintrag darf die Seite nicht aufhalten --
|
||
dann lieber ohne Entwurf anfangen als gar nicht. */
|
||
const entwurfLesen = () => {
|
||
try { return JSON.parse(localStorage.getItem(ENTWURF) || '{}') || {}; }
|
||
catch { return {}; }
|
||
};
|
||
const entwurfSchreiben = (w) => {
|
||
try { localStorage.setItem(ENTWURF, JSON.stringify(w)); } catch { /* voll oder gesperrt */ }
|
||
};
|
||
|
||
const ZUSTAND_WORT = {
|
||
neu: 'Deine Anfrage liegt vor.',
|
||
gespraech: 'DogFather möchte mit dir reden.',
|
||
warteschlange: 'Du stehst auf der Warteliste.',
|
||
angenommen: 'Du bist dabei.',
|
||
abgelehnt: 'Diesmal nicht.',
|
||
};
|
||
|
||
(async () => {
|
||
let r;
|
||
try {
|
||
const a = await fetch('/workspace/api/bewerbung', { credentials: 'same-origin' });
|
||
if (a.status === 401) { location.assign('/workspace/'); return; }
|
||
if (!a.ok) { melde('Der Fragebogen ist gerade nicht erreichbar.'); return; }
|
||
r = await a.json();
|
||
} catch {
|
||
melde('Der Fragebogen ist gerade nicht erreichbar.');
|
||
return;
|
||
}
|
||
$('ladezeile').hidden = true;
|
||
|
||
/* DAS WORT KOMMT VOM SERVER. Bleibt es aus, steht „Anfrage
|
||
stellen" da -- eine ehrliche Ueberschrift, die nichts Falsches
|
||
behauptet. Ein Platzhalter („…") waere hier schlechter: Er sieht
|
||
kaputt aus, obwohl die Seite vollstaendig arbeitet. */
|
||
if (r.rolle_name) {
|
||
const t = $('titel');
|
||
if (t) t.textContent = r.rolle_name + ' werden';
|
||
const m = $('marke-was');
|
||
if (m) m.textContent = r.rolle_name + ' werden';
|
||
document.title = r.rolle_name + ' werden · Das Rudel';
|
||
}
|
||
|
||
/* ---------------------------------------------------------------
|
||
1. WAS SCHON IST -- Stand oder Sperrfrist
|
||
--------------------------------------------------------------- */
|
||
const stand = $('stand');
|
||
let zeigeFormular = r.darf;
|
||
|
||
if (r.meine) {
|
||
stand.hidden = false;
|
||
stand.append(el('p', 'bw-stand__wort',
|
||
ZUSTAND_WORT[r.meine.zustand] || 'Deine Anfrage liegt vor.'));
|
||
if (r.meine.grund) {
|
||
/* DER SATZ AUS DER ENTSCHEIDUNG GEHÖRT DER PERSON. Er wird beim
|
||
Ablehnen verlangt, und zwar genau dafür -- nicht für die
|
||
Akte. Ihn hier wegzulassen hieße, ihn umsonst verlangt zu
|
||
haben. */
|
||
stand.append(el('p', 'bw-stand__grund', r.meine.grund));
|
||
}
|
||
if (r.offen) {
|
||
stand.append(el('p', 'leise',
|
||
'Solange sie offen ist, kannst du keine zweite schicken. '
|
||
+ 'Du hörst von uns – das kann ein paar Tage dauern.'));
|
||
zeigeFormular = false;
|
||
}
|
||
}
|
||
|
||
if (r.sperre) {
|
||
stand.hidden = false;
|
||
stand.append(el('p', 'bw-stand__sperre',
|
||
'In ' + r.sperre.tage + ' Tagen kannst du es wieder versuchen.'));
|
||
stand.append(el('p', 'leise',
|
||
'Eine Absage heißt hier „noch nicht“, nicht „nie“. '
|
||
+ 'Bis dahin: mitreden, Wünsche einstellen, dabei sein.'));
|
||
zeigeFormular = false;
|
||
}
|
||
|
||
if (!r.darf) {
|
||
stand.hidden = false;
|
||
stand.append(el('p', 'leise',
|
||
'Diese Seite ist für die Community. Du bist schon im Team.'));
|
||
}
|
||
|
||
if (!zeigeFormular) return;
|
||
|
||
/* ---------------------------------------------------------------
|
||
2. DER BOGEN
|
||
--------------------------------------------------------------- */
|
||
const bogen = $('bogen');
|
||
const ziel = $('gruppen');
|
||
const werte = entwurfLesen();
|
||
const felder = new Map();
|
||
|
||
for (const g of r.gruppen) {
|
||
const fragen = r.fragen.filter((f) => f.gruppe === g.schluessel);
|
||
if (!fragen.length) continue;
|
||
|
||
const block = el('section', 'bw-gruppe');
|
||
const kopf = el('div', 'bw-gruppe__kopf');
|
||
kopf.append(el('h2', 'bw-gruppe__titel', g.name));
|
||
/* `g.text` und NICHT `g.unter` (berichtigt 19.09.2026): Der Server
|
||
schickt das Feld `text`, gelesen wurde `unter` -- also immer
|
||
`undefined`. Damit fehlten ALLE SECHS Einleitungen, und die
|
||
CSS-Regel dafuer lief ins Leere. Genau diese Saetze nehmen dem
|
||
Bogen den Verhoer-Charakter ("Kein Test mit richtigen
|
||
Antworten"); uebrig blieben sechs nackte Ueberschriften ueber
|
||
fuenfzehn Fragen. */
|
||
if (g.text) kopf.append(el('p', 'bw-gruppe__unter', g.text));
|
||
block.append(kopf);
|
||
|
||
for (const f of fragen) {
|
||
const kurz = f.art === 'kurz';
|
||
const grenze = kurz ? r.grenzen.kurz : r.grenzen.lang;
|
||
const id = 'f-' + f.schluessel;
|
||
|
||
const feld = el('div', 'bw-feld');
|
||
const schild = el('label', 'bw-feld__schild');
|
||
schild.htmlFor = id;
|
||
schild.append(document.createTextNode(f.titel));
|
||
/* PFLICHT WIRD ANGESCHRIEBEN, NICHT ERZWUNGEN-BIS-ES-KLEMMT.
|
||
Ein Sternchen ohne Erklärung ist eine Falle; hier steht das
|
||
Wort. Und was NICHT Pflicht ist, steht auch dran -- sonst
|
||
füllt man aus Vorsicht alles aus und schreibt bei drei
|
||
Fragen nichts Ehrliches mehr. */
|
||
schild.append(el('span', f.pflicht ? 'bw-feld__pflicht' : 'bw-feld__frei',
|
||
f.pflicht ? 'nötig' : 'freiwillig'));
|
||
feld.append(schild);
|
||
|
||
if (f.hinweis) feld.append(el('p', 'bw-feld__hinweis', f.hinweis));
|
||
|
||
const eingabe = kurz ? document.createElement('input')
|
||
: document.createElement('textarea');
|
||
eingabe.id = id;
|
||
eingabe.className = 'bw-feld__eingabe';
|
||
eingabe.maxLength = grenze;
|
||
if (kurz) { eingabe.type = 'text'; eingabe.autocomplete = 'off'; }
|
||
else { eingabe.rows = 4; }
|
||
eingabe.value = werte[f.schluessel] || '';
|
||
feld.append(eingabe);
|
||
|
||
/* Der Zähler zählt, was noch geht -- nicht, was schon steht.
|
||
„Noch 80 Zeichen" ist eine Auskunft; „180 von 1200" ist eine
|
||
Aufforderung, mehr zu schreiben. Deshalb meldet er sich auch
|
||
erst, wenn es eng wird. */
|
||
const zaehler = el('p', 'bw-feld__zaehler');
|
||
const nachziehen = () => {
|
||
const rest = grenze - eingabe.value.length;
|
||
zaehler.textContent = rest < 80 ? ('noch ' + rest + ' Zeichen') : '';
|
||
zaehler.hidden = rest >= 80;
|
||
};
|
||
nachziehen();
|
||
eingabe.addEventListener('input', () => {
|
||
werte[f.schluessel] = eingabe.value;
|
||
entwurfSchreiben(werte);
|
||
nachziehen();
|
||
feld.classList.remove('bw-feld--fehlt');
|
||
});
|
||
feld.append(zaehler);
|
||
|
||
block.append(feld);
|
||
felder.set(f.schluessel, { feld, eingabe, frage: f });
|
||
}
|
||
ziel.append(block);
|
||
}
|
||
|
||
$('wer-liest').textContent = 'Wer das liest: ' + (r.wer_liest || '');
|
||
bogen.hidden = false;
|
||
|
||
/* ---------------------------------------------------------------
|
||
3. ABSCHICKEN
|
||
--------------------------------------------------------------- */
|
||
bogen.addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
melde('');
|
||
|
||
/* Die Pflichtliste kommt aus den Fragen des Servers -- dieselbe
|
||
Quelle, die er selbst prüft. */
|
||
const fehlt = [...felder.values()]
|
||
.filter((x) => x.frage.pflicht && !x.eingabe.value.trim());
|
||
for (const x of felder.values()) x.feld.classList.remove('bw-feld--fehlt');
|
||
if (fehlt.length) {
|
||
for (const x of fehlt) x.feld.classList.add('bw-feld--fehlt');
|
||
melde(fehlt.length === 1
|
||
? 'Eine Antwort fehlt noch – sie ist markiert.'
|
||
: fehlt.length + ' Antworten fehlen noch – sie sind markiert.');
|
||
fehlt[0].eingabe.focus();
|
||
fehlt[0].feld.scrollIntoView({ block: 'center', behavior: window.sanft?.() || 'auto' });
|
||
return;
|
||
}
|
||
|
||
const knopf = $('abschicken');
|
||
knopf.disabled = true;
|
||
knopf.textContent = 'wird geschickt …';
|
||
|
||
const antworten = {};
|
||
for (const [k, x] of felder) {
|
||
const v = x.eingabe.value.trim();
|
||
if (v) antworten[k] = v;
|
||
}
|
||
|
||
try {
|
||
const a = await fetch('/workspace/api/bewerbung', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
credentials: 'same-origin',
|
||
body: JSON.stringify({ antworten }),
|
||
});
|
||
const d = await a.json().catch(() => ({}));
|
||
if (!a.ok) {
|
||
melde(sagWas(d.fehler, 'Das hat nicht geklappt.'));
|
||
knopf.disabled = false;
|
||
knopf.textContent = 'Anfrage abschicken';
|
||
return;
|
||
}
|
||
/* ERST JETZT den Entwurf löschen -- nicht vorher. Wäre er beim
|
||
Absenden schon weg und der Server antwortet nicht, stünde man
|
||
vor einem leeren Formular und einer halben Stunde Arbeit im
|
||
Nichts. */
|
||
try { localStorage.removeItem(ENTWURF); } catch { /* egal */ }
|
||
|
||
bogen.hidden = true;
|
||
const fertig = $('stand');
|
||
fertig.hidden = false;
|
||
fertig.textContent = '';
|
||
fertig.append(el('p', 'bw-stand__wort', 'Angekommen.'));
|
||
fertig.append(el('p', 'leise',
|
||
'DogFather und die rechte Hand lesen sie – sonst niemand. '
|
||
+ 'Du hörst von uns; das kann ein paar Tage dauern.'));
|
||
const zurueck = el('a', 'knopf', 'Zurück zum Rudel');
|
||
zurueck.href = 'bereich.html?b=mitmachen';
|
||
fertig.append(zurueck);
|
||
fertig.scrollIntoView({ block: 'start', behavior: window.sanft?.() || 'auto' });
|
||
} catch {
|
||
melde('Das hat nicht geklappt. Dein Entwurf ist noch da.');
|
||
knopf.disabled = false;
|
||
knopf.textContent = 'Anfrage abschicken';
|
||
}
|
||
});
|
||
})();
|
||
})();
|