feat(daily-prep): add Prep-log and Clear-day buttons to MyDay header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-06-04 08:18:30 +02:00
parent a8670ee23a
commit c45f892591
6 changed files with 101 additions and 0 deletions

View File

@@ -68,6 +68,8 @@
"enterKey": "ENTER", "enterKey": "ENTER",
"notesPinnedRow": "Notizen (Tagesnotizen)", "notesPinnedRow": "Notizen (Tagesnotizen)",
"prepareDay": "Tag vorbereiten", "prepareDay": "Tag vorbereiten",
"prepLog": "Vorbereitungs-Log",
"clearDay": "Tag leeren",
"overdue": "ÜBERFÄLLIG", "overdue": "ÜBERFÄLLIG",
"tasks": "AUFGABEN", "tasks": "AUFGABEN",
"clearCompletedTip": "Alle abgeschlossenen löschen", "clearCompletedTip": "Alle abgeschlossenen löschen",

View File

@@ -68,6 +68,8 @@
"enterKey": "ENTER", "enterKey": "ENTER",
"notesPinnedRow": "Notes (daily notes)", "notesPinnedRow": "Notes (daily notes)",
"prepareDay": "Prepare day", "prepareDay": "Prepare day",
"prepLog": "Prep log",
"clearDay": "Clear day",
"overdue": "OVERDUE", "overdue": "OVERDUE",
"tasks": "TASKS", "tasks": "TASKS",
"clearCompletedTip": "Clear all completed", "clearCompletedTip": "Clear all completed",

View File

@@ -27,6 +27,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
public event EventHandler? FocusAddTaskRequested; public event EventHandler? FocusAddTaskRequested;
public event EventHandler? TasksChanged; public event EventHandler? TasksChanged;
public event Action? NotesRequested; public event Action? NotesRequested;
public event Action? PrepRequested;
public void RequestFocusAddTask() => FocusAddTaskRequested?.Invoke(this, EventArgs.Empty); public void RequestFocusAddTask() => FocusAddTaskRequested?.Invoke(this, EventArgs.Empty);
[RelayCommand] [RelayCommand]
@@ -40,10 +41,22 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
private async Task PrepareDayAsync() private async Task PrepareDayAsync()
{ {
if (_worker is null) return; if (_worker is null) return;
PrepRequested?.Invoke();
try { await _worker.RunDailyPrepNowAsync(); } try { await _worker.RunDailyPrepNowAsync(); }
catch { /* worker offline; broadcast will reconcile on return */ } catch { /* worker offline; broadcast will reconcile on return */ }
} }
[RelayCommand]
private void ShowPrepLog() => PrepRequested?.Invoke();
[RelayCommand]
private async Task ClearDayAsync()
{
if (_worker is null) return;
try { await _worker.ClearMyDayAsync(); }
catch { /* worker offline; broadcast will reconcile on return */ }
}
public ObservableCollection<TaskRowViewModel> Items { get; } = new(); public ObservableCollection<TaskRowViewModel> Items { get; } = new();
public ObservableCollection<TaskRowViewModel> OverdueItems { get; } = new(); public ObservableCollection<TaskRowViewModel> OverdueItems { get; } = new();
public ObservableCollection<TaskRowViewModel> OpenItems { get; } = new(); public ObservableCollection<TaskRowViewModel> OpenItems { get; } = new();

View File

@@ -199,6 +199,7 @@ public sealed partial class IslandsShellViewModel : ViewModelBase
Lists.SelectionChanged += (_, _) => Tasks.LoadForList(Lists.SelectedList); Lists.SelectionChanged += (_, _) => Tasks.LoadForList(Lists.SelectedList);
Tasks.SelectionChanged += (_, _) => Details.Bind(Tasks.SelectedTask); Tasks.SelectionChanged += (_, _) => Details.Bind(Tasks.SelectedTask);
Tasks.NotesRequested += () => Details.ShowNotes(); Tasks.NotesRequested += () => Details.ShowNotes();
Tasks.PrepRequested += () => Details.ShowPrep();
Tasks.TasksChanged += (_, _) => _ = Lists.RefreshCountsAsync(); Tasks.TasksChanged += (_, _) => _ = Lists.RefreshCountsAsync();
Tasks.OpenListSettingsRequested += (_, _) => Tasks.OpenListSettingsRequested += (_, _) =>
{ {

View File

@@ -82,6 +82,22 @@
Command="{Binding PrepareDayCommand}" Command="{Binding PrepareDayCommand}"
Content="{loc:Tr tasks.prepareDay}"/> Content="{loc:Tr tasks.prepareDay}"/>
<!-- Prep Log button (My Day only) -->
<Button DockPanel.Dock="Top"
Classes="btn" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
Margin="16,0,16,8"
IsVisible="{Binding IsMyDayList}"
Command="{Binding ShowPrepLogCommand}"
Content="{loc:Tr tasks.prepLog}"/>
<!-- Clear Day button (My Day only) -->
<Button DockPanel.Dock="Top"
Classes="btn" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
Margin="16,0,16,8"
IsVisible="{Binding IsMyDayList}"
Command="{Binding ClearDayCommand}"
Content="{loc:Tr tasks.clearDay}"/>
<!-- Task list --> <!-- Task list -->
<ScrollViewer> <ScrollViewer>
<StackPanel Margin="10,4"> <StackPanel Margin="10,4">

View File

@@ -0,0 +1,67 @@
using ClaudeDo.Data;
using ClaudeDo.Ui.ViewModels.Islands;
using Microsoft.EntityFrameworkCore;
namespace ClaudeDo.Ui.Tests.ViewModels;
public class TasksIslandDailyPrepTests : IDisposable
{
private readonly string _dbPath;
public TasksIslandDailyPrepTests()
{
_dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_tasks_prep_{Guid.NewGuid():N}.db");
using var ctx = NewContext();
ctx.Database.EnsureCreated();
}
public void Dispose()
{
try { File.Delete(_dbPath); } catch { }
try { File.Delete(_dbPath + "-wal"); } catch { }
try { File.Delete(_dbPath + "-shm"); } catch { }
}
private ClaudeDoDbContext NewContext()
{
var opts = new DbContextOptionsBuilder<ClaudeDoDbContext>()
.UseSqlite($"Data Source={_dbPath}")
.Options;
return new ClaudeDoDbContext(opts);
}
private sealed class TestDbFactory : IDbContextFactory<ClaudeDoDbContext>
{
private readonly Func<ClaudeDoDbContext> _create;
public TestDbFactory(Func<ClaudeDoDbContext> create) => _create = create;
public ClaudeDoDbContext CreateDbContext() => _create();
}
private sealed class DefaultStub : StubWorkerClient { }
private TasksIslandViewModel NewTasksVm(StubWorkerClient stub) =>
new(new TestDbFactory(NewContext), worker: stub);
[Fact]
public async Task ClearDayCommand_calls_worker()
{
var stub = new DefaultStub();
var vm = NewTasksVm(stub);
await vm.ClearDayCommand.ExecuteAsync(null);
Assert.Equal(1, stub.ClearMyDayCalls);
}
[Fact]
public async Task PrepareDayCommand_raises_PrepRequested()
{
var vm = NewTasksVm(new DefaultStub());
var raised = false;
vm.PrepRequested += () => raised = true;
await vm.PrepareDayCommand.ExecuteAsync(null);
Assert.True(raised);
}
}