feat(ui): add merge-target dropdown and merge-all controls to planning detail
- Add SubtaskDiffDto and CombinedDiffResultDto to PlanningDtos.cs - Extend IWorkerClient with 5 planning merge methods and 5 events - Implement methods and hub subscriptions on WorkerClient - Add Status and WorktreeState to SubtaskRowViewModel - Add MergeTargetBranches, SelectedMergeTarget, CanMergeAll, MergeAllDisabledReason, MergeAllError, RecomputeCanMergeAll, MergeAllCommand, ReviewCombinedDiffCommand (Task 14 TODO) to DetailsIslandViewModel - Add planning merge section to DetailsIslandView.axaml (merge target ComboBox + buttons + error label), gated on Task.IsPlanningParent - Add 4 xUnit tests covering CanMergeAll logic and DTO shape Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,12 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
public event Action<string>? ListUpdatedEvent;
|
||||
public event Action<WorkerLogEntry>? WorkerLogReceivedEvent;
|
||||
|
||||
public event Action<string, string>? PlanningMergeStartedEvent;
|
||||
public event Action<string, string>? PlanningSubtaskMergedEvent;
|
||||
public event Action<string, string, IReadOnlyList<string>>? PlanningMergeConflictEvent;
|
||||
public event Action<string>? PlanningMergeAbortedEvent;
|
||||
public event Action<string>? PlanningCompletedEvent;
|
||||
|
||||
public WorkerClient(string signalRUrl)
|
||||
{
|
||||
_hub = new HubConnectionBuilder()
|
||||
@@ -123,6 +129,31 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
{
|
||||
WorkerLogReceivedEvent?.Invoke(new WorkerLogEntry(message, level, timestampUtc));
|
||||
});
|
||||
|
||||
_hub.On<string, string>("PlanningMergeStarted", (planningTaskId, targetBranch) =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => PlanningMergeStartedEvent?.Invoke(planningTaskId, targetBranch));
|
||||
});
|
||||
|
||||
_hub.On<string, string>("PlanningSubtaskMerged", (planningTaskId, subtaskId) =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => PlanningSubtaskMergedEvent?.Invoke(planningTaskId, subtaskId));
|
||||
});
|
||||
|
||||
_hub.On<string, string, IReadOnlyList<string>>("PlanningMergeConflict", (planningTaskId, subtaskId, conflictedFiles) =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => PlanningMergeConflictEvent?.Invoke(planningTaskId, subtaskId, conflictedFiles));
|
||||
});
|
||||
|
||||
_hub.On<string>("PlanningMergeAborted", planningTaskId =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => PlanningMergeAbortedEvent?.Invoke(planningTaskId));
|
||||
});
|
||||
|
||||
_hub.On<string>("PlanningCompleted", planningTaskId =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => PlanningCompletedEvent?.Invoke(planningTaskId));
|
||||
});
|
||||
}
|
||||
|
||||
public Task StartAsync()
|
||||
@@ -362,6 +393,46 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
|
||||
public async Task<int> GetPendingDraftCountAsync(string taskId, CancellationToken ct = default)
|
||||
=> await _hub.InvokeAsync<int>("GetPendingDraftCountAsync", taskId, ct);
|
||||
|
||||
public async Task<IReadOnlyList<SubtaskDiffDto>> GetPlanningAggregateAsync(string planningTaskId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _hub.InvokeAsync<List<SubtaskDiffDto>>("GetPlanningAggregate", planningTaskId);
|
||||
return result ?? [];
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CombinedDiffResultDto?> BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hub.InvokeAsync<CombinedDiffResultDto>("BuildPlanningIntegrationBranch", planningTaskId, targetBranch);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MergeAllPlanningAsync(string planningTaskId, string targetBranch)
|
||||
{
|
||||
await _hub.InvokeAsync("MergeAllPlanning", planningTaskId, targetBranch);
|
||||
}
|
||||
|
||||
public async Task ContinuePlanningMergeAsync(string planningTaskId)
|
||||
{
|
||||
await _hub.InvokeAsync("ContinuePlanningMerge", planningTaskId);
|
||||
}
|
||||
|
||||
public async Task AbortPlanningMergeAsync(string planningTaskId)
|
||||
{
|
||||
await _hub.InvokeAsync("AbortPlanningMerge", planningTaskId);
|
||||
}
|
||||
|
||||
// IWorkerClient explicit implementations (drop typed return values)
|
||||
async Task IWorkerClient.StartPlanningSessionAsync(string taskId, CancellationToken ct)
|
||||
=> await StartPlanningSessionAsync(taskId, ct);
|
||||
|
||||
Reference in New Issue
Block a user