diff --git a/server/pruef-nachwuchs.mjs b/server/pruef-nachwuchs.mjs index d0e37257..4b1a8b88 100644 --- a/server/pruef-nachwuchs.mjs +++ b/server/pruef-nachwuchs.mjs @@ -1519,6 +1519,67 @@ melde("=== 17. Der Anschluss im Browser ==="); await seite.screenshot({ path: "server/pruef-nachwuchs-anschluss.png", fullPage: true }); melde(" -- Bildschirmfoto: server/pruef-nachwuchs-anschluss.png"); + + /* ---- BEIDE RICHTUNGEN (17.09.2026) ------------------------------- + + Die Stufe „Im Team" sagt zweimal „ab hier zaehlt die + Entwicklungskarte" -- und bis heute gab es keinen Weg dorthin. + Dasselbe Muster wie der fehlende Ausgang aus dem Trichter: ein + Text, der einen Weg verspricht, den es nicht gibt. + + Geprueft wird nicht, ob ein Link DASTEHT, sondern ob man am + Ziel ankommt -- und ob der Rueckweg ebenfalls steht. Ein Link, + der auf einer Liste endet, auf der man die Person wieder suchen + muss, hat nichts erspart. */ + melde(""); + melde("=== 18. Hin und zurueck ==="); + { + const weiter = await seite.evaluate(() => + document.querySelector(".t-person__weiter")?.getAttribute("href") || ""); + ok(/^entwicklung\.html\?zeigen=person-\d+$/.test(weiter), + `von der Talentkarte fuehrt ein Weg zur Entwicklungskarte (${weiter})`); + + await seite.goto(`http://127.0.0.1:${PORT}/workspace/${weiter}`, + { waitUntil: "domcontentloaded" }); + await seite.waitForTimeout(2000); + + const dort = await seite.evaluate(() => ({ + /* Die Karte ist WIRKLICH offen -- nicht nur die Kachel + markiert. Wer dem Link folgt, will die Karte sehen. */ + karteOffen: !document.getElementById("karte")?.hidden, + name: document.querySelector(".e-karte__kopf")?.textContent?.trim().slice(0, 40) || "", + markiert: document.querySelectorAll('.e-person[data-treffer="ja"]').length, + herkunft: document.querySelector(".e-herkunft")?.textContent?.trim() || "", + zurueck: document.querySelector(".e-herkunft__weg")?.getAttribute("href") || "", + })); + ok(dort.karteOffen, "die Karte ist beim Ankommen offen"); + ok(/Marek/.test(dort.name), `und es ist die richtige (${dort.name})`); + ok(dort.markiert === 1, + `die Kachel dazu ist hervorgehoben (${dort.markiert})`); + ok(/Trichter/.test(dort.herkunft), + `und die Karte sagt, woher er kommt ("${dort.herkunft}")`); + ok(dort.zurueck === "talente.html", `mit dem Weg zurueck (${dort.zurueck})`); + + /* GEGENPROBE: Wer NICHT aus dem Trichter kam, bekommt diese + Zeile nicht -- sonst hiesse „kam ueber den Trichter" nur, + dass ueberall etwas steht. Kessi ist vor der Verknuepfung ins + Team gekommen und hat deshalb keine. */ + const ohne = await seite.evaluate(async () => { + const kacheln = [...document.querySelectorAll(".e-person")]; + const andere = kacheln.find((k) => !/Marek/.test(k.textContent || "")); + if (!andere) return { da: false }; + andere.click(); + await new Promise((r) => setTimeout(r, 1200)); + return { + da: true, + name: document.querySelector(".e-karte__kopf")?.textContent?.trim().slice(0, 30) || "", + herkunft: document.querySelectorAll(".e-herkunft").length, + }; + }); + ok(ohne.da, `es gibt eine zweite Karte zum Gegenpruefen (${ohne.name})`); + ok(ohne.da && ohne.herkunft === 0, + `und die traegt keine Herkunft (${ohne.herkunft})`); + } } finally { await browser.close(); } diff --git a/server/workspace-entwicklung.js b/server/workspace-entwicklung.js index 4cb0c1ce..3108a30a 100644 --- a/server/workspace-entwicklung.js +++ b/server/workspace-entwicklung.js @@ -476,8 +476,44 @@ entwicklungRouter.get("/workspace/api/entwicklung/person/:id", nurLeitung, (req, } } + /* WIE DIESER MENSCH GEKOMMEN IST (17.09.2026). + + Die Talentkarte sagt seit heute, wer aus ihr geworden ist. Die + Gegenrichtung fehlte: Wer hier eine Karte aufschlaegt, sieht + Namen und Punkte -- aber nicht, dass derselbe Mensch vier Wochen + lang beobachtet wurde, wer ihn eingearbeitet hat und was dabei + aufgefallen ist. + + Das ist keine Nebensaechlichkeit. Beim ersten Beurteilen ist + genau das die Frage: „Woher kommt der eigentlich?" -- und wer sie + nicht beantwortet bekommt, faengt bei null an, obwohl vier Wochen + Beobachtung vorliegen. + + NUR DER HINWEIS, NICHT DIE DATEN. Die Merkmale aus dem Trichter + hierher zu kopieren waere bequem und falsch: Es sind zwei + verschiedene Kataloge mit zwei verschiedenen Fragen (taugt er? / + wie entwickelt er sich?). Eine Zahl aus dem einen im anderen + sieht aus wie eine Beurteilung und ist keine. Hier steht der + WEG dorthin -- lesen kann man ihn selbst. */ + const herkunft = db().prepare( + `SELECT e.id, e.titel, s.stufe, s.seit, b.name AS buddy_name + FROM talent_stufe s + JOIN eintraege e ON e.id = s.eintrag_id + LEFT JOIN personen b ON b.id = s.buddy_id + WHERE s.person_id = ? AND e.bereich = 'talente' + ORDER BY s.seit DESC LIMIT 1`).get(p.id); + res.json({ person: { id: p.id, name: p.name, rolle_name: ROLLEN_NAME[p.rolle] ?? p.rolle }, + /* Null, wenn niemand die Karte verknuepft hat -- und das ist der + Normalfall fuer alle, die vor dem 17.09. ins Team kamen. Eine + erfundene Herkunft waere schlimmer als keine. */ + herkunft: herkunft ? { + eintrag_id: herkunft.id, + titel: herkunft.titel, + seit: herkunft.seit, + buddy_name: herkunft.buddy_name || null, + } : null, stufen: ENTWICKLUNG_STUFEN, erwartung: ENTWICKLUNG_ERWARTUNG, bloecke, diff --git a/workspace/app.webmanifest b/workspace/app.webmanifest index 85e9cc20..31c02c43 100644 --- a/workspace/app.webmanifest +++ b/workspace/app.webmanifest @@ -22,25 +22,25 @@ }, "icons": [ { - "src": "/assets/img/app-symbole/workspace-192.png?v=202609180011", + "src": "/assets/img/app-symbole/workspace-192.png?v=202609180100", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-512.png?v=202609180011", + "src": "/assets/img/app-symbole/workspace-512.png?v=202609180100", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609180011", + "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609180100", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609180011", + "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609180100", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/workspace/assets/css/entwicklung.css b/workspace/assets/css/entwicklung.css index 7c2fc859..b7703246 100644 --- a/workspace/assets/css/entwicklung.css +++ b/workspace/assets/css/entwicklung.css @@ -1349,3 +1349,52 @@ font-size: .88rem; } +/* Der Weg von der Talentkarte zur Entwicklungskarte. Die Stufe sagt + zweimal „ab hier zaehlt die Entwicklungskarte" -- bis heute ohne + einen Weg dorthin. */ +.t-person__weiter { + font-size: .8rem; font-weight: 600; + color: var(--text-leise); + text-decoration: underline; + text-underline-offset: 3px; + text-decoration-color: color-mix(in srgb, var(--text-leise) 40%, transparent); +} +.t-person__weiter:hover { color: var(--text); } +.t-person__weiter:focus-visible { outline: 2px solid var(--akzent); outline-offset: 3px; } + +/* ===================================================================== + WIE DIESER MENSCH GEKOMMEN IST (17.09.2026) + + Die Gegenrichtung: Wer eine Entwicklungskarte aufschlaegt, sieht + Namen und Punkte -- aber nicht, dass derselbe Mensch vier Wochen + beobachtet wurde. Beim ersten Beurteilen ist genau das die Frage. + + Eine Zeile, kein Kasten: Es ist Herkunft, nicht Inhalt. + ===================================================================== */ +.e-herkunft { + display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 9px; + margin: 0 0 12px; + padding-bottom: 10px; + border-bottom: 1px solid color-mix(in srgb, var(--rand) 70%, transparent); + font-size: .82rem; +} +.e-herkunft__schild { + /* 11,52 px -- die Hausgrenze liegt bei 11,5. Der erste Anlauf stand + auf .7rem (11,2) und wurde von pruef-css-klassen gefunden: eine + Stelle mehr als die Grundlinie. Zurueckhaltend wird die Zeile ueber + FARBE, nicht ueber Groesse. */ + font-size: .72rem; font-weight: 700; letter-spacing: .03em; + text-transform: uppercase; + color: var(--text-still); +} +.e-herkunft__weg { + font-weight: 600; + color: var(--text-leise); + text-decoration: underline; + text-underline-offset: 3px; + text-decoration-color: color-mix(in srgb, var(--text-leise) 40%, transparent); +} +.e-herkunft__weg:hover { color: var(--text); } +.e-herkunft__weg:focus-visible { outline: 2px solid var(--akzent); outline-offset: 3px; } +.e-herkunft__buddy { color: var(--text-still); } + diff --git a/workspace/assets/js/entwicklung.js b/workspace/assets/js/entwicklung.js index 05e473e5..2f637b37 100644 --- a/workspace/assets/js/entwicklung.js +++ b/workspace/assets/js/entwicklung.js @@ -252,9 +252,28 @@ /* NUR HIER WIRD GESPRUNGEN: Wer eine Person anklickt, will zu ihrer Karte. Überall sonst bleibt die Seite stehen, wo sie steht. */ k.addEventListener('click', () => karte(p.id, true)); + /* VON AUSSEN AUF EINE KARTE (17.09.2026). + + Die Talentseite verweist seit heute hierher -- bis dahin oeffnete + sich eine Karte NUR durch Klick auf ihre Kachel. Wer dem Link + folgte, landete auf einer Liste und musste die Person suchen; + genau der Schritt, den der Link ersparen sollte. + + `?zeigen=` ist der vorhandene Weg (kopf.js), nicht ein zweiter. + Er wird an der Kachel ausgewertet, waehrend sie entsteht -- + vorher gibt es sie nicht, und ein Sprung ins Nichts scrollt + kommentarlos an den Seitenanfang. */ + if (window.zielSchluessel?.() === `person-${p.id}`) { + gesucht = k; + } return k; } + /* Welche Kachel von aussen gesucht wurde (`?zeigen=person-…`). + Sie wird beim Bauen gemerkt und erst danach angesprungen -- vorher + haengt sie nicht im Dokument und hat keine Position. */ + let gesucht = null; + /* ---- Die Karte einer Person ----------------------------------------- */ /* DIE SEITE BEWEGT SICH NUR NOCH, WENN MAN ES WILL (15.09.2026). @@ -297,6 +316,31 @@ letzteKarte = { id, daten: r }; ziel.textContent = ''; + /* WIE DIESER MENSCH GEKOMMEN IST (17.09.2026). + + Wer eine Karte aufschlaegt, sieht Namen und Punkte -- aber nicht, + dass derselbe Mensch vier Wochen lang beobachtet wurde und wer + ihn eingearbeitet hat. Beim ersten Beurteilen ist genau das die + Frage: „Woher kommt der eigentlich?" + + NUR DER WEG, NICHT DIE DATEN. Die Merkmale aus dem Trichter + hierher zu kopieren waere bequem und falsch -- zwei Kataloge, + zwei Fragen (taugt er? / wie entwickelt er sich?). Eine Zahl aus + dem einen sieht im anderen aus wie eine Beurteilung und ist + keine. */ + if (r.herkunft) { + const h = el('p', 'e-herkunft'); + h.append(el('span', 'e-herkunft__schild', 'Kam über den Trichter')); + const hin = el('a', 'e-herkunft__weg', r.herkunft.titel); + hin.href = 'talente.html'; + h.append(hin); + if (r.herkunft.buddy_name) { + h.append(el('span', 'e-herkunft__buddy', + '· eingearbeitet von ' + r.herkunft.buddy_name)); + } + ziel.append(h); + } + const kopf = el('div', 'e-karte__kopf'); kopf.append(el('h2', 'e-karte__name', r.person.name)); kopf.append(el('span', 'e-karte__rolle', r.person.rolle_name)); @@ -768,7 +812,22 @@ ziel.append(el('p', 'leise', 'Noch niemand im Team. Zugänge legst du in „Personen & Zugänge" an.')); } + gesucht = null; for (const p of lage.personen) ziel.append(personKarte(p, lage)); + /* ERST JETZT SPRINGEN. Die Kachel entsteht in der Schleife + darueber; waehrenddessen haengt sie noch nicht im Dokument + und hat keine Position. Ein Sprung dorthin waere ein Sprung + an den Seitenanfang -- lautlos, und man haelt den Link fuer + kaputt. + + Die Karte wird MIT geoeffnet: Wer von der Talentseite kommt, + will die Karte sehen und nicht die Kachel dazu. Genau der + Schritt, den der Link ersparen soll. */ + if (gesucht) { + const wen = Number(gesucht.dataset.person); + window.zielMarkieren?.([gesucht], 'diese eine Karte'); + if (wen) await karte(wen, true); + } await ampel(); } else { /* Für die Beurteilten ist DIESE Seite die eigene Karte — das, diff --git a/workspace/assets/js/talente.js b/workspace/assets/js/talente.js index a655e268..e336e12f 100644 --- a/workspace/assets/js/talente.js +++ b/workspace/assets/js/talente.js @@ -286,6 +286,22 @@ wer.append(el('span', 't-person__schon', `${schon} ${schon === 1 ? 'offene Aufgabe' : 'offene Aufgaben'}`)); } + /* DER WEG ZUR ENTWICKLUNGSKARTE (17.09.2026). + + Der Satz dieser Stufe sagt zweimal „ab hier zaehlt die + Entwicklungskarte" -- und es gab keinen Weg dorthin. Wer ihn + las, musste die Seite selbst finden und die Person dort + heraussuchen. Genau dasselbe Muster wie beim fehlenden Ausgang + aus dem Trichter: ein Text, der einen Weg verspricht, den es + nicht gibt. + + `?zeigen=` ist der Weg, den das Haus dafuer hat (kopf.js) -- + keine zweite Mechanik. */ + if (k.person.aktiv) { + const zurKarte = el('a', 't-person__weiter', 'Entwicklungskarte'); + zurKarte.href = `entwicklung.html?zeigen=person-${k.person.id}`; + wer.append(zurKarte); + } a.append(wer); if (k.person.aktiv) startAufgaben(a, k, schon); } diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 6bb0fe32..ccec8bdf 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -12,19 +12,19 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - + + + + + + - +
@@ -291,10 +291,10 @@ - - - - - + + + + +