#nullable enable using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows.Input; using Marechai.App.Presentation.Models; using Marechai.App.Services; using Marechai.App.Services.Caching; using Microsoft.UI.Xaml.Media.Imaging; using Uno.Extensions.Navigation; namespace Marechai.App.Presentation.ViewModels; public partial class CompaniesViewModel : ObservableObject { private readonly List _allCompanies = []; private readonly CompaniesService _companiesService; private readonly IStringLocalizer _localizer; private readonly ILogger _logger; private readonly CompanyLogoCache _logoCache; private readonly INavigator _navigator; [ObservableProperty] private ObservableCollection _companiesList = []; [ObservableProperty] private int _companyCount; [ObservableProperty] private string _companyCountText = string.Empty; [ObservableProperty] private string _errorMessage = string.Empty; [ObservableProperty] private bool _hasError; [ObservableProperty] private bool _isDataLoaded; [ObservableProperty] private bool _isLoading; [ObservableProperty] private string _searchQuery = string.Empty; public CompaniesViewModel(CompaniesService companiesService, CompanyLogoCache logoCache, IStringLocalizer localizer, ILogger logger, INavigator navigator) { _companiesService = companiesService; _logoCache = logoCache; _localizer = localizer; _logger = logger; _navigator = navigator; LoadData = new AsyncRelayCommand(LoadDataAsync); GoBackCommand = new AsyncRelayCommand(GoBackAsync); NavigateToCompanyCommand = new AsyncRelayCommand(NavigateToCompanyAsync); } public IAsyncRelayCommand LoadData { get; } public ICommand GoBackCommand { get; } public IAsyncRelayCommand NavigateToCompanyCommand { get; } public string Title { get; } = "Companies"; partial void OnSearchQueryChanged(string value) { // Automatically filter when SearchQuery changes UpdateFilter(value); } /// /// Loads companies count and list from the API /// private async Task LoadDataAsync() { try { IsLoading = true; ErrorMessage = string.Empty; HasError = false; IsDataLoaded = false; CompaniesList.Clear(); _allCompanies.Clear(); // Load companies List companies = await _companiesService.GetAllCompaniesAsync(); // Set count CompanyCount = companies.Count; CompanyCountText = _localizer["Companies in the database"]; // Build the full list in memory foreach(CompanyDto company in companies) { // Extract id from company int companyId = company.Id ?? 0; // Convert DateTimeOffset? to DateTime? DateTime? foundedDate = company.Founded?.DateTime; // Load logo if available SvgImageSource? logoSource = null; if(company.LastLogo.HasValue) { try { Stream? logoStream = await _logoCache.GetLogoAsync(company.LastLogo.Value); logoSource = new SvgImageSource(); await logoSource.SetSourceAsync(logoStream.AsRandomAccessStream()); } catch(Exception ex) { _logger.LogWarning("Failed to load logo for company {CompanyId}: {Exception}", companyId, ex.Message); } } _allCompanies.Add(new CompanyListItem { Id = companyId, Name = company.Name ?? string.Empty, FoundationDate = foundedDate, LogoImageSource = logoSource }); } // Apply current filter (will show all if SearchQuery is empty) UpdateFilter(SearchQuery); if(CompaniesList.Count == 0) { ErrorMessage = _localizer["No companies found"].Value; HasError = true; } else IsDataLoaded = true; } catch(Exception ex) { _logger.LogError("Error loading companies data: {Exception}", ex.Message); ErrorMessage = _localizer["Failed to load companies data. Please try again later."].Value; HasError = true; } finally { IsLoading = false; } } /// /// Handles back navigation /// private async Task GoBackAsync() { await _navigator.NavigateViewModelAsync(this); } /// /// Navigates to company detail view /// private async Task NavigateToCompanyAsync(CompanyListItem? company) { if(company is null) return; _logger.LogInformation("Navigating to company: {CompanyName} (ID: {CompanyId})", company.Name, company.Id); // Navigate to company detail view with navigation parameter var navParam = new CompanyDetailNavigationParameter { CompanyId = company.Id, NavigationSource = this }; await _navigator.NavigateViewModelAsync(this, data: navParam); } /// /// Updates the filtered list based on search query /// private void UpdateFilter(string? query) { string lowerQuery = string.IsNullOrWhiteSpace(query) ? string.Empty : query.Trim().ToLowerInvariant(); CompaniesList.Clear(); if(string.IsNullOrEmpty(lowerQuery)) { // No filter, show all companies foreach(CompanyListItem company in _allCompanies) CompaniesList.Add(company); } else { // Filter companies by name (case-insensitive) var filtered = _allCompanies.Where(c => c.Name.Contains(lowerQuery, StringComparison.OrdinalIgnoreCase)) .ToList(); foreach(CompanyListItem company in filtered) CompaniesList.Add(company); } } } /// /// Data model for a company in the list /// public class CompanyListItem { public int Id { get; set; } public string Name { get; set; } = string.Empty; public DateTime? FoundationDate { get; set; } public SvgImageSource? LogoImageSource { get; set; } public string FoundationDateDisplay => FoundationDate.HasValue ? FoundationDate.Value.ToString("MMMM d, yyyy") : string.Empty; }