Files
ulapp/FieldLogger/ViewModels/JobsViewModel.cs
2026-08-20 22:00:31 -05:00

141 lines
4.6 KiB
C#

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<JobListItem> 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();
}
}