Drei Accounts, eine Aufgabenliste: der Kanal

Filipe: "ich hab ja auch 2 neben account, hasidog und dogfather clips."
Der Arbeitsplatz wusste davon nichts -- es gab genau eine Welt, und die
hiess nirgends.

DAS WAR KEIN FEHLENDES FELD, SONDERN EINE MEHRDEUTIGKEIT.
"Schneide drei Ausschnitte" ist bei drei Accounts keine Aufgabe,
sondern eine Frage. Wer sie bekommt, muss nachfragen -- oder raet.
Raet er falsch, ist die Arbeit nicht halb getan, sondern am falschen
Ort, und das faellt erst auf, wenn jemand hinsieht.

WAS DAZUGEKOMMEN IST
- KANAELE in workspace.js (DogFather, HasiDog, DogFather Clips), jeder
  mit einem Satz, der ihn erklaert. Ein Auswahlfeld mit drei Namen und
  ohne ein Wort dazu ist eine Ratefrage fuer jemanden, der neu ist.
- Spalte `kanal` an den Aufgaben, per ADD COLUMN: kein Tabellenneubau,
  keine CHECK-Regel, alte Zeilen bleiben leer.
- Auswahl in beiden Formularen, ein farbiges Zeichen auf der Karte
  (gedeckte Toene -- auf dem Brett stehen bis zu vierzig Karten).

LEER IST EIN GUELTIGER ZUSTAND, kein fehlender. Vieles gilt fuer alles:
eine Absprache im Team, ein Zugang, eine Auswertung. Ein Pflichtfeld
haette dafuer einen falschen Kanal erzwungen, und ein falscher Eintrag
ist schlechter als ein leerer. Deshalb steht dort "Für alle" und kein
Strich.

WARUM EINE LISTE IM QUELLTEXT UND KEINE TABELLE
Sonst gilt hier: Eine abgeschriebene Liste altert. Diese ist keine
Abschrift -- sie laesst sich aus nichts ableiten, weil sie eine
Tatsache ueber Filipes Betrieb ist. Einen Kanal dazuzunehmen ist eine
Zeile. Sobald sich das oefter aendert als ein paarmal im Jahr, gehoert
sie in die Verwaltung; vorher waere das eine Oberflaeche fuer drei
Zeilen.

NEU: pruef-kanaele (21 Pruefungen)
Vier Fragen, und die dritte wird gern vergessen: Kann man ihn setzen?
Faellt ein erfundener auf? Erfaehrt jemand, der ihn nicht benutzen
darf, dass es ihn gibt? Und laesst er sich wieder ENTFERNEN -- ein
Feld, das `""` als "unveraendert" behandelt, macht aus einem
Loeschversuch ein Nichts-Tun, ohne Fehlermeldung.

Abschnitt 6 fragt die Datenbank selbst. Ohne ihn koennte alles gruen
sein, obwohl der Wert nur durch die Auskunft zurueckgereicht wird.

