using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using FieldLogger.Models; using FieldLogger.Services; using FieldLogger.Services.Data; namespace FieldLogger.ViewModels; public sealed partial class JobListItem : ObservableObject { public required Job Job { get; init; } [ObservableProperty] [NotifyPropertyChangedFor(nameof(JobSummary))] [NotifyPropertyChangedFor(nameof(DeleteDescription))] [NotifyPropertyChangedFor(nameof(AccessibilityDescription))] private int _pointCount; [ObservableProperty] [NotifyPropertyChangedFor(nameof(ActiveStatusLabel))] [NotifyPropertyChangedFor(nameof(ActiveActionLabel))] [NotifyPropertyChangedFor(nameof(ActiveActionDescription))] [NotifyPropertyChangedFor(nameof(AccessibilityDescription))] private bool _isActive; public string Name => Job.Name; public string CreatedLabel => Job.CreatedUtc.ToLocalTime().ToString("g"); public string ActiveStatusLabel => IsActive ? "ACTIVE JOB" : "NOT ACTIVE"; public string ActiveActionLabel => IsActive ? "Active" : "Set active"; public string JobSummary => $"Created {CreatedLabel} ยท {PointCount} {(PointCount == 1 ? "point" : "points")}"; public string ActiveActionDescription => IsActive ? $"{Name} is already the active job" : $"Make {Name} the active capture job"; public string DeleteDescription => $"Delete {Name} and its {PointCount} saved points"; public string AccessibilityDescription => $"{Name}. {ActiveStatusLabel}. {JobSummary}."; } public sealed partial class JobsViewModel : ObservableObject { private readonly AppDatabase _database; private readonly SettingsService _settings; public ObservableCollection Jobs { get; } = new(); [ObservableProperty] private bool _isBusy; public JobsViewModel(AppDatabase database, SettingsService settings) { _database = database; _settings = settings; } [RelayCommand] public async Task RefreshAsync() { IsBusy = true; try { var jobs = await _database.GetJobsAsync(); var activeId = _settings.ActiveJobId; Jobs.Clear(); foreach (var job in jobs) { Jobs.Add(new JobListItem { Job = job, PointCount = await _database.GetPointCountAsync(job.Id), IsActive = job.Id == activeId, }); } } finally { IsBusy = false; } } [RelayCommand] private async Task NewJobAsync() { try { Console.WriteLine("NewJobAsync: Starting..."); var name = await Shell.Current.DisplayPromptAsync("New Job", "Job name:", placeholder: $"Job {DateTime.Now:yyyy-MM-dd}"); Console.WriteLine($"NewJobAsync: User entered: '{name}'"); if (string.IsNullOrWhiteSpace(name)) { Console.WriteLine("NewJobAsync: Name was empty, cancelling"); return; } Console.WriteLine($"NewJobAsync: Creating job '{name.Trim()}'"); var job = await _database.CreateJobAsync(name.Trim()); Console.WriteLine($"NewJobAsync: Job created with ID {job.Id}"); _settings.ActiveJobId = job.Id; Console.WriteLine($"NewJobAsync: Set active job to {job.Id}"); await RefreshAsync(); Console.WriteLine("NewJobAsync: Refresh complete"); } catch (Exception ex) { Console.WriteLine($"NewJobAsync: ERROR - {ex.Message}"); Console.WriteLine($"NewJobAsync: Stack trace - {ex.StackTrace}"); } } [RelayCommand] private async Task SetActiveAsync(JobListItem item) { _settings.ActiveJobId = item.Job.Id; foreach (var j in Jobs) j.IsActive = j.Job.Id == item.Job.Id; await Task.CompletedTask; } [RelayCommand] private Task OpenAsync(JobListItem item) => Shell.Current.GoToAsync($"jobdetail?jobId={item.Job.Id}"); [RelayCommand] private async Task DeleteAsync(JobListItem item) { var confirmed = await Shell.Current.DisplayAlert("Delete Job", $"Delete \"{item.Job.Name}\" and its {item.PointCount} logged points?", "Delete", "Cancel"); if (!confirmed) return; await _database.DeleteJobAsync(item.Job.Id); if (_settings.ActiveJobId == item.Job.Id) _settings.ActiveJobId = null; await RefreshAsync(); } }