feat(daily-prep): trigger planning from inside the prep-log window with an empty-state hint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,6 @@
|
|||||||
"addPlaceholder": "Aufgabe hinzufügen…",
|
"addPlaceholder": "Aufgabe hinzufügen…",
|
||||||
"enterKey": "ENTER",
|
"enterKey": "ENTER",
|
||||||
"notesPinnedRow": "Notizen (Tagesnotizen)",
|
"notesPinnedRow": "Notizen (Tagesnotizen)",
|
||||||
"prepareDay": "Tag vorbereiten",
|
|
||||||
"clearDayTip": "Tag leeren",
|
"clearDayTip": "Tag leeren",
|
||||||
"prepLogTip": "Vorbereitungs-Log",
|
"prepLogTip": "Vorbereitungs-Log",
|
||||||
"overdue": "ÜBERFÄLLIG",
|
"overdue": "ÜBERFÄLLIG",
|
||||||
@@ -140,7 +139,9 @@
|
|||||||
"previewBtn": "Vorschau",
|
"previewBtn": "Vorschau",
|
||||||
"editBtn": "Bearbeiten",
|
"editBtn": "Bearbeiten",
|
||||||
"descriptionPlaceholder": "Aufgabendetails hinzufügen (Markdown unterstützt)...",
|
"descriptionPlaceholder": "Aufgabendetails hinzufügen (Markdown unterstützt)...",
|
||||||
"prepTitle": "Tagesvorbereitung"
|
"prepTitle": "Tagesvorbereitung",
|
||||||
|
"planDay": "Tag planen",
|
||||||
|
"prepEmpty": "Heute noch keine Vorbereitung — klick Tag planen"
|
||||||
},
|
},
|
||||||
"agent": {
|
"agent": {
|
||||||
"stopTip": "Agent stoppen",
|
"stopTip": "Agent stoppen",
|
||||||
|
|||||||
@@ -66,7 +66,6 @@
|
|||||||
"addPlaceholder": "Add a task…",
|
"addPlaceholder": "Add a task…",
|
||||||
"enterKey": "ENTER",
|
"enterKey": "ENTER",
|
||||||
"notesPinnedRow": "Notes (daily notes)",
|
"notesPinnedRow": "Notes (daily notes)",
|
||||||
"prepareDay": "Prepare day",
|
|
||||||
"clearDayTip": "Clear day",
|
"clearDayTip": "Clear day",
|
||||||
"prepLogTip": "Prep log",
|
"prepLogTip": "Prep log",
|
||||||
"overdue": "OVERDUE",
|
"overdue": "OVERDUE",
|
||||||
@@ -140,7 +139,9 @@
|
|||||||
"previewBtn": "Preview",
|
"previewBtn": "Preview",
|
||||||
"editBtn": "Edit",
|
"editBtn": "Edit",
|
||||||
"descriptionPlaceholder": "Add task details (markdown supported)...",
|
"descriptionPlaceholder": "Add task details (markdown supported)...",
|
||||||
"prepTitle": "Daily prep"
|
"prepTitle": "Daily prep",
|
||||||
|
"planDay": "Plan day",
|
||||||
|
"prepEmpty": "No prep run today yet — click Plan day"
|
||||||
},
|
},
|
||||||
"agent": {
|
"agent": {
|
||||||
"stopTip": "Stop agent",
|
"stopTip": "Stop agent",
|
||||||
|
|||||||
@@ -346,6 +346,8 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
RecomputeCanMergeAll();
|
RecomputeCanMergeAll();
|
||||||
ReviewCombinedDiffCommand.NotifyCanExecuteChanged();
|
ReviewCombinedDiffCommand.NotifyCanExecuteChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PrepLog.CollectionChanged += (_, _) => OnPropertyChanged(nameof(ShowPrepEmptyState));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTaskMessage(string taskId, string line)
|
private void OnTaskMessage(string taskId, string line)
|
||||||
@@ -381,6 +383,18 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
AppendClaudeText(formatted, target, buf);
|
AppendClaudeText(formatted, target, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task PlanDayAsync()
|
||||||
|
{
|
||||||
|
if (_worker is null) return;
|
||||||
|
try { await _worker.RunDailyPrepNowAsync(); }
|
||||||
|
catch { /* worker offline; PrepStarted/PrepLine will reconcile */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowPrepEmptyState => !IsPrepRunning && PrepLog.Count == 0;
|
||||||
|
|
||||||
|
partial void OnIsPrepRunningChanged(bool value) => OnPropertyChanged(nameof(ShowPrepEmptyState));
|
||||||
|
|
||||||
private void OnPrepStarted()
|
private void OnPrepStarted()
|
||||||
{
|
{
|
||||||
PrepLog.Clear();
|
PrepLog.Clear();
|
||||||
|
|||||||
@@ -37,15 +37,6 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
|
|||||||
NotesRequested?.Invoke();
|
NotesRequested?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task PrepareDayAsync()
|
|
||||||
{
|
|
||||||
if (_worker is null) return;
|
|
||||||
PrepRequested?.Invoke();
|
|
||||||
try { await _worker.RunDailyPrepNowAsync(); }
|
|
||||||
catch { /* worker offline; broadcast will reconcile on return */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void ShowPrepLog() => PrepRequested?.Invoke();
|
private void ShowPrepLog() => PrepRequested?.Invoke();
|
||||||
|
|
||||||
|
|||||||
@@ -303,9 +303,23 @@
|
|||||||
<islands:NotesEditorView DataContext="{Binding Notes}"/>
|
<islands:NotesEditorView DataContext="{Binding Notes}"/>
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel IsVisible="{Binding IsPrepMode}">
|
<Panel IsVisible="{Binding IsPrepMode}">
|
||||||
<islands:SessionTerminalView
|
<DockPanel>
|
||||||
Entries="{Binding PrepLog}" Label="daily-prep"
|
<Border DockPanel.Dock="Top" Padding="12,8">
|
||||||
IsRunning="{Binding IsPrepRunning}"/>
|
<Button Classes="btn primary"
|
||||||
|
Command="{Binding PlanDayCommand}"
|
||||||
|
IsEnabled="{Binding !IsPrepRunning}"
|
||||||
|
Content="{loc:Tr details.planDay}"/>
|
||||||
|
</Border>
|
||||||
|
<Panel>
|
||||||
|
<islands:SessionTerminalView
|
||||||
|
Entries="{Binding PrepLog}" Label="daily-prep"
|
||||||
|
IsRunning="{Binding IsPrepRunning}"/>
|
||||||
|
<TextBlock IsVisible="{Binding ShowPrepEmptyState}"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource TextMuteBrush}"
|
||||||
|
Text="{loc:Tr details.prepEmpty}"/>
|
||||||
|
</Panel>
|
||||||
|
</DockPanel>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -79,14 +79,6 @@
|
|||||||
Command="{Binding OpenNotesCommand}"
|
Command="{Binding OpenNotesCommand}"
|
||||||
Content="{loc:Tr tasks.notesPinnedRow}"/>
|
Content="{loc:Tr tasks.notesPinnedRow}"/>
|
||||||
|
|
||||||
<!-- Prepare 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 PrepareDayCommand}"
|
|
||||||
Content="{loc:Tr tasks.prepareDay}"/>
|
|
||||||
|
|
||||||
<!-- Task list -->
|
<!-- Task list -->
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel Margin="10,4">
|
<StackPanel Margin="10,4">
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public abstract class StubWorkerClient : IWorkerClient
|
|||||||
#pragma warning restore CS0067
|
#pragma warning restore CS0067
|
||||||
|
|
||||||
public int ClearMyDayCalls { get; private set; }
|
public int ClearMyDayCalls { get; private set; }
|
||||||
|
public int RunDailyPrepNowCalls { get; private set; }
|
||||||
|
|
||||||
public void RaisePrepStarted() => PrepStartedEvent?.Invoke();
|
public void RaisePrepStarted() => PrepStartedEvent?.Invoke();
|
||||||
public void RaisePrepLine(string line) => PrepLineEvent?.Invoke(line);
|
public void RaisePrepLine(string line) => PrepLineEvent?.Invoke(line);
|
||||||
@@ -71,7 +72,7 @@ public abstract class StubWorkerClient : IWorkerClient
|
|||||||
public virtual Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask;
|
public virtual Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask;
|
||||||
public virtual Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult<string?>(null);
|
public virtual Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult<string?>(null);
|
||||||
public virtual Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult("");
|
public virtual Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult("");
|
||||||
public virtual Task<bool> RunDailyPrepNowAsync() => Task.FromResult(false);
|
public virtual Task<bool> RunDailyPrepNowAsync() { RunDailyPrepNowCalls++; return Task.FromResult(false); }
|
||||||
public virtual Task ClearMyDayAsync() { ClearMyDayCalls++; return Task.CompletedTask; }
|
public virtual Task ClearMyDayAsync() { ClearMyDayCalls++; return Task.CompletedTask; }
|
||||||
public virtual Task<AppSettingsDto?> GetAppSettingsAsync() => Task.FromResult<AppSettingsDto?>(null);
|
public virtual Task<AppSettingsDto?> GetAppSettingsAsync() => Task.FromResult<AppSettingsDto?>(null);
|
||||||
public virtual Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day) => Task.FromResult(new List<DailyNoteDto>());
|
public virtual Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day) => Task.FromResult(new List<DailyNoteDto>());
|
||||||
|
|||||||
@@ -92,4 +92,20 @@ public class DetailsIslandPrepModeTests : IDisposable
|
|||||||
|
|
||||||
Assert.NotEmpty(vm.PrepLog);
|
Assert.NotEmpty(vm.PrepLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PlanDayCommand_calls_worker()
|
||||||
|
{
|
||||||
|
var stub = new DefaultStub();
|
||||||
|
var vm = NewDetailsVm(stub);
|
||||||
|
await vm.PlanDayCommand.ExecuteAsync(null);
|
||||||
|
Assert.Equal(1, stub.RunDailyPrepNowCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ShowPrepEmptyState_true_when_empty_and_not_running()
|
||||||
|
{
|
||||||
|
var vm = NewDetailsVm(new DefaultStub());
|
||||||
|
Assert.True(vm.ShowPrepEmptyState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,15 +53,4 @@ public class TasksIslandDailyPrepTests : IDisposable
|
|||||||
Assert.Equal(1, stub.ClearMyDayCalls);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user