fix(ui): Aktions- und Bestätigungs-Konsistenz (UX-Audit #3)
Sechs Konsistenz-Fixes: Delete-Task ins Zeilen-Kontextmenü (routet über den Worker wie DetailsIslandViewModel.DeleteTaskAsync, damit ein gelöschtes Child den WaitingForChildren-Parent korrekt weiterschaltet); EnqueueCommand als "Send to queue"-Button im Detail-Pane verdrahtet; Settings-Eintrag im Listen-Kontextmenü ergänzt; Worktree-Discard und Reset-All laufen jetzt über das jeweils vorhandene Confirm-Hook (ConfirmAction / neuer ConfirmAsync-Hook auf WorktreesSettingsTabViewModel) statt ohne Rückfrage bzw. über ein Inline-Reveal-Banner; killSessionTip/closeSession und die deutschen Queue-Strings im usageMonitor vereinheitlicht. Kein zweiter Dialog-Mechanismus eingeführt — überall die vorhandenen Hooks wiederverwendet.
This commit is contained in:
@@ -1146,6 +1146,42 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
"vm.queue.baseDirtyWarning", warning.ModifiedCount, warning.UntrackedCount));
|
||||
}
|
||||
|
||||
// Row-level delete for the task context menu. Routed through the worker (mirrors
|
||||
// DetailsIslandViewModel.DeleteTaskAsync) so a deleted child correctly advances a
|
||||
// WaitingForChildren parent instead of bypassing TaskStateService.
|
||||
[RelayCommand]
|
||||
private async Task DeleteTaskAsync(TaskRowViewModel? row)
|
||||
{
|
||||
if (row is null || _worker is null) return;
|
||||
if (ConfirmAsync is not null)
|
||||
{
|
||||
var ok = await ConfirmAsync(Loc.T("vm.tasksIsland.deleteTaskConfirm", row.Title));
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
bool deleted;
|
||||
string? error;
|
||||
try
|
||||
{
|
||||
(deleted, error) = await _worker.DeleteTaskAsync(row.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorReported?.Invoke(Loc.T("vm.tasksIsland.deleteTaskFailed", ex.Message));
|
||||
return;
|
||||
}
|
||||
if (!deleted)
|
||||
{
|
||||
ErrorReported?.Invoke(error ?? Loc.T("vm.tasksIsland.deleteTaskFailed", "unknown error"));
|
||||
return;
|
||||
}
|
||||
|
||||
Items.Remove(row);
|
||||
Regroup();
|
||||
UpdateSubtitle();
|
||||
TasksChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SendToQueueAsync(TaskRowViewModel? row)
|
||||
{
|
||||
|
||||
@@ -15,12 +15,15 @@ public sealed partial class WorktreesSettingsTabViewModel : ViewModelBase
|
||||
[ObservableProperty] private bool _worktreeAutoCleanupEnabled;
|
||||
[ObservableProperty] private int _worktreeAutoCleanupDays = 7;
|
||||
|
||||
[ObservableProperty] private bool _showResetConfirm;
|
||||
[ObservableProperty] private string _statusMessage = "";
|
||||
[ObservableProperty] private bool _isBusy;
|
||||
|
||||
public OperationStatus ResetStatus { get; } = new();
|
||||
|
||||
/// <summary>Wired by <c>WindowDialogService.ShowSettingsAsync</c>. A missing hook (null)
|
||||
/// must not reset silently — <see cref="ConfirmResetAll"/> aborts instead.</summary>
|
||||
public Func<string, Task<bool>>? ConfirmAsync { get; set; }
|
||||
|
||||
public IReadOnlyList<string> WorktreeStrategies { get; } = new[] { "sibling", "central" };
|
||||
|
||||
public WorktreesSettingsTabViewModel(IWorkerClient worker) => _worker = worker;
|
||||
@@ -52,13 +55,13 @@ public sealed partial class WorktreesSettingsTabViewModel : ViewModelBase
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
|
||||
[RelayCommand] private void RequestResetConfirm() => ShowResetConfirm = true;
|
||||
[RelayCommand] private void CancelResetConfirm() => ShowResetConfirm = false;
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ConfirmResetAll()
|
||||
{
|
||||
ShowResetConfirm = false; StatusMessage = "";
|
||||
if (ConfirmAsync is null) return;
|
||||
if (!await ConfirmAsync(Loc.T("settings.worktrees.confirmRemoveAll"))) return;
|
||||
|
||||
StatusMessage = "";
|
||||
using var op = ResetStatus.Begin(Loc.T("ops.worktrees.resetting"));
|
||||
try
|
||||
{
|
||||
|
||||
@@ -290,6 +290,8 @@ public sealed partial class WorktreesOverviewModalViewModel : ViewModelBase
|
||||
private async Task Discard(WorktreeOverviewRowViewModel? row)
|
||||
{
|
||||
if (row is null || row.State != WorktreeState.Active) return;
|
||||
if (ConfirmAction is not null && !await ConfirmAction(Loc.T("vm.worktreesOverview.discardConfirm", row.TaskTitle))) return;
|
||||
|
||||
var (ok, err) = await _worker.SetWorktreeStateAsync(row.TaskId, WorktreeState.Discarded);
|
||||
if (ok) row.State = WorktreeState.Discarded;
|
||||
else StatusMessage = err ?? Loc.T("vm.worktreesOverview.discardFailed");
|
||||
|
||||
Reference in New Issue
Block a user