using ClaudeDo.Ui.Localization; 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; // 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] private DateTime? _startDate; [ObservableProperty] private DateTime? _endDate; [ObservableProperty] private string _statusMessage = ""; public bool HasReport => !string.IsNullOrWhiteSpace(ReportMarkdown); public bool EmptyStateVisible => !HasReport && !GenerateOperation.IsRunning; 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() => !GenerateOperation.IsRunning; [RelayCommand(CanExecute = nameof(CanGenerate))] private async Task Generate() { if (!RangeValid) { StatusMessage = Loc.T("vm.weeklyReport.invalidRange"); return; } using var op = GenerateOperation.Begin(Loc.T("ops.reports.generatingWeekly")); StatusMessage = ""; try { ReportMarkdown = await _worker.GenerateWeekReportAsync( DateOnly.FromDateTime(StartDate!.Value), DateOnly.FromDateTime(EndDate!.Value)); } catch (Exception ex) { StatusMessage = Loc.T("vm.weeklyReport.error", ex.Message); } } }