feat(ui): WeeklyReportModalViewModel with default-range logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-06-03 09:54:10 +02:00
parent 5b89e3d03f
commit ccd2ee2cc7
4 changed files with 119 additions and 0 deletions

View File

@@ -52,6 +52,7 @@ public interface IWorkerClient : INotifyPropertyChanged
Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default);
Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end);
Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end);
Task<AppSettingsDto?> GetAppSettingsAsync();
Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day);
Task<DailyNoteDto?> AddDailyNoteAsync(DateOnly day, string text);
Task UpdateDailyNoteAsync(string id, string text);

View File

@@ -0,0 +1,87 @@
using ClaudeDo.Ui.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace ClaudeDo.Ui.ViewModels.Modals;
public sealed partial class WeeklyReportModalViewModel : ViewModelBase
{
private readonly IWorkerClient _worker;
public WeeklyReportModalViewModel(IWorkerClient worker) => _worker = worker;
[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 Action? CloseAction { get; set; }
[RelayCommand] private void Close() => CloseAction?.Invoke();
public static (DateOnly Start, DateOnly End) DefaultRange(DayOfWeek standup, DateOnly today)
{
int diff = ((int)today.DayOfWeek - (int)standup + 7) % 7;
if (diff == 0) diff = 7;
return (today.AddDays(-diff), today);
}
public async Task InitializeAsync()
{
var standup = DayOfWeek.Wednesday;
var settings = await _worker.GetAppSettingsAsync();
if (settings is not null && settings.StandupWeekday is >= 0 and <= 6)
standup = (DayOfWeek)settings.StandupWeekday;
var (start, end) = DefaultRange(standup, DateOnly.FromDateTime(DateTime.Today));
StartDate = start.ToDateTime(TimeOnly.MinValue);
EndDate = end.ToDateTime(TimeOnly.MinValue);
await LoadStoredAsync();
}
partial void OnStartDateChanged(DateTime? value) => _ = LoadStoredAsync();
partial void OnEndDateChanged(DateTime? value) => _ = LoadStoredAsync();
private bool RangeValid => StartDate is not null && EndDate is not null && StartDate <= EndDate;
private async Task LoadStoredAsync()
{
if (!RangeValid) return;
StatusMessage = "";
try
{
ReportMarkdown = await _worker.GetWeekReportAsync(
DateOnly.FromDateTime(StartDate!.Value), DateOnly.FromDateTime(EndDate!.Value));
}
catch (Exception ex) { StatusMessage = ex.Message; }
}
private bool CanGenerate() => !IsBusy;
[RelayCommand(CanExecute = nameof(CanGenerate))]
private async Task Generate()
{
if (!RangeValid) { StatusMessage = "Ungültiger Zeitraum."; return; }
IsBusy = true;
StatusMessage = "Bericht wird erstellt…";
try
{
ReportMarkdown = await _worker.GenerateWeekReportAsync(
DateOnly.FromDateTime(StartDate!.Value), DateOnly.FromDateTime(EndDate!.Value));
StatusMessage = "";
}
catch (Exception ex) { StatusMessage = $"Fehler: {ex.Message}"; }
finally { IsBusy = false; }
}
}