mirror of
https://github.com/claunia/marechai.git
synced 2025-12-16 19:14:25 +00:00
530 lines
20 KiB
C#
530 lines
20 KiB
C#
#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 Marechai.Data;
|
|
using Microsoft.UI.Xaml.Media.Imaging;
|
|
using Uno.Extensions.Navigation;
|
|
|
|
namespace Marechai.App.Presentation.ViewModels;
|
|
|
|
public partial class CompanyDetailViewModel : ObservableObject
|
|
{
|
|
private readonly CompanyDetailService _companyDetailService;
|
|
private readonly FlagCache _flagCache;
|
|
private readonly IStringLocalizer _localizer;
|
|
private readonly ILogger<CompanyDetailViewModel> _logger;
|
|
private readonly CompanyLogoCache _logoCache;
|
|
private readonly INavigator _navigator;
|
|
|
|
[ObservableProperty]
|
|
private CompanyDto? _company;
|
|
|
|
[ObservableProperty]
|
|
private int _companyId;
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<CompanyLogoItem> _companyLogos = [];
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<CompanyDetailMachine> _computers = [];
|
|
|
|
[ObservableProperty]
|
|
private string _computersFilterText = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private string _consoelsFilterText = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<CompanyDetailMachine> _consoles = [];
|
|
|
|
[ObservableProperty]
|
|
private string _errorMessage = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<CompanyDetailMachine> _filteredComputers = [];
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<CompanyDetailMachine> _filteredConsoles = [];
|
|
|
|
[ObservableProperty]
|
|
private SvgImageSource? _flagImageSource;
|
|
|
|
[ObservableProperty]
|
|
private bool _hasError;
|
|
|
|
[ObservableProperty]
|
|
private bool _isDataLoaded;
|
|
|
|
[ObservableProperty]
|
|
private bool _isLoading;
|
|
|
|
[ObservableProperty]
|
|
private SvgImageSource? _logoImageSource;
|
|
|
|
[ObservableProperty]
|
|
private CompanyDto? _soldToCompany;
|
|
|
|
public CompanyDetailViewModel(CompanyDetailService companyDetailService, FlagCache flagCache,
|
|
CompanyLogoCache logoCache, IStringLocalizer localizer,
|
|
ILogger<CompanyDetailViewModel> logger, INavigator navigator)
|
|
{
|
|
_companyDetailService = companyDetailService;
|
|
_flagCache = flagCache;
|
|
_logoCache = logoCache;
|
|
_localizer = localizer;
|
|
_logger = logger;
|
|
_navigator = navigator;
|
|
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
|
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
|
NavigateToMachineCommand = new AsyncRelayCommand<CompanyDetailMachine>(NavigateToMachineAsync);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the display text for the company's status
|
|
/// </summary>
|
|
public string CompanyStatusDisplay => Company != null ? GetStatusMessage(Company) : string.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the display text for the company's founded date
|
|
/// </summary>
|
|
public string CompanyFoundedDateDisplay => Company != null ? GetFoundedDateDisplay(Company) : string.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets whether flag content is available
|
|
/// </summary>
|
|
public bool HasFlagContent => FlagImageSource != null;
|
|
|
|
/// <summary>
|
|
/// Gets whether logo content is available
|
|
/// </summary>
|
|
public bool HasLogoContent => LogoImageSource != null;
|
|
|
|
/// <summary>
|
|
/// Gets whether company has multiple logos
|
|
/// </summary>
|
|
public bool HasMultipleLogos => CompanyLogos.Count > 1;
|
|
|
|
public IAsyncRelayCommand LoadData { get; }
|
|
public ICommand GoBackCommand { get; }
|
|
public IAsyncRelayCommand<CompanyDetailMachine> NavigateToMachineCommand { get; }
|
|
public string Title { get; } = "Company Details";
|
|
|
|
partial void OnCompanyChanged(CompanyDto? oldValue, CompanyDto? newValue)
|
|
{
|
|
// Notify that computed properties have changed
|
|
OnPropertyChanged(nameof(CompanyStatusDisplay));
|
|
OnPropertyChanged(nameof(CompanyFoundedDateDisplay));
|
|
}
|
|
|
|
partial void OnFlagImageSourceChanged(SvgImageSource? oldValue, SvgImageSource? newValue)
|
|
{
|
|
// Notify that HasFlagContent has changed
|
|
OnPropertyChanged(nameof(HasFlagContent));
|
|
}
|
|
|
|
partial void OnLogoImageSourceChanged(SvgImageSource? oldValue, SvgImageSource? newValue)
|
|
{
|
|
// Notify that HasLogoContent has changed
|
|
OnPropertyChanged(nameof(HasLogoContent));
|
|
}
|
|
|
|
partial void OnCompanyLogosChanged(ObservableCollection<CompanyLogoItem>? oldValue,
|
|
ObservableCollection<CompanyLogoItem> newValue)
|
|
{
|
|
// Notify that HasMultipleLogos has changed
|
|
OnPropertyChanged(nameof(HasMultipleLogos));
|
|
}
|
|
|
|
partial void OnComputersFilterTextChanged(string value)
|
|
{
|
|
FilterComputers(value);
|
|
}
|
|
|
|
partial void OnConsoelsFilterTextChanged(string value)
|
|
{
|
|
FilterConsoles(value);
|
|
}
|
|
|
|
private void FilterComputers(string filterText)
|
|
{
|
|
ObservableCollection<CompanyDetailMachine> filtered = string.IsNullOrWhiteSpace(filterText)
|
|
? new ObservableCollection<
|
|
CompanyDetailMachine>(Computers)
|
|
: new
|
|
ObservableCollection<
|
|
CompanyDetailMachine>(Computers.Where(c =>
|
|
c.Name.Contains(filterText,
|
|
StringComparison
|
|
.OrdinalIgnoreCase)));
|
|
|
|
FilteredComputers = filtered;
|
|
}
|
|
|
|
private void FilterConsoles(string filterText)
|
|
{
|
|
ObservableCollection<CompanyDetailMachine> filtered = string.IsNullOrWhiteSpace(filterText)
|
|
? new ObservableCollection<
|
|
CompanyDetailMachine>(Consoles)
|
|
: new
|
|
ObservableCollection<
|
|
CompanyDetailMachine>(Consoles.Where(c =>
|
|
c.Name.Contains(filterText,
|
|
StringComparison
|
|
.OrdinalIgnoreCase)));
|
|
|
|
FilteredConsoles = filtered;
|
|
}
|
|
|
|
private async Task NavigateToMachineAsync(CompanyDetailMachine? machine)
|
|
{
|
|
if(machine == null) return;
|
|
|
|
var navParam = new MachineViewNavigationParameter
|
|
{
|
|
MachineId = machine.Id,
|
|
NavigationSource = this
|
|
};
|
|
|
|
await _navigator.NavigateViewModelAsync<MachineViewViewModel>(this, data: navParam);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the formatted founding date with unknown handling
|
|
/// </summary>
|
|
public string GetFoundedDateDisplay(CompanyDto company)
|
|
{
|
|
if(company.Founded is null) return string.Empty;
|
|
|
|
DateTime date = company.Founded.Value.DateTime;
|
|
|
|
if(company.FoundedMonthIsUnknown ?? false) return $"{date.Year}.";
|
|
|
|
if(company.FoundedDayIsUnknown ?? false) return $"{date:Y}.";
|
|
|
|
return $"{date:D}.";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the formatted sold/event date with unknown handling
|
|
/// </summary>
|
|
public string GetEventDateDisplay(CompanyDto? company, bool monthUnknown = false, bool dayUnknown = false)
|
|
{
|
|
if(company?.Sold is null) return _localizer["unknown date"].Value;
|
|
|
|
DateTime date = company.Sold.Value.DateTime;
|
|
|
|
if(monthUnknown || (company.SoldMonthIsUnknown ?? false)) return $"{date.Year}";
|
|
|
|
if(dayUnknown || (company.SoldDayIsUnknown ?? false)) return $"{date:Y}";
|
|
|
|
return $"{date:D}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the status message for the company
|
|
/// </summary>
|
|
public string GetStatusMessage(CompanyDto company)
|
|
{
|
|
return company.Status switch
|
|
{
|
|
1 => _localizer["Company is active."].Value,
|
|
2 => GetSoldStatusMessage(company),
|
|
3 => GetMergedStatusMessage(company),
|
|
4 => GetBankruptcyMessage(company),
|
|
5 => GetDefunctMessage(company),
|
|
6 => GetRenamedStatusMessage(company),
|
|
_ => _localizer["Current company status is unknown."].Value
|
|
};
|
|
}
|
|
|
|
private string GetSoldStatusMessage(CompanyDto company)
|
|
{
|
|
if(SoldToCompany != null)
|
|
{
|
|
return string.Format(_localizer["Company sold to {0} on {1}."].Value,
|
|
SoldToCompany.Name,
|
|
GetEventDateDisplay(company));
|
|
}
|
|
|
|
if(company.Sold != null)
|
|
{
|
|
return string.Format(_localizer["Company sold on {0} to an unknown company."].Value,
|
|
GetEventDateDisplay(company));
|
|
}
|
|
|
|
return SoldToCompany != null
|
|
? string.Format(_localizer["Company sold to {0} on an unknown date."].Value, SoldToCompany.Name)
|
|
: _localizer["Company was sold to an unknown company on an unknown date."].Value;
|
|
}
|
|
|
|
private string GetMergedStatusMessage(CompanyDto company)
|
|
{
|
|
if(SoldToCompany != null)
|
|
{
|
|
return string.Format(_localizer["Company merged on {0} to form {1}."].Value,
|
|
GetEventDateDisplay(company),
|
|
SoldToCompany.Name);
|
|
}
|
|
|
|
if(company.Sold != null)
|
|
{
|
|
return string.Format(_localizer["Company merged on {0} to form an unknown company."].Value,
|
|
GetEventDateDisplay(company));
|
|
}
|
|
|
|
return SoldToCompany != null
|
|
? string.Format(_localizer["Company merged on an unknown date to form {0}."].Value,
|
|
SoldToCompany.Name)
|
|
: _localizer["Company merged to form an unknown company on an unknown date."].Value;
|
|
}
|
|
|
|
private string GetBankruptcyMessage(CompanyDto company) => company.Sold != null
|
|
? string.Format(_localizer
|
|
["Company declared bankruptcy on {0}."]
|
|
.Value,
|
|
GetEventDateDisplay(company))
|
|
: _localizer
|
|
["Company declared bankruptcy on an unknown date."]
|
|
.Value;
|
|
|
|
private string GetDefunctMessage(CompanyDto company) => company.Sold != null
|
|
? string.Format(_localizer
|
|
["Company ceased operations on {0}."]
|
|
.Value,
|
|
GetEventDateDisplay(company))
|
|
: _localizer
|
|
["Company ceased operations on an unknown date."]
|
|
.Value;
|
|
|
|
private string GetRenamedStatusMessage(CompanyDto company)
|
|
{
|
|
if(SoldToCompany != null)
|
|
{
|
|
return string.Format(_localizer["Company renamed to {0} on {1}."].Value,
|
|
SoldToCompany.Name,
|
|
GetEventDateDisplay(company));
|
|
}
|
|
|
|
if(company.Sold != null)
|
|
{
|
|
return string.Format(_localizer["Company was renamed on {0} to an unknown name."].Value,
|
|
GetEventDateDisplay(company));
|
|
}
|
|
|
|
return SoldToCompany != null
|
|
? string.Format(_localizer["Company renamed to {0} on an unknown date."].Value, SoldToCompany.Name)
|
|
: _localizer["Company renamed to an unknown name on an unknown date."].Value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads company details from the API
|
|
/// </summary>
|
|
private async Task LoadDataAsync()
|
|
{
|
|
try
|
|
{
|
|
IsLoading = true;
|
|
ErrorMessage = string.Empty;
|
|
HasError = false;
|
|
IsDataLoaded = false;
|
|
FlagImageSource = null;
|
|
LogoImageSource = null;
|
|
CompanyLogos.Clear();
|
|
|
|
if(CompanyId <= 0)
|
|
{
|
|
ErrorMessage = _localizer["Invalid company ID."].Value;
|
|
HasError = true;
|
|
|
|
return;
|
|
}
|
|
|
|
// Load company details
|
|
Company = await _companyDetailService.GetCompanyByIdAsync(CompanyId);
|
|
|
|
if(Company is null)
|
|
{
|
|
ErrorMessage = _localizer["Company not found."].Value;
|
|
HasError = true;
|
|
|
|
return;
|
|
}
|
|
|
|
// Load flag if country is available
|
|
if(Company.CountryId is not null)
|
|
{
|
|
try
|
|
{
|
|
var countryCode = (short)(Company.CountryId ?? 0);
|
|
Stream? flagStream = await _flagCache.GetFlagAsync(countryCode);
|
|
|
|
var flagSource = new SvgImageSource();
|
|
await flagSource.SetSourceAsync(flagStream.AsRandomAccessStream());
|
|
FlagImageSource = flagSource;
|
|
|
|
_logger.LogInformation("Successfully loaded flag for country code {CountryCode}", countryCode);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
_logger.LogError("Failed to load flag for country {CountryId}: {Exception}",
|
|
Company.CountryId,
|
|
ex.Message);
|
|
|
|
// Continue without flag if loading fails
|
|
}
|
|
}
|
|
|
|
if(Company.SoldToId != null)
|
|
{
|
|
int soldToId = Company.SoldToId ?? 0;
|
|
if(soldToId > 0) SoldToCompany = await _companyDetailService.GetSoldToCompanyAsync(soldToId);
|
|
}
|
|
|
|
// Load logo if available
|
|
if(Company.LastLogo.HasValue)
|
|
{
|
|
try
|
|
{
|
|
Stream? logoStream = await _logoCache.GetLogoAsync(Company.LastLogo.Value);
|
|
|
|
var logoSource = new SvgImageSource();
|
|
await logoSource.SetSourceAsync(logoStream.AsRandomAccessStream());
|
|
LogoImageSource = logoSource;
|
|
|
|
_logger.LogInformation("Successfully loaded logo for company {CompanyId}", CompanyId);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
_logger.LogError("Failed to load logo for company {CompanyId}: {Exception}", CompanyId, ex.Message);
|
|
|
|
// Continue without logo if loading fails
|
|
}
|
|
}
|
|
|
|
// Load all logos for carousel
|
|
try
|
|
{
|
|
// Get all logos for this company
|
|
List<CompanyLogoDto> logosList = await _companyDetailService.GetCompanyLogosAsync(CompanyId);
|
|
|
|
// Convert to list with extracted years for sorting
|
|
var logosWithYears = logosList.Select(logo => new
|
|
{
|
|
Logo = logo,
|
|
logo.Year
|
|
})
|
|
.OrderBy(l => l.Year)
|
|
.ToList();
|
|
|
|
var loadedLogos = new ObservableCollection<CompanyLogoItem>();
|
|
|
|
foreach(var logoData in logosWithYears)
|
|
{
|
|
try
|
|
{
|
|
if(logoData.Logo.Guid == null) continue;
|
|
|
|
Stream? logoStream = await _logoCache.GetLogoAsync(logoData.Logo.Guid.Value);
|
|
var logoSource = new SvgImageSource();
|
|
await logoSource.SetSourceAsync(logoStream.AsRandomAccessStream());
|
|
|
|
loadedLogos.Add(new CompanyLogoItem
|
|
{
|
|
LogoGuid = logoData.Logo.Guid.Value,
|
|
LogoSource = logoSource,
|
|
Year = logoData.Year
|
|
});
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
_logger.LogError("Failed to load carousel logo: {Exception}", ex.Message);
|
|
}
|
|
}
|
|
|
|
// Assign the new collection (this will trigger OnCompanyLogosChanged)
|
|
CompanyLogos = loadedLogos;
|
|
|
|
_logger.LogInformation("Loaded {Count} logos for company {CompanyId}", CompanyLogos.Count, CompanyId);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
_logger.LogError("Failed to load company logos for carousel: {Exception}", ex.Message);
|
|
}
|
|
|
|
// Load computers and consoles made by this company
|
|
List<MachineDto> machines = await _companyDetailService.GetComputersByCompanyAsync(CompanyId);
|
|
Computers.Clear();
|
|
Consoles.Clear();
|
|
FilteredComputers.Clear();
|
|
FilteredConsoles.Clear();
|
|
|
|
foreach(MachineDto machine in machines)
|
|
{
|
|
int machineId = machine.Id ?? 0;
|
|
|
|
var machineItem = new CompanyDetailMachine
|
|
{
|
|
Id = machineId,
|
|
Name = machine.Name ?? string.Empty
|
|
};
|
|
|
|
// Categorize by machine type enum
|
|
if(machine.Type == (int)MachineType.Computer)
|
|
Computers.Add(machineItem);
|
|
else if(machine.Type == (int)MachineType.Console) Consoles.Add(machineItem);
|
|
}
|
|
|
|
// Initialize filtered lists
|
|
FilteredComputers = new ObservableCollection<CompanyDetailMachine>(Computers);
|
|
FilteredConsoles = new ObservableCollection<CompanyDetailMachine>(Consoles);
|
|
|
|
IsDataLoaded = true;
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
_logger.LogError("Error loading company details: {Exception}", ex.Message);
|
|
ErrorMessage = _localizer["Failed to load company details. Please try again later."].Value;
|
|
HasError = true;
|
|
}
|
|
finally
|
|
{
|
|
IsLoading = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles back navigation
|
|
/// </summary>
|
|
private async Task GoBackAsync()
|
|
{
|
|
await _navigator.NavigateViewModelAsync<CompaniesViewModel>(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Data model for a machine in the company detail view
|
|
/// </summary>
|
|
public class CompanyDetailMachine
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = string.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Data model for a company logo in the carousel
|
|
/// </summary>
|
|
public class CompanyLogoItem
|
|
{
|
|
public Guid LogoGuid { get; set; }
|
|
public SvgImageSource? LogoSource { get; set; }
|
|
public int? Year { get; set; }
|
|
} |