feat(ui): Ticket-Projekt pro Liste und Import ueber das Kontextmenue

This commit is contained in:
mika kuns
2026-08-27 13:36:44 +02:00
parent 020f93d55e
commit e91ade9cad
8 changed files with 102 additions and 12 deletions
@@ -11,6 +11,10 @@ using Microsoft.EntityFrameworkCore;
namespace ClaudeDo.Ui.ViewModels.Modals;
// UI-only projection of TicketProjectDto for the ComboBox display text, plus a synthetic
// Id=0 "none" entry (mirrors AgentConfigEditorViewModel's "(none)" AgentInfo placeholder).
public sealed record TicketProjectOption(int Id, string Display);
public sealed partial class ListSettingsModalViewModel : ViewModelBase
{
private readonly IWorkerClient _worker;
@@ -39,6 +43,10 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
// When on, the queue picker holds back a queued task whose declared scope globs overlap a
// running/awaiting-merge sibling in this list (ListConfigEntity.SerializeOnFileOverlap).
[ObservableProperty] private bool _serializeOnFileOverlap;
// Dropdown hidden entirely when no ticket-system base URL is configured (Settings → Files tab).
[ObservableProperty] private bool _ticketsAvailable;
[ObservableProperty] private TicketProjectOption? _selectedTicketProject;
public ObservableCollection<TicketProjectOption> TicketProjects { get; } = new();
public ObservableCollection<string> CommitTypeOptions { get; } = new(CommitTypeRegistry.Types);
@@ -75,6 +83,24 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
var cfg = await _worker.GetListConfigAsync(listId);
VerifyCommand = cfg?.VerifyCommand ?? "";
SerializeOnFileOverlap = cfg?.SerializeOnFileOverlap ?? false;
var ticketSettings = await _worker.GetTicketSettingsAsync();
TicketsAvailable = !string.IsNullOrWhiteSpace(ticketSettings?.ApiBaseUrl);
TicketProjects.Clear();
if (TicketsAvailable)
{
TicketProjects.Add(new TicketProjectOption(0, Loc.T("vm.listSettings.ticketProjectNone")));
foreach (var p in await _worker.GetTicketProjectsAsync())
TicketProjects.Add(new TicketProjectOption(p.Id, $"{p.DepartmentName} / {p.Title}"));
SelectedTicketProject = cfg?.TicketProjectId is int id
? TicketProjects.FirstOrDefault(t => t.Id == id) ?? TicketProjects[0]
: TicketProjects[0];
}
else
{
SelectedTicketProject = null;
}
}
[RelayCommand]
@@ -88,9 +114,15 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
IsManual,
FindingsTracked));
// Tri-state: dropdown never shown (no base URL) -> null, leave the stored link alone.
// Dropdown shown -> always an explicit value, 0 for "— none —" to actively clear it.
// Never null here, or an explicit unlink would silently fail to reach the worker.
var ticketProjectId = TicketsAvailable ? (SelectedTicketProject?.Id ?? 0) : (int?)null;
await Agent.SaveAsync(
string.IsNullOrWhiteSpace(VerifyCommand) ? null : VerifyCommand,
SerializeOnFileOverlap);
SerializeOnFileOverlap,
ticketProjectId);
CloseAction?.Invoke();
}