Die rechte Hand fuehrt ihre Aufgaben, jede Karte hat denselben Fuss, und Dubletten lassen sich aufraeumen
=== 1. BEARBEITEN UND LOESCHEN, WAS SIE ANGELEGT HAT ===
Filipe: "kuemmer dich bitte auch drum dass die rechte hand, wenn sie
aufgaben an die modis oder linke hand erstellt, will ich dass sie die
moeglichkeit hat die auch zu bearbeiten und zu loeschen bitte.
perfektionier das fuer sie und fuer dogfather."
WARUM ES VORHER NICHT GING, und es sah nicht danach aus: `creator_id`
heisst nicht "wer hat sie angelegt", sondern "zu wem gehoert sie" (so
steht es am Tabellenkopf). Verteilt die rechte Hand eine Aufgabe an
einen Modi, steht dort der MODI. Sie erfuellte damit an ihrer eigenen
Aufgabe keine der drei Bedingungen von `darfAendern` und bekam 403 --
auf einen Knopf, den die Oberflaeche ihr trotzdem anbot, weil sie ihn
an `darf_verteilen` haengte: eine Auskunft ueber die PERSON, wo die
Frage der AUFGABE gilt.
Die Spalte `erstellt_von` gibt es seit jeher und wird beim Anlegen
gefuellt -- die Sichtbarkeitsregeln fragen sie an sechs Stellen ab. Sie
stand nur nie in dieser einen Zeile. Und sie fehlte in SPALTEN, kam
also in keiner Aufgabe mit: Die neue Regel waere ein Vergleich gegen
`undefined` geblieben.
DIE REGEL IST ALLGEMEIN, NICHT AUF EINE ROLLE GEMUENZT: wer etwas
angelegt hat, darf es auch aendern. Ein Rollenname waere die naechste
zweite Wahrheit -- in dieser Woche ist genau das dreimal veraltet.
LOESCHEN BEKOMMT EINE EIGENE FRAGE, weil es das Einzige ist, was sich
nicht zuruecknehmen laesst: `darfAufgabenVerteilen(person) &&
darfAendern(person, aufgabe)`. Damit darf sie ihre eigenen -- und der
Modi, bei dem die Aufgabe LIEGT, darf sie weiterhin bearbeiten, aber
nicht verschwinden lassen. Ablehnen und Abbrechen sind die Wege dafuer.
Die Loesch-Route holt die Aufgabe jetzt mit der Sichtbarkeitsregel und
antwortet mit 404 statt 403, wenn es sie fuer diese Person nicht gibt
-- sonst liesse sich durch Ausprobieren herausfinden, welche Nummern
vergeben sind. Beim Aendern stand das schon so, eine Route weiter oben.
pruef-verteilen: 19 -> 30 Punkte. Mit drei Gegenproben, ohne die "sie
darf" auch dann gruen waere, wenn jeder alles duerfte: der Modi wird
abgewiesen (403), die Aufgabe steht danach noch da, und eine FREMDE
Aufgabe loescht sie nicht.
=== 2. JEDE KARTE HAT DENSELBEN FUSS ===
Filipe: "wer hat sie soll bitte bei all diesen aufgaben stehen. bei all
diesen kategorien da. ... es soll auch immer gleich aussehen und nicht
manchmal verschoben und so."
ZWEI URSACHEN, und keine davon war Zufall:
a) "Wer hat sie?" entstand nur, solange oben "Alle" gewaehlt war
(`if (anAlle)`). Wer auf einen Namen tippte, verlor den Knopf an
ALLEN zwoelf Karten, ohne dass irgendwo stand, warum. Die Auskunft
"wer aus dem Team hat diese Vorlage" haengt aber an der VORLAGE,
nicht an der Auswahl -- sie daran zu binden war der Fehler.
b) Der Fuss war EINE Reihe mit `flex-wrap`, und wie viele Angaben
darin stehen, haengt von der Karte ab: "Frist" immer, "fuer:
Rolle" manchmal, "liegt bei 4 von 5" nur, wenn schon jemand sie
hat. Karten ohne den dritten Text hatten noch Platz fuer einen
Knopf, Karten mit ihm nicht -- also stand "An alle" mal neben der
Frist und mal darunter. Zwoelf Karten, drei verschiedene Fuesse.
Jetzt zwei Reihen mit fester Aufgabe: oben, was man LIEST; unten, was
man DRUECKT. Die Knopfreihe ist immer die letzte Zeile und sitzt am
unteren Rand, also stehen die Knoepfe bei allen Karten einer Reihe auf
derselben Hoehe -- auch wenn der Text darueber verschieden lang ist.
Die Rueckseite verteilt jetzt IMMER an alle. Vorher nahm sie
`katalogZiel()`; solange sie nur bei "Alle" existierte, war das
dasselbe. Seit sie immer da ist, waere es eine Falle: Der Knopf sagt
"Nachholen - 3 fehlen" und gaebe sie einer einzigen Person.
=== 3. DUBLETTEN AUFRAEUMEN ===
Filipe zu "Diene x6 - Ghost x6 - Marina x6 - Miss x6" bei "0 von 24":
"mach aus den 6 1 mal bitte, ich hab mich da geirrt."
`tools/aufgaben-doppelte.mjs` raeumt das auf. Es TUT VON SICH AUS
NICHTS: ohne `--wirklich` zeigt es nur, was passieren wuerde. Mit
`--wirklich` legt es ZUERST eine Kopie der Datenbank an (`VACUUM INTO`,
nicht `cp` -- eine blosse Dateikopie kann das WAL verlieren) und nennt
den Befehl, mit dem man zurueckkommt.
WELCHE BLEIBT, ist nicht beliebig: eine erledigte, wenn es sie gibt
(getane Arbeit wirft man nicht weg), sonst eine begonnene, sonst die
aelteste. An einer Wegwerf-Datenbank durchgespielt: 12 Aufgaben, zwei
Menschen, einer mit einer erledigten darunter -- es blieben genau die
richtigen zwei stehen, die Einzelaufgabe blieb unberuehrt, und das
Nachzaehlen am Ende meldete null Dubletten.
Geprueft: pruef-verteilen (30), pruef-vorlagen (24),
pruef-aufgaben-vorlagen, pruef-aufgabenbrett, pruef-modi-katalog (150),
pruef-bewerbung-aufgaben (101).
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -759,7 +759,26 @@
|
||||
Marke je Mensch und nicht nur ein Zaehler. */
|
||||
const schonDa = new Map();
|
||||
const schonWer = new Map(); // Vorlage -> Map(Personennummer -> Aufgabe)
|
||||
if (anAlle) {
|
||||
/* ==== WER SIE HAT, WIRD IMMER AUSGERECHNET (25.09.2026) =========
|
||||
|
||||
Filipe: „wer hat sie soll bitte bei all diesen aufgaben stehen.
|
||||
bei all diesen kategorien da. … es soll auch immer gleich
|
||||
aussehen und nicht manchmal verschoben und so."
|
||||
|
||||
Hier stand `if (anAlle)`. Die Rueckseite der Karte -- und damit
|
||||
der Knopf „Wer hat sie?" -- entstand deshalb nur, solange oben
|
||||
„Alle" gewaehlt war. Wer auf einen Namen tippte, verlor den
|
||||
Knopf an ALLEN zwoelf Karten, ohne dass irgendwo stand, warum.
|
||||
|
||||
DIE AUSKUNFT IST DIESELBE, egal wen man oben gewaehlt hat: „wer
|
||||
aus dem Team hat diese Vorlage schon". Sie haengt an der
|
||||
Vorlage, nicht an der Auswahl. Sie deshalb an die Auswahl zu
|
||||
binden war der Fehler.
|
||||
|
||||
`schonDa` BLEIBT, WIE ES WAR: Das ist die andere Frage („liegt
|
||||
sie bei DER Person, die ich gerade meine") und die gehoert
|
||||
weiterhin zur Auswahl. Zwei Fragen, zwei Karten. */
|
||||
{
|
||||
const zielIds = new Set(zielbar.map((p) => String(p.id)));
|
||||
for (const a of U.aufgaben()) {
|
||||
if (!a.vorlage) continue;
|
||||
@@ -775,9 +794,10 @@
|
||||
if (!bisher || (nochOffen(a) && !nochOffen(bisher))) {
|
||||
schonWer.get(a.vorlage).set(werHats, a);
|
||||
}
|
||||
schonDa.set(a.vorlage, a);
|
||||
if (anAlle) schonDa.set(a.vorlage, a);
|
||||
}
|
||||
} else if (wem || !darfVerteilenV) {
|
||||
}
|
||||
if (!anAlle && (wem || !darfVerteilenV)) {
|
||||
for (const a of U.aufgaben()) {
|
||||
if (!a.vorlage) continue;
|
||||
if (wem && String(a.verantwortlich_id) !== String(wem)) continue;
|
||||
@@ -906,24 +926,47 @@
|
||||
/* WER SIE HAT -- je Mensch, nicht als Zahl. Bei EINER gewaehlten
|
||||
Person bleibt es beim alten Weg: Dort ist `schon` bereits die
|
||||
individuelle Auskunft. */
|
||||
const werHat = anAlle ? (schonWer.get(v.schluessel) || new Map()) : null;
|
||||
const alleHabenSie = anAlle && zielbar.length > 0 && werHat.size >= zielbar.length;
|
||||
const werHat = schonWer.get(v.schluessel) || new Map();
|
||||
const alleHabenSie = zielbar.length > 0 && werHat.size >= zielbar.length;
|
||||
/* DIE KARTE TRITT ERST ZURUECK, WENN SIE WIRKLICH DURCH IST.
|
||||
Bei "alle" heisst `schon` ab EINEM Menschen -- eine Vorlage,
|
||||
die einer von fuenf hat, halb auszublenden waere das Gegenteil
|
||||
der Auskunft: Sie ist die, die noch Arbeit macht. */
|
||||
/* ==== ZWEI FESTE REIHEN STATT EINER, DIE UMBRICHT =============
|
||||
(25.09.2026)
|
||||
|
||||
Filipe: „es soll auch immer gleich aussehen und nicht manchmal
|
||||
verschoben und so."
|
||||
|
||||
WORAN ES LAG, und es war kein Zufall: Im Fuss standen Angaben
|
||||
und Knoepfe in EINER Reihe mit `flex-wrap`. Wie viele Angaben
|
||||
es gibt, haengt aber von der Karte ab -- „Frist", manchmal
|
||||
„fuer: Rolle", manchmal „liegt bei 4 von 5". Karten ohne den
|
||||
dritten Text hatten in der ersten Reihe noch Platz fuer einen
|
||||
Knopf, Karten mit ihm nicht. Also stand „An alle" mal neben
|
||||
der Frist und mal darunter, und „Wer hat sie?" rutschte
|
||||
hinterher. Zwoelf Karten, drei verschiedene Fuesse.
|
||||
|
||||
JETZT ZWEI REIHEN MIT FESTER AUFGABE: oben, was man LIEST,
|
||||
unten, was man DRUECKT. Die Knopfreihe sitzt am unteren Rand
|
||||
der Karte (`margin-top: auto` im Stilblatt), damit sie bei
|
||||
allen Karten einer Zeile auf derselben Hoehe steht -- auch
|
||||
wenn der Text darueber verschieden lang ist. */
|
||||
if (anAlle ? alleHabenSie : !!schon) karte.dataset.schon = 'ja';
|
||||
karte.append(el('h3', 'vorlagenbrett__titel', v.titel));
|
||||
karte.append(el('p', 'vorlagenbrett__text', v.text));
|
||||
const fuss = el('div', 'vorlagenbrett__fuss');
|
||||
fuss.append(el('span', 'vorlagenbrett__frist',
|
||||
const fussInfo = el('div', 'vorlagenbrett__fuss-info');
|
||||
const fussTun = el('div', 'vorlagenbrett__fuss-tun');
|
||||
fuss.append(fussInfo, fussTun);
|
||||
fussInfo.append(el('span', 'vorlagenbrett__frist',
|
||||
v.tage === 1 ? 'Frist: morgen' : `Frist: in ${v.tage} Tagen`));
|
||||
/* Die empfohlene Rolle steht im Dokument neben jeder Aufgabe und
|
||||
ist die halbe Auskunft: "Technik-Check" heisst etwas anderes,
|
||||
je nachdem, wer ihn macht. */
|
||||
if (v.rolle) fuss.append(el('span', 'vorlagenbrett__frist', `für: ${v.rolle}`));
|
||||
if (v.rolle) fussInfo.append(el('span', 'vorlagenbrett__frist', `für: ${v.rolle}`));
|
||||
if (schon && !anAlle) {
|
||||
fuss.append(el('span', 'vorlagenbrett__schon',
|
||||
fussInfo.append(el('span', 'vorlagenbrett__schon',
|
||||
`schon übernommen · ${U.statusName[schon.status] || schon.status}`));
|
||||
} else if (anAlle && werHat.size) {
|
||||
/* BEI "ALLE" IST EIN FREMDER STATUS KEINE AUSKUNFT: Fuenf
|
||||
@@ -931,7 +974,7 @@
|
||||
einzigen -- welcher, haengt von der Reihenfolge der Daten ab.
|
||||
Der Fuss sagt deshalb nur, WIE WEIT es ist; WER, steht eine
|
||||
Zeile tiefer namentlich. */
|
||||
fuss.append(el('span', 'vorlagenbrett__schon', alleHabenSie
|
||||
fussInfo.append(el('span', 'vorlagenbrett__schon', alleHabenSie
|
||||
? `liegt bei allen ${zielbar.length}`
|
||||
: `liegt bei ${werHat.size} von ${zielbar.length}`));
|
||||
}
|
||||
@@ -986,22 +1029,22 @@
|
||||
person_id: katalogZiel(), nochmal: wirklichNochmal,
|
||||
});
|
||||
});
|
||||
fuss.append(k);
|
||||
fussTun.append(k);
|
||||
} else if (meine) {
|
||||
/* SCHON BEWORBEN. Der Satz sagt, WER antwortet -- sonst wartet
|
||||
man auf niemanden und fragt am nächsten Tag im Chat. */
|
||||
fuss.append(el('span', 'vorlagenbrett__warte',
|
||||
fussInfo.append(el('span', 'vorlagenbrett__warte',
|
||||
'beworben – DogFather oder die rechte Hand antworten'));
|
||||
const zurueck = el('button', 'knopf knopf--klein', 'Zurückziehen');
|
||||
zurueck.type = 'button';
|
||||
zurueck.addEventListener('click', () => bewerbungZurueck(zurueck, meine));
|
||||
fuss.append(zurueck);
|
||||
fussTun.append(zurueck);
|
||||
} else if (!schon) {
|
||||
const k = el('button', 'knopf knopf--klein', 'Bewerben');
|
||||
k.type = 'button';
|
||||
k.title = 'DogFather oder die rechte Hand entscheiden, ob du sie bekommst.';
|
||||
k.addEventListener('click', () => bewerben(k, v));
|
||||
fuss.append(k);
|
||||
fussTun.append(k);
|
||||
}
|
||||
/* WER SIE SCHON HAT, BEKOMMT KEINEN BEWERBEN-KNOPF. Der Server
|
||||
lehnt das ohnehin ab (liegt_schon_bei_dir) -- ein Knopf, der
|
||||
@@ -1030,7 +1073,7 @@
|
||||
sogar. Wer nur die auffuehrt, die sie haben, beantwortet
|
||||
„wie weit sind wir", aber nicht „wen muss ich noch fragen".
|
||||
Die Fehlenden stehen deshalb mit da, gedaempft, aber lesbar. */
|
||||
if (anAlle && darfVerteilenV && zielbar.length) {
|
||||
if (darfVerteilenV && zielbar.length) {
|
||||
const reihe = el('div', 'vorlagenbrett__wer');
|
||||
reihe.setAttribute('role', 'list');
|
||||
reihe.setAttribute('aria-label', `Wer „${v.titel}“ schon hat`);
|
||||
@@ -1102,9 +1145,18 @@
|
||||
: 'Nur die, die sie noch nicht haben. Niemand bekommt sie doppelt.';
|
||||
nachholen.addEventListener('click', () => {
|
||||
zuletztVerteilt = v.schluessel;
|
||||
/* DIE RUECKSEITE GEHT IMMER AN ALLE (25.09.2026).
|
||||
|
||||
Hier stand `katalogZiel()` -- also die Person, die oben
|
||||
gewaehlt ist. Solange die Rueckseite nur bei „Alle"
|
||||
existierte, war das dasselbe. Seit sie immer da ist,
|
||||
waere es eine Falle: Der Knopf sagt „Nachholen – 3
|
||||
fehlen" und gaebe sie einer einzigen Person. Die
|
||||
Rueckseite beantwortet eine Frage ueber das TEAM, also
|
||||
handelt ihr Knopf auch am Team. */
|
||||
uebernehmen(nachholen, {
|
||||
art: 'katalog', nr: v.nr, gruppe: kGruppe,
|
||||
person_id: katalogZiel(), nochmal: alleHabenSie,
|
||||
person_id: 'alle', nochmal: alleHabenSie,
|
||||
});
|
||||
});
|
||||
fussH.append(nachholen);
|
||||
@@ -1126,7 +1178,11 @@
|
||||
'Wer hat sie?');
|
||||
umdrehen.type = 'button';
|
||||
umdrehen.addEventListener('click', () => { karte.dataset.gedreht = 'ja'; });
|
||||
vorn.querySelector('.vorlagenbrett__fuss')?.append(umdrehen);
|
||||
/* IMMER AN DERSELBEN STELLE: in der Knopfreihe, hinter dem
|
||||
Verteilen-Knopf. Vorher ging er an den Fuss als Ganzes und
|
||||
landete damit hinter dem letzten TEXT -- auf Karten ohne
|
||||
„liegt bei" war das eine Zeile hoeher als auf Karten mit. */
|
||||
vorn.querySelector('.vorlagenbrett__fuss-tun')?.append(umdrehen);
|
||||
|
||||
/* Und wenn gerade DIESE Karte verteilt wurde: gedreht zeigen. */
|
||||
if (zuletztVerteilt === v.schluessel) {
|
||||
|
||||
Reference in New Issue
Block a user