Kanban: Voraussetzung gibt schon bei "Wartet" frei
- Eine abhängige Aufgabe bleibt gesperrt (in "Offen"), bis jede Voraussetzung in "Wartet" oder "Erledigt" steht – vorher erst bei "Erledigt" - Server (UNLOCKING), Karte, Dialog und Meldungen angepasst; Test prüft "In Arbeit" (gesperrt) und "Wartet" (frei) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fef9c8f16e
commit
fdbb31065f
8 files changed
+33
-24
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { initials, PRIORITIES } from './board.js';
|
||||
import { initials, PRIORITIES, UNLOCKING } from './board.js';
|
||||
|
||||
// Karte einer Aufgabe im Board (ziehbar) und Anzeige des Bearbeiters
|
||||
|
||||
@@ -13,11 +13,11 @@ export function Assignee({ id, users }) {
|
||||
);
|
||||
}
|
||||
|
||||
// Titel der noch offenen Voraussetzungen einer Aufgabe
|
||||
// Titel der Voraussetzungen, die noch nicht in "Wartet" oder "Erledigt" stehen
|
||||
export function waitingFor(task, tasks) {
|
||||
return (task.requires || [])
|
||||
.map((id) => tasks.find((t) => t.id === id))
|
||||
.filter((t) => t && t.status !== 'erledigt')
|
||||
.filter((t) => t && !UNLOCKING.includes(t.status))
|
||||
.map((t) => t.title);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function TaskCard({ task, users, waitingFor: waiting = [], draggi
|
||||
</span>
|
||||
{task.description && <span className="task-desc">{task.description}</span>}
|
||||
{task.blocked && (
|
||||
<span className="task-blocked" title="Gesperrt, bis die Voraussetzungen erledigt sind">
|
||||
<span className="task-blocked" title="Gesperrt, bis die Voraussetzungen in „Wartet“ oder „Erledigt“ stehen">
|
||||
<span aria-hidden="true">🔒</span> wartet auf {waiting.join(', ') || 'Voraussetzungen'}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { fmtDate, personName } from '../../api.js';
|
||||
import { COLUMNS, PRIORITIES } from './board.js';
|
||||
import { COLUMNS, PRIORITIES, UNLOCKING } from './board.js';
|
||||
|
||||
// Aufgabe anlegen oder bearbeiten: Titel, Beschreibung, Checkliste, Status, Priorität, Bearbeiter,
|
||||
// Voraussetzungen (gesperrt, bis diese erledigt sind); löschen
|
||||
// Voraussetzungen (gesperrt, bis diese in "Wartet" oder "Erledigt" stehen); löschen
|
||||
|
||||
export default function TaskDialog({ ref, target, users, tasks = [], me, onSave, onDelete }) {
|
||||
const task = target?.task;
|
||||
@@ -22,7 +22,7 @@ export default function TaskDialog({ ref, target, users, tasks = [], me, onSave,
|
||||
const others = tasks
|
||||
.filter((t) => t.id !== task?.id)
|
||||
.sort((a, b) => Number(form.requires.includes(b.id)) - Number(form.requires.includes(a.id)) || a.title.localeCompare(b.title, 'de'));
|
||||
const missing = form.requires.map((id) => tasks.find((t) => t.id === id)).filter((t) => t && t.status !== 'erledigt');
|
||||
const missing = form.requires.map((id) => tasks.find((t) => t.id === id)).filter((t) => t && !UNLOCKING.includes(t.status));
|
||||
const blocked = missing.length > 0;
|
||||
const toggleRequire = (id) => setForm((f) => ({ ...f, requires: f.requires.includes(id) ? f.requires.filter((x) => x !== id) : [...f.requires, id] }));
|
||||
const setCheck = (index, patch) => setForm((f) => ({ ...f, checklist: f.checklist.map((c, i) => (i === index ? { ...c, ...patch } : c)) }));
|
||||
@@ -42,7 +42,7 @@ export default function TaskDialog({ ref, target, users, tasks = [], me, onSave,
|
||||
// Noch nicht hinzugefügter Checklisten-Text soll nicht verloren gehen
|
||||
const checklist = newCheck.trim() ? [...form.checklist, { text: newCheck.trim(), done: false }] : form.checklist;
|
||||
if (blocked && form.status !== 'offen' && form.status !== task?.status) {
|
||||
setError(`Gesperrt – erst erledigen: ${missing.map((t) => `„${t.title}“`).join(', ')}`);
|
||||
setError(`Gesperrt – erst auf „Wartet“ oder „Erledigt“ setzen: ${missing.map((t) => `„${t.title}“`).join(', ')}`);
|
||||
return;
|
||||
}
|
||||
setBusy(true);
|
||||
@@ -136,7 +136,7 @@ export default function TaskDialog({ ref, target, users, tasks = [], me, onSave,
|
||||
</label>
|
||||
</div>
|
||||
<fieldset className="link-picker">
|
||||
<legend>Setzt voraus <small>– gesperrt, bis diese Aufgaben erledigt sind</small></legend>
|
||||
<legend>Setzt voraus <small>– gesperrt, bis diese Aufgaben in „Wartet“ oder „Erledigt“ stehen</small></legend>
|
||||
{others.length === 0 ? (
|
||||
<p className="hint">Keine anderen Aufgaben in diesem Projekt.</p>
|
||||
) : (
|
||||
@@ -146,13 +146,13 @@ export default function TaskDialog({ ref, target, users, tasks = [], me, onSave,
|
||||
<label className="checkbox">
|
||||
<input type="checkbox" checked={form.requires.includes(t.id)} onChange={() => toggleRequire(t.id)} />
|
||||
{t.title}
|
||||
<small>{t.status === 'erledigt' ? ' ✓ erledigt' : ''}</small>
|
||||
<small>{t.status === 'erledigt' ? ' ✓ erledigt' : t.status === 'wartet' ? ' ✓ wartet' : ''}</small>
|
||||
</label>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
{blocked && <p className="hint task-blocked">🔒 Gesperrt, bis erledigt: {missing.map((t) => t.title).join(', ')}</p>}
|
||||
{blocked && <p className="hint task-blocked">🔒 Gesperrt, bis in „Wartet“ oder „Erledigt“: {missing.map((t) => t.title).join(', ')}</p>}
|
||||
</fieldset>
|
||||
{error && <p className="error">{error}</p>}
|
||||
{task && (
|
||||
|
||||
@@ -27,3 +27,6 @@ export function positionAt(cards, index) {
|
||||
if (after == null) return before + 1;
|
||||
return (before + after) / 2;
|
||||
}
|
||||
|
||||
// Eine Voraussetzung gibt ihre abhängigen Aufgaben frei, sobald sie in einer dieser Spalten steht
|
||||
export const UNLOCKING = ['wartet', 'erledigt'];
|
||||
@@ -116,10 +116,10 @@ export default function KanbanBoard() {
|
||||
if (!drag) return;
|
||||
// Die Karte behält ihre Priorität: einsortiert wird nur zwischen Karten derselben Stufe
|
||||
const moved = data.tasks.find((t) => t.id === drag.id);
|
||||
// Gesperrt: bleibt in "Offen", bis die Voraussetzungen erledigt sind
|
||||
// Gesperrt: bleibt in "Offen", bis die Voraussetzungen in "Wartet" oder "Erledigt" stehen
|
||||
if (moved.blocked && status !== 'offen' && moved.status !== status) {
|
||||
setDrag(null);
|
||||
setError(`„${moved.title}“ ist gesperrt – erst erledigen: ${waitingFor(moved, allTasks).join(', ')}`);
|
||||
setError(`„${moved.title}“ ist gesperrt – erst auf „Wartet“ oder „Erledigt“ setzen: ${waitingFor(moved, allTasks).join(', ')}`);
|
||||
return;
|
||||
}
|
||||
const cards = byColumn[status].filter((t) => t.id !== drag.id);
|
||||
|
||||
Reference in new issue
Block a user