diff --git a/pruef-personen.png b/pruef-personen.png index 2f6ef233..b85ac91c 100644 Binary files a/pruef-personen.png and b/pruef-personen.png differ diff --git a/server/pruef-personen-liste.mjs b/server/pruef-personen-liste.mjs index 5b3250ff..1f374562 100644 --- a/server/pruef-personen-liste.mjs +++ b/server/pruef-personen-liste.mjs @@ -72,6 +72,14 @@ async function seiteFuer(kontext) { const lesen = (seite) => seite.evaluate(() => ({ namen: [...document.querySelectorAll(".person__name")].map((n) => n.textContent.replace(" (du)", "")), + /* Die Abschnitte in der Reihenfolge, in der sie tatsächlich stehen -- + nicht in der, in der sie gemeint waren. */ + abschnitte: [...document.querySelectorAll(".gruppe")].map((g) => ({ + name: g.querySelector(".gruppe__name")?.textContent, + zahl: g.querySelector(".gruppe__zahl")?.textContent, + unter: g.querySelector(".gruppe__unter")?.textContent, + personen: [...g.querySelectorAll(".person__name")].map((n) => n.textContent.replace(" (du)", "")), + })), knopfDa: !document.getElementById("rest-schalter")?.hidden, knopfText: document.getElementById("rest-schalter-text")?.textContent, auf: document.getElementById("rest-schalter")?.getAttribute("aria-expanded"), @@ -90,6 +98,8 @@ console.log("\n1) Standardmäßig nur DogFather"); console.log(" sichtbar:", r.namen.join(", ")); console.log(" Knopf: ", r.knopfText); ok(r.namen.length === 2, `zwei Personen sichtbar (${r.namen.length})`); + ok(r.abschnitte.length === 1 && r.abschnitte[0].name === "DogFather", + `zugeklappt steht nur der DogFather-Abschnitt da (${r.abschnitte.map((a) => a.name).join(", ") || "keiner"})`); ok(r.namen.includes("Dogfather") && r.namen.includes("VanVan"), `Dogfather und VanVan (${r.namen.join(", ")})`); ok(!r.namen.includes("Tili"), "kein Creator"); @@ -113,6 +123,28 @@ console.log("\n2) Auf Knopfdruck kommen alle dazu"); ok(/Nur DogFather zeigen/.test(r.knopfText || ""), `Knopf bietet das Zuklappen an (${r.knopfText})`); ok(r.auf === "true", `aria-expanded=true (${r.auf})`); ok(r.ueber <= 0, `nichts steht über (${r.ueber}px)`); + + console.log(" Abschnitte:", r.abschnitte.map((a) => `${a.name}(${a.zahl})`).join(" → ")); + ok(r.abschnitte.length === 4, `vier Abschnitte (${r.abschnitte.length})`); + ok(r.abschnitte.map((a) => a.name).join(",") === "DogFather,Manager,Scout,Creator", + `Reihenfolge wie auf der Zugangsseite (${r.abschnitte.map((a) => a.name).join(", ")})`); + ok(r.abschnitte.map((a) => a.zahl).join(",") === "2,1,2,2", + `Anzahl je Rolle (${r.abschnitte.map((a) => a.zahl).join(", ")})`); + + /* Der eigentliche Fehler von vorher: Der GESPERRTE Manager stand ganz + unten unter den Creators, weil "aktiv" vor der Rolle sortierte. */ + const manager = r.abschnitte.find((a) => a.name === "Manager"); + ok(manager?.personen.includes("BanaStift"), + `der gesperrte Manager steht bei den Managern (${manager?.personen.join(", ")})`); + const creator = r.abschnitte.find((a) => a.name === "Creator"); + ok(!creator?.personen.includes("BanaStift"), "und nicht mehr bei den Creators"); + ok(creator?.personen.join(",") === "SpongBobSchwammKopf,Tili", + `innerhalb der Rolle nach Namen (${creator?.personen.join(", ")})`); + + /* Dieselben Zusatztexte wie auf der Zugangsseite -- wer sich eben + angemeldet hat, findet hier dieselbe Sprache wieder. */ + ok(r.abschnitte[2]?.unter === "Eigene Pipeline & Kontakte", + `Zusatztext wörtlich von der Zugangsseite ("${r.abschnitte[2]?.unter}")`); } console.log("\n3) Der Zustand überlebt das Neuladen"); diff --git a/server/workspace-personen.js b/server/workspace-personen.js index 9c1520d4..40580840 100644 --- a/server/workspace-personen.js +++ b/server/workspace-personen.js @@ -73,6 +73,19 @@ function nurDogFatherBeiLeitung(req, res, next) { /* ---------- Übersicht --------------------------------------------------- */ +/* Sortierung: Die ROLLE zuerst, dann erst der Zustand. + + Vorher stand "p.aktiv DESC" ganz vorn -- dadurch wanderte jede + gesperrte Person ans Ende der GESAMTEN Liste, quer durch alle Rollen. + Ein gesperrter Manager stand also unter den Creators, und die + Reihenfolge DogFather-Manager-Scout-Creator, die ueberall sonst gilt, + war ausgerechnet auf der Personenseite aufgehoben. + + Jetzt: Rolle, dann Gesperrtes ans Ende SEINER Rolle, dann Name. + + (Der Kommentar steht bewusst hier und nicht in der Abfrage: Er enthaelt + Rueckwaerts-Anfuehrungszeichen, und die beenden mitten in einer + Zeichenkette genau diese -- der Server startete danach nicht mehr.) */ personenRouter.get("/workspace/api/verwaltung/personen", (req, res) => { try { res.json({ @@ -87,7 +100,7 @@ personenRouter.get("/workspace/api/verwaltung/personen", (req, res) => { (SELECT COUNT(*) FROM betreuung y WHERE y.betreuer_id = p.id) AS betreut_anzahl FROM personen p LEFT JOIN betreuung b ON b.creator_id = p.id - ORDER BY p.aktiv DESC, ${ROLLEN_SORTIERUNG.replace("rolle", "p.rolle")}, p.name`).all(), + ORDER BY ${ROLLEN_SORTIERUNG.replace("rolle", "p.rolle")}, p.aktiv DESC, p.name`).all(), /* Wer ueberhaupt als zustaendig eingetragen werden kann. */ betreuer: db().prepare( "SELECT id, name FROM personen WHERE rolle = 'scout' AND aktiv = 1 ORDER BY name").all(), diff --git a/workspace/assets/css/personen.css b/workspace/assets/css/personen.css index 605794ad..ecc116e9 100644 --- a/workspace/assets/css/personen.css +++ b/workspace/assets/css/personen.css @@ -169,3 +169,16 @@ "Person anlegen" – die Hauptsache gehört ganz nach außen, sonst springt der Blick beim Suchen daran vorbei. */ .kopf-knoepfe { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } + +/* Die Anzahl neben der Rollen-Überschrift. Sitzt zwischen Name und + Erklärung, damit sie zur Überschrift gehört und nicht zum Zusatztext. */ +.gruppe__zahl { + min-width: 20px; padding: 1px 7px; text-align: center; + font-size: .72rem; font-weight: 700; color: var(--text-still); + background: rgba(255, 255, 255, .06); border-radius: 999px; + font-variant-numeric: tabular-nums; +} + +/* In der Personenliste liegen die Karten direkt untereinander; der + Abstand zur nächsten Rolle kommt von .gruppe selbst. */ +.gruppe .person:last-child { margin-bottom: 0; } diff --git a/workspace/assets/js/personen.js b/workspace/assets/js/personen.js index 8884fd20..31c90d61 100644 --- a/workspace/assets/js/personen.js +++ b/workspace/assets/js/personen.js @@ -243,13 +243,51 @@ let alleZeigen = false; try { alleZeigen = localStorage.getItem(SPEICHER) === 'ja'; } catch { /* egal */ } + /* Jede Rolle bekommt einen eigenen Abschnitt, in der Reihenfolge der + ZUGANGSSEITE: DogFather, Manager, Scout, Creator. Die Zusatztexte + stehen dort wörtlich genauso -- wer sich eben angemeldet hat, findet + hier dieselbe Sprache wieder. + + Eine Rolle ohne Personen wird weggelassen. Eine leere Überschrift + ist kein Ordnungsmerkmal, sondern eine Lücke. */ + const ROLLEN_ABSCHNITTE = [ + { rolle: 'admin', name: 'DogFather', unter: 'Gesamtübersicht & Freigaben' }, + { rolle: 'manager', name: 'Manager', unter: 'Betreuung & Freigaben' }, + { rolle: 'scout', name: 'Scout', unter: 'Eigene Pipeline & Kontakte' }, + { rolle: 'creator', name: 'Creator', unter: 'Eigener Bereich & Fortschritt' }, + ]; + function listeZeigen() { const ziel = $('liste'); ziel.textContent = ''; const oben = alle.filter((p) => p.rolle === 'admin'); const rest = alle.filter((p) => p.rolle !== 'admin'); - for (const p of (alleZeigen ? alle : oben)) ziel.append(zeile(p)); + const sichtbar = alleZeigen ? alle : oben; + + for (const abschnitt of ROLLEN_ABSCHNITTE) { + const drin = sichtbar.filter((p) => p.rolle === abschnitt.rolle); + if (!drin.length) continue; + + const block = document.createElement('section'); + block.className = 'gruppe'; + const kopf = document.createElement('div'); + kopf.className = 'gruppe__kopf'; + const h = document.createElement('h2'); + h.className = 'gruppe__name'; + h.textContent = abschnitt.name; + const zahl = document.createElement('span'); + zahl.className = 'gruppe__zahl'; + zahl.textContent = String(drin.length); + const unter = document.createElement('span'); + unter.className = 'gruppe__unter'; + unter.textContent = abschnitt.unter; + kopf.append(h, zahl, unter); + block.append(kopf); + + for (const p of drin) block.append(zeile(p)); + ziel.append(block); + } const schalter = $('rest-schalter'); schalter.hidden = rest.length === 0; diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 8be15cf4..5618ca30 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -7,9 +7,9 @@ - - - + + + @@ -134,8 +134,8 @@ - - - + + + diff --git a/workspace/automation.html b/workspace/automation.html index f90ed2c0..a4fe20f6 100644 --- a/workspace/automation.html +++ b/workspace/automation.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -127,8 +127,8 @@ - - - + + + diff --git a/workspace/bereich.html b/workspace/bereich.html index 3055c944..df2992ce 100644 --- a/workspace/bereich.html +++ b/workspace/bereich.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -98,8 +98,8 @@ - - - + + + diff --git a/workspace/calls.html b/workspace/calls.html index 32e310d9..6d7acb5a 100644 --- a/workspace/calls.html +++ b/workspace/calls.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -49,9 +49,9 @@ - - - - + + + + diff --git a/workspace/content.html b/workspace/content.html index 5675162f..59a0cf3a 100644 --- a/workspace/content.html +++ b/workspace/content.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -153,8 +153,8 @@ - - - + + + diff --git a/workspace/dateien.html b/workspace/dateien.html index 56e61548..904b3740 100644 --- a/workspace/dateien.html +++ b/workspace/dateien.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -88,8 +88,8 @@ - - - + + + diff --git a/workspace/index.html b/workspace/index.html index 1fd075f5..b9722d15 100644 --- a/workspace/index.html +++ b/workspace/index.html @@ -25,7 +25,7 @@ type="image/webp" media="(max-width: 899px)" /> - + @@ -143,6 +143,6 @@

- + diff --git a/workspace/kalender.html b/workspace/kalender.html index c153380d..966646f0 100644 --- a/workspace/kalender.html +++ b/workspace/kalender.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -94,8 +94,8 @@ - - - + + + diff --git a/workspace/personen.html b/workspace/personen.html index 2de6b1e3..c120ff94 100644 --- a/workspace/personen.html +++ b/workspace/personen.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -116,8 +116,8 @@ - - - + + + diff --git a/workspace/profil.html b/workspace/profil.html index dcce1a86..029b08ee 100644 --- a/workspace/profil.html +++ b/workspace/profil.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -127,8 +127,8 @@ - - - + + + diff --git a/workspace/report.html b/workspace/report.html index adb717ae..64c3e10c 100644 --- a/workspace/report.html +++ b/workspace/report.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -86,9 +86,9 @@ - - - - + + + + diff --git a/workspace/scouting.html b/workspace/scouting.html index 9937a75e..e9485af6 100644 --- a/workspace/scouting.html +++ b/workspace/scouting.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -124,8 +124,8 @@ - - - + + + diff --git a/workspace/start.html b/workspace/start.html index 82a975c5..505e0bae 100644 --- a/workspace/start.html +++ b/workspace/start.html @@ -7,8 +7,8 @@ - - + + @@ -68,8 +68,8 @@

- - - + + + diff --git a/workspace/startcheck.html b/workspace/startcheck.html index 02b28905..7543f3de 100644 --- a/workspace/startcheck.html +++ b/workspace/startcheck.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -65,9 +65,9 @@ - - - - + + + + diff --git a/workspace/uebersicht.html b/workspace/uebersicht.html index 386c9abb..ac44025b 100644 --- a/workspace/uebersicht.html +++ b/workspace/uebersicht.html @@ -7,9 +7,9 @@ - - - + + + @@ -52,7 +52,7 @@ - - + + diff --git a/workspace/wissen.html b/workspace/wissen.html index 6075c793..f0422c6d 100644 --- a/workspace/wissen.html +++ b/workspace/wissen.html @@ -7,10 +7,10 @@ - - - - + + + + @@ -170,8 +170,8 @@ - - - + + +