chore(claude-do): merge [A4] WeeklyReport, DailyPrep und Planning-Aktionen auf Opera

ClaudeDo-Task: 7c6f2f66-8cd7-4abb-8151-aed42ae739b5
This commit is contained in:
Mika Kuns
2026-08-12 10:10:57 +02:00
9 changed files with 342 additions and 21 deletions
@@ -9,24 +9,32 @@ public sealed partial class WeeklyReportModalViewModel : ViewModelBase
{
private readonly IWorkerClient _worker;
public WeeklyReportModalViewModel(IWorkerClient worker) => _worker = worker;
public WeeklyReportModalViewModel(IWorkerClient worker)
{
_worker = worker;
// OperationStatus is a nested ObservableObject — [NotifyCanExecuteChangedFor] can't see
// into it, so IsRunning changes are relayed by hand.
GenerateOperation.PropertyChanged += (_, e) =>
{
if (e.PropertyName != nameof(OperationStatus.IsRunning)) return;
GenerateCommand.NotifyCanExecuteChanged();
OnPropertyChanged(nameof(EmptyStateVisible));
};
}
public OperationStatus GenerateOperation { get; } = new();
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasReport))]
[NotifyPropertyChangedFor(nameof(EmptyStateVisible))]
private string? _reportMarkdown;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(EmptyStateVisible))]
[NotifyCanExecuteChangedFor(nameof(GenerateCommand))]
private bool _isBusy;
[ObservableProperty] private DateTime? _startDate;
[ObservableProperty] private DateTime? _endDate;
[ObservableProperty] private string _statusMessage = "";
public bool HasReport => !string.IsNullOrWhiteSpace(ReportMarkdown);
public bool EmptyStateVisible => !HasReport && !IsBusy;
public bool EmptyStateVisible => !HasReport && !GenerateOperation.IsRunning;
public Action? CloseAction { get; set; }
[RelayCommand] private void Close() => CloseAction?.Invoke();
@@ -68,21 +76,19 @@ public sealed partial class WeeklyReportModalViewModel : ViewModelBase
catch (Exception ex) { StatusMessage = ex.Message; }
}
private bool CanGenerate() => !IsBusy;
private bool CanGenerate() => !GenerateOperation.IsRunning;
[RelayCommand(CanExecute = nameof(CanGenerate))]
private async Task Generate()
{
if (!RangeValid) { StatusMessage = Loc.T("vm.weeklyReport.invalidRange"); return; }
IsBusy = true;
StatusMessage = Loc.T("vm.weeklyReport.generating");
using var op = GenerateOperation.Begin(Loc.T("ops.reports.generatingWeekly"));
StatusMessage = "";
try
{
ReportMarkdown = await _worker.GenerateWeekReportAsync(
DateOnly.FromDateTime(StartDate!.Value), DateOnly.FromDateTime(EndDate!.Value));
StatusMessage = "";
}
catch (Exception ex) { StatusMessage = Loc.T("vm.weeklyReport.error", ex.Message); }
finally { IsBusy = false; }
}
}