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:
2026-09-23 16:25:06 +02:00
co-authored by Claude Opus 5
parent 1851d447c3
commit b1319e3124
46 changed files with 1363 additions and 624 deletions
+363 -23
View File
@@ -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;
});
}