mirror of
https://github.com/claunia/marechai.git
synced 2025-12-16 19:14:25 +00:00
Add consoles pages.
This commit is contained in:
@@ -155,7 +155,7 @@ public partial class ComputersViewModel : ObservableObject
|
||||
_logger.LogInformation("Navigating to computers by letter: {Letter}", letter);
|
||||
_filterContext.FilterType = ComputerListFilterType.Letter;
|
||||
_filterContext.FilterValue = letter.ToString();
|
||||
await _navigator.NavigateViewModelAsync<ComputersListViewModel>(this);
|
||||
await _navigator.NavigateRouteAsync(this, "list-computers");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
@@ -175,7 +175,7 @@ public partial class ComputersViewModel : ObservableObject
|
||||
_logger.LogInformation("Navigating to computers by year: {Year}", year);
|
||||
_filterContext.FilterType = ComputerListFilterType.Year;
|
||||
_filterContext.FilterValue = year.ToString();
|
||||
await _navigator.NavigateViewModelAsync<ComputersListViewModel>(this);
|
||||
await _navigator.NavigateRouteAsync(this, "list-computers");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
@@ -195,7 +195,7 @@ public partial class ComputersViewModel : ObservableObject
|
||||
_logger.LogInformation("Navigating to all computers");
|
||||
_filterContext.FilterType = ComputerListFilterType.All;
|
||||
_filterContext.FilterValue = string.Empty;
|
||||
await _navigator.NavigateViewModelAsync<ComputersListViewModel>(this);
|
||||
await _navigator.NavigateRouteAsync(this, "list-computers");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
|
||||
249
Marechai.App/Presentation/ViewModels/ConsolesListViewModel.cs
Normal file
249
Marechai.App/Presentation/ViewModels/ConsolesListViewModel.cs
Normal file
@@ -0,0 +1,249 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.App.Helpers;
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Services;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel for displaying a filtered list of consoles
|
||||
/// </summary>
|
||||
public partial class ConsolesListViewModel : ObservableObject
|
||||
{
|
||||
private readonly ConsolesService _consolesService;
|
||||
private readonly IConsolesListFilterContext _filterContext;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<ConsolesListViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<ConsoleListItem> _consolesList = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _filterDescription = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _pageTitle = string.Empty;
|
||||
|
||||
public ConsolesListViewModel(ConsolesService consolesService, IStringLocalizer localizer,
|
||||
ILogger<ConsolesListViewModel> logger, INavigator navigator,
|
||||
IConsolesListFilterContext filterContext)
|
||||
{
|
||||
_consolesService = consolesService;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
_filterContext = filterContext;
|
||||
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
||||
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
||||
NavigateToConsoleCommand = new AsyncRelayCommand<ConsoleListItem>(NavigateToConsoleAsync);
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadData { get; }
|
||||
public ICommand GoBackCommand { get; }
|
||||
public IAsyncRelayCommand<ConsoleListItem> NavigateToConsoleCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filter type
|
||||
/// </summary>
|
||||
public ConsoleListFilterType FilterType
|
||||
{
|
||||
get => _filterContext.FilterType;
|
||||
set => _filterContext.FilterType = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filter value
|
||||
/// </summary>
|
||||
public string FilterValue
|
||||
{
|
||||
get => _filterContext.FilterValue;
|
||||
set => _filterContext.FilterValue = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads consoles based on the current filter
|
||||
/// </summary>
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
IsDataLoaded = false;
|
||||
ConsolesList.Clear();
|
||||
|
||||
_logger.LogInformation("LoadDataAsync called. FilterType={FilterType}, FilterValue={FilterValue}",
|
||||
FilterType,
|
||||
FilterValue);
|
||||
|
||||
// Update title and filter description based on filter type
|
||||
UpdateFilterDescription();
|
||||
|
||||
// Load consoles from the API based on the current filter
|
||||
await LoadConsolesFromApiAsync();
|
||||
|
||||
_logger.LogInformation("LoadConsolesFromApiAsync completed. ConsolesList.Count={Count}",
|
||||
ConsolesList.Count);
|
||||
|
||||
if(ConsolesList.Count == 0)
|
||||
{
|
||||
ErrorMessage = _localizer["No consoles found for this filter"].Value;
|
||||
HasError = true;
|
||||
|
||||
_logger.LogWarning("No consoles found for filter: {FilterType} {FilterValue}", FilterType, FilterValue);
|
||||
}
|
||||
else
|
||||
IsDataLoaded = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading consoles: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load consoles. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the title and filter description based on the current filter
|
||||
/// </summary>
|
||||
private void UpdateFilterDescription()
|
||||
{
|
||||
switch(FilterType)
|
||||
{
|
||||
case ConsoleListFilterType.All:
|
||||
PageTitle = _localizer["All Consoles"];
|
||||
FilterDescription = _localizer["Browsing all consoles in the database"];
|
||||
|
||||
break;
|
||||
|
||||
case ConsoleListFilterType.Letter:
|
||||
if(!string.IsNullOrEmpty(FilterValue) && FilterValue.Length == 1)
|
||||
{
|
||||
PageTitle = $"{_localizer["Consoles Starting with"]} {FilterValue}";
|
||||
FilterDescription = $"{_localizer["Showing consoles that start with"]} {FilterValue}";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ConsoleListFilterType.Year:
|
||||
if(!string.IsNullOrEmpty(FilterValue) && int.TryParse(FilterValue, out int year))
|
||||
{
|
||||
PageTitle = $"{_localizer["Consoles from"]} {year}";
|
||||
FilterDescription = $"{_localizer["Showing consoles released in"]} {year}";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads consoles from the API based on the current filter
|
||||
/// </summary>
|
||||
private async Task LoadConsolesFromApiAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<MachineDto> consoles = FilterType switch
|
||||
{
|
||||
ConsoleListFilterType.Letter when FilterValue.Length == 1 =>
|
||||
await _consolesService.GetConsolesByLetterAsync(FilterValue[0]),
|
||||
|
||||
ConsoleListFilterType.Year when int.TryParse(FilterValue, out int year) =>
|
||||
await _consolesService.GetConsolesByYearAsync(year),
|
||||
|
||||
_ => await _consolesService.GetAllConsolesAsync()
|
||||
};
|
||||
|
||||
// Add consoles to the list sorted by name
|
||||
foreach(MachineDto console in consoles.OrderBy(c => c.Name))
|
||||
{
|
||||
int year = console.Introduced?.Year ?? 0;
|
||||
int id = UntypedNodeExtractor.ExtractInt(console.Id);
|
||||
|
||||
_logger.LogInformation("Console: {Name}, Introduced: {Introduced}, Year: {Year}, Company: {Company}, ID: {Id}",
|
||||
console.Name,
|
||||
console.Introduced,
|
||||
year,
|
||||
console.Company,
|
||||
id);
|
||||
|
||||
ConsolesList.Add(new ConsoleListItem
|
||||
{
|
||||
Id = id,
|
||||
Name = console.Name ?? string.Empty,
|
||||
Year = year,
|
||||
Manufacturer = console.Company ?? string.Empty
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading consoles from API");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates back to the consoles main view
|
||||
/// </summary>
|
||||
private async Task GoBackAsync()
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<ConsolesViewModel>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the console detail view
|
||||
/// </summary>
|
||||
private async Task NavigateToConsoleAsync(ConsoleListItem? console)
|
||||
{
|
||||
if(console is null) return;
|
||||
|
||||
_logger.LogInformation("Navigating to console detail: {ConsoleName} (ID: {ConsoleId})",
|
||||
console.Name,
|
||||
console.Id);
|
||||
|
||||
var navParam = new MachineViewNavigationParameter
|
||||
{
|
||||
MachineId = console.Id,
|
||||
NavigationSource = this
|
||||
};
|
||||
|
||||
await _navigator.NavigateViewModelAsync<MachineViewViewModel>(this, data: navParam);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data model for a console in the list
|
||||
/// </summary>
|
||||
public class ConsoleListItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public int Year { get; set; }
|
||||
public string Manufacturer { get; set; } = string.Empty;
|
||||
}
|
||||
207
Marechai.App/Presentation/ViewModels/ConsolesViewModel.cs
Normal file
207
Marechai.App/Presentation/ViewModels/ConsolesViewModel.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.App.Services;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public partial class ConsolesViewModel : ObservableObject
|
||||
{
|
||||
private readonly ConsolesService _consolesService;
|
||||
private readonly IConsolesListFilterContext _filterContext;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<ConsolesViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _consoleCount;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _consoleCountText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<char> _lettersList = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _maximumYear;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _minimumYear;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _yearsGridTitle = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<int> _yearsList = [];
|
||||
|
||||
public ConsolesViewModel(ConsolesService consolesService, IStringLocalizer localizer,
|
||||
ILogger<ConsolesViewModel> logger, INavigator navigator,
|
||||
IConsolesListFilterContext filterContext)
|
||||
{
|
||||
_consolesService = consolesService;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
_filterContext = filterContext;
|
||||
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
||||
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
||||
NavigateByLetterCommand = new AsyncRelayCommand<char>(NavigateByLetterAsync);
|
||||
NavigateByYearCommand = new AsyncRelayCommand<int>(NavigateByYearAsync);
|
||||
NavigateAllConsolesCommand = new AsyncRelayCommand(NavigateAllConsolesAsync);
|
||||
|
||||
InitializeLetters();
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadData { get; }
|
||||
public ICommand GoBackCommand { get; }
|
||||
public IAsyncRelayCommand<char> NavigateByLetterCommand { get; }
|
||||
public IAsyncRelayCommand<int> NavigateByYearCommand { get; }
|
||||
public IAsyncRelayCommand NavigateAllConsolesCommand { get; }
|
||||
public string Title { get; } = "Consoles";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the alphabet list (A-Z)
|
||||
/// </summary>
|
||||
private void InitializeLetters()
|
||||
{
|
||||
LettersList.Clear();
|
||||
|
||||
for(var c = 'A'; c <= 'Z'; c++) LettersList.Add(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads consoles count, minimum and maximum years from the API
|
||||
/// </summary>
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
IsDataLoaded = false;
|
||||
YearsList.Clear();
|
||||
|
||||
// Load all data in parallel for better performance
|
||||
Task<int> countTask = _consolesService.GetConsolesCountAsync();
|
||||
Task<int> minYearTask = _consolesService.GetMinimumYearAsync();
|
||||
Task<int> maxYearTask = _consolesService.GetMaximumYearAsync();
|
||||
await Task.WhenAll(countTask, minYearTask, maxYearTask);
|
||||
|
||||
ConsoleCount = countTask.Result;
|
||||
MinimumYear = minYearTask.Result;
|
||||
MaximumYear = maxYearTask.Result;
|
||||
|
||||
// Update display text
|
||||
ConsoleCountText = _localizer["Consoles in the database"];
|
||||
|
||||
// Generate years list
|
||||
if(MinimumYear > 0 && MaximumYear > 0)
|
||||
{
|
||||
for(int year = MinimumYear; year <= MaximumYear; year++) YearsList.Add(year);
|
||||
|
||||
YearsGridTitle = $"Browse by Year ({MinimumYear} - {MaximumYear})";
|
||||
}
|
||||
|
||||
if(ConsoleCount == 0)
|
||||
{
|
||||
ErrorMessage = _localizer["No consoles found"].Value;
|
||||
HasError = true;
|
||||
}
|
||||
else
|
||||
IsDataLoaded = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error loading consoles data: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load consoles data. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles back navigation
|
||||
/// </summary>
|
||||
private async Task GoBackAsync()
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<MainViewModel>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to consoles filtered by letter
|
||||
/// </summary>
|
||||
private async Task NavigateByLetterAsync(char letter)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to consoles by letter: {Letter}", letter);
|
||||
_filterContext.FilterType = ConsoleListFilterType.Letter;
|
||||
_filterContext.FilterValue = letter.ToString();
|
||||
await _navigator.NavigateRouteAsync(this, "list-consoles");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to letter consoles: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to consoles filtered by year
|
||||
/// </summary>
|
||||
private async Task NavigateByYearAsync(int year)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to consoles by year: {Year}", year);
|
||||
_filterContext.FilterType = ConsoleListFilterType.Year;
|
||||
_filterContext.FilterValue = year.ToString();
|
||||
await _navigator.NavigateRouteAsync(this, "list-consoles");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to year consoles: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to all consoles view
|
||||
/// </summary>
|
||||
private async Task NavigateAllConsolesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to all consoles");
|
||||
_filterContext.FilterType = ConsoleListFilterType.All;
|
||||
_filterContext.FilterValue = string.Empty;
|
||||
await _navigator.NavigateRouteAsync(this, "list-consoles");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to all consoles: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,6 +127,22 @@ public partial class MachineViewViewModel : ObservableObject
|
||||
return;
|
||||
}
|
||||
|
||||
// If we came from ConsolesListViewModel, navigate back to consoles list
|
||||
if(_navigationSource is ConsolesListViewModel)
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<ConsolesListViewModel>(this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we came from ComputersListViewModel, navigate back to computers list
|
||||
if(_navigationSource is ComputersListViewModel)
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<ComputersListViewModel>(this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, try to go back in the navigation stack
|
||||
await _navigator.GoBack(this);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user