feat: Parent-Child TaskFlow — Delegated durch sichtbare Child-Tasks ersetzt
- Delegated State aus Board, Entities, DTOs, Frontend-Spalten und Tests entfernt - Parent-Tasks bleiben InProgress waehrend delegierter Agentenarbeit - Child-Tasks laufen sichtbar mit normalen States und parentTaskId - Doku: README, Phase 3, Changelog, Controller-Kommentare angepasst - openclaw-task-board-flow.md als Referenzdoku hinzugefuegt - 73/73 Backend-Tests gruen, Frontend-Build gruen
This commit is contained in:
@@ -92,7 +92,6 @@ function statusLabel(state: string): string {
|
||||
const map: Record<string, string> = {
|
||||
'Backlog': 'Offen',
|
||||
'In progress': 'In Bearbeitung',
|
||||
'Delegated': 'Delegiert',
|
||||
'Review': 'Review',
|
||||
'Blocked': 'Blockiert',
|
||||
'Done': 'Erledigt',
|
||||
@@ -105,7 +104,6 @@ function statusClass(state: string): string {
|
||||
if (s === 'done') return 'is-done'
|
||||
if (s === 'blocked') return 'is-blocked'
|
||||
if (s === 'review') return 'is-review'
|
||||
if (s === 'delegated') return 'is-delegated'
|
||||
if (s === 'in progress') return 'is-progress'
|
||||
return 'is-backlog'
|
||||
}
|
||||
@@ -148,8 +146,27 @@ function relativeTime(date?: string | null): string {
|
||||
return `vor ${days} d`
|
||||
}
|
||||
|
||||
function progressHint(taskLike: Pick<TaskDto, 'lastActivityMessage' | 'expectedFrom'>): string {
|
||||
return taskLike.lastActivityMessage?.trim() || (taskLike.expectedFrom ? `Wartet auf ${taskLike.expectedFrom}` : 'Noch kein relevanter Progress-Status')
|
||||
function childStatusSummary(taskId: string): string {
|
||||
const childItems = children.value.filter(child => child.parentTaskId === taskId)
|
||||
if (!childItems.length) return ''
|
||||
|
||||
const counts = {
|
||||
inProgress: childItems.filter(child => child.state === 'In progress').length,
|
||||
review: childItems.filter(child => child.state === 'Review').length,
|
||||
blocked: childItems.filter(child => child.state === 'Blocked').length,
|
||||
done: childItems.filter(child => child.state === 'Done').length,
|
||||
}
|
||||
|
||||
const parts = [] as string[]
|
||||
if (counts.inProgress) parts.push(`${counts.inProgress} in Arbeit`)
|
||||
if (counts.review) parts.push(`${counts.review} im Review`)
|
||||
if (counts.blocked) parts.push(`${counts.blocked} blockiert`)
|
||||
if (counts.done) parts.push(`${counts.done} erledigt`)
|
||||
return parts.length ? `Child-Tasks: ${parts.join(' · ')}` : `Child-Tasks: ${childItems.length}`
|
||||
}
|
||||
|
||||
function progressHint(taskLike: Pick<TaskDto, 'id' | 'lastActivityMessage' | 'expectedFrom'>): string {
|
||||
return taskLike.lastActivityMessage?.trim() || childStatusSummary(taskLike.id) || (taskLike.expectedFrom ? `Wartet auf ${taskLike.expectedFrom}` : 'Noch kein relevanter Progress-Status')
|
||||
}
|
||||
|
||||
/* ── API calls ───────────────────────────────── */
|
||||
@@ -362,6 +379,7 @@ function handleKeydown(e: KeyboardEvent) {
|
||||
<span class="meta-chip">{{ task.priority }} Priorität</span>
|
||||
<span v-if="task.isAgentTask" class="meta-chip">🤖 Agent-Task</span>
|
||||
<span v-if="task.expectedFrom" class="meta-chip">⏳ Erwartet: {{ task.expectedFrom }}</span>
|
||||
<span v-if="task.parentTaskId" class="meta-chip">↳ Sichtbare Child-Task</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -390,7 +408,7 @@ function handleKeydown(e: KeyboardEvent) {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="task.isAgentTask" class="progress-banner">
|
||||
<div v-if="task.isAgentTask || childStatusSummary(task.id)" class="progress-banner">
|
||||
<strong>Letzter Fortschritt:</strong> {{ progressHint(task) }}
|
||||
</div>
|
||||
|
||||
@@ -424,7 +442,7 @@ function handleKeydown(e: KeyboardEvent) {
|
||||
<option value="Medium">Medium</option>
|
||||
<option value="Low">Low</option>
|
||||
</select>
|
||||
<input v-model="subtaskAssign" class="galaxy-input narrow" placeholder="Zuständig (bao, iris…)" />
|
||||
<input v-model="subtaskAssign" class="galaxy-input narrow" placeholder="Zuständig (bao, iris, researcher…)" />
|
||||
<button class="btn-primary btn-sm" @click="createSubtask" :disabled="creatingSubtask">
|
||||
{{ creatingSubtask ? 'Erstelle…' : 'Anlegen' }}
|
||||
</button>
|
||||
@@ -455,6 +473,8 @@ function handleKeydown(e: KeyboardEvent) {
|
||||
>
|
||||
<option value="Backlog">Offen</option>
|
||||
<option value="In progress">In Arbeit</option>
|
||||
<option value="Review">Review</option>
|
||||
<option value="Blocked">Blockiert</option>
|
||||
<option value="Done">Erledigt</option>
|
||||
</select>
|
||||
<button
|
||||
@@ -520,7 +540,6 @@ function handleKeydown(e: KeyboardEvent) {
|
||||
<select v-model="form.state" class="galaxy-input galaxy-select" :disabled="!canChangeState">
|
||||
<option value="Backlog">Offen</option>
|
||||
<option value="In progress">In Bearbeitung</option>
|
||||
<option value="Delegated">Delegiert</option>
|
||||
<option value="Review">Review</option>
|
||||
<option value="Blocked">Blockiert</option>
|
||||
<option value="Done">Erledigt</option>
|
||||
@@ -543,6 +562,8 @@ function handleKeydown(e: KeyboardEvent) {
|
||||
<option value="programmer">🛠 Programmer</option>
|
||||
<option value="reviewer">🔎 Reviewer</option>
|
||||
<option value="architekt">🏛 Architekt</option>
|
||||
<option value="researcher">🔬 Researcher</option>
|
||||
<option value="executor">⚡ Executor</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="sidebar-field">
|
||||
@@ -559,6 +580,7 @@ function handleKeydown(e: KeyboardEvent) {
|
||||
<div><dt>Erstellt</dt><dd>{{ formatDate(task.createdAt) }}</dd></div>
|
||||
<div><dt>Geändert</dt><dd>{{ formatDate(task.updatedAt, true) }}</dd></div>
|
||||
<div v-if="task.isAgentTask"><dt>Letzter Status</dt><dd>{{ relativeTime(task.lastActivityAt ?? task.updatedAt) }}</dd></div>
|
||||
<div v-if="task.parentTaskId"><dt>Task-Typ</dt><dd>Sichtbare Child-Task</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
@@ -678,7 +700,6 @@ function handleKeydown(e: KeyboardEvent) {
|
||||
|
||||
.state-badge.is-backlog { color: #fde68a; background: rgba(251,191,36,.12); border-color: rgba(251,191,36,.25); }
|
||||
.state-badge.is-progress { color: #86efac; background: rgba(34,197,94,.12); border-color: rgba(34,197,94,.25); }
|
||||
.state-badge.is-delegated { color: #d8b4fe; background: rgba(168,85,247,.12); border-color: rgba(168,85,247,.25); }
|
||||
.state-badge.is-review { color: #fdba74; background: rgba(249,115,22,.12); border-color: rgba(249,115,22,.25); }
|
||||
.state-badge.is-blocked { color: #fda4af; background: rgba(244,63,94,.12); border-color: rgba(244,63,94,.25); }
|
||||
.state-badge.is-done { color: #86efac; background: rgba(34,197,94,.12); border-color: rgba(34,197,94,.25); }
|
||||
|
||||
Reference in New Issue
Block a user