feat(ui): add delete-list button to List Settings modal
This commit is contained in:
@@ -17,7 +17,7 @@ MVVM with CommunityToolkit.Mvvm source generators:
|
|||||||
- **TaskEditorView** — Modal dialog for task create/edit
|
- **TaskEditorView** — Modal dialog for task create/edit
|
||||||
- **ListEditorView** — Modal dialog for list create/edit
|
- **ListEditorView** — Modal dialog for list create/edit
|
||||||
- **StatusBarView** — Connection status indicator, active task display
|
- **StatusBarView** — Connection status indicator, active task display
|
||||||
- **ListSettingsModalView** — edits list name, working dir, default commit type, and per-list Model/SystemPrompt/AgentPath. Opened via context menu or gear button on a list row.
|
- **ListSettingsModalView** — edits list name, working dir, default commit type, and per-list Model/SystemPrompt/AgentPath; also deletes the list (and its tasks) via a confirmed "Delete list" button. Opened via context menu or gear button on a list row.
|
||||||
- **RepoImportModalView** — bulk-creates lists from git repos discovered under chosen parent folders. Opened via the folder button beside "New list" in the Lists island, or the "Add repos as lists…" Help-menu item. Repos already wired to a list show as disabled/"(already added)".
|
- **RepoImportModalView** — bulk-creates lists from git repos discovered under chosen parent folders. Opened via the folder button beside "New list" in the Lists island, or the "Add repos as lists…" Help-menu item. Repos already wired to a list show as disabled/"(already added)".
|
||||||
- **DetailsIslandView** — contains an "Agent settings (overrides)" expander with per-task Model/SystemPrompt/AgentPath, showing inherited effective values. Disabled while task is running.
|
- **DetailsIslandView** — contains an "Agent settings (overrides)" expander with per-task Model/SystemPrompt/AgentPath, showing inherited effective values. Disabled while task is running.
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
|||||||
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
|
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
|
||||||
await vm.LoadAsync(rawId, row.Name, row.WorkingDir, row.DefaultCommitType);
|
await vm.LoadAsync(rawId, row.Name, row.WorkingDir, row.DefaultCommitType);
|
||||||
await ShowListSettingsModal(vm);
|
await ShowListSettingsModal(vm);
|
||||||
await RefreshRowAsync(row.Id);
|
if (vm.Deleted) await LoadAsync();
|
||||||
|
else await RefreshRowAsync(row.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
@@ -225,7 +226,8 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
|||||||
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
|
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
|
||||||
await vm.LoadAsync(entity.Id, entity.Name, entity.WorkingDir, entity.DefaultCommitType);
|
await vm.LoadAsync(entity.Id, entity.Name, entity.WorkingDir, entity.DefaultCommitType);
|
||||||
await ShowListSettingsModal(vm);
|
await ShowListSettingsModal(vm);
|
||||||
await RefreshRowAsync(item.Id);
|
if (vm.Deleted) await LoadAsync();
|
||||||
|
else await RefreshRowAsync(item.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,28 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using ClaudeDo.Data;
|
||||||
using ClaudeDo.Data.Models;
|
using ClaudeDo.Data.Models;
|
||||||
|
using ClaudeDo.Data.Repositories;
|
||||||
using ClaudeDo.Ui.Services;
|
using ClaudeDo.Ui.Services;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace ClaudeDo.Ui.ViewModels.Modals;
|
namespace ClaudeDo.Ui.ViewModels.Modals;
|
||||||
|
|
||||||
public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly WorkerClient _worker;
|
private readonly WorkerClient _worker;
|
||||||
|
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
||||||
|
|
||||||
public string ListId { get; set; } = "";
|
public string ListId { get; set; } = "";
|
||||||
|
|
||||||
|
// True after the list was deleted, so the caller reloads the list nav instead of refreshing the row.
|
||||||
|
public bool Deleted { get; private set; }
|
||||||
|
|
||||||
|
// Wired by the view to prompt yes/no before deleting and to surface a blocking-FK error.
|
||||||
|
public Func<string, Task<bool>>? ConfirmAsync { get; set; }
|
||||||
|
public Func<string, Task>? ShowErrorAsync { get; set; }
|
||||||
|
|
||||||
[ObservableProperty] private string _name = "";
|
[ObservableProperty] private string _name = "";
|
||||||
[ObservableProperty] private string _workingDir = "";
|
[ObservableProperty] private string _workingDir = "";
|
||||||
[ObservableProperty] private string _defaultCommitType = CommitTypeRegistry.DefaultType;
|
[ObservableProperty] private string _defaultCommitType = CommitTypeRegistry.DefaultType;
|
||||||
@@ -29,9 +40,10 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
|||||||
|
|
||||||
public Action? CloseAction { get; set; }
|
public Action? CloseAction { get; set; }
|
||||||
|
|
||||||
public ListSettingsModalViewModel(WorkerClient worker)
|
public ListSettingsModalViewModel(WorkerClient worker, IDbContextFactory<ClaudeDoDbContext> dbFactory)
|
||||||
{
|
{
|
||||||
_worker = worker;
|
_worker = worker;
|
||||||
|
_dbFactory = dbFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadAsync(
|
public async Task LoadAsync(
|
||||||
@@ -78,6 +90,35 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
|||||||
CloseAction?.Invoke();
|
CloseAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task DeleteAsync()
|
||||||
|
{
|
||||||
|
var displayName = string.IsNullOrWhiteSpace(Name) ? "Untitled" : Name;
|
||||||
|
if (ConfirmAsync is not null)
|
||||||
|
{
|
||||||
|
var ok = await ConfirmAsync($"Delete list \"{displayName}\" and all its tasks? This cannot be undone.");
|
||||||
|
if (!ok) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var ctx = await _dbFactory.CreateDbContextAsync();
|
||||||
|
var lists = new ListRepository(ctx);
|
||||||
|
await lists.DeleteAsync(ListId);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (
|
||||||
|
ex.Message.Contains("FOREIGN KEY", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| ex.InnerException?.Message.Contains("FOREIGN KEY", StringComparison.OrdinalIgnoreCase) == true)
|
||||||
|
{
|
||||||
|
if (ShowErrorAsync is not null)
|
||||||
|
await ShowErrorAsync("This list has planning sessions with child tasks. Discard those first, then delete the list.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Deleted = true;
|
||||||
|
CloseAction?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void Cancel() => CloseAction?.Invoke();
|
private void Cancel() => CloseAction?.Invoke();
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ public partial class ListsIslandView : UserControl
|
|||||||
{
|
{
|
||||||
var window = new ListSettingsModalView { DataContext = modal };
|
var window = new ListSettingsModalView { DataContext = modal };
|
||||||
modal.CloseAction = () => window.Close();
|
modal.CloseAction = () => window.Close();
|
||||||
|
modal.ConfirmAsync = ShowConfirmAsync;
|
||||||
|
modal.ShowErrorAsync = ShowErrorDialogAsync;
|
||||||
var top = TopLevel.GetTopLevel(this) as Window;
|
var top = TopLevel.GetTopLevel(this) as Window;
|
||||||
if (top is null) window.Show();
|
if (top is null) window.Show();
|
||||||
else await window.ShowDialog(top);
|
else await window.ShowDialog(top);
|
||||||
@@ -93,6 +95,43 @@ public partial class ListsIslandView : UserControl
|
|||||||
private static System.Threading.Tasks.Task JumpToTaskAsync(IslandsShellViewModel s, string listId, string taskId)
|
private static System.Threading.Tasks.Task JumpToTaskAsync(IslandsShellViewModel s, string listId, string taskId)
|
||||||
=> JumpToTaskHelper.SelectAsync(s, listId, taskId);
|
=> JumpToTaskHelper.SelectAsync(s, listId, taskId);
|
||||||
|
|
||||||
|
private async System.Threading.Tasks.Task ShowErrorDialogAsync(string message)
|
||||||
|
{
|
||||||
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
||||||
|
if (owner is null) return;
|
||||||
|
|
||||||
|
var ok = new Button { Content = "OK", MinWidth = 90 };
|
||||||
|
var dialog = new Window
|
||||||
|
{
|
||||||
|
Title = "Error",
|
||||||
|
Width = 360,
|
||||||
|
SizeToContent = SizeToContent.Height,
|
||||||
|
CanResize = false,
|
||||||
|
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||||
|
ShowInTaskbar = false,
|
||||||
|
Background = this.FindResource("SurfaceBrush") as IBrush,
|
||||||
|
Content = new StackPanel
|
||||||
|
{
|
||||||
|
Spacing = 16,
|
||||||
|
Margin = new Thickness(20),
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new TextBlock { Text = message, TextWrapping = TextWrapping.Wrap },
|
||||||
|
new StackPanel
|
||||||
|
{
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
Spacing = 8,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Right,
|
||||||
|
Children = { ok },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ok.Click += (_, _) => dialog.Close();
|
||||||
|
await dialog.ShowDialog(owner);
|
||||||
|
}
|
||||||
|
|
||||||
private async System.Threading.Tasks.Task<bool> ShowConfirmAsync(string message)
|
private async System.Threading.Tasks.Task<bool> ShowConfirmAsync(string message)
|
||||||
{
|
{
|
||||||
var owner = TopLevel.GetTopLevel(this) as Window;
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
||||||
|
|||||||
@@ -42,6 +42,10 @@
|
|||||||
<Setter Property="Foreground" Value="{DynamicResource DeepBrush}"/>
|
<Setter Property="Foreground" Value="{DynamicResource DeepBrush}"/>
|
||||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style Selector="Button.danger">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource BloodBrush}"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
</Style>
|
||||||
</Window.Styles>
|
</Window.Styles>
|
||||||
|
|
||||||
<Border Background="{DynamicResource SurfaceBrush}"
|
<Border Background="{DynamicResource SurfaceBrush}"
|
||||||
@@ -168,12 +172,14 @@
|
|||||||
Background="{DynamicResource DeepBrush}"
|
Background="{DynamicResource DeepBrush}"
|
||||||
BorderBrush="{DynamicResource LineBrush}"
|
BorderBrush="{DynamicResource LineBrush}"
|
||||||
BorderThickness="0,1,0,0">
|
BorderThickness="0,1,0,0">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8"
|
<Grid ColumnDefinitions="Auto,*,Auto" VerticalAlignment="Center" Margin="16,0">
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
<Button Grid.Column="0" Content="Delete list" Classes="danger"
|
||||||
Margin="16,0">
|
Command="{Binding DeleteCommand}" MinWidth="90"/>
|
||||||
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8">
|
||||||
<Button Content="Cancel" Command="{Binding CancelCommand}" MinWidth="90"/>
|
<Button Content="Cancel" Command="{Binding CancelCommand}" MinWidth="90"/>
|
||||||
<Button Content="Save" Classes="primary" Command="{Binding SaveCommand}" MinWidth="90"/>
|
<Button Content="Save" Classes="primary" Command="{Binding SaveCommand}" MinWidth="90"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
Reference in New Issue
Block a user