Anfragen fuer Modi: ein Fragebogen, vier Wege, eine Bruecke
Filipe, mit dem Bildschirmfoto des Bretts "Mitmachen": "ich will dass die leute sich da anonym bei mir melden koennen ... wie ein fragebogen wieso die person modi werden will warum sie sollte, positiv negative sachen ... und dan soll ich annehmen ablehnen, in warteschlange stellen oder sofort kommunizieren koennen. und wenn ich annehme dan soll diese person automatisch zu talente kategorie weiter geleitet werden." Seine zwei Entscheidungen auf Rueckfrage: "Vertraulich, aber nicht namenlos" und "Du und die rechte Hand". WAS ES GIBT 15 Fragen in 6 Gruppen, 9 davon Pflicht. Vier davon standen nicht auf seinem Zettel und kommen aus der Recherche zu Moderator-Bewerbungen: Alter, frühere Sperren, "ein Freund bricht die Regeln - was machst du?" und "was kannst du nicht so gut?". Die letzte ist die aussagekraeftigste: Wer darauf "nichts" schreibt, hat sich gerade selbst beantwortet. Vier Wege: Reden - Warteschlange - Annehmen - Ablehnen. "Reden" steht vorne, weil die haeufigste richtige Antwort auf eine Bewerbung eine Rueckfrage ist und keine Entscheidung; stuende "Annehmen" oben, wuerde es geklickt. "Sofort kommunizieren" ist ein Satz, den sie liest. Kein Chat: Ein Zugang aus der Community steht ausdruecklich in keiner Chatliste (AUSSEN_ROLLEN). Der Satz ist damit der einzige Weg, dieser Person etwas zu sagen -- deshalb ist er bei "Reden" und "Ablehnen" Pflicht, und WELCHE Wege ihn verlangen, steht im Katalog und nicht zweimal. Nach "Annehmen" entsteht ein Talent auf der Stufe "Angesprochen". Die ersten beiden Stufen fragen, ob ueberhaupt Interesse besteht; wer sich selbst meldet, hat das beantwortet. WAS DABEI HERAUSKAM, DAS NICHT GESUCHT WAR aufgaben.js entschied mit `p.rolle === 'modi' || p.rolle === 'hand'`, wer eine Katalogaufgabe bekommen kann. Diese Datei ist ohne Anmeldung aus dem offenen Netz lesbar -- damit stand der verborgene Zugang darin nachzulesen. Jetzt entscheidet es der Server und schickt ein Ja/Nein. Gefunden von der Wortleck-Pruefung, nicht vom Auge. pruef-struktur meldete teilen.html seit heute frueh als "von nirgendwo verlinkt" -- und lag falsch: Auf sie zeigt `share_target.action` im Manifest. Die Manifeste sind jetzt Verweisquelle, nicht die eine Seite eine Ausnahme. Und im Bildschirmfoto stand der Schriftzug der Buehne mitten im Text einer Anfrage: `color-mix(..., transparent)` mischt mit DURCHSICHTIG. Deckend ist `#ffffff 6%`, so wie es .t-karte macht. Dieselbe Stelle hat mich heute schon einmal erwischt. GEPRUEFT pruef-bewerbung 77, davon 14 im Browser. Vier Gegenproben, jede trifft genau ihr Ziel: Bruecke ohne "genau einmal" -> 2 rot, Vertraulichkeit weg -> 5 rot, Nachricht-Pflicht weg -> 2 rot, Knopf ohne Rollenfrage -> 1 rot. Dazu unveraendert gruen: rollen 315, modi-verborgen 80, modi-katalog 49, entwicklung 43, treff, bereiche-lesend, haus-seiten, alle-wege, rechtetafel, css-klassen, struktur, deutsche-texte, start-ansicht, sicht, aufgabenbrett, womit, kanaele.
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
/* =====================================================================
|
||||
DIE ANFRAGE AUS DER COMMUNITY (17.09.2026)
|
||||
|
||||
Zwei Seiten teilen sich diese Datei: der Fragebogen (bewerben.html)
|
||||
und der Eingang (bewerbungen.html). Eine gemeinsame Datei und nicht
|
||||
zwei -- die Anfrage soll in beiden Richtungen gleich aussehen, und
|
||||
zwei Dateien wären zwei Gelegenheiten, eine davon zu vergessen.
|
||||
|
||||
DIE FLAECHEN SIND DECKEND, UND DAS WAR EIN FEHLER WERT.
|
||||
|
||||
Zuerst stand hier `color-mix(in srgb, var(--tinte) 94%, transparent)`.
|
||||
Das mischt mit DURCHSICHTIG -- 94 % heisst also: 6 % Foto scheint
|
||||
durch. Auf dem Bildschirmfoto stand der Schriftzug der Buehne mitten
|
||||
im Text einer Anfrage. Gefunden hat das kein Messwert, sondern das
|
||||
Hinsehen; dieselbe Stelle hat mich am 17.09. schon einmal erwischt
|
||||
(Talente, leerer Zustand).
|
||||
|
||||
Richtig ist `#ffffff 6%` -- derselbe Ton, aber deckend. So macht es
|
||||
das Haus in .t-karte.
|
||||
|
||||
ALLES GEDECKT, NICHTS BLINKT. Hier liest jemand Sätze, die ein
|
||||
anderer über sich selbst geschrieben hat; und auf der anderen Seite
|
||||
sitzt jemand, der gerade nach Worten sucht. Beides verträgt keine
|
||||
Signalfarben. Rot kommt nur an einer Stelle vor: an einem Feld, das
|
||||
wirklich fehlt -- und auch dort nur als Rand.
|
||||
===================================================================== */
|
||||
|
||||
/* =====================================================================
|
||||
1. DER FRAGEBOGEN
|
||||
===================================================================== */
|
||||
|
||||
.bw-stand {
|
||||
margin: 0 0 22px;
|
||||
padding: 16px 18px;
|
||||
border: 1px solid var(--rand);
|
||||
border-radius: 14px;
|
||||
/* EIGENE FLÄCHE, KEIN DURCHSCHEINEN. Auf der Bühne liegt ein Motiv;
|
||||
ohne deckenden Grund steht der Text darauf statt darüber. Das ist
|
||||
am 17.09. schon einmal passiert (Talente, leerer Zustand). */
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
}
|
||||
.bw-stand__wort { margin: 0; font-size: 1.05rem; font-weight: 700; }
|
||||
.bw-stand__grund {
|
||||
margin: 10px 0 0;
|
||||
padding: 10px 12px;
|
||||
border-inline-start: 3px solid var(--akzent);
|
||||
border-radius: 0 8px 8px 0;
|
||||
background: color-mix(in srgb, var(--akzent) 8%, transparent);
|
||||
line-height: 1.55;
|
||||
}
|
||||
.bw-stand__sperre { margin: 0; font-size: 1.05rem; font-weight: 700; }
|
||||
.bw-stand .knopf { margin-top: 14px; }
|
||||
|
||||
.bw-bogen { display: block; }
|
||||
|
||||
.bw-gruppe {
|
||||
margin: 0 0 26px;
|
||||
padding: 16px 18px;
|
||||
border: 1px solid var(--rand);
|
||||
border-radius: 14px;
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
}
|
||||
.bw-gruppe__kopf { margin-bottom: 14px; }
|
||||
.bw-gruppe__titel { margin: 0; font-size: 1.02rem; font-weight: 700; }
|
||||
.bw-gruppe__unter { margin: 4px 0 0; font-size: .86rem; opacity: .75; }
|
||||
|
||||
.bw-feld { margin: 0 0 18px; }
|
||||
.bw-feld:last-child { margin-bottom: 0; }
|
||||
|
||||
.bw-feld__schild {
|
||||
display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
|
||||
margin: 0 0 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
/* „nötig" und „freiwillig" stehen als WORT da und nicht als Sternchen.
|
||||
Ein Sternchen muss man erklären; wer die Erklärung überliest, füllt
|
||||
aus Vorsicht alles aus und schreibt am Ende nirgends etwas
|
||||
Ehrliches. */
|
||||
.bw-feld__pflicht,
|
||||
.bw-feld__frei {
|
||||
font-size: .74rem; font-weight: 600;
|
||||
letter-spacing: .04em; text-transform: uppercase;
|
||||
padding: 2px 7px; border-radius: 999px;
|
||||
}
|
||||
.bw-feld__pflicht {
|
||||
color: var(--akzent);
|
||||
background: color-mix(in srgb, var(--akzent) 12%, transparent);
|
||||
}
|
||||
.bw-feld__frei { opacity: .6; background: color-mix(in srgb, var(--rand) 40%, transparent); }
|
||||
|
||||
.bw-feld__hinweis { margin: 0 0 6px; font-size: .86rem; opacity: .75; line-height: 1.5; }
|
||||
|
||||
.bw-feld__eingabe {
|
||||
display: block; width: 100%;
|
||||
padding: 10px 12px;
|
||||
font: inherit; font-size: .95rem; line-height: 1.55;
|
||||
color: inherit;
|
||||
background: color-mix(in srgb, var(--tinte) 88%, #ffffff 12%);
|
||||
border: 1px solid var(--rand); border-radius: 10px;
|
||||
resize: vertical;
|
||||
}
|
||||
.bw-feld__eingabe:focus-visible {
|
||||
outline: 2px solid var(--akzent); outline-offset: 2px;
|
||||
border-color: var(--akzent);
|
||||
}
|
||||
/* Ein fehlendes Pflichtfeld wird MARKIERT, nicht angeschrien: Rand und
|
||||
ein Wort in der Meldung oben. Rot flächig wäre auf fünfzehn Feldern
|
||||
eine Wand. */
|
||||
.bw-feld--fehlt .bw-feld__eingabe {
|
||||
border-color: color-mix(in srgb, var(--warn) 70%, var(--rand));
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--warn) 40%, transparent);
|
||||
}
|
||||
|
||||
.bw-feld__zaehler { margin: 4px 0 0; font-size: .78rem; opacity: .65; text-align: end; }
|
||||
|
||||
.bw-wer-liest {
|
||||
margin: 0 0 14px;
|
||||
font-size: .86rem; opacity: .8;
|
||||
padding: 10px 12px;
|
||||
border: 1px dashed var(--rand); border-radius: 10px;
|
||||
}
|
||||
|
||||
.bw-abschluss { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
|
||||
|
||||
/* =====================================================================
|
||||
2. DER EINGANG
|
||||
===================================================================== */
|
||||
|
||||
.bw-zahlen {
|
||||
display: flex; gap: 12px; flex-wrap: wrap;
|
||||
margin: 0 0 22px;
|
||||
}
|
||||
|
||||
.bw-karte {
|
||||
margin: 0 0 20px;
|
||||
padding: 16px 18px;
|
||||
border: 1px solid var(--rand);
|
||||
border-radius: 14px;
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
}
|
||||
/* Der Zustand als feine Kante links -- er sagt beim Scrollen, was
|
||||
erledigt ist, ohne dass man das Schild lesen muss. */
|
||||
.bw-karte[data-zustand="neu"] { border-inline-start: 3px solid var(--akzent); }
|
||||
.bw-karte[data-zustand="gespraech"],
|
||||
.bw-karte[data-zustand="warteschlange"] {
|
||||
border-inline-start: 3px solid color-mix(in srgb, var(--akzent) 45%, var(--rand));
|
||||
}
|
||||
/* ENTSCHIEDENE KARTEN TRETEN ZURUECK -- ueber die SCHRIFT, nicht ueber
|
||||
`opacity`. Ein durchsichtiges Element nimmt seine Flaeche mit, und
|
||||
hinter dieser Seite liegt ein Foto: Aus "etwas leiser" waere wieder
|
||||
"das Motiv scheint durch" geworden. Derselbe Fehler, zehn Zeilen
|
||||
weiter oben. */
|
||||
.bw-karte[data-zustand="angenommen"] .bw-karte__bogen,
|
||||
.bw-karte[data-zustand="abgelehnt"] .bw-karte__bogen,
|
||||
.bw-karte[data-zustand="angenommen"] .bw-karte__kopf,
|
||||
.bw-karte[data-zustand="abgelehnt"] .bw-karte__kopf { color: color-mix(in srgb, var(--text) 78%, transparent); }
|
||||
|
||||
.bw-karte__kopf {
|
||||
display: flex; align-items: flex-start; justify-content: space-between;
|
||||
gap: 12px; flex-wrap: wrap;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.bw-karte__wer { min-width: 0; }
|
||||
.bw-karte__name { margin: 0; font-size: 1.08rem; font-weight: 700; }
|
||||
.bw-karte__unter { margin: 3px 0 0; font-size: .84rem; opacity: .72; }
|
||||
|
||||
.bw-marke {
|
||||
font-size: .76rem; font-weight: 600;
|
||||
letter-spacing: .04em; text-transform: uppercase;
|
||||
padding: 3px 9px; border-radius: 999px;
|
||||
border: 1px solid var(--rand);
|
||||
background: color-mix(in srgb, var(--tinte) 90%, #ffffff 10%);
|
||||
}
|
||||
|
||||
.bw-karte__frueher { margin: 0 0 12px; }
|
||||
.bw-karte__grund {
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
border-inline-start: 3px solid var(--akzent);
|
||||
border-radius: 0 8px 8px 0;
|
||||
background: color-mix(in srgb, var(--akzent) 8%, transparent);
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.bw-karte__bogen { margin: 0 0 16px; }
|
||||
.bw-karte__gruppe {
|
||||
margin: 16px 0 8px;
|
||||
font-size: .78rem; font-weight: 700;
|
||||
letter-spacing: .06em; text-transform: uppercase;
|
||||
opacity: .65;
|
||||
}
|
||||
.bw-karte__gruppe:first-child { margin-top: 0; }
|
||||
|
||||
.bw-antwort { margin: 0 0 10px; }
|
||||
.bw-antwort__frage { margin: 0; font-size: .84rem; opacity: .7; }
|
||||
.bw-antwort__text {
|
||||
margin: 2px 0 0;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
/* Was NICHT beantwortet wurde, steht dabei. Eine übersprungene
|
||||
freiwillige Frage ist selbst eine Auskunft. */
|
||||
.bw-karte__ohne { margin: 14px 0 0; font-size: .8rem; opacity: .55; line-height: 1.5; }
|
||||
|
||||
.bw-wahl {
|
||||
margin-top: 14px; padding-top: 14px;
|
||||
border-top: 1px solid var(--rand);
|
||||
}
|
||||
.bw-wahl__schild { margin: 0 0 8px; font-size: .84rem; opacity: .75; }
|
||||
.bw-wahl__knoepfe { display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
|
||||
.bw-weg {
|
||||
font: inherit; font-size: .88rem; font-weight: 600;
|
||||
color: inherit;
|
||||
padding: 8px 14px;
|
||||
min-height: 40px;
|
||||
border: 1px solid var(--rand); border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--tinte) 90%, #ffffff 10%);
|
||||
cursor: pointer;
|
||||
}
|
||||
.bw-weg:hover { border-color: color-mix(in srgb, var(--akzent) 60%, var(--rand)); }
|
||||
.bw-weg[aria-pressed="true"] {
|
||||
border-color: var(--akzent);
|
||||
background: color-mix(in srgb, var(--akzent) 16%, transparent);
|
||||
}
|
||||
.bw-weg:disabled { opacity: .45; cursor: default; }
|
||||
.bw-weg:focus-visible { outline: 2px solid var(--akzent); outline-offset: 2px; }
|
||||
|
||||
.bw-wahl__feld { margin: 12px 0 0; }
|
||||
.bw-wahl__feldschild { display: block; margin: 0 0 4px; font-size: .86rem; opacity: .8; }
|
||||
.bw-wahl__eingabe {
|
||||
display: block; width: 100%;
|
||||
padding: 10px 12px;
|
||||
font: inherit; font-size: .93rem; line-height: 1.55;
|
||||
color: inherit;
|
||||
background: color-mix(in srgb, var(--tinte) 88%, #ffffff 12%);
|
||||
border: 1px solid var(--rand); border-radius: 10px;
|
||||
resize: vertical;
|
||||
}
|
||||
.bw-wahl__eingabe:focus-visible {
|
||||
outline: 2px solid var(--akzent); outline-offset: 2px;
|
||||
border-color: var(--akzent);
|
||||
}
|
||||
.bw-wahl .knopf { margin-top: 12px; }
|
||||
|
||||
.bw-leer {
|
||||
padding: 22px 20px;
|
||||
border: 1px dashed var(--rand); border-radius: 14px;
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
}
|
||||
.bw-leer__wort { margin: 0 0 6px; font-size: 1.02rem; font-weight: 700; }
|
||||
|
||||
.bw-hinweis {
|
||||
margin: 0 0 16px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid color-mix(in srgb, var(--akzent) 45%, var(--rand));
|
||||
border-radius: 12px;
|
||||
background: color-mix(in srgb, var(--akzent) 10%, transparent);
|
||||
}
|
||||
|
||||
/* Der Weg von der Talente-Seite hierher. */
|
||||
.bw-tor {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
gap: 14px; flex-wrap: wrap;
|
||||
margin: 0 0 20px;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--rand); border-radius: 12px;
|
||||
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
|
||||
}
|
||||
.bw-tor[data-neu="ja"] {
|
||||
border-color: color-mix(in srgb, var(--akzent) 50%, var(--rand));
|
||||
background: color-mix(in srgb, var(--akzent) 10%, transparent);
|
||||
}
|
||||
.bw-tor__wort { margin: 0; font-size: .95rem; }
|
||||
@@ -1497,7 +1497,13 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe
|
||||
KEINE SCHWELLE, KEINE WARNFARBE. Was „zu viel" ist, hängt vom
|
||||
Menschen ab; eine feste Grenze wäre geraten. Was NICHT geraten
|
||||
ist: dass etwas überfällig liegt. Nur das wird markiert. */
|
||||
const zielbar = personenListe.filter((p) => p.rolle === 'modi' || p.rolle === 'hand');
|
||||
/* WER IN FRAGE KOMMT, SAGT DER SERVER (17.09.2026).
|
||||
|
||||
Hier standen zwei Rollennamen. Diese Datei ist ohne Anmeldung
|
||||
aus dem offenen Netz lesbar -- damit war der verborgene Zugang
|
||||
darin nachzulesen. Gefunden von der Wortleck-Pruefung, nicht vom
|
||||
Auge. */
|
||||
const zielbar = personenListe.filter((p) => p.fuer_katalog);
|
||||
if (zielbar.length && $('feld-verant') && !$('feld-verant').hidden) {
|
||||
/* DIE DRITTE REIHE IST ETWAS ANDERES als die zwei darueber: Sie
|
||||
waehlt keinen Ausschnitt des Katalogs, sondern einen MENSCHEN.
|
||||
|
||||
@@ -1101,6 +1101,32 @@
|
||||
ziel.append(liste);
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DER NAECHSTE SCHRITT (17.09.2026)
|
||||
|
||||
Der Server schickt entweder einen fertigen Block oder nichts. Diese
|
||||
Funktion weiss nicht, worum es geht -- und genau das ist der Punkt:
|
||||
Die Frage "darf diese Person?" haengt an der Rolle, und diese Datei
|
||||
kann ohne Anmeldung aus dem offenen Netz gelesen werden.
|
||||
|
||||
Beim naechsten Bereich, der so einen Schritt braucht, ist hier
|
||||
nichts zu tun.
|
||||
===================================================================== */
|
||||
function weiterZeigen(w) {
|
||||
const kasten = $('weiter');
|
||||
if (!kasten) return;
|
||||
kasten.textContent = '';
|
||||
if (!w || !w.ziel) { kasten.hidden = true; return; }
|
||||
kasten.hidden = false;
|
||||
kasten.dataset.neu = 'ja';
|
||||
const text = el('div');
|
||||
text.append(el('p', 'bw-tor__wort', w.titel || ''));
|
||||
if (w.text) text.append(el('p', 'leise', w.text));
|
||||
const hin = el('a', 'knopf', w.knopf || 'Weiter');
|
||||
hin.href = w.ziel;
|
||||
kasten.append(text, hin);
|
||||
}
|
||||
|
||||
async function laden() {
|
||||
try {
|
||||
await uebernahmenLaden();
|
||||
@@ -1109,6 +1135,7 @@
|
||||
const daten = await a.json();
|
||||
einstellung = daten.einstellung;
|
||||
treffBrett = !!daten.treff;
|
||||
weiterZeigen(daten.weiter);
|
||||
await lageHolen();
|
||||
filterBauen(daten.eintraege || []);
|
||||
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
/* =====================================================================
|
||||
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 · Der Treff';
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
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));
|
||||
if (g.unter) kopf.append(el('p', 'bw-gruppe__unter', g.unter));
|
||||
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: 'smooth' });
|
||||
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(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 Treff');
|
||||
zurueck.href = 'bereich.html?b=mitmachen';
|
||||
fertig.append(zurueck);
|
||||
fertig.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
||||
} catch {
|
||||
melde('Das hat nicht geklappt. Dein Entwurf ist noch da.');
|
||||
knopf.disabled = false;
|
||||
knopf.textContent = 'Anfrage abschicken';
|
||||
}
|
||||
});
|
||||
})();
|
||||
})();
|
||||
@@ -0,0 +1,264 @@
|
||||
/* =====================================================================
|
||||
DER EINGANG FÜR DIE ANFRAGEN AUS DER COMMUNITY (17.09.2026)
|
||||
|
||||
Filipe: „und dan soll ich annehmen ablehnen, in warteschlange stellen
|
||||
oder sofort kommunizieren können."
|
||||
|
||||
VIER WEGE, EINE ANTWORT. Die Reihenfolge kommt vom Server und ist
|
||||
dort begründet: „Reden" steht vorne, weil die häufigste richtige
|
||||
Antwort auf eine Bewerbung eine Rückfrage ist und nicht eine
|
||||
Entscheidung. Stünde „Annehmen" oben, würde es geklickt.
|
||||
|
||||
„SOFORT KOMMUNIZIEREN" HEISST HIER: EIN SATZ, DEN SIE LIEST.
|
||||
|
||||
Nicht ein Chat -- ein Zugang aus der Community steht ausdrücklich
|
||||
nicht in den Chatlisten (AUSSEN_ROLLEN, 17.09.). Es gibt also genau
|
||||
einen Weg, dieser Person etwas zu sagen: den Satz, der zur
|
||||
Entscheidung gehört. Er erscheint auf ihrer eigenen Seite. Deshalb
|
||||
ist er bei „Reden" und „Ablehnen" Pflicht -- welche das sind, steht
|
||||
im Katalog und nicht hier.
|
||||
|
||||
WAS DIESE SEITE NICHT TUT: sortieren lassen, filtern, suchen. Die
|
||||
Reihenfolge ist die Arbeitsanweisung (neu zuerst, dann was liegt),
|
||||
und wer sie umstellen kann, arbeitet irgendwann die bequemen zuerst
|
||||
ab. Bei zwanzig Anfragen im Jahr ist Suchen ohnehin keine Frage.
|
||||
===================================================================== */
|
||||
(() => {
|
||||
const $ = (id) => document.getElementById(id);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
const ZUSTAND_WORT = {
|
||||
neu: 'Neu', gespraech: 'Im Gespräch', warteschlange: 'Warteliste',
|
||||
angenommen: 'Angenommen', abgelehnt: 'Abgelehnt',
|
||||
};
|
||||
|
||||
/* WIE LANGE SIE SCHON LIEGT, in Worten. Eine Zahl allein sagt
|
||||
nichts -- „seit 23 Tagen" sagt, dass etwas schiefläuft. */
|
||||
const liegtSeit = (tage) => {
|
||||
if (tage <= 0) return 'heute gekommen';
|
||||
if (tage === 1) return 'seit gestern';
|
||||
return 'liegt seit ' + tage + ' Tagen';
|
||||
};
|
||||
|
||||
let daten = null;
|
||||
|
||||
const zeichnen = () => {
|
||||
/* ---------- Die Zahlen oben ---------- */
|
||||
const z = $('zahlen');
|
||||
z.textContent = '';
|
||||
z.hidden = false;
|
||||
const offen = (daten.zahlen.neu || 0) + (daten.zahlen.gespraech || 0)
|
||||
+ (daten.zahlen.warteschlange || 0);
|
||||
const paare = [
|
||||
['neu', 'Neu', daten.zahlen.neu || 0],
|
||||
['offen', 'Offen insgesamt', offen],
|
||||
['dabei', 'Angenommen', daten.zahlen.angenommen || 0],
|
||||
];
|
||||
for (const [art, schild, wert] of paare) {
|
||||
const kasten = el('div', 'bilanz-zahl');
|
||||
kasten.dataset.art = art;
|
||||
kasten.append(el('strong', 'bilanz-zahl__wert', String(wert)));
|
||||
kasten.append(el('span', 'bilanz-zahl__schild', schild));
|
||||
z.append(kasten);
|
||||
}
|
||||
|
||||
/* ---------- Die Anfragen ---------- */
|
||||
const liste = $('liste');
|
||||
liste.textContent = '';
|
||||
|
||||
if (!daten.bewerbungen.length) {
|
||||
const leer = el('div', 'bw-leer');
|
||||
leer.append(el('p', 'bw-leer__wort', 'Noch hat sich niemand gemeldet.'));
|
||||
leer.append(el('p', 'leise',
|
||||
'Der Weg dorthin steht im Treff unter „Mitmachen“. Wenn dort '
|
||||
+ 'nichts ankommt, liegt es fast nie an den Leuten, sondern '
|
||||
+ 'daran, dass niemand weiß, dass es geht – sag es im Stream.'));
|
||||
liste.append(leer);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const b of daten.bewerbungen) {
|
||||
const karte = el('article', 'bw-karte');
|
||||
karte.dataset.zustand = b.zustand;
|
||||
|
||||
/* ---- Kopf ---- */
|
||||
const kopf = el('header', 'bw-karte__kopf');
|
||||
const links = el('div', 'bw-karte__wer');
|
||||
links.append(el('h2', 'bw-karte__name', b.nenn_name || 'ohne Namen'));
|
||||
const unter = el('p', 'bw-karte__unter');
|
||||
unter.append(document.createTextNode(liegtSeit(b.tage)));
|
||||
/* DER KONTONAME STEHT DABEI, aber klein und dahinter. Er ist
|
||||
nötig, um die Person wiederzufinden -- gemeint ist sie aber
|
||||
mit dem Namen, den sie selbst genannt hat. */
|
||||
if (b.konto_name && b.konto_name !== b.nenn_name) {
|
||||
unter.append(document.createTextNode(' · Zugang: ' + b.konto_name));
|
||||
}
|
||||
links.append(unter);
|
||||
kopf.append(links);
|
||||
kopf.append(el('span', 'bw-marke', ZUSTAND_WORT[b.zustand] || b.zustand));
|
||||
karte.append(kopf);
|
||||
|
||||
/* ---- Was schon entschieden wurde ---- */
|
||||
if (b.zustand !== 'neu' && (b.grund || b.von_name)) {
|
||||
const alt = el('div', 'bw-karte__frueher');
|
||||
if (b.grund) alt.append(el('p', 'bw-karte__grund', b.grund));
|
||||
if (b.von_name) {
|
||||
alt.append(el('p', 'leise', 'entschieden von ' + b.von_name));
|
||||
}
|
||||
karte.append(alt);
|
||||
}
|
||||
|
||||
/* ---- Die Antworten, nach Gruppen ---- */
|
||||
const bogen = el('div', 'bw-karte__bogen');
|
||||
for (const g of daten.gruppen) {
|
||||
const fragen = daten.fragen.filter(
|
||||
(f) => f.gruppe === g.schluessel && b.antworten[f.schluessel]);
|
||||
if (!fragen.length) continue;
|
||||
bogen.append(el('h3', 'bw-karte__gruppe', g.name));
|
||||
for (const f of fragen) {
|
||||
const paar = el('div', 'bw-antwort');
|
||||
paar.append(el('p', 'bw-antwort__frage', f.titel));
|
||||
paar.append(el('p', 'bw-antwort__text', b.antworten[f.schluessel]));
|
||||
bogen.append(paar);
|
||||
}
|
||||
}
|
||||
/* WAS NICHT BEANTWORTET WURDE, WIRD GENANNT -- nicht verschwiegen.
|
||||
Eine freiwillige Frage, die jemand überspringt, ist selbst eine
|
||||
Auskunft; sie einfach wegzulassen versteckt sie. */
|
||||
const offenFragen = daten.fragen.filter((f) => !b.antworten[f.schluessel]);
|
||||
if (offenFragen.length) {
|
||||
bogen.append(el('p', 'bw-karte__ohne',
|
||||
'Ohne Antwort: ' + offenFragen.map((f) => f.titel).join(' · ')));
|
||||
}
|
||||
karte.append(bogen);
|
||||
|
||||
/* ---- Die Entscheidung ---- */
|
||||
const wahl = el('div', 'bw-wahl');
|
||||
const schild = el('p', 'bw-wahl__schild', 'Was passiert damit?');
|
||||
wahl.append(schild);
|
||||
|
||||
const feldkasten = el('div', 'bw-wahl__feld');
|
||||
feldkasten.hidden = true;
|
||||
const feldSchild = el('label', 'bw-wahl__feldschild');
|
||||
const feld = document.createElement('textarea');
|
||||
feld.className = 'bw-wahl__eingabe';
|
||||
feld.rows = 3;
|
||||
feld.maxLength = 600;
|
||||
feld.id = 'grund-' + b.id;
|
||||
feldSchild.htmlFor = feld.id;
|
||||
feldkasten.append(feldSchild, feld);
|
||||
|
||||
const reihe = el('div', 'bw-wahl__knoepfe');
|
||||
let gewaehlt = null;
|
||||
|
||||
const senden = el('button', 'knopf', 'Übernehmen');
|
||||
senden.type = 'button';
|
||||
senden.hidden = true;
|
||||
|
||||
for (const w of daten.wege) {
|
||||
const k = el('button', 'bw-weg', w.name);
|
||||
k.type = 'button';
|
||||
k.dataset.weg = w.schluessel;
|
||||
k.setAttribute('aria-pressed', 'false');
|
||||
k.title = w.text || '';
|
||||
if (w.schluessel === b.zustand) k.disabled = true;
|
||||
k.addEventListener('click', () => {
|
||||
gewaehlt = w;
|
||||
for (const a of reihe.querySelectorAll('.bw-weg')) {
|
||||
a.setAttribute('aria-pressed', String(a === k));
|
||||
}
|
||||
/* OB EIN SATZ NÖTIG IST, sagt der Weg selbst -- die Regel
|
||||
kommt aus derselben Antwort, die der Server prüft. */
|
||||
feldkasten.hidden = false;
|
||||
feldSchild.textContent = w.nachrichtSchild || 'Ein Satz dazu';
|
||||
feld.placeholder = w.nachricht === 'pflicht' ? 'nötig' : 'kann leer bleiben';
|
||||
senden.hidden = false;
|
||||
senden.textContent = w.name + ' – übernehmen';
|
||||
});
|
||||
reihe.append(k);
|
||||
}
|
||||
|
||||
senden.addEventListener('click', async () => {
|
||||
if (!gewaehlt) return;
|
||||
melde('');
|
||||
const grund = feld.value.trim();
|
||||
if (gewaehlt.nachricht === 'pflicht' && grund.length < (daten.nachricht_min || 10)) {
|
||||
melde('Dazu gehört ein Satz – er steht danach auf ihrer Seite.');
|
||||
feld.focus();
|
||||
return;
|
||||
}
|
||||
senden.disabled = true;
|
||||
senden.textContent = 'einen Moment …';
|
||||
try {
|
||||
const a = await fetch('/workspace/api/bewerbung/' + b.id + '/weg', {
|
||||
method: 'PUT',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({ weg: gewaehlt.schluessel, grund }),
|
||||
});
|
||||
const d = await a.json().catch(() => ({}));
|
||||
if (!a.ok) {
|
||||
melde(d.fehler || 'Das hat nicht geklappt.');
|
||||
senden.disabled = false;
|
||||
senden.textContent = gewaehlt.name + ' – übernehmen';
|
||||
return;
|
||||
}
|
||||
/* NACH EINER ANNAHME steht der Weg zum Talent da -- und zwar
|
||||
als Angebot, nicht als Sprung. Wer gerade fünf Anfragen
|
||||
durchgeht, will nicht nach der ersten auf einer anderen
|
||||
Seite landen. */
|
||||
await laden();
|
||||
if (d.weiter) {
|
||||
const hin = el('p', 'bw-hinweis');
|
||||
const a2 = el('a', 'schritt', 'Bei den Talenten ansehen');
|
||||
a2.href = d.weiter.replace('/workspace/', '');
|
||||
hin.append(document.createTextNode('Daraus wurde ein Talent. '));
|
||||
hin.append(a2);
|
||||
$('liste').prepend(hin);
|
||||
}
|
||||
} catch {
|
||||
melde('Das hat nicht geklappt.');
|
||||
senden.disabled = false;
|
||||
senden.textContent = gewaehlt.name + ' – übernehmen';
|
||||
}
|
||||
});
|
||||
|
||||
wahl.append(reihe, feldkasten, senden);
|
||||
karte.append(wahl);
|
||||
liste.append(karte);
|
||||
}
|
||||
};
|
||||
|
||||
const laden = async () => {
|
||||
let a;
|
||||
try {
|
||||
a = await fetch('/workspace/api/bewerbungen', { credentials: 'same-origin' });
|
||||
} catch {
|
||||
melde('Die Anfragen sind gerade nicht erreichbar.');
|
||||
return;
|
||||
}
|
||||
if (a.status === 401) { location.assign('/workspace/'); return; }
|
||||
if (a.status === 404 || a.status === 403) {
|
||||
$('ladezeile').hidden = true;
|
||||
melde('Diese Seite ist für DogFather und die rechte Hand.');
|
||||
return;
|
||||
}
|
||||
if (!a.ok) { melde('Die Anfragen sind gerade nicht erreichbar.'); return; }
|
||||
daten = await a.json();
|
||||
$('ladezeile').hidden = true;
|
||||
zeichnen();
|
||||
};
|
||||
|
||||
laden();
|
||||
})();
|
||||
@@ -646,7 +646,61 @@
|
||||
} catch { /* umgeleitet */ }
|
||||
}
|
||||
|
||||
/* DIE ANFRAGEN -- nur die Zahl, nicht der Inhalt (17.09.2026).
|
||||
|
||||
Eine eigene, winzige Auskunft. Den vollen Eingang zu laden hiesse,
|
||||
vertrauliche Saetze in eine Seite zu holen, die sie gar nicht
|
||||
anzeigt -- im Zwischenspeicher des Browsers landen sie trotzdem.
|
||||
|
||||
SCHWEIGT BEI 404. Wer diese Seite sieht, darf die Anfragen zwar in
|
||||
aller Regel auch sehen; aber die Rechtetafel ist umstellbar, und
|
||||
ein Kasten, der dann ins Leere fuehrt, ist schlechter als keiner. */
|
||||
async function anfragenZeigen() {
|
||||
const kasten = $('anfragen-tor');
|
||||
if (!kasten) return;
|
||||
let d;
|
||||
try {
|
||||
const a = await hole('/workspace/api/bewerbungen-zahl');
|
||||
if (!a.ok) return;
|
||||
d = await a.json();
|
||||
} catch { return; }
|
||||
|
||||
kasten.textContent = '';
|
||||
kasten.hidden = false;
|
||||
if (d.neu > 0) kasten.dataset.neu = 'ja'; else delete kasten.dataset.neu;
|
||||
|
||||
const text = document.createElement('div');
|
||||
const wort = document.createElement('p');
|
||||
wort.className = 'bw-tor__wort';
|
||||
/* DIE ZAHL STEHT IM SATZ, nicht als nackte Ziffer daneben. „2 neue
|
||||
Anfragen" sagt, was zu tun ist; eine 2 in einem Kreis sagt es
|
||||
nicht. */
|
||||
wort.textContent = d.neu > 0
|
||||
? (d.neu === 1 ? 'Eine neue Anfrage wartet.' : d.neu + ' neue Anfragen warten.')
|
||||
: (d.offen > 0
|
||||
? (d.offen === 1 ? 'Eine Anfrage ist noch offen.' : d.offen + ' Anfragen sind noch offen.')
|
||||
: 'Keine offenen Anfragen.');
|
||||
text.append(wort);
|
||||
|
||||
const leise = document.createElement('p');
|
||||
leise.className = 'leise';
|
||||
leise.textContent = 'Wer sich selbst gemeldet hat. Nimmst du an, '
|
||||
+ 'entsteht daraus hier ein Talent auf der Stufe „Angesprochen“.';
|
||||
text.append(leise);
|
||||
|
||||
const hin = document.createElement('a');
|
||||
hin.className = d.neu > 0 ? 'knopf' : 'schritt';
|
||||
hin.href = 'bewerbungen.html';
|
||||
hin.textContent = d.neu > 0 ? 'Ansehen' : 'Anfragen öffnen';
|
||||
|
||||
kasten.append(text, hin);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try { await ichHolen(); await laden(); } catch { /* umgeleitet */ }
|
||||
try {
|
||||
await ichHolen();
|
||||
await laden();
|
||||
await anfragenZeigen();
|
||||
} catch { /* umgeleitet */ }
|
||||
})();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user