pruef-aufgabenbrett, pruef-modi-katalog 49, pruef-modi-kategorien 25
und pruef-css-klassen unveraendert gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-17 03:01:27 +02:00
co-authored by Claude Opus 5
parent fa8e86ad81
commit b66fc379c8
35 changed files with 833 additions and 362 deletions
+29
View File
@@ -1095,3 +1095,32 @@
color: var(--text-still);
border-style: dashed;
}
/* =====================================================================
DER KANAL AN DER AUFGABE (17.09.2026)
Filipe: „ich hab ja auch 2 neben account, hasidog und dogfather
clips." Drei Accounts, eine Aufgabenliste — ohne ein Zeichen dafür
ist „schneide drei Ausschnitte" keine Aufgabe, sondern eine Frage.
EIN ZEICHEN, KEIN ZWEITER SATZ. Die Zeile unter dem Titel trägt
schon Frist, Kategorie, Person und Bereich. Ein weiteres „· Wort"
wäre darin untergegangen; ein eigener kleiner Rahmen wird gelesen,
ohne lauter zu sein.
GEDECKTE TÖNE, KEIN NEON: Die Farbe soll die drei unterscheidbar
machen, nicht um Aufmerksamkeit kämpfen — auf diesem Brett stehen
bis zu vierzig Karten untereinander.
------------------------------------------------------------------- */
.aufgabe__kanal {
--k-ton: #6f89a8;
display: inline-flex; align-items: center;
padding: 1px 8px; border-radius: 999px;
font-size: .72rem; font-weight: 600; letter-spacing: .02em;
color: color-mix(in srgb, var(--k-ton) 78%, #ffffff 22%);
border: 1px solid color-mix(in srgb, var(--k-ton) 34%, transparent);
background: color-mix(in srgb, var(--k-ton) 12%, transparent);
}
.aufgabe__kanal[data-kanal="dogfather"] { --k-ton: #c2544f; }
.aufgabe__kanal[data-kanal="hasidog"] { --k-ton: #7d6bbd; }
.aufgabe__kanal[data-kanal="clips"] { --k-ton: #3f9c8a; }
+60
View File
@@ -238,6 +238,17 @@
viel Aufwand und sieben Gelegenheiten fuer einen Unterschied,
fuer eine Zeile Text. Dieselbe schlichte Form wie "· Bereich X"
daneben tut es. */
/* DER KANAL STEHT VOR DER KATEGORIE. Er beantwortet die groebere
Frage -- WO ueberhaupt -- und die muss man zuerst wissen, um die
feinere zu verstehen. */
if (a.kanal) {
const kName = kanaele.find((k) => k.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((k) => k.wert === a.kategorie)?.name;
if (name) zeile.append(el('span', null, '· ' + name));
@@ -739,6 +750,7 @@
die Frage auf, fuer wen sie da ist. Genau die soll niemand stellen.
===================================================================== */
let kategorien = [];
let kanaele = [];
/* FUER WESSEN AUFGABEN die Kategorie gilt. Kommt fertig vom Server:
null -> fuer alle eigenen (das Feld steht immer da)
@@ -772,6 +784,47 @@
ausschliesslich an eigenen Aufgaben, und dort gilt sie unabhaengig
davon, wen sie eintraegt. Sonst entscheidet die gewaehlte Person.
Wer eine leere Liste bekommt, sieht das Feld nie. */
/* ---- DIE KANAELE (17.09.2026) --------------------------------------
Filipe: "ich hab ja auch 2 neben account, hasidog und dogfather
clips." Ohne diesen Wert ist "schneide drei Ausschnitte" bei drei
Accounts keine Aufgabe, sondern eine Frage.
EINFACHER ALS BEI DEN KATEGORIEN, und das ist Absicht: Die
Kategorie haengt daran, FUER WEN die Aufgabe ist (`kategorie_fuer`);
ein Kanal gilt fuer jede Aufgabe, die jemand aus dem Haus
anfasst. Zwei verschiedene Sichtbarkeitsregeln fuer zwei Felder,
die nebeneinander stehen, waeren eine Gelegenheit, die falsche zu
ziehen. Hier gibt es genau eine Frage: Darf diese Person
ueberhaupt Kanaele benutzen? */
function kanaeleEinsetzen() {
if (!kanaele.length) return;
for (const ziel of ['f-kanal', 'b-kanal']) {
const feld = $(ziel);
if (!feld) continue;
const vorher = feld.value;
/* "Für alle" ist kein Platzhalter, sondern eine Antwort -- vieles
ist nicht kanalgebunden (eine Absprache, ein Zugang, eine
Auswertung). Deshalb steht dort ein Wort und kein Strich. */
feld.textContent = '';
feld.append(el('option', null, 'Für alle'));
feld.firstChild.value = '';
for (const k of kanaele) {
const o = el('option', null, k.name);
o.value = k.wert;
if (k.text) o.title = k.text;
feld.append(o);
}
feld.value = vorher;
}
}
function kanalFeldZeigen(praefix) {
const huelle = $(praefix === 'f' ? 'feld-kanal' : 'b-feld-kanal');
if (!huelle) return;
huelle.hidden = !kanaele.length;
if (!kanaele.length && $(`${praefix}-kanal`)) $(`${praefix}-kanal`).value = '';
}
function kategorieNoetig(creatorId, verantId) {
if (kategorieFuer === null) return true;
if (!Array.isArray(kategorieFuer) || !kategorieFuer.length) return false;
@@ -806,12 +859,16 @@
alleAufgaben = daten.aufgaben || [];
aufwandUebernehmen(daten);
kategorien = daten.kategorien || [];
kanaele = daten.kanaele || [];
/* `null` muss `null` BLEIBEN -- ein `|| []` daraus zu machen waere
der stille Fehler: Aus "gilt immer" wuerde "gilt nie", und das
Feld verschwaende fuer genau die, die es brauchen. */
kategorieFuer = daten.kategorie_fuer === undefined ? [] : daten.kategorie_fuer;
kategorienEinsetzen();
kategorieFeldZeigen('f');
kanaeleEinsetzen();
kanalFeldZeigen('f');
kanalFeldZeigen('b');
lageZeigen(alleAufgaben);
neuZeichnen();
/* Erst NACH dem Zeichnen -- vorher gibt es die Karten nicht, und
@@ -853,6 +910,7 @@
daten.verantwortlich_extern = $('f-verant').dataset.extern || null;
}
if (!$('feld-kategorie')?.hidden) daten.kategorie = $('f-kategorie').value || '';
if (!$('feld-kanal')?.hidden) daten.kanal = $('f-kanal').value || '';
knopfEl.disabled = true;
try {
@@ -891,6 +949,7 @@
}
kategorieFeldZeigen('b');
if ($('b-kategorie')) $('b-kategorie').value = a.kategorie || '';
if ($('b-kanal')) $('b-kanal').value = a.kanal || '';
if ($('b-aufwand')) $('b-aufwand').value = a.aufwand || '';
window.anhaengeLaden?.(a.id);
dialog.showModal();
@@ -920,6 +979,7 @@
daten.verantwortlich_extern = $('b-verant').dataset.extern || null;
}
if (!$('b-feld-kategorie')?.hidden) daten.kategorie = $('b-kategorie').value || '';
if (!$('b-feld-kanal')?.hidden) daten.kanal = $('b-kanal').value || '';
$('b-speichern').disabled = true;
try {
const a = await hole(`/workspace/api/aufgaben/${bearbeitet.id}`, {