Aufgabenbrett: eine Karte ist ein Stueck Arbeit, keine Tabellenzeile
Filipe: "so wie alles gerade ist ist es zu wenig und zu viel zu
gleich". Die Zahl dahinter, an der echten Datenbank gemessen: 115
offene Aufgaben -- aber nur 17 verschiedene. Jede stand acht Mal da.
URSACHE, nicht Symptom: Am 22.09.2026 wurde dieselbe Vorlage zweimal
verteilt, nachgemessen zwischen 16:05:24 und 16:05:53. Zweimal
gedrueckt, weil beim ersten Mal scheinbar nichts passierte. 56 der
115 Karten sind exakte Doppelte.
WAS SICH AENDERT
Aufgaben mit gleichem Titel, gleicher Frist und gleicher Kategorie
stehen jetzt als EINE Karte da: "0 von 8", ein Balken, und die Leute
als Pillen in ihrer Statusfarbe. Wer sie doppelt hat, traegt x2.
Der Hinweis auf die Doppelten steht EINMAL ueber dem Brett, mit Zahl
-- nicht siebzehnmal an den Karten. Eine Warnung, die auf jeder Karte
steht, ist Tapete (Lehre vom 03.09.2026).
Die Spalten wachsen nach dem, was sie tragen: "Offen" mit 17 Karten
bekam vorher genauso viel Platz wie "Erledigt" mit einer -- beide
369 px, gemessen. Innerhalb der Spalte legen sich die Karten
nebeneinander, sobald Platz da ist (auto-fill, keine feste Spaltenzahl).
Das Vorlagenbrett steht oben, solange das Brett leer ist, und wandert
darunter, sobald etwas daliegt. Die urspruengliche Begruendung ("wer
ein leeres Brett hat, soll nicht daran vorbeiscrollen") gilt nur fuer
den leeren Fall.
Am Handy wird aus "Wer hat gerade was" eine wischbare Reihe statt
gestapelter Pillen, mit Randschattierung als Hinweis, dass es
weitergeht.
GEMESSEN (echte Datenlage: 17 Vorlagen, vier Leute, doppelt verteilt)
Computer 46 077 px -> 2 445 px (18,8-fach kuerzer)
Handy 44 216 px -> 5 763 px ( 7,7-fach kuerzer)
Brett beginnt am Handy bei 798 statt 901 px -- die erste Aufgabe
ist damit ohne Scrollen sichtbar.
DREI BEFUNDE NEBENBEI, ALLE VON MIR
1. team.css: Der Schreiben-Knopf auf der Team-Lage stand bei 42 px.
Am 22.09. habe ich beim Kartenumbau das Polster von 10 auf 9 px
gesenkt und ihn damit unter die Hausregel gedrueckt. Jetzt
min-height statt Polsterrechnung -- die Hoehe haengt nicht mehr
daran, ob jemand spaeter an der Schriftgroesse dreht.
2. chat.css: Die Knoepfe der Aufnahmeleiste standen bei 40 px, der
Weg-Knopf der GIF-Kiste bei 28. Beide in der Nacht zum 23.09.
gebaut. Die Leiste bekommt volle 44 px; der GIF-Knopf bleibt klein
sichtbar und waechst nur in der TREFFERFLAECHE (28 + 2x8 = 44),
und das nur am Finger -- mit der Maus zielt man genau, eine
unsichtbar vergroesserte Flaeche waere dort eine Falle.
3. pruef-chat-anhaenge meldete "aus der Kiste genommen (1 uebrig)".
Kein Codefehler: Die Pruefung setzte `window.confirm = () => true`,
und heute frueh ist dort der Hausdialog an die Stelle getreten. Sie
klickte, die Seite fragte, niemand antwortete. Genau der Fall, vor
dem der Kopf von helfer-nachfrage.mjs seit dem 19.09. warnt -- zum
zweiten Mal, an einer neuen Stelle. Jetzt ueber `bestaetige`, und
damit prueft die Zeile ab sofort mit, DASS gefragt wird.
PRUEFUNGEN
pruef-modi-katalog zaehlte Karten und erwartete +1. Seit der
Gruppierung ist das die alte Anordnung, nicht die Sache: Sie zaehlt
jetzt AUFGABEN ueber data-id/data-ids und meldet "2 -> 3 Aufgaben in
2 Karten" -- damit ist beides belegt, das Anlegen und das
Zusammenfassen.
Alles gruen: aufgabenbrett 49, zuteilung 75, bewerbung-aufgaben 101,
modi-katalog 125, chat-anhaenge 109, chat-optik 56, tippziele 11,
teamlage-karten 39, sprung 43, textform 50, formulare 23,
css-klassen 33, zeichen 7, ueberlappung (20 Paare).
Handy-Rundgang: 220 Seitenaufrufe, 112 776 Elemente, 0 Befunde.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+363
-23
@@ -180,6 +180,95 @@
|
||||
feld.focus();
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
EINE KARTE IST EIN STUECK ARBEIT -- NICHT EINE TABELLENZEILE
|
||||
(23.09.2026)
|
||||
|
||||
Filipe: "so wie alles gerade ist ist es zu wenig und zu viel zu
|
||||
gleich."
|
||||
|
||||
Das ist genau richtig beobachtet, und die Zahlen dahinter sind
|
||||
diese (am 23.09.2026 in der echten Datenbank nachgemessen):
|
||||
|
||||
115 offene Aufgaben -- aber nur 17 VERSCHIEDENE
|
||||
vier Modis, jede Aufgabe an jede verteilt -> x4
|
||||
und die Verteilung lief ZWEIMAL -> x8
|
||||
|
||||
Das Brett zeigte daraufhin 136 Karten in EINER Spalte, waehrend
|
||||
drei Spalten leer danebenstanden. Gemessen war die Seite 46 077 px
|
||||
hoch bei 950 px Bildschirm -- 48 Bildschirme Scrollen. Am Handy 53.
|
||||
Das ist das "zu viel". Das "zu wenig" ist dasselbe von der anderen
|
||||
Seite: Nirgends stand, WAS eigentlich zu tun ist. Man musste 136
|
||||
Karten lesen, um 17 Dinge zu erfahren.
|
||||
|
||||
WAS SICH AENDERT: Aufgaben mit demselben Titel, derselben Frist und
|
||||
derselben Kategorie sind EIN Stueck Arbeit, an mehrere verteilt.
|
||||
Sie bekommen EINE Karte mit den Leuten darauf. Aus 136 Karten
|
||||
werden 17.
|
||||
|
||||
WARUM DAS NICHT NUR ZUSAMMENFASSEN IST: Auf der Sammelkarte sieht
|
||||
man etwas, das vorher NICHT sichtbar war -- naemlich wie weit die
|
||||
Gruppe ist ("0 von 4") und wer noch nicht angefangen hat. Vorher
|
||||
musste man vier Karten suchen, die 30 Karten auseinanderlagen.
|
||||
|
||||
UND SIE ZEIGT DIE DOPPELTEN. Steht jemand zweimal in derselben
|
||||
Gruppe, ist die Aufgabe doppelt verteilt worden. Das sieht man
|
||||
jetzt auf einen Blick statt gar nicht.
|
||||
|
||||
WER ALLEIN AN ETWAS SITZT, bekommt unveraendert seine Karte --
|
||||
eine Gruppe aus einem Menschen ist einfach eine Karte. Der
|
||||
Sonderfall ist damit kein Sonderfall, sondern der Normalfall mit
|
||||
der Zahl 1.
|
||||
===================================================================== */
|
||||
|
||||
/* Wie weit ein Status ist. Die Gruppe steht dort, wo ihr am
|
||||
WENIGSTEN fortgeschrittenes Mitglied steht -- denn das ist die
|
||||
Arbeit, die noch zu tun ist. Wuerde die Gruppe bei "erledigt"
|
||||
stehen, sobald EINE Person fertig ist, verschwaende sie aus dem
|
||||
Blick, obwohl drei noch nicht angefangen haben. */
|
||||
const RANG = { offen: 0, arbeit: 1, review: 2, erledigt: 3 };
|
||||
|
||||
/** Aus einer Liste Aufgaben werden Stuecke Arbeit. */
|
||||
function gruppieren(liste) {
|
||||
const nach = new Map();
|
||||
for (const a of liste) {
|
||||
/* DER SCHLUESSEL IST DAS, WAS EIN MENSCH SIEHT: derselbe Titel,
|
||||
dieselbe Frist, dieselbe Kategorie. Nicht das Vorlagenfeld --
|
||||
das ist bei beiden Verteilrunden gleich und wuerde die
|
||||
Doppelten unsichtbar machen, statt sie zu zeigen.
|
||||
\u0000 als Trenner, weil es in keinem Titel vorkommt; ein
|
||||
Bindestrich koennte selbst im Titel stehen. */
|
||||
const schluessel = [a.titel, a.frist || '', a.kategorie || ''].join('\u0000');
|
||||
let g = nach.get(schluessel);
|
||||
if (!g) { g = { schluessel, alle: [] }; nach.set(schluessel, g); }
|
||||
g.alle.push(a);
|
||||
}
|
||||
for (const g of nach.values()) {
|
||||
/* Die Karte zeigt die Angaben der am wenigsten fortgeschrittenen
|
||||
Aufgabe -- dieselbe, die auch die Spalte bestimmt. */
|
||||
g.alle.sort((x, y) => (RANG[x.status] ?? 0) - (RANG[y.status] ?? 0) || x.id - y.id);
|
||||
g.erste = g.alle[0];
|
||||
g.status = g.erste.status;
|
||||
g.fertig = g.alle.filter((a) => a.status === 'erledigt').length;
|
||||
|
||||
/* Wer ist beteiligt -- und wer steht doppelt drin? */
|
||||
const leute = new Map();
|
||||
for (const a of g.alle) {
|
||||
const kennung = a.verantwortlich_id != null
|
||||
? 'p' + a.verantwortlich_id
|
||||
: 'x' + (a.verantwortlich_name || '?');
|
||||
if (!leute.has(kennung)) {
|
||||
leute.set(kennung, { name: a.verantwortlich_name || 'Niemand',
|
||||
id: a.verantwortlich_id ?? null, aufgaben: [] });
|
||||
}
|
||||
leute.get(kennung).aufgaben.push(a);
|
||||
}
|
||||
g.leute = [...leute.values()].sort((x, y) => x.name.localeCompare(y.name, 'de'));
|
||||
g.doppelt = g.leute.filter((p) => p.aufgaben.length > 1);
|
||||
}
|
||||
return [...nach.values()];
|
||||
}
|
||||
|
||||
function karte(a) {
|
||||
const k = el('article', 'karte');
|
||||
k.dataset.prio = a.prioritaet;
|
||||
@@ -681,42 +770,285 @@
|
||||
brett.textContent = '';
|
||||
brett.setAttribute('aria-busy', 'false');
|
||||
|
||||
const gruppen = gruppieren(aufgaben);
|
||||
|
||||
/* ---- WO DAS VORLAGENBRETT STEHT, HAENGT AM BRETT --------------
|
||||
|
||||
Im HTML steht es UEBER dem Brett, und die Begruendung dort ist
|
||||
richtig: "Wer ein leeres Brett hat, soll nicht erst daran
|
||||
vorbeiscrollen, um zu sehen, womit man anfaengt."
|
||||
|
||||
Sie gilt nur fuer ein LEERES Brett. Liegen siebzehn Sachen da,
|
||||
ist der Vorlagenkasten das, woran man vorbeiscrollt -- gemessen
|
||||
114 px am Handy, vor der ersten Aufgabe.
|
||||
|
||||
Also keine feste Wahl, sondern eine Regel: oben, solange nichts
|
||||
daliegt; darunter, sobald etwas daliegt. Die urspruengliche
|
||||
Absicht bleibt damit erhalten, und der volle Fall wird besser. */
|
||||
const vorlagen = $('vorlagenbrett');
|
||||
if (vorlagen && vorlagen.parentElement) {
|
||||
const dahinter = gruppen.length ? brett.nextSibling : brett;
|
||||
if (vorlagen.nextSibling !== dahinter || gruppen.length === 0) {
|
||||
vorlagen.parentElement.insertBefore(vorlagen,
|
||||
gruppen.length ? brett.nextSibling : brett);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- EIN BAND, WENN ETWAS DOPPELT VERTEILT WURDE --------------
|
||||
|
||||
Am 22.09.2026 hat Ghost dieselbe Vorlage zweimal verteilt --
|
||||
nachgemessen zwischen 16:05:24 und 16:05:53, also innerhalb von
|
||||
29 Sekunden. Zweimal gedrueckt, weil beim ersten Mal scheinbar
|
||||
nichts passierte. Ergebnis: 56 Aufgabenpaare, die niemand
|
||||
auseinanderhalten kann.
|
||||
|
||||
DAS BAND SAGT ES EINMAL, MIT ZAHL. Nicht siebzehnmal an den
|
||||
Karten, und nicht gar nicht. Es verschwindet von selbst, sobald
|
||||
nichts mehr doppelt ist -- ein Hinweis, den man wegklicken
|
||||
muesste, kommt beim naechsten Laden wieder. */
|
||||
const doppelPaare = gruppen.reduce((n, g) =>
|
||||
n + g.leute.reduce((m, p) => m + Math.max(0, p.aufgaben.length - 1), 0), 0);
|
||||
if (doppelPaare > 0) {
|
||||
const band = el('div', 'doppelband');
|
||||
band.setAttribute('role', 'status');
|
||||
band.append(el('span', 'doppelband__zahl', String(doppelPaare)));
|
||||
const text = el('div', 'doppelband__text');
|
||||
text.append(el('strong', null, doppelPaare === 1
|
||||
? 'Eine Aufgabe steht doppelt im Brett.'
|
||||
: doppelPaare + ' Aufgaben stehen doppelt im Brett.'));
|
||||
text.append(el('span', null,
|
||||
'Dieselbe Vorlage wurde zweimal verteilt. An den Karten steht '
|
||||
+ '×2 bei den Leuten, die sie doppelt haben.'));
|
||||
band.append(text);
|
||||
brett.parentElement.insertBefore(band, brett);
|
||||
}
|
||||
|
||||
for (const s of SPALTEN) {
|
||||
const eigene = aufgaben.filter((a) => a.status === s.status);
|
||||
const eigene = gruppen.filter((g) => g.status === s.status);
|
||||
/* ZWEI ZAHLEN, UND SIE SAGEN VERSCHIEDENES: wie viele STUECKE
|
||||
ARBEIT hier liegen -- und wie viele Aufgaben daraus geworden
|
||||
sind. Nur die grosse Zahl zu zeigen war das "zu viel"; nur die
|
||||
kleine waere das "zu wenig". */
|
||||
const stueck = eigene.reduce((n, g) => n + g.alle.length, 0);
|
||||
const spalte = el('section', 'spalte');
|
||||
spalte.dataset.status = s.status;
|
||||
/* Die Karte merkt sich, wozu sie gehoert -- daraus findet der
|
||||
Sprung vom Bericht die richtigen wieder, ohne die Liste ein
|
||||
zweites Mal durchzugehen. */
|
||||
/* LEERE SPALTEN SCHRUMPFEN. Drei leere Spalten nahmen drei
|
||||
Viertel des Bildschirms ein, um dreimal "nichts" zu sagen.
|
||||
Sie verschwinden nicht -- eine Spalte, die fehlt, kann man
|
||||
auch nicht fuellen -- aber sie treten zur Seite. */
|
||||
spalte.dataset.leer = eigene.length ? 'nein' : 'ja';
|
||||
/* WIE VIEL PLATZ DIESE SPALTE BEKOMMT, haengt daran, wie viel
|
||||
sie traegt.
|
||||
|
||||
Bis eben bekam jede nicht-leere Spalte gleich viel: "Offen"
|
||||
mit siebzehn Karten war genauso breit wie "Erledigt" mit
|
||||
einer -- beide 369 px, gemessen. Die eine lief dadurch
|
||||
5 600 px nach unten, waehrend neben der anderen Platz frei
|
||||
blieb.
|
||||
|
||||
GEDECKELT BEI SECHS. Ohne Deckel bekaeme eine Spalte mit 100
|
||||
Karten das Hundertfache und alle anderen einen Faden. Sechs
|
||||
ist der Punkt, ab dem mehr Breite nichts mehr bringt, weil
|
||||
ohnehin nur so viele Karten nebeneinander passen. */
|
||||
spalte.style.setProperty('--wuchs', String(Math.min(Math.max(eigene.length, 1), 6)));
|
||||
|
||||
const kopf = el('h2', 'spalte__kopf');
|
||||
kopf.append(el('span', 'spalte__punkt'), el('span', null, s.name),
|
||||
el('span', 'spalte__zahl', String(eigene.length)));
|
||||
spalte.append(kopf);
|
||||
/* Was diese Spalte bedeutet -- steht immer da, nicht nur wenn sie
|
||||
leer ist. "Review" sagte einem Neuen nichts -- deshalb heisst
|
||||
die Spalte seit dem 20.09.2026 "Zur Freigabe". */
|
||||
spalte.append(el('p', 'spalte__unter', s.unter));
|
||||
/* Die kleine Zeile darunter nur, wenn zusammengefasst wurde --
|
||||
sonst stuende dort "17 Aufgaben" neben der 17 darueber. */
|
||||
if (stueck > eigene.length) {
|
||||
spalte.append(el('p', 'spalte__summe',
|
||||
stueck + ' Aufgaben, an mehrere verteilt'));
|
||||
}
|
||||
|
||||
if (!eigene.length) spalte.append(el('p', 'spalte__leer', s.leer));
|
||||
else for (const a of eigene) {
|
||||
const k = karte(a);
|
||||
k.dataset.status = a.status;
|
||||
if (a.status !== 'erledigt' && a.frist && a.frist < heute) k.dataset.faellig = 'ja';
|
||||
/* "Heute faellig" ist ein eigener Zustand, kein Sonderfall von
|
||||
"ueberfaellig": Die Frist ist HEUTE und damit noch nicht
|
||||
vorbei. Ohne dieses Merkmal koennte der Sprung von der
|
||||
Startseite ("Heute faellig") nichts finden -- er zeigt dann
|
||||
eine leere Auswahl, und man glaubt, man sei falsch
|
||||
abgebogen. */
|
||||
if (a.status !== 'erledigt' && a.frist === heute) k.dataset.heute = 'ja';
|
||||
spalte.append(k);
|
||||
else {
|
||||
/* EIN EIGENER KASTEN UM DIE KARTEN.
|
||||
|
||||
Er tut nichts ausser: Er erlaubt dem CSS, die Karten bei
|
||||
viel Platz NEBENEINANDER zu legen statt untereinander.
|
||||
Gemessen am 23.09.2026: Bei 1600 px Breite stand die volle
|
||||
Spalte 350 px breit da und lief 5 776 px nach unten,
|
||||
waehrend rechts daneben 800 px leer blieben.
|
||||
|
||||
WARUM EIN RASTER UND NICHT columns: Mehrspaltiger
|
||||
Textfluss zerschneidet Elemente mitten durch -- am
|
||||
19.09.2026 hat genau das auf der Befinden-Seite die
|
||||
Fragegruppen halbiert, und "break-inside: avoid" hatte
|
||||
still aufgehoert zu wirken. Ein Raster legt ganze Kacheln
|
||||
nebeneinander und kann gar nicht schneiden. */
|
||||
const kasten = el('div', 'spalte__karten');
|
||||
for (const g of eigene) {
|
||||
const k = g.alle.length === 1 ? karte(g.erste) : sammelKarte(g);
|
||||
k.dataset.status = g.status;
|
||||
const a = g.erste;
|
||||
if (g.status !== 'erledigt' && a.frist && a.frist < heute) k.dataset.faellig = 'ja';
|
||||
/* "Heute faellig" ist ein eigener Zustand, kein Sonderfall von
|
||||
"ueberfaellig": Die Frist ist HEUTE und damit noch nicht
|
||||
vorbei. Ohne dieses Merkmal koennte der Sprung von der
|
||||
Startseite ("Heute faellig") nichts finden. */
|
||||
if (g.status !== 'erledigt' && a.frist === heute) k.dataset.heute = 'ja';
|
||||
kasten.append(k);
|
||||
}
|
||||
spalte.append(kasten);
|
||||
}
|
||||
|
||||
brett.append(spalte);
|
||||
}
|
||||
}
|
||||
|
||||
/** Eine Karte fuer ein Stueck Arbeit, an dem mehrere sitzen. */
|
||||
function sammelKarte(g) {
|
||||
const a = g.erste;
|
||||
const k = el('article', 'karte karte--sammel');
|
||||
k.dataset.prio = a.prioritaet;
|
||||
k.dataset.id = String(a.id);
|
||||
/* ALLE NUMMERN STEHEN AN DER KARTE. Der Personenfilter und der
|
||||
Sprung von der Startseite suchen ueber die Nummer; mit nur
|
||||
EINER wuerden sie die Karte uebersehen, sobald die gesuchte
|
||||
Aufgabe die zweite der Gruppe ist. */
|
||||
k.dataset.ids = g.alle.map((x) => x.id).join(',');
|
||||
|
||||
const kopf = el('div', 'karte__kopf');
|
||||
kopf.append(el('h3', 'karte__titel', a.titel));
|
||||
/* WIE WEIT SIND WIR -- als Zahl, nicht als Gefuehl. */
|
||||
const stand = el('span', 'sammel__stand', g.fertig + ' von ' + g.alle.length);
|
||||
stand.dataset.fertig = g.fertig === g.alle.length ? 'ja' : 'nein';
|
||||
kopf.append(stand);
|
||||
k.append(kopf);
|
||||
|
||||
/* Der Balken sagt dasselbe noch einmal fuer das Auge. Farbe ALLEIN
|
||||
traegt nichts -- deshalb steht die Zahl daneben und nicht statt
|
||||
ihrer. */
|
||||
const balken = el('div', 'sammel__balken');
|
||||
balken.setAttribute('role', 'img');
|
||||
balken.setAttribute('aria-label', g.fertig + ' von ' + g.alle.length + ' erledigt');
|
||||
const fuell = el('span');
|
||||
fuell.style.setProperty('--anteil', String(Math.round(g.fertig / g.alle.length * 100)));
|
||||
balken.append(fuell);
|
||||
k.append(balken);
|
||||
|
||||
/* DIE BESCHREIBUNG STEHT NICHT AUF DER KARTE, SONDERN DAHINTER.
|
||||
|
||||
Auf einem Brett sucht man: was, wer, wie weit, bis wann. Der
|
||||
Titel beantwortet das "was" -- "Chat moderieren und neue
|
||||
Zuschauer begruessen" braucht keine Erlaeuterung, um erkannt zu
|
||||
werden. Die Beschreibung ist die Feinheit fuer den, der die
|
||||
Aufgabe UEBERNIMMT, nicht fuer den, der das Brett ueberfliegt.
|
||||
|
||||
Gemessen war sie vier bis sechs Zeilen je Karte. Bei siebzehn
|
||||
Karten sind das ueber achthundert Pixel fuer etwas, das man
|
||||
beim Suchen ueberspringt.
|
||||
|
||||
SIE IST NICHT WEG -- ein Knopf, und sie steht da. Ein Knopf,
|
||||
der nur erscheint, wenn es auch etwas zu lesen gibt. */
|
||||
/* KEIN EIGENER KNOPF FUER DIE BESCHREIBUNG.
|
||||
|
||||
Es stand hier einer, und er war gemessen 44 px hoch -- die
|
||||
Hausregel fuer etwas, das man mit dem Finger trifft. Bei
|
||||
siebzehn Karten sind das 750 px fuer eine Moeglichkeit, die man
|
||||
selten braucht: Auf einem Brett sucht man, man liest nicht.
|
||||
|
||||
DIE BESCHREIBUNG IST TROTZDEM DA. Jeder Name auf der Karte
|
||||
oeffnet genau die Aufgabe dieser Person -- und dort steht sie,
|
||||
vollstaendig und aenderbar. Ein Weg statt zwei, und der
|
||||
vorhandene ist der bessere: Man will ohnehin meist etwas TUN,
|
||||
nicht nur nachlesen.
|
||||
|
||||
Auf der EINZELkarte bleibt sie stehen. Dort ist sie kein
|
||||
Vielfaches, sondern ein Text. */
|
||||
|
||||
const zeile = el('div', 'karte__zeile');
|
||||
if (a.prioritaet === 'hoch' && g.status !== 'erledigt') {
|
||||
zeile.append(el('span', 'karte__wichtig', 'wichtig'));
|
||||
}
|
||||
if (a.eskalation) {
|
||||
const e = el('span', 'karte__eskal', a.eskalation.wort);
|
||||
e.dataset.stufe = a.eskalation.stufe;
|
||||
zeile.append(e);
|
||||
}
|
||||
/* Frist, Kanal und Kategorie genau wie auf der Einzelkarte --
|
||||
dieselben Hilfsmittel, dieselben Klassen. Zwei Schreibweisen
|
||||
fuer dieselbe Angabe waeren zwei Stellen, an denen sie sich
|
||||
auseinanderentwickeln. */
|
||||
if (a.frist) {
|
||||
const faellig = !!a.eskalation && a.eskalation.grund === 'frist';
|
||||
zeile.append(el('span', 'karte__frist' + (faellig ? ' karte__frist--faellig' : ''),
|
||||
'bis ' + datumHuebsch(a.frist)));
|
||||
}
|
||||
if (a.kanal) {
|
||||
const kName = kanaele.find((x) => x.wert === a.kanal)?.name;
|
||||
if (kName) {
|
||||
const z = el('span', 'aufgabe__kanal', kName);
|
||||
z.dataset.kanal = a.kanal;
|
||||
zeile.append(z);
|
||||
}
|
||||
}
|
||||
if (a.kategorie) {
|
||||
const name = kategorien.find((x) => x.wert === a.kategorie)?.name;
|
||||
if (name) zeile.append(el('span', null, '· ' + name));
|
||||
}
|
||||
if (zeile.childElementCount) k.append(zeile);
|
||||
|
||||
/* ---- Die Leute. Jeder Name traegt seinen eigenen Stand. ------- */
|
||||
const reihe = el('div', 'sammel__leute');
|
||||
reihe.setAttribute('role', 'group');
|
||||
reihe.setAttribute('aria-label', 'Wer daran sitzt');
|
||||
for (const p of g.leute) {
|
||||
/* Der Stand dieser Person ist der ihrer am wenigsten
|
||||
fortgeschrittenen Aufgabe -- dieselbe Regel wie fuer die
|
||||
Gruppe, damit auf der Karte nicht zwei Rechnungen stehen. */
|
||||
const seine = [...p.aufgaben].sort((x, y) => (RANG[x.status] ?? 0) - (RANG[y.status] ?? 0));
|
||||
const b = el('button', 'sammel__person');
|
||||
b.type = 'button';
|
||||
b.dataset.status = seine[0].status;
|
||||
/* ZWEI SCHREIBWEISEN DESSELBEN NAMENS -- der volle und der
|
||||
kurze. Welche zu sehen ist, entscheidet das CSS nach der
|
||||
Breite; der Browser entscheidet das besser als eine Zahl in
|
||||
JavaScript, die beim naechsten Geraet nicht mehr stimmt.
|
||||
|
||||
Gemessen: Vier Namen brauchten am Handy zwei Reihen (74 px),
|
||||
vier Buchstaben brauchen eine (38 px). Der volle Name bleibt
|
||||
im aria-label und im title -- wer ihn hoeren oder lesen will,
|
||||
bekommt ihn. */
|
||||
b.append(el('span', 'sammel__punkt'));
|
||||
b.append(el('span', 'sammel__voll', p.name));
|
||||
b.append(el('span', 'sammel__kurz', (p.name.trim()[0] || '?').toUpperCase()));
|
||||
if (p.aufgaben.length > 1) {
|
||||
/* DOPPELT VERTEILT. Das steht AN der Person und nicht nur im
|
||||
Satz darunter -- man sucht sonst, welche der vier gemeint
|
||||
ist. */
|
||||
b.append(el('span', 'sammel__mal', '\u00d7' + p.aufgaben.length));
|
||||
b.dataset.doppelt = 'ja';
|
||||
}
|
||||
const wort = { offen: 'noch nicht angefangen', arbeit: 'arbeitet daran',
|
||||
review: 'wartet auf Freigabe', erledigt: 'fertig' }[seine[0].status] || seine[0].status;
|
||||
b.setAttribute('aria-label', p.name + ': ' + wort
|
||||
+ (p.aufgaben.length > 1 ? ', steht ' + p.aufgaben.length + '-mal drin' : ''));
|
||||
b.title = b.getAttribute('aria-label');
|
||||
b.addEventListener('click', () => dialogOeffnen(seine[0]));
|
||||
reihe.append(b);
|
||||
}
|
||||
k.append(reihe);
|
||||
|
||||
/* DER SATZ STEHT NICHT MEHR AN JEDER KARTE.
|
||||
|
||||
Beim ersten Bauen stand er hier -- und damit siebzehnmal
|
||||
untereinander, wortgleich. Ein Hinweis, der auf jeder Karte
|
||||
steht, ist kein Hinweis mehr, sondern Tapete; genau die Lehre
|
||||
vom 03.09.2026 ("eine Warnung, die immer kommt, ist keine
|
||||
Warnung"). Ausserdem kostete er zwei Zeilen je Karte.
|
||||
|
||||
Was an der Karte BLEIBT, ist das "x2" an der betroffenen
|
||||
Person -- das ist die Auskunft, die man an DIESER Stelle
|
||||
braucht. Der Satz dazu steht einmal ueber dem Brett, mit der
|
||||
Gesamtzahl, wo man auch etwas dagegen tun kann. */
|
||||
return k;
|
||||
}
|
||||
|
||||
/* ---------- Sprung vom Bericht -------------------------------------------
|
||||
|
||||
"wenn man auf die Kisten drueckt, sofort zu diesem Punkt
|
||||
@@ -954,10 +1286,18 @@
|
||||
if (!brett) return;
|
||||
brett.querySelectorAll('.karte[data-id]').forEach((k) => {
|
||||
if (personId === null) { k.hidden = false; return; }
|
||||
const a = alleAufgaben.find((x) => x.id === Number(k.dataset.id));
|
||||
const dabei = !!a && (
|
||||
a.verantwortlich_id === personId
|
||||
|| (a.zuteilung || []).some((z) => z.person_id === personId));
|
||||
/* ALLE NUMMERN DER KARTE, nicht nur die erste. Seit eine Karte
|
||||
ein STUECK ARBEIT ist, stehen dort bis zu acht Aufgaben. Mit
|
||||
nur der ersten verschwaende die Karte aus dem Filter, sobald
|
||||
die gesuchte Person die zweite ist -- und das sieht aus wie
|
||||
"diese Person hat nichts zu tun". */
|
||||
const nummern = (k.dataset.ids || k.dataset.id || '')
|
||||
.split(',').map(Number).filter(Number.isFinite);
|
||||
const dabei = nummern.some((nr) => {
|
||||
const a = alleAufgaben.find((x) => x.id === nr);
|
||||
return !!a && (a.verantwortlich_id === personId
|
||||
|| (a.zuteilung || []).some((z) => z.person_id === personId));
|
||||
});
|
||||
k.hidden = !dabei;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,7 +105,12 @@
|
||||
var kopf = el('div', 'z-band__kopf');
|
||||
kopf.append(el('h2', 'z-band__titel', 'Wer hat gerade was'));
|
||||
kopf.append(el('p', 'z-band__satz',
|
||||
'Eine Zeile je Person. Tippe eine an, um nur ihre Aufgaben zu sehen.'));
|
||||
/* "Eine Zeile je Person" stimmte, solange es Zeilen waren. Seit
|
||||
dem 23.09.2026 stehen die Leute nebeneinander -- der Satz
|
||||
beschrieb also eine Anordnung, die es nicht mehr gibt. Ein
|
||||
Hinweistext, der etwas anderes sagt als der Bildschirm zeigt,
|
||||
ist schlimmer als keiner: Man sucht dann nach Zeilen. */
|
||||
'Tippe jemanden an, um nur seine Aufgaben zu sehen.'));
|
||||
kasten.append(kopf);
|
||||
|
||||
var leute = daten.leute || [];
|
||||
|
||||
Reference in New Issue
Block a user