From 9ba238f4ade7cf5ade5d5e697c2fb22a61961ec9 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Wed, 29 Apr 2026 10:40:09 +0200 Subject: [PATCH] feat(ui): status/tag context menu + ThemedDatePicker in task row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds "Set status" and "Tags" submenus to the row context menu (tags list is built lazily on Opening from AllTags ∪ row tags). Replaces the schedule flyout's separate DATE / TIME pickers with a single ThemedDatePicker in date+time mode. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Views/Islands/TaskRowView.axaml | 25 +++++---- .../Views/Islands/TaskRowView.axaml.cs | 52 ++++++++++++++++--- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml b/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml index 73d2545..ae7b119 100644 --- a/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml @@ -1,6 +1,7 @@ @@ -30,7 +31,7 @@ Classes.selected="{Binding IsSelected}" Classes.done="{Binding Done}"> - + @@ -38,6 +39,16 @@ IsVisible="{Binding CanRemoveFromQueue}" Click="OnRemoveFromQueueClick"/> + + + + + + + + + + - - - - - - - + (tag, ignoreCase: true, out var status)) return; + await vm.SetStatusOnRowAsync(row, status); + } + + private void OnContextMenuOpening(object? sender, System.ComponentModel.CancelEventArgs e) + { + if (DataContext is not TaskRowViewModel row || FindTasksVm() is not { } vm) return; + + // Build the union of all known tags + tags currently on this row, so a row's + // own tags stay reachable from the menu even if the global list is stale. + var rowTags = row.Tags.ToHashSet(); + var union = vm.AllTags.Concat(rowTags).Distinct().OrderBy(t => t).ToList(); + + TagsMenu.Items.Clear(); + if (union.Count == 0) + { + TagsMenu.Items.Add(new MenuItem { Header = "(no tags yet)", IsEnabled = false }); + return; + } + foreach (var name in union) + { + var prefix = rowTags.Contains(name) ? "✓ " : " "; + var item = new MenuItem { Header = prefix + name, Tag = name }; + item.Click += OnToggleTagClick; + TagsMenu.Items.Add(item); + } + } + + private async void OnToggleTagClick(object? sender, RoutedEventArgs e) + { + if (sender is not MenuItem mi || mi.Tag is not string name) return; + if (DataContext is not TaskRowViewModel row || FindTasksVm() is not { } vm) return; + await vm.ToggleTagOnRowAsync(row, name); + } + private void OnScheduleForClick(object? sender, RoutedEventArgs e) { if (DataContext is not TaskRowViewModel row) return; _pendingScheduleRow = row; - var seed = row.ScheduledFor ?? DateTime.Now.AddHours(1); - ScheduleDate.SelectedDate = new DateTimeOffset(seed.Date, TimeSpan.Zero); - ScheduleTime.SelectedTime = seed.TimeOfDay; + ScheduleDate.SelectedDate = row.ScheduledFor ?? DateTime.Now.AddHours(1); ScheduleAnchor.Flyout?.ShowAt(ScheduleAnchor); } @@ -83,9 +125,7 @@ public partial class TaskRowView : UserControl { ScheduleAnchor.Flyout?.Hide(); if (_pendingScheduleRow is null || ScheduleDate.SelectedDate is null) return; - var date = ScheduleDate.SelectedDate.Value.Date; - var time = ScheduleTime.SelectedTime ?? TimeSpan.FromHours(9); - var when = date + time; + var when = ScheduleDate.SelectedDate.Value; if (FindTasksVm() is { } tvm) await tvm.SetScheduledForAsync(_pendingScheduleRow, when); _pendingScheduleRow = null;