feat(review): unify review actions into the Git-tab cockpit

Grow the detail-pane Git tab into the review+merge cockpit: target,
pre-flight mergeability, inspect actions, then the four review verbs
(Approve & Merge / Send back / Park / Cancel) plus a demoted
Reset (discard branch).

The decision block is gated independently of the merge controls so
sandbox (no-worktree) review tasks still get the buttons.

- Add ParkReviewCommand (-> RejectReviewToIdleAsync)
- Send back (reject-to-queue) disabled until feedback is entered
- Remove the mislabeled [Continue]/[Reset] line from the Output tab
- Accent dot on the Git tab while awaiting review
This commit is contained in:
Mika Kuns
2026-06-18 15:52:41 +02:00
parent b75a7b1b5a
commit 43fb506e87
3 changed files with 210 additions and 77 deletions

View File

@@ -337,7 +337,12 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
public Func<string, System.Threading.Tasks.Task>? ShowErrorAsync { get; set; }
// ── Review ──────────────────────────────────────────────────────────────
[ObservableProperty] private string _reviewFeedback = "";
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasReviewFeedback))]
[NotifyCanExecuteChangedFor(nameof(RejectReviewCommand))]
private string _reviewFeedback = "";
public bool HasReviewFeedback => !string.IsNullOrWhiteSpace(ReviewFeedback);
// Kept for backwards-compat surface — delegates to Merge.RequestConflictResolution
public Func<string, string, System.Threading.Tasks.Task>? RequestConflictResolution
@@ -1169,7 +1174,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
}
}
[RelayCommand]
[RelayCommand(CanExecute = nameof(HasReviewFeedback))]
private async System.Threading.Tasks.Task RejectReviewAsync()
{
if (Task is null || !_worker.IsConnected) return;
@@ -1180,6 +1185,15 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
ReviewFeedback = "";
}
// Park: set the task aside (back to Idle), keeping its worktree intact.
[RelayCommand]
private async System.Threading.Tasks.Task ParkReviewAsync()
{
if (Task is null || !_worker.IsConnected) return;
try { await _worker.RejectReviewToIdleAsync(Task.Id); }
catch { /* stale review action; broadcast reconciles */ }
}
[RelayCommand]
private async System.Threading.Tasks.Task ResetReviewAsync()
{