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
@@ -285,6 +285,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
var ok = false;
try
{
var ticketsConfigured = await IsTicketsConfiguredAsync();
await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
var lists = new ListRepository(ctx);
var seedNames = new HashSet<string>(new[] { "My Day", "Important", "Planned" });
@@ -293,6 +294,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
foreach (var l in await lists.GetAllAsync(ct))
{
if (seedNames.Contains(l.Name)) continue;
var cfg = await lists.GetConfigAsync(l.Id, ct);
var item = new ListNavItemViewModel
{
Id = $"user:{l.Id}",
@@ -304,6 +306,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
DefaultCommitType = l.DefaultCommitType,
IsManual = l.IsManual,
FindingsTracked = l.FindingsTracked,
HasTicketProject = ticketsConfigured && cfg?.TicketProjectId > 0,
};
Items.Add(item);
UserLists.Add(item);
@@ -486,10 +489,12 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
var lists = new ListRepository(ctx);
var entity = await lists.GetByIdAsync(rawId);
if (entity is null) return;
var cfg = await lists.GetConfigAsync(rawId);
var hasTicketProject = await IsTicketsConfiguredAsync() && cfg?.TicketProjectId > 0;
if (row is null)
{
await AddRowAsync(lists, entity);
await AddRowAsync(lists, entity, hasTicketProject);
return;
}
@@ -498,16 +503,47 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
row.DefaultCommitType = entity.DefaultCommitType;
row.IsManual = entity.IsManual;
row.FindingsTracked = entity.FindingsTracked;
row.HasTicketProject = hasTicketProject;
RecomputeHasNoLinkedRepo();
}
catch { /* best-effort refresh */ }
}
private async Task<bool> IsTicketsConfiguredAsync()
{
if (_worker is null) return false;
try
{
var settings = await _worker.GetTicketSettingsAsync();
return !string.IsNullOrWhiteSpace(settings?.ApiBaseUrl);
}
catch { return false; }
}
[RelayCommand]
private async Task ImportTicketsAsync(ListNavItemViewModel? row)
{
if (row is null || _worker is null) return;
var rawId = row.Id.StartsWith("user:", StringComparison.Ordinal) ? row.Id["user:".Length..] : row.Id;
try
{
var result = await _worker.ImportTicketsAsync(rawId);
// No FlashFooterInfo variant exists in this project — the footer strip only has an
// error channel (ErrorReported -> IslandsShellViewModel.FlashFooterError). A silent
// success is the worse option, so the result reuses the same channel.
ErrorReported?.Invoke(Loc.T("lists.importTicketsResult", result.Examined, result.Created));
}
catch (Exception ex)
{
ErrorReported?.Invoke(ex.Message);
}
}
/// <summary>
/// A `create_list` from a running MCP session has no local row yet — build one from the DB
/// entity and insert it at its DB sort position, instead of waiting for the next full reload.
/// </summary>
private async Task AddRowAsync(ListRepository lists, ListEntity entity)
private async Task AddRowAsync(ListRepository lists, ListEntity entity, bool hasTicketProject = false)
{
var dotColors = new[] { "Moss", "Peat", "Sage" };
var item = new ListNavItemViewModel
@@ -521,6 +557,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
DefaultCommitType = entity.DefaultCommitType,
IsManual = entity.IsManual,
FindingsTracked = entity.FindingsTracked,
HasTicketProject = hasTicketProject,
};
var seedNames = new HashSet<string>(new[] { "My Day", "Important", "Planned" });