Personen als Kacheln statt Zeilen
Filipe: "gestalte diese seite auch anders bitte. viel krasser geiler uebersichtlicher." Auf das Angebot, die Rollen als Kacheln statt Zeilen zu bauen: "will ich." Entwurf gezeigt, Antwort: "so live". Eine Person war eine Zeile ueber die volle Breite -- links der Name, darunter fuenf halbe Saetze, rechts die Knoepfe. Gemessen: 1128 x 74 px, eine pro Reihe, mit achthundert Pixeln Luft in der Mitte. Jetzt 368 x 221 px, drei nebeneinander: sechs Creator brauchen zwei Reihen statt sechs. Die Angaben stehen in einem Zahlenband statt als Satzreihe -- aus "offene Aufgaben: 0 · 2 offene Sitzung(en) · betreut 3 Creator" werden Felder mit grosser Zahl und kleinem Wort, in jeder Kachel an derselben Stelle. Man vergleicht zwei Personen mit dem Auge, statt in jeder Zeile an einer anderen Stelle nach derselben Zahl zu suchen. Dazu: "vor 21 Tagen" statt "2026-09-15 00:26" (das genaue Datum bleibt als Titel dran), ein Namenszeichen in der Rollenfarbe mit schmalem Farbstreifen oben, und "gesperrt" als Marke in der Warnfarbe statt als graues Wort zwischen fuenf grauen Woertern. ES FAELLT NICHTS WEG. Name, Rolle, gesperrt, Anmeldung, Aufgaben, Sitzungen, betreute Creator, zugeteilte Scouts, "gehoert zu", die Zustaendigkeitsauswahl und alle Knoepfe -- die Messung prueft das ausdruecklich mit, huebsch und unvollstaendig waere schlechter als vorher. Keine feste Spaltenzahl: "auto-fill" laesst den Browser rechnen, bei 1160 px sind es drei, bei 390 px eine. Eine Zahl waere die sechste Wiederholung desselben Fehlers in diesem Haus. Nebenbei zwei Altlasten: .marke-rolle und .betreuung__schild standen auf 11,2 px, unter der Hausgrenze von 11,5. Mit Gegenprobe belegt, dass das schon vorher so war -- jetzt .75rem. Gemessen: 14 Messungen, 0 Befunde (1765 px und 390 px). Gruen: pruef-personen-kachel (45), pruef-personen-liste, pruef-css-klassen. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -2,26 +2,119 @@
|
||||
Personenverwaltung.
|
||||
=================================================================== */
|
||||
|
||||
/* ==== PERSONEN ALS KACHELN (24.09.2026) =============================
|
||||
|
||||
Filipe: "gestalte diese seite auch anders bitte. viel krasser
|
||||
geiler uebersichtlicher." Auf das Angebot, die Rollen als Kacheln
|
||||
statt Zeilen zu bauen: "will ich."
|
||||
|
||||
WAS SICH AENDERT: Eine Person war eine Zeile ueber die volle Breite
|
||||
-- links der Name, darunter fuenf halbe Saetze, rechts die Knoepfe.
|
||||
Bei 1765 px lief dieselbe Zeile ueber 1160 px, mit achthundert
|
||||
Pixeln Luft in der Mitte. Zehn Personen hiessen zehn Zeilen
|
||||
untereinander, und wer zwei vergleichen wollte, musste in jeder
|
||||
Zeile an einer anderen Stelle nach derselben Zahl suchen.
|
||||
|
||||
JETZT EIN RASTER AUS KACHELN. Drei bis vier nebeneinander statt
|
||||
einer pro Zeile: Dieselben zehn Personen brauchen drei Reihen statt
|
||||
zehn. Und in jeder Kachel steht jede Angabe an derselben Stelle --
|
||||
die Zahlen fluchten untereinander, man sieht mit einem Blick, wer
|
||||
sieben offene Aufgaben hat und wer keine.
|
||||
|
||||
ES FAELLT NICHTS WEG. Jede Angabe, die vorher in der Zeile stand,
|
||||
steht auch jetzt da: Name, Rolle, gesperrt, letzte Anmeldung,
|
||||
offene Aufgaben, Sitzungen, betreute Creator, zugeteilte Scouts,
|
||||
Zugehoerigkeit, die Zustaendigkeitsauswahl und alle Knoepfe. */
|
||||
|
||||
.gruppe__raster {
|
||||
display: grid; gap: 12px;
|
||||
/* KEINE FESTE SPALTENZAHL. "auto-fill" laesst den Browser rechnen:
|
||||
bei 1160 px sind es drei, bei 390 px eine. Eine Zahl hier waere
|
||||
genau der Fehler, der in diesem Haus schon fuenfmal passiert ist
|
||||
-- eine Rechnung von gestern, die still falsch wird. */
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
|
||||
}
|
||||
|
||||
.person {
|
||||
display: grid; gap: 4px 16px; align-items: center;
|
||||
grid-template-columns: 1fr auto;
|
||||
padding: 14px 16px; margin-bottom: 9px;
|
||||
display: flex; flex-direction: column; gap: 11px;
|
||||
padding: 14px 15px 13px; margin-bottom: 0;
|
||||
background: var(--flaeche);
|
||||
border: 1px solid var(--rand);
|
||||
border-radius: var(--radius-klein);
|
||||
/* Die Kacheln einer Reihe werden gleich hoch, damit die Knoepfe
|
||||
unten fluchten (siehe margin-top bei .person__knoepfe). */
|
||||
height: 100%;
|
||||
}
|
||||
/* DIE ROLLENFARBE ALS SCHMALER STREIFEN OBEN. Dieselben Farben, die
|
||||
das Haus fuer die Rollen ohnehin fuehrt -- gedeckt beigemischt,
|
||||
nicht als Leuchtbalken: Bei zehn Kacheln nebeneinander waere ein
|
||||
voller Farbton Unruhe statt Ordnung. */
|
||||
.person { --r: var(--text-still); }
|
||||
.person[data-rolle="spicy"] { --r: var(--spicy-haupt); }
|
||||
.person[data-rolle="admin"] { --r: var(--dogi-haupt); }
|
||||
.person[data-rolle="manager"] { --r: var(--manager-haupt); }
|
||||
.person[data-rolle="scout"] { --r: var(--scout-haupt); }
|
||||
.person[data-rolle="creator"] { --r: var(--creator-haupt); }
|
||||
.person { border-top: 2px solid color-mix(in srgb, var(--r) 50%, transparent); }
|
||||
|
||||
.person[data-aktiv="nein"] { opacity: .58; }
|
||||
|
||||
.person__name { font-weight: 600; font-size: .98rem; }
|
||||
.person__innen { display: flex; flex-direction: column; gap: 11px; }
|
||||
|
||||
.person__zeile {
|
||||
display: flex; flex-wrap: wrap; gap: 8px;
|
||||
margin-top: 2px; font-size: .79rem; color: var(--text-still);
|
||||
.person__kopf { display: flex; align-items: center; gap: 11px; min-width: 0; }
|
||||
|
||||
/* DAS NAMENSZEICHEN. Gab es bisher nur in der Ueberschrift einer
|
||||
zugeklappten Gruppe. In der Kachel ist es das, woran man eine
|
||||
Person wiedererkennt, bevor man den Namen gelesen hat. */
|
||||
.person__zeichen {
|
||||
flex: 0 0 auto;
|
||||
width: 42px; height: 42px; border-radius: 50%;
|
||||
display: grid; place-items: center;
|
||||
font-size: .92rem; font-weight: 800; letter-spacing: .03em;
|
||||
color: var(--r);
|
||||
background: color-mix(in srgb, var(--r) 14%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--r) 32%, transparent);
|
||||
}
|
||||
|
||||
.person__wer { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
|
||||
.person__name {
|
||||
font-weight: 600; font-size: .98rem;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.person__marken { display: flex; flex-wrap: wrap; gap: 5px; }
|
||||
|
||||
/* ---- Das Zahlenband ----
|
||||
Ein Raster statt einer Reihe: So stehen die Felder in jeder Kachel
|
||||
an derselben Stelle, auch wenn eine Person drei Felder hat und die
|
||||
naechste fuenf. */
|
||||
.person__werte {
|
||||
display: grid; gap: 9px 10px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(74px, 1fr));
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid color-mix(in srgb, var(--rand) 75%, transparent);
|
||||
}
|
||||
.person__wert { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
|
||||
.person__wert-zahl {
|
||||
font-size: 1.22rem; font-weight: 700; color: var(--text); line-height: 1.15;
|
||||
}
|
||||
/* Die Zeitangabe ist keine Zahl -- sie darf nicht so schreien wie
|
||||
eine, steht aber im selben Band. */
|
||||
.person__wert--zeit .person__wert-zahl { font-size: .87rem; font-weight: 600; }
|
||||
.person__wert-wort {
|
||||
font-size: .75rem; color: var(--text-still); line-height: 1.25;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
|
||||
.person__bezug { font-size: .79rem; color: var(--text-still); }
|
||||
|
||||
.marke-rolle {
|
||||
padding: 1px 9px; border-radius: 999px;
|
||||
font-size: .7rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
|
||||
/* 11,2 px waren unter der Hausgrenze von 11,5 px (24.09.2026).
|
||||
Gemessen, nicht gelesen: In der Kachel steht die Marke gross
|
||||
genug neben dem Namen, um aufzufallen -- das war der Grund fuer
|
||||
die kleine Schrift, und in einer Zeile mit fuenf grauen Saetzen
|
||||
war er auch richtig. Hier braucht sie ihn nicht mehr. */
|
||||
font-size: .75rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
|
||||
}
|
||||
/* Alle FUENF Rollen, aus den zentralen Farben (08.09.2026).
|
||||
|
||||
@@ -39,7 +132,22 @@
|
||||
.marke-rolle[data-rolle="scout"] { background: color-mix(in srgb, var(--scout-haupt) 16%, transparent); color: var(--scout-haupt); }
|
||||
.marke-rolle[data-rolle="creator"] { background: color-mix(in srgb, var(--creator-haupt) 16%, transparent); color: var(--creator-haupt); }
|
||||
|
||||
.person__knoepfe { display: flex; gap: 7px; flex-wrap: wrap; justify-content: flex-end; }
|
||||
/* DIE KNOEPFE STEHEN UNTEN, bei allen Kacheln einer Reihe auf
|
||||
derselben Hoehe. "margin-top: auto" schiebt sie ans Ende der
|
||||
Kachel, egal wie viele Felder darueber stehen. Und links statt
|
||||
rechts: In einer 300 px breiten Kachel beginnt jede Zeile links,
|
||||
eine rechtsbuendige Knopfreihe waere die einzige Ausnahme. */
|
||||
.person__knoepfe {
|
||||
display: flex; gap: 7px; flex-wrap: wrap;
|
||||
justify-content: flex-start; margin-top: auto;
|
||||
}
|
||||
|
||||
/* Gesperrt ist jetzt eine Marke in der Warnfarbe, kein graues Wort in
|
||||
einer Reihe grauer Woerter. */
|
||||
.marke-rolle[data-rolle="gesperrt"] {
|
||||
background: color-mix(in srgb, var(--warn) 16%, transparent);
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
/* ---------- Code-Anzeige ----------------------------------------------
|
||||
|
||||
@@ -105,7 +213,11 @@
|
||||
eigener Vorgang. */
|
||||
.betreuung { display: flex; align-items: center; gap: 9px; margin-top: 9px; flex-wrap: wrap; }
|
||||
.betreuung__schild {
|
||||
font-size: .7rem; font-weight: 700; letter-spacing: .07em;
|
||||
/* Auch hier 11,2 px statt der 11,5 px, die im Haus die Untergrenze
|
||||
sind (24.09.2026, beim Umbau gemessen). Beides zusammen -- Marke
|
||||
und Schild -- waren die einzigen zwei Stellen auf der Seite
|
||||
darunter. */
|
||||
font-size: .75rem; font-weight: 700; letter-spacing: .07em;
|
||||
text-transform: uppercase; color: var(--text-still);
|
||||
}
|
||||
.betreuung__wahl {
|
||||
@@ -312,7 +424,9 @@
|
||||
|
||||
/* 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; }
|
||||
/* (Der eigene Abstand der letzten Kachel ist entfallen -- die Kacheln
|
||||
stehen in einem Raster mit "gap", keine hat mehr einen eigenen
|
||||
Abstand nach unten.) */
|
||||
|
||||
/* Der Löschknopf. Ruhig wie die anderen, bis man ihn ansieht – dann
|
||||
deutlich rot. Ein dauerhaft roter Knopf in jeder Zeile macht die
|
||||
|
||||
+115
-15
@@ -439,23 +439,101 @@ Der Code gilt einmal fuer die `
|
||||
return (teile[0][0] + teile[teile.length - 1][0]).toUpperCase();
|
||||
}
|
||||
|
||||
/* WIE LANGE IST DAS HER? (24.09.2026)
|
||||
|
||||
Dastand "letzte Anmeldung: 2026-09-14 08:12". Das ist eine genaue
|
||||
Angabe und eine schlechte Antwort -- die Frage, die man an eine
|
||||
letzte Anmeldung stellt, lautet "ist der noch dabei?", und dafuer
|
||||
muss man das Datum erst im Kopf gegen heute rechnen. "vor drei
|
||||
Tagen" beantwortet sie sofort.
|
||||
|
||||
Das genaue Datum geht nicht verloren, es steht als Titel dran --
|
||||
wer den Zeitpunkt braucht, faehrt mit der Maus darueber. */
|
||||
function herZeit(iso) {
|
||||
if (!iso) return 'noch nie';
|
||||
const ms = Date.now() - Date.parse(iso);
|
||||
if (!Number.isFinite(ms)) return '—';
|
||||
const min = Math.floor(ms / 60000);
|
||||
if (min < 2) return 'gerade eben';
|
||||
if (min < 60) return `vor ${min} Min.`;
|
||||
const std = Math.floor(min / 60);
|
||||
if (std < 24) return `vor ${std} Std.`;
|
||||
const tage = Math.floor(std / 24);
|
||||
if (tage === 1) return 'gestern';
|
||||
if (tage < 31) return `vor ${tage} Tagen`;
|
||||
const monate = Math.floor(tage / 30.44);
|
||||
if (monate < 12) return `vor ${monate} Mon.`;
|
||||
const jahre = Math.floor(tage / 365);
|
||||
return `vor ${jahre} ${jahre === 1 ? 'Jahr' : 'Jahren'}`;
|
||||
}
|
||||
|
||||
/** Ein Feld im Zahlenband: die Zahl gross, das Wort klein darunter. */
|
||||
function wertFeld(ziel, wert, wort, titel) {
|
||||
const f = el('div', 'person__wert');
|
||||
f.append(el('span', 'person__wert-zahl', String(wert)));
|
||||
f.append(el('span', 'person__wert-wort', wort));
|
||||
if (titel) f.title = titel;
|
||||
ziel.append(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
function zeile(p) {
|
||||
const k = el('div', 'person');
|
||||
k.dataset.aktiv = p.aktiv ? 'ja' : 'nein';
|
||||
/* DIE ROLLENFARBE GEHOERT AN DIE KACHEL, nicht nur an die kleine
|
||||
Marke. In einem Raster sucht das Auge zuerst die Farbe und liest
|
||||
erst danach -- dieselben Rollenfarben, die das Haus ohnehin
|
||||
fuehrt. Alles Weitere macht das CSS. */
|
||||
k.dataset.rolle = p.rolle;
|
||||
|
||||
const links = el('div');
|
||||
links.append(el('div', 'person__name', p.name + (p.id === ich.id ? ' (du)' : '')));
|
||||
const links = el('div', 'person__innen');
|
||||
|
||||
const z = el('div', 'person__zeile');
|
||||
/* ---- Kopf: Zeichen, Name, Rolle ---------------------------------
|
||||
Das Namenszeichen gab es bisher nur in der Ueberschrift einer
|
||||
zugeklappten Gruppe. In der Kachel ist es der Anker, an dem man
|
||||
eine Person wiedererkennt, bevor man den Namen gelesen hat. */
|
||||
const kopf = el('div', 'person__kopf');
|
||||
const zeichen = el('div', 'person__zeichen', initialen(p.name));
|
||||
zeichen.dataset.rolle = p.rolle;
|
||||
zeichen.setAttribute('aria-hidden', 'true');
|
||||
kopf.append(zeichen);
|
||||
|
||||
const wer = el('div', 'person__wer');
|
||||
wer.append(el('div', 'person__name', p.name + (p.id === ich.id ? ' (du)' : '')));
|
||||
const marken = el('div', 'person__marken');
|
||||
const rolle = el('span', 'marke-rolle', ROLLENNAME[p.rolle] || p.rolle);
|
||||
rolle.dataset.rolle = p.rolle;
|
||||
z.append(rolle);
|
||||
if (!p.aktiv) z.append(el('span', null, 'gesperrt'));
|
||||
z.append(el('span', null, `letzte Anmeldung: ${datum(p.letzter_login)}`));
|
||||
z.append(el('span', null, `offene Aufgaben: ${p.offene_aufgaben}`));
|
||||
if (p.sitzungen) z.append(el('span', null, `${p.sitzungen} offene Sitzung(en)`));
|
||||
marken.append(rolle);
|
||||
/* GESPERRT WAR BISHER EIN WORT IN GRAUER SCHRIFT zwischen fuenf
|
||||
anderen grauen Woertern -- auf einer Kachel, die ohnehin blasser
|
||||
ist, hat es niemand gesehen. Jetzt ist es eine Marke in der
|
||||
Warnfarbe. */
|
||||
if (!p.aktiv) {
|
||||
const g = el('span', 'marke-rolle', 'gesperrt');
|
||||
g.dataset.rolle = 'gesperrt';
|
||||
marken.append(g);
|
||||
}
|
||||
wer.append(marken);
|
||||
kopf.append(wer);
|
||||
links.append(kopf);
|
||||
|
||||
/* ---- Das Zahlenband ---------------------------------------------
|
||||
DIESELBEN ANGABEN WIE VORHER, nichts faellt weg -- nur nicht
|
||||
mehr als Reihe halber Saetze ("offene Aufgaben: 7 · 2 offene
|
||||
Sitzung(en) · betreut 3 Creator"). In einem Raster stehen sie
|
||||
damit in jeder Kachel an derselben Stelle: Man vergleicht zwei
|
||||
Personen mit dem Auge, statt in jeder Zeile an einer anderen
|
||||
Stelle nach derselben Zahl zu suchen. */
|
||||
const werte = el('div', 'person__werte');
|
||||
wertFeld(werte, p.offene_aufgaben,
|
||||
p.offene_aufgaben === 1 ? 'Aufgabe' : 'Aufgaben', 'offene Aufgaben');
|
||||
if (p.sitzungen) {
|
||||
wertFeld(werte, p.sitzungen,
|
||||
p.sitzungen === 1 ? 'Sitzung' : 'Sitzungen', 'offene Sitzungen');
|
||||
}
|
||||
if (p.betreut_anzahl) {
|
||||
z.append(el('span', null, `betreut ${p.betreut_anzahl} Creator`));
|
||||
wertFeld(werte, p.betreut_anzahl, 'betreut',
|
||||
`betreut ${p.betreut_anzahl} Creator`);
|
||||
}
|
||||
/* Bei einem Manager: wie viele Scouts an ihm haengen. Ohne diese Zahl
|
||||
muesste man die ganze Liste durchgehen, um zu sehen, ob eine
|
||||
@@ -465,14 +543,25 @@ Der Code gilt einmal fuer die `
|
||||
Element haette eine eigene Klasse gebraucht, die es im Stil nicht
|
||||
gibt, und waere als nackter Text erschienen. */
|
||||
if (p.rolle === 'manager' && p.scouts_anzahl) {
|
||||
z.append(el('span', null,
|
||||
`${p.scouts_anzahl} ${p.scouts_anzahl === 1 ? 'Scout' : 'Scouts'} zugeteilt`));
|
||||
wertFeld(werte, p.scouts_anzahl,
|
||||
p.scouts_anzahl === 1 ? 'Scout' : 'Scouts', 'Scouts zugeteilt');
|
||||
}
|
||||
/* Beim Scout umgekehrt: zu wem er gehoert. */
|
||||
|
||||
/* DIE ZEIT STEHT ALS LETZTES FELD im selben Band. Sie ist keine
|
||||
Zahl und darf deshalb nicht so schreien wie eine -- aber sie
|
||||
gehoert dorthin, wo alles andere ueber diese Person steht. */
|
||||
const zuletzt = el('div', 'person__wert person__wert--zeit');
|
||||
zuletzt.append(el('span', 'person__wert-zahl', herZeit(p.letzter_login)));
|
||||
zuletzt.append(el('span', 'person__wert-wort', 'angemeldet'));
|
||||
zuletzt.title = `letzte Anmeldung: ${datum(p.letzter_login)}`;
|
||||
werte.append(zuletzt);
|
||||
links.append(werte);
|
||||
|
||||
/* Beim Scout: zu wem er gehoert. Das ist keine Zahl, sondern ein
|
||||
Name -- der steht als eigene Zeile unter dem Band. */
|
||||
if (p.rolle === 'scout' && p.manager_name) {
|
||||
z.append(el('span', null, `gehört zu ${p.manager_name}`));
|
||||
links.append(el('div', 'person__bezug', `gehört zu ${p.manager_name}`));
|
||||
}
|
||||
links.append(z);
|
||||
|
||||
/* Zuständigkeit. Steht nur bei Creator und nur dann, wenn es
|
||||
überhaupt einen Scout gibt, dem man sie geben könnte. */
|
||||
@@ -1016,7 +1105,18 @@ Der Code gilt einmal fuer die `
|
||||
});
|
||||
block.append(kopf);
|
||||
|
||||
if (auf) for (const p of abschnitt.drin) block.append(zeile(p));
|
||||
/* DIE KACHELN STEHEN IN EINEM RASTER, nicht untereinander.
|
||||
|
||||
"auto-fill" statt einer festen Spaltenzahl: Wie viele
|
||||
nebeneinander passen, weiss der Browser besser als jede Zahl,
|
||||
die ich hier hinschreiben koennte -- und dieselbe Regel gilt
|
||||
auf dem Handy wie auf 1765 px. Eine feste Zahl waere die
|
||||
sechste Wiederholung desselben Fehlers in diesem Haus. */
|
||||
if (auf) {
|
||||
const raster = el('div', 'gruppe__raster');
|
||||
for (const p of abschnitt.drin) raster.append(zeile(p));
|
||||
block.append(raster);
|
||||
}
|
||||
ziel.append(block);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user