style(ui): tasks header toolbar and add-task row
- Reformat subtitle to "{Weekday}, {Month} {Day} · {open} open".
- Add right-aligned running/review status pill (kbd style).
- Add header icon toolbar: Sort, Eye (toggle completed), MoreHorizontal.
- Wire Eye to IsShowingCompleted [ObservableProperty] on the VM.
- Style add-task row as rounded Surface2 border with dashed Plus circle,
borderless TextBox, and ENTER kbd chip visible on focus.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using ClaudeDo.Data;
|
||||
@@ -19,12 +20,21 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
public void RequestFocusAddTask() => FocusAddTaskRequested?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
public ObservableCollection<TaskRowViewModel> Items { get; } = new();
|
||||
public ObservableCollection<TaskRowViewModel> OverdueItems { get; } = new();
|
||||
public ObservableCollection<TaskRowViewModel> OpenItems { get; } = new();
|
||||
public ObservableCollection<TaskRowViewModel> CompletedItems { get; } = new();
|
||||
|
||||
[ObservableProperty] private string _newTaskTitle = "";
|
||||
[ObservableProperty] private TaskRowViewModel? _selectedTask;
|
||||
[ObservableProperty] private string _headerTitle = "";
|
||||
[ObservableProperty] private string _headerEyebrow = "";
|
||||
[ObservableProperty] private string _subtitle = "";
|
||||
[ObservableProperty] private string _statusPill = "";
|
||||
[ObservableProperty] private bool _hasStatusPill;
|
||||
[ObservableProperty] private bool _isShowingCompleted = true;
|
||||
[ObservableProperty] private bool _hasOverdue;
|
||||
[ObservableProperty] private bool _hasCompleted;
|
||||
[ObservableProperty] private string _completedHeader = "COMPLETED";
|
||||
|
||||
public TasksIslandViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory)
|
||||
{
|
||||
@@ -40,10 +50,15 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
|
||||
_currentList = list;
|
||||
Items.Clear();
|
||||
OverdueItems.Clear();
|
||||
OpenItems.Clear();
|
||||
CompletedItems.Clear();
|
||||
HasOverdue = false;
|
||||
HasCompleted = false;
|
||||
if (list is null) return;
|
||||
|
||||
HeaderTitle = list.Name;
|
||||
HeaderEyebrow = DateTime.Now.ToString("dddd · MMM dd").ToUpperInvariant();
|
||||
HeaderEyebrow = DateTime.Now.ToString("dddd · MMM dd", CultureInfo.InvariantCulture).ToUpperInvariant();
|
||||
|
||||
_ = LoadForListAsync(list, ct);
|
||||
}
|
||||
@@ -74,17 +89,56 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
foreach (var t in filtered)
|
||||
Items.Add(TaskRowViewModel.FromEntity(t));
|
||||
|
||||
Regroup();
|
||||
UpdateSubtitle();
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
|
||||
private void Regroup()
|
||||
{
|
||||
OverdueItems.Clear();
|
||||
OpenItems.Clear();
|
||||
CompletedItems.Clear();
|
||||
|
||||
var today = DateTime.Today;
|
||||
foreach (var r in Items)
|
||||
{
|
||||
if (r.Done)
|
||||
CompletedItems.Add(r);
|
||||
else if (r.ScheduledFor is { } d && d.Date < today)
|
||||
OverdueItems.Add(r);
|
||||
else
|
||||
OpenItems.Add(r);
|
||||
}
|
||||
|
||||
HasOverdue = OverdueItems.Count > 0;
|
||||
HasCompleted = CompletedItems.Count > 0;
|
||||
CompletedHeader = $"COMPLETED · {CompletedItems.Count}";
|
||||
}
|
||||
|
||||
private void UpdateSubtitle()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var open = Items.Count(i => !i.Done);
|
||||
var running = Items.Count(i => i.Status == TaskStatus.Running);
|
||||
var review = Items.Count(i => i.Status == TaskStatus.Done && i.Branch != null);
|
||||
Subtitle = $"{open} open · {running} running · {review} in review";
|
||||
|
||||
var weekday = now.ToString("dddd", CultureInfo.CurrentCulture);
|
||||
var month = now.ToString("MMM", CultureInfo.CurrentCulture);
|
||||
var day = now.Day;
|
||||
Subtitle = $"{weekday}, {month} {day} · {open} open";
|
||||
|
||||
if (running > 0 || review > 0)
|
||||
{
|
||||
StatusPill = $"{running} running · {review} review";
|
||||
HasStatusPill = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusPill = "";
|
||||
HasStatusPill = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -102,7 +156,9 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
db.Tasks.Add(entity);
|
||||
await db.SaveChangesAsync();
|
||||
Items.Insert(0, TaskRowViewModel.FromEntity(entity));
|
||||
var row = TaskRowViewModel.FromEntity(entity);
|
||||
Items.Insert(0, row);
|
||||
Regroup();
|
||||
NewTaskTitle = "";
|
||||
UpdateSubtitle();
|
||||
}
|
||||
@@ -119,6 +175,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
row.Status = entity.Status;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
Regroup();
|
||||
UpdateSubtitle();
|
||||
}
|
||||
|
||||
@@ -138,6 +195,15 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
[RelayCommand]
|
||||
private void Select(TaskRowViewModel row) => SelectedTask = row;
|
||||
|
||||
[RelayCommand]
|
||||
private void ToggleShowCompleted() => IsShowingCompleted = !IsShowingCompleted;
|
||||
|
||||
[RelayCommand]
|
||||
private void Sort() { /* placeholder — UI-only */ }
|
||||
|
||||
[RelayCommand]
|
||||
private void More() { /* placeholder — UI-only */ }
|
||||
|
||||
partial void OnSelectedTaskChanged(TaskRowViewModel? value)
|
||||
{
|
||||
foreach (var i in Items) i.IsSelected = ReferenceEquals(i, value);
|
||||
|
||||
Reference in New Issue
Block a user