feat(ui): queue planning subtasks sequentially and surface waiting status

Adds a "Queue subtasks sequentially" context-menu entry on rows with planning children, wires it to WorkerHub.QueuePlanningSubtasksAsync via IWorkerClient. TaskRowViewModel exposes IsWaiting/StatusChipClass for the new Waiting status, and HasPlanningChildren keeps parents expandable after they leave the planning state. TasksIslandViewModel auto-collapses parents whose every child is Done and includes Waiting children in the queued virtual list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-04-25 09:37:04 +02:00
parent 45320427e8
commit 8f94dddbc5
6 changed files with 61 additions and 2 deletions

View File

@@ -25,6 +25,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
[ObservableProperty] private bool _dropHintBelow;
[ObservableProperty] private string? _parentTaskId;
[ObservableProperty] private bool _isExpanded = true;
[ObservableProperty] private bool _hasPlanningChildren;
public DateTime CreatedAt { get; init; }
public string CreatedAtFormatted => CreatedAt == default ? "—" : $"Created {CreatedAt:MMM d}";
@@ -34,7 +35,9 @@ public sealed partial class TaskRowViewModel : ViewModelBase
public int StepsCompleted { get; init; }
public bool IsChild => !string.IsNullOrEmpty(ParentTaskId);
public bool IsPlanningParent => Status == TaskStatus.Planning || Status == TaskStatus.Planned;
public bool IsPlanningParent => Status == TaskStatus.Planning
|| Status == TaskStatus.Planned
|| HasPlanningChildren;
public bool IsDraft => Status == TaskStatus.Draft;
public bool CanOpenPlanningSession => Status == TaskStatus.Manual && !IsChild;
@@ -54,6 +57,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
public bool IsOverdue => ScheduledFor is { } d && d.Date < DateTime.Today && !Done;
public bool IsRunning => Status == TaskStatus.Running;
public bool IsQueued => Status == TaskStatus.Queued;
public bool IsWaiting => Status == TaskStatus.Waiting;
public bool HasSchedule => ScheduledFor.HasValue;
public bool HasLiveTail => IsRunning && !string.IsNullOrEmpty(LiveTail);
@@ -67,6 +71,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
TaskStatus.Failed => "error",
TaskStatus.Done => "review",
TaskStatus.Queued => "queued",
TaskStatus.Waiting => "waiting",
_ => "idle",
};
@@ -75,6 +80,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
OnPropertyChanged(nameof(StatusChipClass));
OnPropertyChanged(nameof(IsRunning));
OnPropertyChanged(nameof(IsQueued));
OnPropertyChanged(nameof(IsWaiting));
OnPropertyChanged(nameof(HasLiveTail));
OnPropertyChanged(nameof(IsPlanningParent));
OnPropertyChanged(nameof(PlanningBadge));
@@ -89,6 +95,9 @@ public sealed partial class TaskRowViewModel : ViewModelBase
OnPropertyChanged(nameof(CanOpenPlanningSession));
}
partial void OnHasPlanningChildrenChanged(bool value)
=> OnPropertyChanged(nameof(IsPlanningParent));
partial void OnBranchChanged(string? value) => OnPropertyChanged(nameof(HasBranch));
partial void OnLiveTailChanged(string? value) => OnPropertyChanged(nameof(HasLiveTail));
partial void OnDoneChanged(bool value) => OnPropertyChanged(nameof(IsOverdue));