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:
2026-09-24 00:33:09 +02:00
co-authored by Claude Opus 5
parent 14c8964b24
commit 362d883392
41 changed files with 827 additions and 613 deletions
+115 -15
View File
@@ -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);
}