/* ===================================================================== Talente — der Trichter (11.09.2026) Fünf Stufen, und jede hat GENAU EINEN nächsten Schritt. Das ist der ganze Unterschied zu einer Liste: Eine Liste wächst und wird nie wieder angesehen; ein Trichter fragt bei jeder Karte „und jetzt?". DREI DINGE, DIE DIESE SEITE ABSICHTLICH TUT: 1. SIE ZEIGT DIE STANDZEIT. „Seit 41 Tagen beobachtet, nie angesprochen" ist eine Auskunft über UNS, nicht über den Kandidaten. Ohne sie wird der Trichter wieder zur Liste. 2. SIE TRENNT WARNZEICHEN VON MERKMALEN. Bei Moderatoren sagt das Fehlverhalten mehr voraus als das Können — ein Warnzeichen neben zwanzig Haken ginge unter. 3. SIE RECHNET NICHT. Keine Punktzahl, keine Eignung in Prozent. Zwanzig Haken und ein Warnzeichen ergeben keine Zahl, sondern ein Gespräch. ===================================================================== */ (() => { 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 melde = (t) => { const f = $('fehler'); if (f) { f.textContent = t; f.hidden = !t; } }; async function hole(pfad, optionen = {}) { const a = await fetch(pfad, { credentials: 'same-origin', ...optionen }); if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); } if (a.status === 404) { location.assign('/workspace/start.html'); throw new Error('unbekannt'); } return a; } let lage = null; const offen = new Set(); // welche Karten aufgeklappt sind /* ---- Der Trichter oben ---------------------------------------------- */ function trichter() { const ziel = $('trichter'); ziel.textContent = ''; ziel.setAttribute('aria-busy', 'false'); for (const s of lage.trichter) { const k = el('div', 't-stufe'); k.dataset.stufe = s.schluessel; k.append(el('span', 't-stufe__zahl', String(s.anzahl))); k.append(el('span', 't-stufe__name', s.name)); if (s.liegt) { /* LIEGENGEBLIEBEN IST UNSERE ZAHL, nicht die des Kandidaten. Sie steht deshalb am Trichter und nicht als Mahnung. */ const z = el('span', 't-stufe__liegt', `${s.liegt} liegt`); z.title = 'So lange, dass es eine Auskunft über uns ist – nicht über sie.'; k.append(z); } ziel.append(k); } } /* ---- Eine Karte ------------------------------------------------------ */ function karte(k) { const a = el('article', 't-karte'); a.dataset.stufe = k.stufe; if (k.liegt) a.dataset.liegt = 'ja'; const kopf = el('div', 't-karte__kopf'); kopf.append(el('h2', 't-karte__titel', k.titel)); kopf.append(el('span', 't-karte__stufe', k.stufe_name)); kopf.append(el('span', 't-karte__zeit', k.tage === null ? '' : `seit ${k.tage} ${k.tage === 1 ? 'Tag' : 'Tagen'}`)); if (k.warnzeichen.length) { const w = el('span', 't-karte__warn', `${k.warnzeichen.length} × hinsehen`); w.title = 'Kein Ausschluss – ein Grund, länger hinzusehen.'; kopf.append(w); } a.append(kopf); if (k.einsatz) a.append(el('p', 't-karte__wo', k.einsatz)); if (k.text) a.append(el('p', 't-karte__text', k.text)); /* DER EINE NÄCHSTE SCHRITT. Nicht drei Möglichkeiten — einer. Wer eine Karte öffnet, soll in zwei Sekunden wissen, was dran ist, und nicht erst eine Entscheidung treffen müssen. */ const schritt = el('div', 't-schritt'); schritt.append(el('p', 't-schritt__satz', k.schritt)); if (k.knopf && k.naechste) { const b = el('button', 'schritt t-schritt__knopf', k.knopf); b.type = 'button'; b.addEventListener('click', async () => { b.disabled = true; const r = await hole(`/workspace/api/talente/${k.id}/stufe`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ stufe: k.naechste }), }); b.disabled = false; if (!r.ok) { melde('Ging nicht.'); return; } offen.add(k.id); await laden(); }); schritt.append(b); } if (k.liegt) { schritt.append(el('p', 't-schritt__liegt', 'Das liegt länger, als es sollte. Entweder weiter – oder bewusst beenden.')); } a.append(schritt); /* ---- Merkmale, aufklappbar ------------------------------------------ Zugeklappt steht dort eine Zahl, aufgeklappt die 21 Merkmale. Immer offen wären es bei zehn Kandidaten 270 Kästchen auf einer Seite — und dann klickt niemand mehr eines an. */ const auf = offen.has(k.id); const schalter = el('button', 't-karte__auf', auf ? 'Merkmale zuklappen' : `Merkmale ansehen (${k.merkmale.length} gesetzt${k.warnzeichen.length ? `, ${k.warnzeichen.length} Warnzeichen` : ''})`); schalter.type = 'button'; schalter.setAttribute('aria-expanded', String(auf)); schalter.addEventListener('click', () => { if (offen.has(k.id)) offen.delete(k.id); else offen.add(k.id); zeichne(); }); a.append(schalter); if (auf) { for (const g of lage.gruppen) { const block = el('section', 't-gruppe'); block.append(el('h3', 't-gruppe__titel', g.name)); block.append(el('p', 't-gruppe__satz', g.text)); const reihe = el('div', 't-merkmale'); for (const m of g.merkmale) reihe.append(merkmal(k, m, false)); block.append(reihe); a.append(block); } const warn = el('section', 't-gruppe t-gruppe--warn'); warn.append(el('h3', 't-gruppe__titel', 'Hinsehen')); warn.append(el('p', 't-gruppe__satz', 'Keines davon ist ein Ausschluss. Bei Moderatoren sagt das Verhalten, ' + 'das stört, mehr voraus als das, was jemand kann.')); const reihe = el('div', 't-merkmale'); for (const m of lage.warnzeichen) reihe.append(merkmal(k, m, true)); warn.append(reihe); a.append(warn); } return a; } function merkmal(k, m, istWarnung) { const gesetzt = (istWarnung ? k.warnzeichen : k.merkmale).includes(m.schluessel); const b = el('button', 't-merkmal'); b.type = 'button'; if (istWarnung) b.dataset.warnung = 'ja'; b.setAttribute('aria-pressed', String(gesetzt)); b.title = m.text; b.append(el('span', 't-merkmal__haken', gesetzt ? '✓' : '+')); b.append(el('span', 't-merkmal__titel', m.titel)); b.addEventListener('click', async () => { b.disabled = true; const r = await hole(`/workspace/api/talente/${k.id}/merkmal/${m.schluessel}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: '{}' }); b.disabled = false; if (!r.ok) { melde('Ging nicht.'); return; } await laden(); }); return b; } /* ---- Zeichnen --------------------------------------------------------- */ function zeichne() { trichter(); const ziel = $('liste'); ziel.textContent = ''; ziel.setAttribute('aria-busy', 'false'); if (!lage.kandidaten.length) { const leer = el('div', 't-leer'); leer.append(el('p', null, 'Noch niemand auf der Liste. Ein Talent entsteht als Eintrag im Brett ' + '„Talente" – dort schreibst du auf, wem du etwas zutraust, und hier ' + 'wird daraus ein Weg.')); const a = el('a', 'schritt', 'Zum Brett „Talente"'); a.href = 'bereich.html?b=talente'; leer.append(a); ziel.append(leer); return; } /* LIEGENGEBLIEBENE ZUERST. Die Reihenfolge ist die eigentliche Arbeitsanweisung dieser Seite: Was liegt, ist das, was dran ist. */ const sortiert = [...lage.kandidaten].sort((a, b) => (b.liegt ? 1 : 0) - (a.liegt ? 1 : 0) || (b.tage || 0) - (a.tage || 0)); for (const k of sortiert) ziel.append(karte(k)); const fuss = el('p', 't-fuss'); fuss.append('Ein neues Talent legst du im Brett an – '); const a = el('a', null, 'Talente-Brett öffnen'); a.href = 'bereich.html?b=talente'; fuss.append(a); fuss.append('. Von dort landet es hier automatisch auf „Aufgefallen".'); ziel.append(fuss); } async function laden() { const a = await hole('/workspace/api/talente/lage'); if (!a.ok) { melde('Ging nicht.'); return; } lage = await a.json(); zeichne(); } (async () => { try { await laden(); } catch { /* umgeleitet */ } })(); })();