diff --git a/Marechai.App/App.xaml b/Marechai.App/App.xaml index e430aa6d..77a46865 100644 --- a/Marechai.App/App.xaml +++ b/Marechai.App/App.xaml @@ -1,19 +1,23 @@ + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:Marechai.App.Presentation.Converters"> - - - - - - - - + + + + + + + + - + + + + - - + + - + \ No newline at end of file diff --git a/Marechai.App/App.xaml.cs b/Marechai.App/App.xaml.cs index d2c04866..0ba22de6 100644 --- a/Marechai.App/App.xaml.cs +++ b/Marechai.App/App.xaml.cs @@ -10,6 +10,7 @@ using Uno.Extensions.Http; using Uno.Extensions.Localization; using Uno.Extensions.Navigation; using Uno.UI; +using CompanyDetailViewModel = Marechai.App.Presentation.ViewModels.CompanyDetailViewModel; using ComputersListViewModel = Marechai.App.Presentation.ViewModels.ComputersListViewModel; using ComputersViewModel = Marechai.App.Presentation.ViewModels.ComputersViewModel; using MachineViewViewModel = Marechai.App.Presentation.ViewModels.MachineViewViewModel; @@ -113,6 +114,8 @@ public partial class App : Application services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services @@ -148,6 +151,7 @@ public partial class App : Application new ViewMap(), new ViewMap(), new ViewMap(), + new ViewMap(), new ViewMap(), new DataViewMap()); @@ -183,7 +187,13 @@ public partial class App : Application ConsolesListViewModel>()) ]), new RouteMap("companies", - views.FindByViewModel()), + views.FindByViewModel(), + Nested: + [ + new RouteMap("detail", + views.FindByViewModel< + CompanyDetailViewModel>()) + ]), new RouteMap("Second", views.FindByViewModel()) ]) diff --git a/Marechai.App/Presentation/Converters/CompanyConverters.cs b/Marechai.App/Presentation/Converters/CompanyConverters.cs new file mode 100644 index 00000000..17881c88 --- /dev/null +++ b/Marechai.App/Presentation/Converters/CompanyConverters.cs @@ -0,0 +1,51 @@ +using System; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Data; + +namespace Marechai.App.Presentation.Converters; + +/// +/// Converts null object to Collapsed visibility +/// +public class ObjectToVisibilityConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, string language) => + value != null ? Visibility.Visible : Visibility.Collapsed; + + public object ConvertBack(object value, Type targetType, object parameter, string language) => + throw new NotImplementedException(); +} + +/// +/// Converts empty/null string to Collapsed visibility +/// +public class StringToVisibilityConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, string language) + { + if(value is string str && !string.IsNullOrWhiteSpace(str)) return Visibility.Visible; + + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) => + throw new NotImplementedException(); +} + +/// +/// Converts zero count to Collapsed visibility, otherwise Visible +/// +public class ZeroToVisibilityConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, string language) + { + if(value is int count && count > 0) return Visibility.Visible; + + if(value is long longCount && longCount > 0) return Visibility.Visible; + + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) => + throw new NotImplementedException(); +} \ No newline at end of file diff --git a/Marechai.App/Presentation/Models/CompanyDetailNavigationParameter.cs b/Marechai.App/Presentation/Models/CompanyDetailNavigationParameter.cs new file mode 100644 index 00000000..d3c6ed48 --- /dev/null +++ b/Marechai.App/Presentation/Models/CompanyDetailNavigationParameter.cs @@ -0,0 +1,35 @@ +/****************************************************************************** +// MARECHAI: Master repository of computing history artifacts information +// ---------------------------------------------------------------------------- +// +// Author(s) : Natalia Portillo +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2026 Natalia Portillo +*******************************************************************************/ + +namespace Marechai.App.Presentation.Models; + +/// +/// Navigation parameter for the CompanyDetailPage containing both the company ID and the navigation source. +/// +public class CompanyDetailNavigationParameter +{ + public required int CompanyId { get; init; } + public object? NavigationSource { get; init; } +} \ No newline at end of file diff --git a/Marechai.App/Presentation/ViewModels/CompaniesViewModel.cs b/Marechai.App/Presentation/ViewModels/CompaniesViewModel.cs index 80b41897..5aeed4e9 100644 --- a/Marechai.App/Presentation/ViewModels/CompaniesViewModel.cs +++ b/Marechai.App/Presentation/ViewModels/CompaniesViewModel.cs @@ -7,6 +7,7 @@ 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; @@ -14,13 +15,12 @@ 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 INavigator _navigator; - private readonly List _allCompanies = []; - [ObservableProperty] private ObservableCollection _companiesList = []; @@ -146,8 +146,14 @@ public partial class CompaniesViewModel : ObservableObject _logger.LogInformation("Navigating to company: {CompanyName} (ID: {CompanyId})", company.Name, company.Id); - // TODO: Implement company detail view - // For now, just log the navigation + // Navigate to company detail view with navigation parameter + var navParam = new CompanyDetailNavigationParameter + { + CompanyId = company.Id, + NavigationSource = this + }; + + await _navigator.NavigateViewModelAsync(this, data: navParam); } /// diff --git a/Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs b/Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs new file mode 100644 index 00000000..b05d5748 --- /dev/null +++ b/Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs @@ -0,0 +1,373 @@ +#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 Marechai.Data; +using Uno.Extensions.Navigation; + +namespace Marechai.App.Presentation.ViewModels; + +public partial class CompanyDetailViewModel : ObservableObject +{ + private readonly CompanyDetailService _companyDetailService; + private readonly IStringLocalizer _localizer; + private readonly ILogger _logger; + private readonly INavigator _navigator; + + [ObservableProperty] + private CompanyDto? _company; + + [ObservableProperty] + private int _companyId; + + [ObservableProperty] + private ObservableCollection _computers = []; + + [ObservableProperty] + private string _computersFilterText = string.Empty; + + [ObservableProperty] + private string _consoelsFilterText = string.Empty; + + [ObservableProperty] + private ObservableCollection _consoles = []; + + [ObservableProperty] + private string _errorMessage = string.Empty; + + [ObservableProperty] + private ObservableCollection _filteredComputers = []; + + [ObservableProperty] + private ObservableCollection _filteredConsoles = []; + + [ObservableProperty] + private bool _hasError; + + [ObservableProperty] + private bool _isDataLoaded; + + [ObservableProperty] + private bool _isLoading; + + [ObservableProperty] + private CompanyDto? _soldToCompany; + + public CompanyDetailViewModel(CompanyDetailService companyDetailService, IStringLocalizer localizer, + ILogger logger, INavigator navigator) + { + _companyDetailService = companyDetailService; + _localizer = localizer; + _logger = logger; + _navigator = navigator; + LoadData = new AsyncRelayCommand(LoadDataAsync); + GoBackCommand = new AsyncRelayCommand(GoBackAsync); + NavigateToMachineCommand = new AsyncRelayCommand(NavigateToMachineAsync); + } + + /// + /// Gets the display text for the company's status + /// + public string CompanyStatusDisplay => Company != null ? GetStatusMessage(Company) : string.Empty; + + /// + /// Gets the display text for the company's founded date + /// + public string CompanyFoundedDateDisplay => Company != null ? GetFoundedDateDisplay(Company) : string.Empty; + + public IAsyncRelayCommand LoadData { get; } + public ICommand GoBackCommand { get; } + public IAsyncRelayCommand 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 OnComputersFilterTextChanged(string value) + { + FilterComputers(value); + } + + partial void OnConsoelsFilterTextChanged(string value) + { + FilterConsoles(value); + } + + private void FilterComputers(string filterText) + { + ObservableCollection 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 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(this, data: navParam); + } + + /// + /// Gets the formatted founding date with unknown handling + /// + 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}."; + } + + /// + /// Gets the formatted sold/event date with unknown handling + /// + 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}"; + } + + /// + /// Gets the status message for the company + /// + 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; + } + + /// + /// Loads company details from the API + /// + private async Task LoadDataAsync() + { + try + { + IsLoading = true; + ErrorMessage = string.Empty; + HasError = false; + IsDataLoaded = false; + + 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 sold-to company if applicable + if(Company.SoldToId != null) + { + int soldToId = UntypedNodeExtractor.ExtractInt(Company.SoldToId); + if(soldToId > 0) SoldToCompany = await _companyDetailService.GetSoldToCompanyAsync(soldToId); + } + + // Load computers and consoles made by this company + List machines = await _companyDetailService.GetComputersByCompanyAsync(CompanyId); + Computers.Clear(); + Consoles.Clear(); + FilteredComputers.Clear(); + FilteredConsoles.Clear(); + + foreach(MachineDto machine in machines) + { + int machineId = UntypedNodeExtractor.ExtractInt(machine.Id); + + 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(Computers); + FilteredConsoles = new ObservableCollection(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; + } + } + + /// + /// Handles back navigation + /// + private async Task GoBackAsync() + { + await _navigator.NavigateViewModelAsync(this); + } +} + +/// +/// Data model for a machine in the company detail view +/// +public class CompanyDetailMachine +{ + public int Id { get; set; } + public string Name { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/Marechai.App/Presentation/ViewModels/MachineViewViewModel.cs b/Marechai.App/Presentation/ViewModels/MachineViewViewModel.cs index 706c9493..181254d8 100644 --- a/Marechai.App/Presentation/ViewModels/MachineViewViewModel.cs +++ b/Marechai.App/Presentation/ViewModels/MachineViewViewModel.cs @@ -31,6 +31,7 @@ using System.Collections.ObjectModel; using System.Threading.Tasks; using Humanizer; using Marechai.App.Helpers; +using Marechai.App.Presentation.Models; using Marechai.App.Services; using Marechai.Data; using Microsoft.UI.Xaml; @@ -127,6 +128,19 @@ public partial class MachineViewViewModel : ObservableObject return; } + // If we came from CompanyDetailViewModel, navigate back to company details + if(_navigationSource is CompanyDetailViewModel companyVm) + { + var navParam = new CompanyDetailNavigationParameter + { + CompanyId = companyVm.CompanyId + }; + + await _navigator.NavigateViewModelAsync(this, data: navParam); + + return; + } + // If we came from ConsolesListViewModel, navigate back to consoles list if(_navigationSource is ConsolesListViewModel) { diff --git a/Marechai.App/Presentation/Views/CompaniesPage.xaml b/Marechai.App/Presentation/Views/CompaniesPage.xaml index 801b4dee..b4a7be2b 100644 --- a/Marechai.App/Presentation/Views/CompaniesPage.xaml +++ b/Marechai.App/Presentation/Views/CompaniesPage.xaml @@ -92,7 +92,23 @@ - + + + + + diff --git a/Marechai.App/Presentation/Views/CompaniesPage.xaml.cs b/Marechai.App/Presentation/Views/CompaniesPage.xaml.cs new file mode 100644 index 00000000..f1954a10 --- /dev/null +++ b/Marechai.App/Presentation/Views/CompaniesPage.xaml.cs @@ -0,0 +1,59 @@ +using Marechai.App.Presentation.ViewModels; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace Marechai.App.Presentation.Views; + +public sealed partial class CompaniesPage : Page +{ + public CompaniesPage() + { + InitializeComponent(); + DataContextChanged += CompaniesPage_DataContextChanged; + Loaded += CompaniesPage_Loaded; + } + + private void CompaniesPage_Loaded(object sender, RoutedEventArgs e) + { + if(DataContext is not CompaniesViewModel viewModel) return; + + // Trigger data loading + _ = viewModel.LoadData.ExecuteAsync(null); + } + + private void CompaniesPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) + { + if(args.NewValue is CompaniesViewModel viewModel) + { + // Trigger data loading when data context changes + _ = viewModel.LoadData.ExecuteAsync(null); + } + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + if(DataContext is CompaniesViewModel viewModel) + { + // Trigger data loading when navigating to the page + _ = viewModel.LoadData.ExecuteAsync(null); + } + } + + private void OnSearchTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) + { + if(args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) + { + // The two-way binding will automatically update SearchQuery in ViewModel, + // which will trigger OnSearchQueryChanged and filter the list + } + } + + private void OnSearchQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) + { + // The two-way binding will automatically update SearchQuery in ViewModel, + // which will trigger OnSearchQueryChanged and filter the list + } +} \ No newline at end of file diff --git a/Marechai.App/Presentation/Views/CompanyDetailPage.xaml b/Marechai.App/Presentation/Views/CompanyDetailPage.xaml new file mode 100644 index 00000000..8378831b --- /dev/null +++ b/Marechai.App/Presentation/Views/CompanyDetailPage.xaml @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Marechai.App/Presentation/Views/CompanyDetailPage.xaml.cs b/Marechai.App/Presentation/Views/CompanyDetailPage.xaml.cs new file mode 100644 index 00000000..dc031e05 --- /dev/null +++ b/Marechai.App/Presentation/Views/CompanyDetailPage.xaml.cs @@ -0,0 +1,77 @@ +#nullable enable + +using System; +using Windows.System; +using Marechai.App.Presentation.Models; +using Marechai.App.Presentation.ViewModels; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace Marechai.App.Presentation.Views; + +public sealed partial class CompanyDetailPage : Page +{ + private object? _navigationSource; + private int? _pendingCompanyId; + + public CompanyDetailPage() + { + InitializeComponent(); + DataContextChanged += CompanyDetailPage_DataContextChanged; + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + int? companyId = null; + + // Handle both int and CompanyDetailNavigationParameter + if(e.Parameter is int intId) + companyId = intId; + else if(e.Parameter is CompanyDetailNavigationParameter navParam) + { + companyId = navParam.CompanyId; + _navigationSource = navParam.NavigationSource; + } + + if(companyId.HasValue) + { + _pendingCompanyId = companyId; + + if(DataContext is CompanyDetailViewModel viewModel) + { + viewModel.CompanyId = companyId.Value; + _ = viewModel.LoadData.ExecuteAsync(null); + } + } + } + + private void CompanyDetailPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) + { + if(DataContext is CompanyDetailViewModel viewModel && _pendingCompanyId.HasValue) + { + viewModel.CompanyId = _pendingCompanyId.Value; + _ = viewModel.LoadData.ExecuteAsync(null); + } + } + + private async void OnTwitterClick(object sender, RoutedEventArgs e) + { + if(DataContext is CompanyDetailViewModel viewModel && viewModel.Company?.Twitter is not null) + { + var uri = new Uri($"https://www.twitter.com/{viewModel.Company.Twitter}"); + await Launcher.LaunchUriAsync(uri); + } + } + + private async void OnFacebookClick(object sender, RoutedEventArgs e) + { + if(DataContext is CompanyDetailViewModel viewModel && viewModel.Company?.Facebook is not null) + { + var uri = new Uri($"https://www.facebook.com/{viewModel.Company.Facebook}"); + await Launcher.LaunchUriAsync(uri); + } + } +} \ No newline at end of file diff --git a/Marechai.App/Presentation/Views/ConsolesListPage.xaml.cs b/Marechai.App/Presentation/Views/ConsolesListPage.xaml.cs new file mode 100644 index 00000000..535f1e93 --- /dev/null +++ b/Marechai.App/Presentation/Views/ConsolesListPage.xaml.cs @@ -0,0 +1,37 @@ +using Marechai.App.Presentation.ViewModels; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Marechai.App.Presentation.Views; + +/// +/// Professional list view for displaying consoles filtered by letter, year, or all. +/// Features responsive layout, modern styling, and smooth navigation. +/// +public sealed partial class ConsolesListPage : Page +{ + public ConsolesListPage() + { + InitializeComponent(); + Loaded += ConsolesListPage_Loaded; + DataContextChanged += ConsolesListPage_DataContextChanged; + } + + private void ConsolesListPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) + { + if(DataContext is ConsolesListViewModel vm) + { + // Load data when DataContext is set + vm.LoadData.Execute(null); + } + } + + private void ConsolesListPage_Loaded(object sender, RoutedEventArgs e) + { + if(DataContext is ConsolesListViewModel vm) + { + // Load data when page is loaded (fallback) + vm.LoadData.Execute(null); + } + } +} \ No newline at end of file diff --git a/Marechai.App/Presentation/Views/ConsolesPage.xaml.cs b/Marechai.App/Presentation/Views/ConsolesPage.xaml.cs new file mode 100644 index 00000000..3d5fbdad --- /dev/null +++ b/Marechai.App/Presentation/Views/ConsolesPage.xaml.cs @@ -0,0 +1,44 @@ +using Marechai.App.Presentation.ViewModels; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace Marechai.App.Presentation.Views; + +public sealed partial class ConsolesPage : Page +{ + public ConsolesPage() + { + InitializeComponent(); + DataContextChanged += ConsolesPage_DataContextChanged; + Loaded += ConsolesPage_Loaded; + } + + private void ConsolesPage_Loaded(object sender, RoutedEventArgs e) + { + if(DataContext is not ConsolesViewModel viewModel) return; + + // Trigger data loading + _ = viewModel.LoadData.ExecuteAsync(null); + } + + private void ConsolesPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) + { + if(args.NewValue is ConsolesViewModel viewModel) + { + // Trigger data loading when data context changes + _ = viewModel.LoadData.ExecuteAsync(null); + } + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + if(DataContext is ConsolesViewModel viewModel) + { + // Trigger data loading when navigating to the page + _ = viewModel.LoadData.ExecuteAsync(null); + } + } +} \ No newline at end of file diff --git a/Marechai.App/Services/CompanyDetailService.cs b/Marechai.App/Services/CompanyDetailService.cs new file mode 100644 index 00000000..b66b7588 --- /dev/null +++ b/Marechai.App/Services/CompanyDetailService.cs @@ -0,0 +1,101 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Marechai.App.Models; + +namespace Marechai.App.Services; + +/// +/// Service for fetching company details from the API +/// +public class CompanyDetailService +{ + private readonly ApiClient _apiClient; + private readonly ILogger _logger; + + public CompanyDetailService(ApiClient apiClient, ILogger logger) + { + _apiClient = apiClient; + _logger = logger; + } + + /// + /// Gets a single company by ID with full details + /// + public async Task GetCompanyByIdAsync(int companyId) + { + try + { + _logger.LogInformation("Fetching company {CompanyId} from API", companyId); + + CompanyDto? company = await _apiClient.Companies[companyId].GetAsync(); + + if(company == null) + { + _logger.LogWarning("Company {CompanyId} not found", companyId); + + return null; + } + + _logger.LogInformation("Successfully fetched company {CompanyId}: {CompanyName}", companyId, company.Name); + + return company; + } + catch(Exception ex) + { + _logger.LogError(ex, "Error fetching company {CompanyId} from API", companyId); + + return null; + } + } + + /// + /// Gets machines (computers) made by a company + /// + public async Task> GetComputersByCompanyAsync(int companyId) + { + try + { + _logger.LogInformation("Fetching computers for company {CompanyId}", companyId); + + List? machines = await _apiClient.Companies[companyId].Machines.GetAsync(); + + if(machines == null) return []; + + _logger.LogInformation("Successfully fetched {Count} computers for company {CompanyId}", machines.Count, companyId); + + return machines; + } + catch(Exception ex) + { + _logger.LogError(ex, "Error fetching computers for company {CompanyId}", companyId); + + return []; + } + } + + /// + /// Gets the sold-to company (when company was sold, merged, or renamed) + /// + public async Task GetSoldToCompanyAsync(int? companyId) + { + if(companyId is null or <= 0) return null; + + try + { + _logger.LogInformation("Fetching sold-to company {CompanyId}", companyId); + + CompanyDto? company = await _apiClient.Companies[companyId.Value].GetAsync(); + + return company; + } + catch(Exception ex) + { + _logger.LogError(ex, "Error fetching sold-to company {CompanyId}", companyId); + + return null; + } + } +} diff --git a/Marechai.App/Strings/en/Resources.resw b/Marechai.App/Strings/en/Resources.resw index bb951adf..16dff704 100644 --- a/Marechai.App/Strings/en/Resources.resw +++ b/Marechai.App/Strings/en/Resources.resw @@ -109,4 +109,61 @@ Logout + + Company is active. + + + Company sold to {0} on {1}. + + + Company sold on {0} to an unknown company. + + + Company sold to {0} on an unknown date. + + + Company was sold to an unknown company on an unknown date. + + + Company merged on {0} to form {1}. + + + Company merged on {0} to form an unknown company. + + + Company merged on an unknown date to form {0}. + + + Company merged to form an unknown company on an unknown date. + + + Current company status is unknown. + + + unknown date + + + Company declared bankruptcy on {0}. + + + Company declared bankruptcy on an unknown date. + + + Company ceased operations on {0}. + + + Company ceased operations on an unknown date. + + + Company renamed to {0} on {1}. + + + Company was renamed on {0} to an unknown name. + + + Company renamed to {0} on an unknown date. + + + Company was renamed on an unknown date to an unknown name. + diff --git a/Marechai.App/Strings/es/Resources.resw b/Marechai.App/Strings/es/Resources.resw index 16b08eb9..72018021 100644 --- a/Marechai.App/Strings/es/Resources.resw +++ b/Marechai.App/Strings/es/Resources.resw @@ -109,4 +109,61 @@ Cerrar sesión + + La empresa está activa. + + + La empresa fue vendida a {0} el {1}. + + + La empresa fue vendida el {0} a una empresa desconocida. + + + La empresa fue vendida a {0} en una fecha desconocida. + + + La empresa fue vendida a una empresa desconocida en una fecha desconocida. + + + La empresa se fusionó el {0} para formar {1}. + + + La empresa se fusionó el {0} para formar una empresa desconocida. + + + La empresa se fusionó en una fecha desconocida para formar {0}. + + + La empresa se fusionó para formar una empresa desconocida en una fecha desconocida. + + + El estado actual de la empresa es desconocido. + + + fecha desconocida + + + La empresa declaró quiebra el {0}. + + + La empresa declaró quiebra en una fecha desconocida. + + + La empresa cesó operaciones el {0}. + + + La empresa cesó operaciones en una fecha desconocida. + + + La empresa fue renombrada a {0} el {1}. + + + La empresa fue renombrada el {0} a un nombre desconocido. + + + La empresa fue renombrada a {0} en una fecha desconocida. + + + La empresa fue renombrada en una fecha desconocida a un nombre desconocido. + diff --git a/Marechai.App/Strings/fr/Resources.resw b/Marechai.App/Strings/fr/Resources.resw index f448255b..d5298761 100644 --- a/Marechai.App/Strings/fr/Resources.resw +++ b/Marechai.App/Strings/fr/Resources.resw @@ -109,4 +109,61 @@ Déconnexion + + L'entreprise est active. + + + L'entreprise a été vendue à {0} le {1}. + + + L'entreprise a été vendue le {0} à une entreprise inconnue. + + + L'entreprise a été vendue à {0} à une date inconnue. + + + L'entreprise a été vendue à une entreprise inconnue à une date inconnue. + + + L'entreprise a fusionné le {0} pour former {1}. + + + L'entreprise a fusionné le {0} pour former une entreprise inconnue. + + + L'entreprise a fusionné à une date inconnue pour former {0}. + + + L'entreprise a fusionné pour former une entreprise inconnue à une date inconnue. + + + L'état actuel de l'entreprise est inconnu. + + + date inconnue + + + L'entreprise a déclaré faillite le {0}. + + + L'entreprise a déclaré faillite à une date inconnue. + + + L'entreprise a cessé ses activités le {0}. + + + L'entreprise a cessé ses activités à une date inconnue. + + + L'entreprise a été renommée en {0} le {1}. + + + L'entreprise a été renommée le {0} en un nom inconnu. + + + L'entreprise a été renommée en {0} à une date inconnue. + + + L'entreprise a été renommée à une date inconnue en un nom inconnu. + diff --git a/Marechai.App/Strings/pt-BR/Resources.resw b/Marechai.App/Strings/pt-BR/Resources.resw index 50e4a3e8..f2f52baa 100644 --- a/Marechai.App/Strings/pt-BR/Resources.resw +++ b/Marechai.App/Strings/pt-BR/Resources.resw @@ -109,4 +109,61 @@ Fazer Logout + + A empresa está ativa. + + + A empresa foi vendida para {0} em {1}. + + + A empresa foi vendida em {0} para uma empresa desconhecida. + + + A empresa foi vendida para {0} em uma data desconhecida. + + + A empresa foi vendida para uma empresa desconhecida em uma data desconhecida. + + + A empresa se fundiu em {0} para formar {1}. + + + A empresa se fundiu em {0} para formar uma empresa desconhecida. + + + A empresa se fundiu em uma data desconhecida para formar {0}. + + + A empresa se fundiu para formar uma empresa desconhecida em uma data desconhecida. + + + O status atual da empresa é desconhecido. + + + data desconhecida + + + A empresa declarou falência em {0}. + + + A empresa declarou falência em uma data desconhecida. + + + A empresa cessou operações em {0}. + + + A empresa cessou operações em uma data desconhecida. + + + A empresa foi renomeada para {0} em {1}. + + + A empresa foi renomeada em {0} para um nome desconhecido. + + + A empresa foi renomeada para {0} em uma data desconhecida. + + + A empresa foi renomeada em uma data desconhecida para um nome desconhecido. +