Kanban: Checklisten und Voraussetzungen zwischen Aufgaben

- Checkliste je Aufgabe (Tabelle kanban_checklist): im Dialog anlegen,
  abhaken, entfernen; Karte zeigt den Fortschritt
- "Setzt voraus" (Tabelle kanban_dependencies): Aufgabe ist gesperrt und
  bleibt in "Offen", bis alle Voraussetzungen erledigt sind; nur innerhalb
  eines Projekts, Kreise und Selbstbezug werden abgelehnt; gelöschte
  Voraussetzungen entsperren, archivierte zählen als erledigt
- Ziehen einer gesperrten Karte aus "Offen" zeigt, worauf sie wartet
- Migrationen: Tabellen-Neuaufbau über Zwischennamen, damit Fremdschlüssel
  anderer Tabellen nicht auf die gelöschte Alt-Tabelle umgebogen werden
  (Test für ganz alte Datenbanken)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-15 19:10:10 +02:00
1 parent a1a1998d1e
commit 9df0e38f52
10 files changed
+375 -28

No files matched your search

+22 -2
View File
@@ -13,11 +13,21 @@ export function Assignee({ id, users }) {
);
}
export default function TaskCard({ task, users, dragging, onOpen, onDragStart, onDragEnd }) {
// Titel der noch offenen Voraussetzungen einer Aufgabe
export function waitingFor(task, tasks) {
return (task.requires || [])
.map((id) => tasks.find((t) => t.id === id))
.filter((t) => t && t.status !== 'erledigt')
.map((t) => t.title);
}
export default function TaskCard({ task, users, waitingFor: waiting = [], dragging, onOpen, onDragStart, onDragEnd }) {
const done = task.checklist?.filter((c) => c.done).length ?? 0;
const total = task.checklist?.length ?? 0;
return (
<button
type="button"
className={`task-card prio-card-${task.priority}${dragging ? ' dragging' : ''}`}
className={`task-card prio-card-${task.priority}${dragging ? ' dragging' : ''}${task.blocked ? ' blocked' : ''}`}
draggable
onDragStart={onDragStart}
onDragEnd={onDragEnd}
@@ -32,8 +42,18 @@ export default function TaskCard({ task, users, dragging, onOpen, onDragStart, o
)}
</span>
{task.description && <span className="task-desc">{task.description}</span>}
{task.blocked && (
<span className="task-blocked" title="Gesperrt, bis die Voraussetzungen erledigt sind">
<span aria-hidden="true">🔒</span> wartet auf {waiting.join(', ') || 'Voraussetzungen'}
</span>
)}
<span className="task-meta">
<Assignee id={task.assignee} users={users} />
{total > 0 && (
<span className={`task-checks${done === total ? ' complete' : ''}`} title="Checkliste">
☑ {done}/{total}
</span>
)}
</span>
</button>
);