Files
dogfather-universe/workspace/assets/js/bewerben.js
T
DogFatherGitandClaude Opus 5 25de892fdb screen5: Aus dem Treff wird das Rudel, aus der Zentrale die IrrenAnstalt
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]>
2026-09-22 14:25:56 +02:00

287 lines
11 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.
/* =====================================================================
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';
}
});
})();
})();