Add software-related request builders and services

- Implement CountRequestBuilder for fetching software count.
- Create CompaniesRequestBuilder for accessing software companies.
- Introduce MaximumYearRequestBuilder and MinimumYearRequestBuilder for year-related queries.
- Enhance ItemRequestBuilder to include CompaniesRequestBuilder.
- Add SoftwareBrowsingService for software data retrieval, including count, year, and filtering by letter, year, and platform.
- Update SoftwareController to handle new endpoints for software count, minimum year, maximum year, and filtering by letter, year, and platform.
- Introduce SoftwareListFilterContext for managing software list filters.
- Add localized strings for software-related UI elements.
- Update kiota-lock.json to reflect changes in API description location.
This commit is contained in:
2026-03-23 14:20:24 +00:00
parent adfa1f1760
commit c8bef2e854
41 changed files with 3780 additions and 4 deletions

View File

@@ -187,12 +187,14 @@ public partial class App : PrismApplication
containerRegistry.RegisterSingleton<SoftwareVariantsService>();
containerRegistry.RegisterSingleton<SoftwareSubvariantsService>();
containerRegistry.RegisterSingleton<SoftwareReleasesService>();
containerRegistry.RegisterSingleton<SoftwareBrowsingService>();
containerRegistry.RegisterSingleton<IComputersListFilterContext, ComputersListFilterContext>();
containerRegistry.RegisterSingleton<IConsolesListFilterContext, ConsolesListFilterContext>();
containerRegistry.RegisterSingleton<IBooksListFilterContext, BooksListFilterContext>();
containerRegistry.RegisterSingleton<IDocumentsListFilterContext, DocumentsListFilterContext>();
containerRegistry.RegisterSingleton<IMagazinesListFilterContext, MagazinesListFilterContext>();
containerRegistry.RegisterSingleton<IPeopleListFilterContext, PeopleListFilterContext>();
containerRegistry.RegisterSingleton<ISoftwareListFilterContext, SoftwareListFilterContext>();
// Register ViewModels explicitly — RegisterForNavigation creates conditional
// (keyed) registrations that the ViewModelLocator can't resolve by type alone
@@ -243,6 +245,10 @@ public partial class App : PrismApplication
containerRegistry.Register<AdminBooksViewModel>();
containerRegistry.Register<AdminDocumentsViewModel>();
containerRegistry.Register<AdminMagazinesViewModel>();
containerRegistry.Register<SoftwareViewModel>();
containerRegistry.Register<SoftwareListViewModel>();
containerRegistry.Register<SoftwareViewViewModel>();
containerRegistry.Register<SoftwareReleaseViewViewModel>();
containerRegistry.Register<AdminSoftwareViewModel>();
containerRegistry.Register<AdminSoftwareFamiliesViewModel>();
containerRegistry.Register<AdminSoftwarePlatformsViewModel>();
@@ -299,6 +305,10 @@ public partial class App : PrismApplication
containerRegistry.RegisterForNavigation<AdminBooksPage, AdminBooksViewModel>();
containerRegistry.RegisterForNavigation<AdminDocumentsPage, AdminDocumentsViewModel>();
containerRegistry.RegisterForNavigation<AdminMagazinesPage, AdminMagazinesViewModel>();
containerRegistry.RegisterForNavigation<SoftwarePage, SoftwareViewModel>();
containerRegistry.RegisterForNavigation<SoftwareListPage, SoftwareListViewModel>();
containerRegistry.RegisterForNavigation<SoftwareViewPage, SoftwareViewViewModel>();
containerRegistry.RegisterForNavigation<SoftwareReleaseViewPage, SoftwareReleaseViewViewModel>();
containerRegistry.RegisterForNavigation<AdminSoftwarePage, AdminSoftwareViewModel>();
containerRegistry.RegisterForNavigation<AdminSoftwareFamiliesPage, AdminSoftwareFamiliesViewModel>();
containerRegistry.RegisterForNavigation<AdminSoftwarePlatformsPage, AdminSoftwarePlatformsViewModel>();

View File

@@ -0,0 +1,11 @@
namespace Marechai.App.Models;
public class SoftwareListItem
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Family { get; set; }
public int? Year { get; set; }
public bool IsOperatingSystem { get; set; }
public bool IsGame { get; set; }
}

View File

@@ -23,5 +23,6 @@ public static class NavParamKeys
public const string SoftwareVersionName = "SoftwareVersionName";
public const string SoftwareVariantId = "SoftwareVariantId";
public const string SoftwareVariantName = "SoftwareVariantName";
public const string SoftwareReleaseId = "SoftwareReleaseId";
public const string PersonId = "PersonId";
}

View File

@@ -100,7 +100,7 @@ public partial class MainViewModel : ObservableObject
NavigateToMagazinesCommand = new RelayCommand(() => NavigateTo(nameof(MagazinesPage)));
NavigateToPeopleCommand = new RelayCommand(() => NavigateTo(nameof(PeoplePage)));
NavigateToProcessorsCommand = new RelayCommand(() => NavigateTo(nameof(ProcessorListPage)));
NavigateToSoftwareCommand = new RelayCommand(() => NavigateTo("software"));
NavigateToSoftwareCommand = new RelayCommand(() => NavigateTo(nameof(SoftwarePage)));
NavigateToSoundSynthesizersCommand = new RelayCommand(() => NavigateTo(nameof(SoundSynthListPage)));
NavigateToUsersCommand = new RelayCommand(() => NavigateTo(nameof(UsersPage)));
NavigateToAdminCompaniesCommand = new RelayCommand(() => NavigateTo(nameof(AdminCompaniesPage)));

View File

@@ -0,0 +1,227 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using Marechai.App.Models;
using Marechai.App.Navigation;
using Marechai.App.Presentation.Views;
using Marechai.App.Services;
using Microsoft.UI.Xaml.Data;
namespace Marechai.App.Presentation.ViewModels;
[Bindable]
public partial class SoftwareListViewModel : ObservableObject, IRegionAware
{
private readonly SoftwareBrowsingService _browsingService;
private readonly ISoftwareListFilterContext _filterContext;
private readonly IStringLocalizer _localizer;
private readonly ILogger<SoftwareListViewModel> _logger;
private readonly IRegionManager _regionManager;
[ObservableProperty]
private ObservableCollection<SoftwareListItem> _softwareList = [];
[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 SoftwareListViewModel(SoftwareBrowsingService browsingService, IStringLocalizer localizer,
ILogger<SoftwareListViewModel> logger, IRegionManager regionManager,
ISoftwareListFilterContext filterContext)
{
_browsingService = browsingService;
_localizer = localizer;
_logger = logger;
_regionManager = regionManager;
_filterContext = filterContext;
LoadData = new AsyncRelayCommand(LoadDataAsync);
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
NavigateToSoftwareCommand = new AsyncRelayCommand<SoftwareListItem>(NavigateToSoftwareAsync);
}
public IAsyncRelayCommand LoadData { get; }
public ICommand GoBackCommand { get; }
public IAsyncRelayCommand<SoftwareListItem> NavigateToSoftwareCommand { get; }
public SoftwareListFilterType FilterType
{
get => _filterContext.FilterType;
set => _filterContext.FilterType = value;
}
public string FilterValue
{
get => _filterContext.FilterValue;
set => _filterContext.FilterValue = value;
}
public bool IsNavigationTarget(NavigationContext navigationContext) => false;
public void OnNavigatedFrom(NavigationContext navigationContext) { }
public void OnNavigatedTo(NavigationContext navigationContext)
{
_ = LoadDataAsync();
}
private async Task LoadDataAsync()
{
try
{
IsLoading = true;
ErrorMessage = string.Empty;
HasError = false;
IsDataLoaded = false;
SoftwareList.Clear();
_logger.LogInformation("LoadDataAsync called. FilterType={FilterType}, FilterValue={FilterValue}",
FilterType,
FilterValue);
UpdateFilterDescription();
await LoadSoftwareFromApiAsync();
_logger.LogInformation("LoadSoftwareFromApiAsync completed. SoftwareList.Count={Count}",
SoftwareList.Count);
if(SoftwareList.Count == 0)
{
ErrorMessage = _localizer["No software found for this filter"].Value;
HasError = true;
_logger.LogWarning("No software found for filter: {FilterType} {FilterValue}",
FilterType,
FilterValue);
}
else
IsDataLoaded = true;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error loading software: {Exception}", ex.Message);
ErrorMessage = _localizer["Failed to load software. Please try again later."].Value;
HasError = true;
}
finally
{
IsLoading = false;
}
}
private void UpdateFilterDescription()
{
switch(FilterType)
{
case SoftwareListFilterType.All:
PageTitle = _localizer["All Software"];
FilterDescription = _localizer["Browsing all software in the database"];
break;
case SoftwareListFilterType.Letter:
if(!string.IsNullOrEmpty(FilterValue) && FilterValue.Length == 1)
{
PageTitle = $"{_localizer["Software Starting with"]} {FilterValue}";
FilterDescription = $"{_localizer["Showing software that start with"]} {FilterValue}";
}
break;
case SoftwareListFilterType.Year:
if(!string.IsNullOrEmpty(FilterValue) && int.TryParse(FilterValue, out int year))
{
PageTitle = $"{_localizer["Software from"]} {year}";
FilterDescription = $"{_localizer["Showing software released in"]} {year}";
}
break;
case SoftwareListFilterType.Platform:
PageTitle = _localizer["Software by Platform"];
FilterDescription = _localizer["Showing software for the selected platform"];
break;
}
}
private async Task LoadSoftwareFromApiAsync()
{
try
{
List<SoftwareDto> software = FilterType switch
{
SoftwareListFilterType.Letter when FilterValue.Length == 1 =>
await _browsingService.GetSoftwareByLetterAsync(FilterValue[0]),
SoftwareListFilterType.Year when int.TryParse(FilterValue, out int year) =>
await _browsingService.GetSoftwareByYearAsync(year),
SoftwareListFilterType.Platform
when int.TryParse(FilterValue, out int platformId) =>
await _browsingService.GetSoftwareByPlatformAsync(platformId),
_ => await _browsingService.GetAllSoftwareAsync()
};
foreach(SoftwareDto sw in software)
{
int id = (int)(sw.Id ?? 0);
var item = new SoftwareListItem
{
Id = id,
Name = sw.Name ?? string.Empty,
Family = sw.Family,
IsOperatingSystem = sw.IsOperatingSystem ?? false,
IsGame = sw.IsGame ?? false
};
SoftwareList.Add(item);
}
}
catch(Exception ex)
{
_logger.LogError(ex, "Error loading software from API");
}
}
private Task GoBackAsync()
{
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwarePage));
return Task.CompletedTask;
}
private Task NavigateToSoftwareAsync(SoftwareListItem? sw)
{
if(sw is null) return Task.CompletedTask;
var parameters = new NavigationParameters
{
{ NavParamKeys.SoftwareId, sw.Id },
{ NavParamKeys.NavigationSource, nameof(SoftwareListViewModel) }
};
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwareViewPage), parameters);
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,217 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using Marechai.App.Navigation;
using Marechai.App.Presentation.Views;
using Marechai.App.Services;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
namespace Marechai.App.Presentation.ViewModels;
[Bindable]
public partial class SoftwareReleaseViewViewModel : ObservableObject, IRegionAware
{
private readonly SoftwareBrowsingService _browsingService;
private readonly IStringLocalizer _localizer;
private readonly ILogger<SoftwareReleaseViewViewModel> _logger;
private readonly IRegionManager _regionManager;
private int _sourceSoftwareId;
[ObservableProperty]
private string _releaseTitle = string.Empty;
[ObservableProperty]
private string? _softwareName;
[ObservableProperty]
private string? _versionString;
[ObservableProperty]
private string? _platform;
[ObservableProperty]
private string? _variant;
[ObservableProperty]
private string? _subvariant;
[ObservableProperty]
private string? _region;
[ObservableProperty]
private string? _publisher;
[ObservableProperty]
private string? _releaseDateDisplay;
[ObservableProperty]
private string _errorMessage = string.Empty;
[ObservableProperty]
private bool _hasError;
[ObservableProperty]
private bool _isDataLoaded;
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private Visibility _showPlatform = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showVariant = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showSubvariant = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showRegion = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showPublisher = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showReleaseDate = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showBarcodes = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showProductCodes = Visibility.Collapsed;
public SoftwareReleaseViewViewModel(ILogger<SoftwareReleaseViewViewModel> logger,
IRegionManager regionManager,
SoftwareBrowsingService browsingService,
IStringLocalizer localizer)
{
_logger = logger;
_regionManager = regionManager;
_browsingService = browsingService;
_localizer = localizer;
}
public ObservableCollection<string> Barcodes { get; } = [];
public ObservableCollection<string> ProductCodes { get; } = [];
public bool IsNavigationTarget(NavigationContext navigationContext) => false;
public void OnNavigatedFrom(NavigationContext navigationContext) { }
public void OnNavigatedTo(NavigationContext navigationContext)
{
if(navigationContext.Parameters.TryGetValue<int>(NavParamKeys.SoftwareId, out int softwareId))
_sourceSoftwareId = softwareId;
if(navigationContext.Parameters.TryGetValue<int>(NavParamKeys.SoftwareReleaseId, out int releaseId))
_ = LoadReleaseAsync(releaseId);
}
[RelayCommand]
public Task GoBack()
{
var parameters = new NavigationParameters
{
{ NavParamKeys.SoftwareId, _sourceSoftwareId },
{ NavParamKeys.NavigationSource, nameof(SoftwareListViewModel) }
};
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwareViewPage), parameters);
return Task.CompletedTask;
}
[RelayCommand]
public Task LoadData()
{
HasError = false;
ErrorMessage = string.Empty;
return Task.CompletedTask;
}
private async Task LoadReleaseAsync(int releaseId)
{
try
{
IsLoading = true;
IsDataLoaded = false;
HasError = false;
ErrorMessage = string.Empty;
Barcodes.Clear();
ProductCodes.Clear();
SoftwareReleaseDto? release = await _browsingService.GetReleaseByIdAsync(releaseId);
if(release is null)
{
HasError = true;
ErrorMessage = _localizer["Release not found"];
IsLoading = false;
return;
}
VersionString = release.SoftwareVersion;
Platform = release.Platform;
Variant = release.Variant;
Subvariant = release.Subvariant;
Region = release.Region;
Publisher = release.Publisher;
if(release.ReleaseDate.HasValue)
ReleaseDateDisplay = release.ReleaseDate.Value.DateTime.ToString("MMMM d, yyyy");
// Build title
ReleaseTitle = VersionString ?? _localizer["Software Release"];
// Load software name for display
if(_sourceSoftwareId > 0)
{
SoftwareDto? software = await _browsingService.GetSoftwareByIdAsync(_sourceSoftwareId);
SoftwareName = software?.Name;
}
// Load barcodes
List<SoftwareBarcodeDto> barcodes = await _browsingService.GetBarcodesAsync(releaseId);
foreach(SoftwareBarcodeDto barcode in barcodes)
Barcodes.Add($"{barcode.Code} ({barcode.Type})");
// Load product codes
List<SoftwareProductCodeDto> productCodes = await _browsingService.GetProductCodesAsync(releaseId);
foreach(SoftwareProductCodeDto pc in productCodes)
ProductCodes.Add($"{pc.Code} ({pc.Issuer})");
UpdateVisibilities();
IsDataLoaded = true;
IsLoading = false;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error loading release {ReleaseId}", releaseId);
HasError = true;
ErrorMessage = ex.Message;
IsLoading = false;
}
}
private void UpdateVisibilities()
{
ShowPlatform = !string.IsNullOrEmpty(Platform) ? Visibility.Visible : Visibility.Collapsed;
ShowVariant = !string.IsNullOrEmpty(Variant) ? Visibility.Visible : Visibility.Collapsed;
ShowSubvariant = !string.IsNullOrEmpty(Subvariant) ? Visibility.Visible : Visibility.Collapsed;
ShowRegion = !string.IsNullOrEmpty(Region) ? Visibility.Visible : Visibility.Collapsed;
ShowPublisher = !string.IsNullOrEmpty(Publisher) ? Visibility.Visible : Visibility.Collapsed;
ShowReleaseDate = !string.IsNullOrEmpty(ReleaseDateDisplay) ? Visibility.Visible : Visibility.Collapsed;
ShowBarcodes = Barcodes.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
ShowProductCodes = ProductCodes.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
}
}

View File

@@ -0,0 +1,241 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using Marechai.App.Navigation;
using Marechai.App.Presentation.Views;
using Marechai.App.Services;
using Microsoft.UI.Xaml.Data;
namespace Marechai.App.Presentation.ViewModels;
[Bindable]
public partial class SoftwareViewModel : ObservableObject
{
private readonly SoftwareBrowsingService _browsingService;
private readonly ISoftwareListFilterContext _filterContext;
private readonly IStringLocalizer _localizer;
private readonly ILogger<SoftwareViewModel> _logger;
private readonly IRegionManager _regionManager;
[ObservableProperty]
private int _softwareCount;
[ObservableProperty]
private string _softwareCountText = 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 = [];
[ObservableProperty]
private string _platformsGridTitle = string.Empty;
[ObservableProperty]
private ObservableCollection<SoftwarePlatformDto> _platformsList = [];
public SoftwareViewModel(SoftwareBrowsingService browsingService, IStringLocalizer localizer,
ILogger<SoftwareViewModel> logger, IRegionManager regionManager,
ISoftwareListFilterContext filterContext)
{
_browsingService = browsingService;
_localizer = localizer;
_logger = logger;
_regionManager = regionManager;
_filterContext = filterContext;
LoadData = new AsyncRelayCommand(LoadDataAsync);
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
NavigateByLetterCommand = new AsyncRelayCommand<char>(NavigateByLetterAsync);
NavigateByYearCommand = new AsyncRelayCommand<int>(NavigateByYearAsync);
NavigateByPlatformCommand = new AsyncRelayCommand<SoftwarePlatformDto>(NavigateByPlatformAsync);
NavigateAllSoftwareCommand = new AsyncRelayCommand(NavigateAllSoftwareAsync);
Title = _localizer["Software"];
InitializeLetters();
}
public IAsyncRelayCommand LoadData { get; }
public ICommand GoBackCommand { get; }
public IAsyncRelayCommand<char> NavigateByLetterCommand { get; }
public IAsyncRelayCommand<int> NavigateByYearCommand { get; }
public IAsyncRelayCommand<SoftwarePlatformDto> NavigateByPlatformCommand { get; }
public IAsyncRelayCommand NavigateAllSoftwareCommand { get; }
public string Title { get; }
private void InitializeLetters()
{
LettersList.Clear();
for(var c = 'A'; c <= 'Z'; c++) LettersList.Add(c);
}
private async Task LoadDataAsync()
{
try
{
IsLoading = true;
ErrorMessage = string.Empty;
HasError = false;
IsDataLoaded = false;
YearsList.Clear();
PlatformsList.Clear();
Task<int> countTask = _browsingService.GetSoftwareCountAsync();
Task<int> minYearTask = _browsingService.GetMinimumYearAsync();
Task<int> maxYearTask = _browsingService.GetMaximumYearAsync();
Task<List<SoftwarePlatformDto>> platformsTask = _browsingService.GetAllPlatformsAsync();
await Task.WhenAll(countTask, minYearTask, maxYearTask, platformsTask);
SoftwareCount = countTask.Result;
MinimumYear = minYearTask.Result;
MaximumYear = maxYearTask.Result;
SoftwareCountText = _localizer["Software in the database"];
if(MinimumYear > 0 && MaximumYear > 0)
{
for(int year = MinimumYear; year <= MaximumYear; year++) YearsList.Add(year);
YearsGridTitle = string.Format(_localizer["Browse by Year ({0} - {1})"], MinimumYear, MaximumYear);
}
List<SoftwarePlatformDto> platforms = platformsTask.Result;
if(platforms.Count > 0)
{
PlatformsGridTitle = _localizer["Browse by Platform"];
foreach(SoftwarePlatformDto platform in platforms) PlatformsList.Add(platform);
}
if(SoftwareCount == 0)
{
ErrorMessage = _localizer["No software found"].Value;
HasError = true;
}
else
IsDataLoaded = true;
}
catch(Exception ex)
{
_logger.LogError("Error loading software data: {Exception}", ex.Message);
ErrorMessage = _localizer["Failed to load software data. Please try again later."].Value;
HasError = true;
}
finally
{
IsLoading = false;
}
}
private Task GoBackAsync()
{
_regionManager.RequestNavigate(RegionNames.Content, nameof(NewsPage));
return Task.CompletedTask;
}
private Task NavigateByLetterAsync(char letter)
{
try
{
_logger.LogInformation("Navigating to software by letter: {Letter}", letter);
_filterContext.FilterType = SoftwareListFilterType.Letter;
_filterContext.FilterValue = letter.ToString();
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwareListPage));
}
catch(Exception ex)
{
_logger.LogError("Error navigating to letter software: {Exception}", ex.Message);
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
HasError = true;
}
return Task.CompletedTask;
}
private Task NavigateByYearAsync(int year)
{
try
{
_logger.LogInformation("Navigating to software by year: {Year}", year);
_filterContext.FilterType = SoftwareListFilterType.Year;
_filterContext.FilterValue = year.ToString();
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwareListPage));
}
catch(Exception ex)
{
_logger.LogError("Error navigating to year software: {Exception}", ex.Message);
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
HasError = true;
}
return Task.CompletedTask;
}
private Task NavigateByPlatformAsync(SoftwarePlatformDto? platform)
{
if(platform is null) return Task.CompletedTask;
try
{
_logger.LogInformation("Navigating to software by platform: {Platform}", platform.Name);
_filterContext.FilterType = SoftwareListFilterType.Platform;
_filterContext.FilterValue = platform.Id?.ToString() ?? string.Empty;
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwareListPage));
}
catch(Exception ex)
{
_logger.LogError("Error navigating to platform software: {Exception}", ex.Message);
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
HasError = true;
}
return Task.CompletedTask;
}
private Task NavigateAllSoftwareAsync()
{
try
{
_logger.LogInformation("Navigating to all software");
_filterContext.FilterType = SoftwareListFilterType.All;
_filterContext.FilterValue = string.Empty;
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwareListPage));
}
catch(Exception ex)
{
_logger.LogError("Error navigating to all software: {Exception}", ex.Message);
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
HasError = true;
}
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,249 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using Marechai.App.Navigation;
using Marechai.App.Presentation.Views;
using Marechai.App.Services;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
namespace Marechai.App.Presentation.ViewModels;
[Bindable]
public partial class SoftwareViewViewModel : ObservableObject, IRegionAware
{
private readonly SoftwareBrowsingService _browsingService;
private readonly IStringLocalizer _localizer;
private readonly ILogger<SoftwareViewViewModel> _logger;
private readonly IRegionManager _regionManager;
private string? _navigationSource;
private int _currentSoftwareId;
[ObservableProperty]
private string _softwareName = string.Empty;
[ObservableProperty]
private string? _family;
[ObservableProperty]
private bool _isOperatingSystem;
[ObservableProperty]
private bool _isGame;
[ObservableProperty]
private string _errorMessage = string.Empty;
[ObservableProperty]
private bool _hasError;
[ObservableProperty]
private bool _isDataLoaded;
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private Visibility _showFamily = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showCompanies = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showVersions = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showOsBadge = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showGameBadge = Visibility.Collapsed;
public SoftwareViewViewModel(ILogger<SoftwareViewViewModel> logger, IRegionManager regionManager,
SoftwareBrowsingService browsingService, IStringLocalizer localizer)
{
_logger = logger;
_regionManager = regionManager;
_browsingService = browsingService;
_localizer = localizer;
}
public ObservableCollection<string> Companies { get; } = [];
public ObservableCollection<VersionDisplayItem> Versions { get; } = [];
public bool IsNavigationTarget(NavigationContext navigationContext) => false;
public void OnNavigatedFrom(NavigationContext navigationContext) { }
public void OnNavigatedTo(NavigationContext navigationContext)
{
if(navigationContext.Parameters.TryGetValue<string>(NavParamKeys.NavigationSource, out string? source))
_navigationSource = source;
if(navigationContext.Parameters.TryGetValue<int>(NavParamKeys.SoftwareId, out int softwareId))
{
_currentSoftwareId = softwareId;
_ = LoadSoftwareAsync(softwareId);
}
}
[RelayCommand]
public Task GoBack()
{
if(_navigationSource == nameof(SoftwareListViewModel))
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwareListPage));
else
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwarePage));
return Task.CompletedTask;
}
[RelayCommand]
public Task NavigateToRelease(ReleaseDisplayItem? release)
{
if(release is null) return Task.CompletedTask;
var parameters = new NavigationParameters
{
{ NavParamKeys.SoftwareReleaseId, release.Id },
{ NavParamKeys.SoftwareId, _currentSoftwareId },
{ NavParamKeys.NavigationSource, nameof(SoftwareViewViewModel) }
};
_regionManager.RequestNavigate(RegionNames.Content, nameof(SoftwareReleaseViewPage), parameters);
return Task.CompletedTask;
}
[RelayCommand]
public Task LoadData()
{
HasError = false;
ErrorMessage = string.Empty;
return Task.CompletedTask;
}
private async Task LoadSoftwareAsync(int softwareId)
{
try
{
IsLoading = true;
IsDataLoaded = false;
HasError = false;
ErrorMessage = string.Empty;
Companies.Clear();
Versions.Clear();
SoftwareDto? software = await _browsingService.GetSoftwareByIdAsync(softwareId);
if(software is null)
{
HasError = true;
ErrorMessage = _localizer["Software not found"];
IsLoading = false;
return;
}
SoftwareName = software.Name ?? string.Empty;
Family = software.Family;
IsOperatingSystem = software.IsOperatingSystem ?? false;
IsGame = software.IsGame ?? false;
// Load companies
List<SoftwareCompanyRoleDto> companies = await _browsingService.GetCompaniesAsync(softwareId);
foreach(SoftwareCompanyRoleDto company in companies)
{
string name = company.Company ?? string.Empty;
string? role = company.Role;
string display = !string.IsNullOrEmpty(role) ? $"{name} ({role})" : name;
Companies.Add(display);
}
// Load versions with their releases
List<SoftwareVersionDto> versions = await _browsingService.GetVersionsAsync(softwareId);
foreach(SoftwareVersionDto version in versions)
{
int versionId = (int)(version.Id ?? 0);
var versionItem = new VersionDisplayItem
{
Id = versionId,
VersionString = version.VersionString ?? string.Empty,
PublicVersion = version.PublicVersion,
Codename = version.Codename
};
// Load releases for this version
List<SoftwareReleaseDto> releases = await _browsingService.GetReleasesByVersionAsync(versionId);
foreach(SoftwareReleaseDto release in releases)
{
string dateDisplay = release.ReleaseDate?.DateTime.ToString("MMMM d, yyyy") ?? string.Empty;
var releaseItem = new ReleaseDisplayItem
{
Id = (int)(release.Id ?? 0),
Platform = release.Platform,
Region = release.Region,
Publisher = release.Publisher,
ReleaseDate = dateDisplay,
Variant = release.Variant
};
versionItem.Releases.Add(releaseItem);
}
Versions.Add(versionItem);
}
UpdateVisibilities();
IsDataLoaded = true;
IsLoading = false;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error loading software {SoftwareId}", softwareId);
HasError = true;
ErrorMessage = ex.Message;
IsLoading = false;
}
}
private void UpdateVisibilities()
{
ShowFamily = !string.IsNullOrEmpty(Family) ? Visibility.Visible : Visibility.Collapsed;
ShowCompanies = Companies.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
ShowVersions = Versions.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
ShowOsBadge = IsOperatingSystem ? Visibility.Visible : Visibility.Collapsed;
ShowGameBadge = IsGame ? Visibility.Visible : Visibility.Collapsed;
}
}
[Bindable]
public class VersionDisplayItem
{
public int Id { get; set; }
public string VersionString { get; set; } = string.Empty;
public string? PublicVersion { get; set; }
public string? Codename { get; set; }
public ObservableCollection<ReleaseDisplayItem> Releases { get; } = [];
}
[Bindable]
public class ReleaseDisplayItem
{
public int Id { get; set; }
public string? Platform { get; set; }
public string? Region { get; set; }
public string? Publisher { get; set; }
public string? ReleaseDate { get; set; }
public string? Variant { get; set; }
}

View File

@@ -0,0 +1,252 @@
<?xml version="1.0"
encoding="utf-8"?>
<Page x:Class="Marechai.App.Presentation.Views.SoftwareListPage"
x:Name="PageRoot"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="using:Marechai.App.Presentation.Converters"
NavigationCacheMode="Required"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header with Back Button and Title -->
<Grid Grid.Row="0"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="0,0,0,1"
Padding="16,12,16,12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Command="{Binding GoBackCommand}"
Style="{ThemeResource AlternateButtonStyle}"
ToolTipService.ToolTip="Go back"
Padding="8"
MinWidth="44"
MinHeight="44"
VerticalAlignment="Center">
<FontIcon Glyph="&#xE72B;"
FontSize="16" />
</Button>
<StackPanel Grid.Column="1"
Margin="16,0,0,0"
VerticalAlignment="Center">
<TextBlock Text="{Binding PageTitle}"
FontSize="20"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<TextBlock Text="{Binding FilterDescription}"
FontSize="12"
Foreground="{ThemeResource SystemBaseMediumColor}"
Margin="0,4,0,0" />
</StackPanel>
</Grid>
<!-- Main Content -->
<Grid Grid.Row="1">
<!-- Loading State -->
<StackPanel Visibility="{Binding IsLoading}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Padding="32"
Spacing="16">
<ProgressRing IsActive="True"
IsIndeterminate="True"
Height="64"
Width="64"
Foreground="{ThemeResource SystemAccentColor}" />
<TextBlock Text="{Binding Source={StaticResource Strings}, Path=LoadingText}"
FontSize="14"
TextAlignment="Center"
Foreground="{ThemeResource SystemBaseMediumColor}" />
</StackPanel>
<!-- Error State -->
<StackPanel Visibility="{Binding HasError}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Padding="24"
Spacing="16"
MaxWidth="400">
<InfoBar IsOpen="True"
Severity="Error"
Title="{Binding Source={StaticResource Strings}, Path=SoftwareErrorInfoBar_Title}"
Message="{Binding ErrorMessage}"
IsClosable="False" />
<Button Content="{Binding Source={StaticResource Strings}, Path=RetryButton}"
Command="{Binding LoadData}"
HorizontalAlignment="Center"
Style="{ThemeResource AccentButtonStyle}" />
</StackPanel>
<!-- Software List -->
<Grid Visibility="{Binding IsDataLoaded}">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<Grid Padding="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Count Header -->
<StackPanel Grid.Row="0"
Padding="16,12"
Orientation="Horizontal"
Spacing="4">
<TextBlock FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}"
Text="{Binding Source={StaticResource Strings}, Path=ResultsText}" />
<TextBlock FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}"
Text="{Binding SoftwareList.Count}" />
<TextBlock FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}"
Text="{Binding Source={StaticResource Strings}, Path=SoftwareLabel}" />
</StackPanel>
<!-- Software Items -->
<ItemsControl Grid.Row="1"
ItemsSource="{Binding SoftwareList}"
Padding="0"
Margin="0,8,0,0"
HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Spacing="0"
HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Padding="0"
Margin="0,0,0,8"
MinHeight="70"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
Command="{Binding DataContext.NavigateToSoftwareCommand, ElementName=PageRoot}"
CommandParameter="{Binding}"
Background="Transparent"
BorderThickness="0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid MinHeight="70"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Border x:Name="ShadowBorder"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12"
Translation="0, 0, 4"
VerticalAlignment="Stretch">
<Border.Shadow>
<ThemeShadow />
</Border.Shadow>
<Grid ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Software Icon -->
<Grid Grid.Column="0"
Width="48"
Height="48"
VerticalAlignment="Center">
<FontIcon Glyph="&#xE74C;"
FontSize="28"
Foreground="{ThemeResource SystemAccentColor}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<!-- Software Info -->
<StackPanel Grid.Column="1"
Spacing="4"
VerticalAlignment="Center"
HorizontalAlignment="Stretch">
<TextBlock Text="{Binding Name}"
FontSize="16"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}"
TextTrimming="CharacterEllipsis" />
<StackPanel Orientation="Horizontal"
Spacing="12">
<StackPanel Orientation="Horizontal"
Spacing="6"
Visibility="{Binding Family, Converter={StaticResource NullToVisibilityConverter}}">
<FontIcon Glyph="&#xE8F1;"
FontSize="13"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Family}"
FontSize="13"
Foreground="{ThemeResource SystemBaseMediumColor}" />
</StackPanel>
</StackPanel>
</StackPanel>
<!-- Navigation Arrow -->
<StackPanel Grid.Column="2"
VerticalAlignment="Center">
<FontIcon Glyph="&#xE72A;"
FontSize="18"
Foreground="{ThemeResource SystemAccentColor}" />
</StackPanel>
</Grid>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="ShadowBorder.Background"
Value="{ThemeResource CardBackgroundFillColorSecondaryBrush}" />
<Setter Target="ShadowBorder.Translation"
Value="0, -2, 8" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="ShadowBorder.Background"
Value="{ThemeResource CardBackgroundFillColorTertiaryBrush}" />
<Setter Target="ShadowBorder.Translation"
Value="0, 0, 2" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ScrollViewer>
</Grid>
</Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,13 @@
#nullable enable
using Microsoft.UI.Xaml.Controls;
namespace Marechai.App.Presentation.Views;
public sealed partial class SoftwareListPage : Page
{
public SoftwareListPage()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,330 @@
<?xml version="1.0"
encoding="utf-8"?>
<Page x:Class="Marechai.App.Presentation.Views.SoftwarePage"
x:Name="PageRoot"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="using:Marechai.App.Presentation.Converters"
xmlns:utu="using:Uno.Toolkit.UI"
NavigationCacheMode="Required"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid utu:SafeArea.Insets="VisibleBounds">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
<utu:NavigationBar Grid.Row="0"
Content="{Binding Path=Title}">
<utu:NavigationBar.MainCommand>
<AppBarButton Label="{Binding Source={StaticResource Strings}, Path=BackButton}" Icon="Back"
Command="{Binding GoBackCommand}"
AutomationProperties.Name="Go back" />
</utu:NavigationBar.MainCommand>
</utu:NavigationBar>
<!-- Content -->
<ScrollViewer Grid.Row="1">
<StackPanel Padding="16"
Spacing="24">
<!-- Software Count Display -->
<StackPanel HorizontalAlignment="Center"
Spacing="8">
<TextBlock Text="{Binding SoftwareCountText}"
TextAlignment="Center"
FontSize="18"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding SoftwareCount}"
TextAlignment="Center"
FontSize="48"
FontWeight="Bold"
Foreground="{ThemeResource SystemAccentColor}" />
</StackPanel>
<!-- Loading State -->
<StackPanel Visibility="{Binding IsLoading}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Padding="32"
Spacing="16">
<ProgressRing IsActive="True"
IsIndeterminate="True"
Height="48"
Width="48" />
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=LoadingText}"
TextAlignment="Center"
FontSize="14" />
</StackPanel>
<!-- Error State -->
<StackPanel Visibility="{Binding HasError}"
Padding="16"
Background="{ThemeResource SystemErrorBackgroundColor}"
CornerRadius="8"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=ErrorTitle}"
FontWeight="Bold"
Foreground="{ThemeResource SystemErrorTextForegroundColor}" />
<TextBlock Text="{Binding ErrorMessage}"
Foreground="{ThemeResource SystemErrorTextForegroundColor}"
TextWrapping="Wrap" />
</StackPanel>
<!-- Main Content (visible when loaded and no error) -->
<StackPanel Visibility="{Binding IsDataLoaded}"
Spacing="24">
<!-- Letters Grid Section -->
<StackPanel Spacing="12">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=BrowseByLetterText}"
FontSize="16"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<ItemsRepeater ItemsSource="{Binding LettersList}"
Layout="{StaticResource LettersGridLayout}">
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}"
Command="{Binding DataContext.NavigateByLetterCommand, ElementName=PageRoot}"
CommandParameter="{Binding}"
Style="{StaticResource KeyboardKeyButtonStyle}" />
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</StackPanel>
<!-- Platforms Grid Section -->
<StackPanel Spacing="12">
<TextBlock Text="{Binding PlatformsGridTitle}"
FontSize="16"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<ItemsRepeater ItemsSource="{Binding PlatformsList}"
Layout="{StaticResource PlatformsGridLayout}">
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.NavigateByPlatformCommand, ElementName=PageRoot}"
CommandParameter="{Binding}"
Style="{StaticResource KeyboardKeyButtonStyle}" />
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</StackPanel>
<!-- Years Grid Section -->
<StackPanel Spacing="12">
<TextBlock Text="{Binding YearsGridTitle}"
FontSize="16"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<ItemsRepeater ItemsSource="{Binding YearsList}"
Layout="{StaticResource YearsGridLayout}">
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}"
Command="{Binding DataContext.NavigateByYearCommand, ElementName=PageRoot}"
CommandParameter="{Binding}"
Style="{StaticResource KeyboardKeyButtonStyle}" />
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</StackPanel>
<!-- All Software Button -->
<StackPanel Spacing="12">
<Button
Content="{Binding Source={StaticResource Strings}, Path=AllSoftwareButton}"
Padding="16,12"
HorizontalAlignment="Stretch"
FontSize="16"
FontWeight="SemiBold"
Command="{Binding NavigateAllSoftwareCommand}"
Style="{StaticResource AccentButtonStyle}" />
</StackPanel>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
<Page.Resources>
<!-- Keyboard Key Button Style -->
<Style x:Key="KeyboardKeyButtonStyle"
TargetType="Button">
<Setter Property="Foreground"
Value="#1A1A1A" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<GradientStop Color="#D6D6D6"
Offset="0" />
<GradientStop Color="#C2C2C2"
Offset="0.55" />
<GradientStop Color="#B0B0B0"
Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush"
Value="#7A7A7A" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="CornerRadius"
Value="6" />
<Setter Property="Padding"
Value="14,12" />
<Setter Property="Margin"
Value="4" />
<Setter Property="FontFamily"
Value="Segoe UI" />
<Setter Property="FontWeight"
Value="SemiBold" />
<Setter Property="FontSize"
Value="15" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="VerticalAlignment"
Value="Stretch" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="MinWidth"
Value="52" />
<Setter Property="MinHeight"
Value="52" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Border x:Name="Shadow"
CornerRadius="6"
Background="#33000000"
Margin="2,4,4,2" />
<Border x:Name="KeyBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid>
<Border CornerRadius="{TemplateBinding CornerRadius}"
BorderBrush="#60FFFFFF"
BorderThickness="1,1,0,0" />
<Border CornerRadius="{TemplateBinding CornerRadius}"
BorderBrush="#30000000"
BorderThickness="0,0,1,1" />
<ContentPresenter x:Name="ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="0"
TextWrapping="NoWrap" />
</Grid>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="KeyBorder.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<GradientStop Color="#E0E0E0"
Offset="0" />
<GradientStop Color="#CFCFCF"
Offset="0.55" />
<GradientStop Color="#BDBDBD"
Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Target="KeyBorder.BorderBrush"
Value="#5F5F5F" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="KeyBorder.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<GradientStop Color="#9C9C9C"
Offset="0" />
<GradientStop Color="#A8A8A8"
Offset="0.55" />
<GradientStop Color="#B4B4B4"
Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Target="KeyBorder.BorderBrush"
Value="#4A4A4A" />
<Setter Target="KeyBorder.RenderTransform">
<Setter.Value>
<TranslateTransform Y="2" />
</Setter.Value>
</Setter>
<Setter Target="Shadow.Opacity"
Value="0.15" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="KeyBorder.Opacity"
Value="0.45" />
<Setter Target="ContentPresenter.Foreground"
Value="#777777" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Target="KeyBorder.BorderBrush"
Value="#3A7AFE" />
<Setter Target="KeyBorder.BorderThickness"
Value="2" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Responsive Grid Layouts -->
<UniformGridLayout x:Key="LettersGridLayout"
ItemsStretch="Fill"
MinItemWidth="44"
MinItemHeight="44"
MaximumRowsOrColumns="13" />
<UniformGridLayout x:Key="PlatformsGridLayout"
ItemsStretch="Fill"
MinItemWidth="80"
MinItemHeight="44"
MaximumRowsOrColumns="6" />
<UniformGridLayout x:Key="YearsGridLayout"
ItemsStretch="Fill"
MinItemWidth="54"
MinItemHeight="44"
MaximumRowsOrColumns="10" />
</Page.Resources>
</Page>

View File

@@ -0,0 +1,22 @@
#nullable enable
using Marechai.App.Presentation.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Marechai.App.Presentation.Views;
public sealed partial class SoftwarePage : Page
{
public SoftwarePage()
{
InitializeComponent();
DataContextChanged += OnDataContextChanged;
}
private async void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
if(args.NewValue is SoftwareViewModel vm)
await vm.LoadData.ExecuteAsync(null);
}
}

View File

@@ -0,0 +1,325 @@
<?xml version="1.0"
encoding="utf-8"?>
<Page x:Class="Marechai.App.Presentation.Views.SoftwareReleaseViewPage"
x:Name="PageRoot"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="using:Marechai.App.Presentation.Converters"
NavigationCacheMode="Disabled"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header with Back Button -->
<Grid Grid.Row="0"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="0,0,0,1"
Padding="12,12,16,12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Command="{Binding GoBackCommand}"
Style="{ThemeResource AlternateButtonStyle}"
ToolTipService.ToolTip="Go back"
Padding="8"
MinWidth="44"
MinHeight="44"
VerticalAlignment="Center">
<FontIcon Glyph="&#xE72B;"
FontSize="16" />
</Button>
<StackPanel Grid.Column="1"
Margin="12,0,0,0"
VerticalAlignment="Center">
<TextBlock Text="{Binding ReleaseTitle}"
FontSize="20"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}"
TextTrimming="CharacterEllipsis" />
<TextBlock Text="{Binding SoftwareName}"
FontSize="14"
Foreground="{ThemeResource SystemBaseMediumColor}"
Visibility="{Binding SoftwareName, Converter={StaticResource NullToVisibilityConverter}}" />
</StackPanel>
</Grid>
<!-- Main Content -->
<Grid Grid.Row="1">
<!-- Loading State -->
<StackPanel Visibility="{Binding IsLoading}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Padding="32"
Spacing="16">
<ProgressRing IsActive="True"
IsIndeterminate="True"
Height="64"
Width="64"
Foreground="{ThemeResource SystemAccentColor}" />
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=LoadingText}"
FontSize="14"
TextAlignment="Center"
Foreground="{ThemeResource SystemBaseMediumColor}" />
</StackPanel>
<!-- Error State -->
<StackPanel Visibility="{Binding HasError}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Padding="24"
Spacing="16"
MaxWidth="400">
<InfoBar IsOpen="True"
Severity="Error"
Title="{Binding Source={StaticResource Strings}, Path=SoftwareErrorInfoBar_Title}"
Message="{Binding ErrorMessage}"
IsClosable="False" />
<Button
Content="{Binding Source={StaticResource Strings}, Path=RetryButton}"
Command="{Binding LoadDataCommand}"
HorizontalAlignment="Center"
Style="{ThemeResource AccentButtonStyle}" />
</StackPanel>
<!-- Release Details -->
<ScrollViewer Visibility="{Binding IsDataLoaded}"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Padding="16"
Spacing="24">
<!-- Version -->
<StackPanel Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareVersionLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<TextBlock Text="{Binding VersionString}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</StackPanel>
<!-- Platform and Region -->
<Grid ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Visibility="{Binding ShowPlatform}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwarePlatformLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<TextBlock Text="{Binding Platform}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1"
Visibility="{Binding ShowRegion}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareRegionLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<TextBlock Text="{Binding Region}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</StackPanel>
</Grid>
<!-- Publisher and Release Date -->
<Grid ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Visibility="{Binding ShowPublisher}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwarePublisherLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<TextBlock Text="{Binding Publisher}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1"
Visibility="{Binding ShowReleaseDate}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareReleaseDateLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<TextBlock Text="{Binding ReleaseDateDisplay}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</StackPanel>
</Grid>
<!-- Variant and Subvariant -->
<Grid ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Visibility="{Binding ShowVariant}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareVariantLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<TextBlock Text="{Binding Variant}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1"
Visibility="{Binding ShowSubvariant}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareSubvariantLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<TextBlock Text="{Binding Subvariant}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</StackPanel>
</Grid>
<!-- Barcodes -->
<StackPanel Visibility="{Binding ShowBarcodes}"
Spacing="12">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareBarcodesLabel}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<ItemsControl ItemsSource="{Binding Barcodes}"
HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,8">
<TextBlock Text="{Binding}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!-- Product Codes -->
<StackPanel Visibility="{Binding ShowProductCodes}"
Spacing="12">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareProductCodesLabel}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<ItemsControl ItemsSource="{Binding ProductCodes}"
HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,8">
<TextBlock Text="{Binding}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,13 @@
#nullable enable
using Microsoft.UI.Xaml.Controls;
namespace Marechai.App.Presentation.Views;
public sealed partial class SoftwareReleaseViewPage : Page
{
public SoftwareReleaseViewPage()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,285 @@
<?xml version="1.0"
encoding="utf-8"?>
<Page x:Class="Marechai.App.Presentation.Views.SoftwareViewPage"
x:Name="PageRoot"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="using:Marechai.App.Presentation.Converters"
NavigationCacheMode="Disabled"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header with Back Button -->
<Grid Grid.Row="0"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="0,0,0,1"
Padding="12,12,16,12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Command="{Binding GoBackCommand}"
Style="{ThemeResource AlternateButtonStyle}"
ToolTipService.ToolTip="Go back"
Padding="8"
MinWidth="44"
MinHeight="44"
VerticalAlignment="Center">
<FontIcon Glyph="&#xE72B;"
FontSize="16" />
</Button>
<StackPanel Grid.Column="1"
Margin="12,0,0,0"
VerticalAlignment="Center">
<TextBlock Text="{Binding SoftwareName}"
FontSize="20"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
</Grid>
<!-- Main Content -->
<Grid Grid.Row="1">
<!-- Loading State -->
<StackPanel Visibility="{Binding IsLoading}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Padding="32"
Spacing="16">
<ProgressRing IsActive="True"
IsIndeterminate="True"
Height="64"
Width="64"
Foreground="{ThemeResource SystemAccentColor}" />
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=LoadingText}"
FontSize="14"
TextAlignment="Center"
Foreground="{ThemeResource SystemBaseMediumColor}" />
</StackPanel>
<!-- Error State -->
<StackPanel Visibility="{Binding HasError}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Padding="24"
Spacing="16"
MaxWidth="400">
<InfoBar IsOpen="True"
Severity="Error"
Title="{Binding Source={StaticResource Strings}, Path=SoftwareErrorInfoBar_Title}"
Message="{Binding ErrorMessage}"
IsClosable="False" />
<Button
Content="{Binding Source={StaticResource Strings}, Path=RetryButton}"
Command="{Binding LoadDataCommand}"
HorizontalAlignment="Center"
Style="{ThemeResource AccentButtonStyle}" />
</StackPanel>
<!-- Software Details -->
<ScrollViewer Visibility="{Binding IsDataLoaded}"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Padding="16"
Spacing="24">
<!-- Badges (OS / Game) -->
<StackPanel Orientation="Horizontal"
Spacing="8">
<Border Visibility="{Binding ShowOsBadge}"
Background="{ThemeResource SystemAccentColor}"
CornerRadius="12"
Padding="12,4">
<TextBlock Text="{Binding Source={StaticResource Strings}, Path=SoftwareIsOSLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="White" />
</Border>
<Border Visibility="{Binding ShowGameBadge}"
Background="#E91E63"
CornerRadius="12"
Padding="12,4">
<TextBlock Text="{Binding Source={StaticResource Strings}, Path=SoftwareIsGameLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="White" />
</Border>
</StackPanel>
<!-- Family -->
<StackPanel Visibility="{Binding ShowFamily}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareFamilyLabel}"
FontSize="12"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<TextBlock Text="{Binding Family}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</StackPanel>
<!-- Companies -->
<StackPanel Visibility="{Binding ShowCompanies}"
Spacing="12">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareCompaniesLabel}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<ItemsControl ItemsSource="{Binding Companies}"
HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,8">
<TextBlock Text="{Binding}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!-- Versions and Releases -->
<StackPanel Visibility="{Binding ShowVersions}"
Spacing="12">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SoftwareVersionsLabel}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<ItemsControl ItemsSource="{Binding Versions}"
HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Spacing="12" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="16,12">
<StackPanel Spacing="8">
<!-- Version Header -->
<StackPanel Orientation="Horizontal"
Spacing="8">
<TextBlock Text="{Binding VersionString}"
FontSize="16"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<TextBlock Text="{Binding PublicVersion}"
FontSize="14"
Foreground="{ThemeResource SystemBaseMediumColor}"
VerticalAlignment="Center"
Visibility="{Binding PublicVersion, Converter={StaticResource NullToVisibilityConverter}}" />
</StackPanel>
<TextBlock Text="{Binding Codename}"
FontSize="13"
FontStyle="Italic"
Foreground="{ThemeResource SystemBaseMediumColor}"
Visibility="{Binding Codename, Converter={StaticResource NullToVisibilityConverter}}" />
<!-- Releases for this version -->
<ItemsControl ItemsSource="{Binding Releases}"
HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Padding="0"
Margin="0"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
Command="{Binding DataContext.NavigateToReleaseCommand, ElementName=PageRoot}"
CommandParameter="{Binding}"
Background="Transparent"
BorderThickness="0">
<Border Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="6"
Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Orientation="Horizontal"
Spacing="8">
<TextBlock Text="{Binding Platform}"
FontSize="13"
Foreground="{ThemeResource TextControlForeground}" />
<TextBlock Text="—"
FontSize="13"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Publisher}"
FontSize="13"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding ReleaseDate}"
FontSize="13"
Foreground="{ThemeResource SystemBaseMediumColor}"
Visibility="{Binding ReleaseDate, Converter={StaticResource NullToVisibilityConverter}}" />
</StackPanel>
<FontIcon Grid.Column="1"
Glyph="&#xE72A;"
FontSize="14"
Foreground="{ThemeResource SystemAccentColor}"
VerticalAlignment="Center" />
</Grid>
</Border>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,13 @@
#nullable enable
using Microsoft.UI.Xaml.Controls;
namespace Marechai.App.Presentation.Views;
public sealed partial class SoftwareViewPage : Page
{
public SoftwareViewPage()
{
InitializeComponent();
}
}

View File

@@ -1847,4 +1847,39 @@
<data name="PersonBooksLabel" xml:space="preserve"><value>Bücher</value></data>
<data name="PersonDocumentsLabel" xml:space="preserve"><value>Dokumente</value></data>
<data name="PersonMagazinesLabel" xml:space="preserve"><value>Zeitschriften</value></data>
<!-- Software Browsing -->
<data name="Software in the database" xml:space="preserve"><value>Software in der Datenbank</value></data>
<data name="No software found" xml:space="preserve"><value>Keine Software gefunden</value></data>
<data name="Failed to load software data. Please try again later." xml:space="preserve"><value>Fehler beim Laden der Softwaredaten. Bitte versuchen Sie es später erneut.</value></data>
<data name="Failed to navigate. Please try again." xml:space="preserve"><value>Fehler bei der Navigation. Bitte versuchen Sie es erneut.</value></data>
<data name="AllSoftwareButton" xml:space="preserve"><value>Alle Software</value></data>
<data name="Browse by Platform" xml:space="preserve"><value>Nach Plattform durchsuchen</value></data>
<data name="All Software" xml:space="preserve"><value>Alle Software</value></data>
<data name="Browsing all software in the database" xml:space="preserve"><value>Alle Software in der Datenbank durchsuchen</value></data>
<data name="Software Starting with" xml:space="preserve"><value>Software beginnend mit</value></data>
<data name="Showing software that start with" xml:space="preserve"><value>Software anzeigen, die beginnt mit</value></data>
<data name="Software from" xml:space="preserve"><value>Software aus</value></data>
<data name="Showing software released in" xml:space="preserve"><value>Software anzeigen, veröffentlicht in</value></data>
<data name="Software by Platform" xml:space="preserve"><value>Software nach Plattform</value></data>
<data name="Showing software for the selected platform" xml:space="preserve"><value>Software für die ausgewählte Plattform anzeigen</value></data>
<data name="No software found for this filter" xml:space="preserve"><value>Keine Software für diesen Filter gefunden</value></data>
<data name="Failed to load software. Please try again later." xml:space="preserve"><value>Fehler beim Laden der Software. Bitte versuchen Sie es später erneut.</value></data>
<data name="SoftwareLabel" xml:space="preserve"><value>Software</value></data>
<data name="SoftwareErrorInfoBar_Title" xml:space="preserve"><value>Fehler beim Laden der Software</value></data>
<data name="Software not found" xml:space="preserve"><value>Software nicht gefunden</value></data>
<data name="SoftwareFamilyLabel" xml:space="preserve"><value>Familie</value></data>
<data name="SoftwareCompaniesLabel" xml:space="preserve"><value>Unternehmen</value></data>
<data name="SoftwareVersionsLabel" xml:space="preserve"><value>Versionen</value></data>
<data name="SoftwareIsOSLabel" xml:space="preserve"><value>Betriebssystem</value></data>
<data name="SoftwareIsGameLabel" xml:space="preserve"><value>Spiel</value></data>
<data name="Release not found" xml:space="preserve"><value>Veröffentlichung nicht gefunden</value></data>
<data name="Software Release" xml:space="preserve"><value>Softwareveröffentlichung</value></data>
<data name="SoftwarePlatformLabel" xml:space="preserve"><value>Plattform</value></data>
<data name="SoftwareRegionLabel" xml:space="preserve"><value>Region</value></data>
<data name="SoftwarePublisherLabel" xml:space="preserve"><value>Herausgeber</value></data>
<data name="SoftwareReleaseDateLabel" xml:space="preserve"><value>Veröffentlichungsdatum</value></data>
<data name="SoftwareVariantLabel" xml:space="preserve"><value>Variante</value></data>
<data name="SoftwareSubvariantLabel" xml:space="preserve"><value>Untervariante</value></data>
<data name="SoftwareBarcodesLabel" xml:space="preserve"><value>Strichcodes</value></data>
<data name="SoftwareProductCodesLabel" xml:space="preserve"><value>Produktcodes</value></data>
</root>

View File

@@ -1987,4 +1987,39 @@
<data name="PersonBooksLabel" xml:space="preserve"><value>Libros</value></data>
<data name="PersonDocumentsLabel" xml:space="preserve"><value>Documentos</value></data>
<data name="PersonMagazinesLabel" xml:space="preserve"><value>Revistas</value></data>
<!-- Software Browsing -->
<data name="Software in the database" xml:space="preserve"><value>Software en la base de datos</value></data>
<data name="No software found" xml:space="preserve"><value>No se encontró software</value></data>
<data name="Failed to load software data. Please try again later." xml:space="preserve"><value>Error al cargar los datos de software. Por favor, inténtelo más tarde.</value></data>
<data name="Failed to navigate. Please try again." xml:space="preserve"><value>Error al navegar. Por favor, inténtelo de nuevo.</value></data>
<data name="AllSoftwareButton" xml:space="preserve"><value>Todo el software</value></data>
<data name="Browse by Platform" xml:space="preserve"><value>Explorar por plataforma</value></data>
<data name="All Software" xml:space="preserve"><value>Todo el software</value></data>
<data name="Browsing all software in the database" xml:space="preserve"><value>Explorando todo el software en la base de datos</value></data>
<data name="Software Starting with" xml:space="preserve"><value>Software que empieza por</value></data>
<data name="Showing software that start with" xml:space="preserve"><value>Mostrando software que empieza por</value></data>
<data name="Software from" xml:space="preserve"><value>Software de</value></data>
<data name="Showing software released in" xml:space="preserve"><value>Mostrando software publicado en</value></data>
<data name="Software by Platform" xml:space="preserve"><value>Software por plataforma</value></data>
<data name="Showing software for the selected platform" xml:space="preserve"><value>Mostrando software para la plataforma seleccionada</value></data>
<data name="No software found for this filter" xml:space="preserve"><value>No se encontró software para este filtro</value></data>
<data name="Failed to load software. Please try again later." xml:space="preserve"><value>Error al cargar el software. Por favor, inténtelo más tarde.</value></data>
<data name="SoftwareLabel" xml:space="preserve"><value>software</value></data>
<data name="SoftwareErrorInfoBar_Title" xml:space="preserve"><value>Error al cargar software</value></data>
<data name="Software not found" xml:space="preserve"><value>Software no encontrado</value></data>
<data name="SoftwareFamilyLabel" xml:space="preserve"><value>Familia</value></data>
<data name="SoftwareCompaniesLabel" xml:space="preserve"><value>Compañías</value></data>
<data name="SoftwareVersionsLabel" xml:space="preserve"><value>Versiones</value></data>
<data name="SoftwareIsOSLabel" xml:space="preserve"><value>Sistema operativo</value></data>
<data name="SoftwareIsGameLabel" xml:space="preserve"><value>Juego</value></data>
<data name="Release not found" xml:space="preserve"><value>Publicación no encontrada</value></data>
<data name="Software Release" xml:space="preserve"><value>Publicación de software</value></data>
<data name="SoftwarePlatformLabel" xml:space="preserve"><value>Plataforma</value></data>
<data name="SoftwareRegionLabel" xml:space="preserve"><value>Región</value></data>
<data name="SoftwarePublisherLabel" xml:space="preserve"><value>Editor</value></data>
<data name="SoftwareReleaseDateLabel" xml:space="preserve"><value>Fecha de publicación</value></data>
<data name="SoftwareVariantLabel" xml:space="preserve"><value>Variante</value></data>
<data name="SoftwareSubvariantLabel" xml:space="preserve"><value>Subvariante</value></data>
<data name="SoftwareBarcodesLabel" xml:space="preserve"><value>Códigos de barras</value></data>
<data name="SoftwareProductCodesLabel" xml:space="preserve"><value>Códigos de producto</value></data>
</root>

View File

@@ -1951,4 +1951,39 @@
<data name="PersonBooksLabel" xml:space="preserve"><value>Livres</value></data>
<data name="PersonDocumentsLabel" xml:space="preserve"><value>Documents</value></data>
<data name="PersonMagazinesLabel" xml:space="preserve"><value>Magazines</value></data>
<!-- Software Browsing -->
<data name="Software in the database" xml:space="preserve"><value>Logiciels dans la base de données</value></data>
<data name="No software found" xml:space="preserve"><value>Aucun logiciel trouvé</value></data>
<data name="Failed to load software data. Please try again later." xml:space="preserve"><value>Échec du chargement des données logicielles. Veuillez réessayer plus tard.</value></data>
<data name="Failed to navigate. Please try again." xml:space="preserve"><value>Échec de la navigation. Veuillez réessayer.</value></data>
<data name="AllSoftwareButton" xml:space="preserve"><value>Tous les logiciels</value></data>
<data name="Browse by Platform" xml:space="preserve"><value>Parcourir par plateforme</value></data>
<data name="All Software" xml:space="preserve"><value>Tous les logiciels</value></data>
<data name="Browsing all software in the database" xml:space="preserve"><value>Parcourir tous les logiciels dans la base de données</value></data>
<data name="Software Starting with" xml:space="preserve"><value>Logiciels commençant par</value></data>
<data name="Showing software that start with" xml:space="preserve"><value>Affichage des logiciels commençant par</value></data>
<data name="Software from" xml:space="preserve"><value>Logiciels de</value></data>
<data name="Showing software released in" xml:space="preserve"><value>Affichage des logiciels publiés en</value></data>
<data name="Software by Platform" xml:space="preserve"><value>Logiciels par plateforme</value></data>
<data name="Showing software for the selected platform" xml:space="preserve"><value>Affichage des logiciels pour la plateforme sélectionnée</value></data>
<data name="No software found for this filter" xml:space="preserve"><value>Aucun logiciel trouvé pour ce filtre</value></data>
<data name="Failed to load software. Please try again later." xml:space="preserve"><value>Échec du chargement des logiciels. Veuillez réessayer plus tard.</value></data>
<data name="SoftwareLabel" xml:space="preserve"><value>logiciels</value></data>
<data name="SoftwareErrorInfoBar_Title" xml:space="preserve"><value>Erreur de chargement des logiciels</value></data>
<data name="Software not found" xml:space="preserve"><value>Logiciel non trouvé</value></data>
<data name="SoftwareFamilyLabel" xml:space="preserve"><value>Famille</value></data>
<data name="SoftwareCompaniesLabel" xml:space="preserve"><value>Entreprises</value></data>
<data name="SoftwareVersionsLabel" xml:space="preserve"><value>Versions</value></data>
<data name="SoftwareIsOSLabel" xml:space="preserve"><value>Système d'exploitation</value></data>
<data name="SoftwareIsGameLabel" xml:space="preserve"><value>Jeu</value></data>
<data name="Release not found" xml:space="preserve"><value>Publication non trouvée</value></data>
<data name="Software Release" xml:space="preserve"><value>Publication logicielle</value></data>
<data name="SoftwarePlatformLabel" xml:space="preserve"><value>Plateforme</value></data>
<data name="SoftwareRegionLabel" xml:space="preserve"><value>Région</value></data>
<data name="SoftwarePublisherLabel" xml:space="preserve"><value>Éditeur</value></data>
<data name="SoftwareReleaseDateLabel" xml:space="preserve"><value>Date de publication</value></data>
<data name="SoftwareVariantLabel" xml:space="preserve"><value>Variante</value></data>
<data name="SoftwareSubvariantLabel" xml:space="preserve"><value>Sous-variante</value></data>
<data name="SoftwareBarcodesLabel" xml:space="preserve"><value>Codes-barres</value></data>
<data name="SoftwareProductCodesLabel" xml:space="preserve"><value>Codes produit</value></data>
</root>

View File

@@ -1847,4 +1847,39 @@
<data name="PersonBooksLabel" xml:space="preserve"><value>Libri</value></data>
<data name="PersonDocumentsLabel" xml:space="preserve"><value>Documenta</value></data>
<data name="PersonMagazinesLabel" xml:space="preserve"><value>Ephemerides</value></data>
<!-- Software Browsing -->
<data name="Software in the database" xml:space="preserve"><value>Programmata in datorum basi</value></data>
<data name="No software found" xml:space="preserve"><value>Nulla programmata inventa</value></data>
<data name="Failed to load software data. Please try again later." xml:space="preserve"><value>Erratum in programmatis datis onerando. Iterum postea conare.</value></data>
<data name="Failed to navigate. Please try again." xml:space="preserve"><value>Erratum in navigando. Iterum conare.</value></data>
<data name="AllSoftwareButton" xml:space="preserve"><value>Omnia programmata</value></data>
<data name="Browse by Platform" xml:space="preserve"><value>Per suggestum inspicere</value></data>
<data name="All Software" xml:space="preserve"><value>Omnia programmata</value></data>
<data name="Browsing all software in the database" xml:space="preserve"><value>Omnia programmata in datorum basi inspiciens</value></data>
<data name="Software Starting with" xml:space="preserve"><value>Programmata incipientes cum</value></data>
<data name="Showing software that start with" xml:space="preserve"><value>Programmata monstrantes incipientes cum</value></data>
<data name="Software from" xml:space="preserve"><value>Programmata ex</value></data>
<data name="Showing software released in" xml:space="preserve"><value>Programmata monstrantes edita in</value></data>
<data name="Software by Platform" xml:space="preserve"><value>Programmata per suggestum</value></data>
<data name="Showing software for the selected platform" xml:space="preserve"><value>Programmata monstrantes pro suggestu electo</value></data>
<data name="No software found for this filter" xml:space="preserve"><value>Nulla programmata inventa pro hoc filtro</value></data>
<data name="Failed to load software. Please try again later." xml:space="preserve"><value>Erratum in programmatis onerando. Iterum postea conare.</value></data>
<data name="SoftwareLabel" xml:space="preserve"><value>programmata</value></data>
<data name="SoftwareErrorInfoBar_Title" xml:space="preserve"><value>Erratum in programmatis onerando</value></data>
<data name="Software not found" xml:space="preserve"><value>Programma non inventum</value></data>
<data name="SoftwareFamilyLabel" xml:space="preserve"><value>Familia</value></data>
<data name="SoftwareCompaniesLabel" xml:space="preserve"><value>Societates</value></data>
<data name="SoftwareVersionsLabel" xml:space="preserve"><value>Versiones</value></data>
<data name="SoftwareIsOSLabel" xml:space="preserve"><value>Systema operandi</value></data>
<data name="SoftwareIsGameLabel" xml:space="preserve"><value>Ludus</value></data>
<data name="Release not found" xml:space="preserve"><value>Editio non inventa</value></data>
<data name="Software Release" xml:space="preserve"><value>Editio programmatis</value></data>
<data name="SoftwarePlatformLabel" xml:space="preserve"><value>Suggestum</value></data>
<data name="SoftwareRegionLabel" xml:space="preserve"><value>Regio</value></data>
<data name="SoftwarePublisherLabel" xml:space="preserve"><value>Editor</value></data>
<data name="SoftwareReleaseDateLabel" xml:space="preserve"><value>Dies editionis</value></data>
<data name="SoftwareVariantLabel" xml:space="preserve"><value>Variatio</value></data>
<data name="SoftwareSubvariantLabel" xml:space="preserve"><value>Subvariatio</value></data>
<data name="SoftwareBarcodesLabel" xml:space="preserve"><value>Codices striati</value></data>
<data name="SoftwareProductCodesLabel" xml:space="preserve"><value>Codices productorum</value></data>
</root>

View File

@@ -1847,4 +1847,39 @@
<data name="PersonBooksLabel" xml:space="preserve"><value>Livros</value></data>
<data name="PersonDocumentsLabel" xml:space="preserve"><value>Documentos</value></data>
<data name="PersonMagazinesLabel" xml:space="preserve"><value>Revistas</value></data>
<!-- Software Browsing -->
<data name="Software in the database" xml:space="preserve"><value>Software na base de dados</value></data>
<data name="No software found" xml:space="preserve"><value>Nenhum software encontrado</value></data>
<data name="Failed to load software data. Please try again later." xml:space="preserve"><value>Falha ao carregar dados de software. Tente novamente mais tarde.</value></data>
<data name="Failed to navigate. Please try again." xml:space="preserve"><value>Falha na navegação. Tente novamente.</value></data>
<data name="AllSoftwareButton" xml:space="preserve"><value>Todo o software</value></data>
<data name="Browse by Platform" xml:space="preserve"><value>Navegar por plataforma</value></data>
<data name="All Software" xml:space="preserve"><value>Todo o software</value></data>
<data name="Browsing all software in the database" xml:space="preserve"><value>Navegando todo o software na base de dados</value></data>
<data name="Software Starting with" xml:space="preserve"><value>Software começando com</value></data>
<data name="Showing software that start with" xml:space="preserve"><value>Mostrando software que começa com</value></data>
<data name="Software from" xml:space="preserve"><value>Software de</value></data>
<data name="Showing software released in" xml:space="preserve"><value>Mostrando software lançado em</value></data>
<data name="Software by Platform" xml:space="preserve"><value>Software por plataforma</value></data>
<data name="Showing software for the selected platform" xml:space="preserve"><value>Mostrando software para a plataforma selecionada</value></data>
<data name="No software found for this filter" xml:space="preserve"><value>Nenhum software encontrado para este filtro</value></data>
<data name="Failed to load software. Please try again later." xml:space="preserve"><value>Falha ao carregar software. Tente novamente mais tarde.</value></data>
<data name="SoftwareLabel" xml:space="preserve"><value>software</value></data>
<data name="SoftwareErrorInfoBar_Title" xml:space="preserve"><value>Erro ao carregar software</value></data>
<data name="Software not found" xml:space="preserve"><value>Software não encontrado</value></data>
<data name="SoftwareFamilyLabel" xml:space="preserve"><value>Família</value></data>
<data name="SoftwareCompaniesLabel" xml:space="preserve"><value>Empresas</value></data>
<data name="SoftwareVersionsLabel" xml:space="preserve"><value>Versões</value></data>
<data name="SoftwareIsOSLabel" xml:space="preserve"><value>Sistema operacional</value></data>
<data name="SoftwareIsGameLabel" xml:space="preserve"><value>Jogo</value></data>
<data name="Release not found" xml:space="preserve"><value>Lançamento não encontrado</value></data>
<data name="Software Release" xml:space="preserve"><value>Lançamento de software</value></data>
<data name="SoftwarePlatformLabel" xml:space="preserve"><value>Plataforma</value></data>
<data name="SoftwareRegionLabel" xml:space="preserve"><value>Região</value></data>
<data name="SoftwarePublisherLabel" xml:space="preserve"><value>Editora</value></data>
<data name="SoftwareReleaseDateLabel" xml:space="preserve"><value>Data de lançamento</value></data>
<data name="SoftwareVariantLabel" xml:space="preserve"><value>Variante</value></data>
<data name="SoftwareSubvariantLabel" xml:space="preserve"><value>Subvariante</value></data>
<data name="SoftwareBarcodesLabel" xml:space="preserve"><value>Códigos de barras</value></data>
<data name="SoftwareProductCodesLabel" xml:space="preserve"><value>Códigos de produto</value></data>
</root>

View File

@@ -2193,4 +2193,39 @@
<data name="PersonBooksLabel" xml:space="preserve"><value>Books</value></data>
<data name="PersonDocumentsLabel" xml:space="preserve"><value>Documents</value></data>
<data name="PersonMagazinesLabel" xml:space="preserve"><value>Magazines</value></data>
<!-- Software Browsing -->
<data name="Software in the database" xml:space="preserve"><value>Software in the database</value></data>
<data name="No software found" xml:space="preserve"><value>No software found</value></data>
<data name="Failed to load software data. Please try again later." xml:space="preserve"><value>Failed to load software data. Please try again later.</value></data>
<data name="Failed to navigate. Please try again." xml:space="preserve"><value>Failed to navigate. Please try again.</value></data>
<data name="AllSoftwareButton" xml:space="preserve"><value>All Software</value></data>
<data name="Browse by Platform" xml:space="preserve"><value>Browse by Platform</value></data>
<data name="All Software" xml:space="preserve"><value>All Software</value></data>
<data name="Browsing all software in the database" xml:space="preserve"><value>Browsing all software in the database</value></data>
<data name="Software Starting with" xml:space="preserve"><value>Software Starting with</value></data>
<data name="Showing software that start with" xml:space="preserve"><value>Showing software that start with</value></data>
<data name="Software from" xml:space="preserve"><value>Software from</value></data>
<data name="Showing software released in" xml:space="preserve"><value>Showing software released in</value></data>
<data name="Software by Platform" xml:space="preserve"><value>Software by Platform</value></data>
<data name="Showing software for the selected platform" xml:space="preserve"><value>Showing software for the selected platform</value></data>
<data name="No software found for this filter" xml:space="preserve"><value>No software found for this filter</value></data>
<data name="Failed to load software. Please try again later." xml:space="preserve"><value>Failed to load software. Please try again later.</value></data>
<data name="SoftwareLabel" xml:space="preserve"><value>software</value></data>
<data name="SoftwareErrorInfoBar_Title" xml:space="preserve"><value>Error loading software</value></data>
<data name="Software not found" xml:space="preserve"><value>Software not found</value></data>
<data name="SoftwareFamilyLabel" xml:space="preserve"><value>Family</value></data>
<data name="SoftwareCompaniesLabel" xml:space="preserve"><value>Companies</value></data>
<data name="SoftwareVersionsLabel" xml:space="preserve"><value>Versions</value></data>
<data name="SoftwareIsOSLabel" xml:space="preserve"><value>Operating System</value></data>
<data name="SoftwareIsGameLabel" xml:space="preserve"><value>Game</value></data>
<data name="Release not found" xml:space="preserve"><value>Release not found</value></data>
<data name="Software Release" xml:space="preserve"><value>Software Release</value></data>
<data name="SoftwarePlatformLabel" xml:space="preserve"><value>Platform</value></data>
<data name="SoftwareRegionLabel" xml:space="preserve"><value>Region</value></data>
<data name="SoftwarePublisherLabel" xml:space="preserve"><value>Publisher</value></data>
<data name="SoftwareReleaseDateLabel" xml:space="preserve"><value>Release Date</value></data>
<data name="SoftwareVariantLabel" xml:space="preserve"><value>Variant</value></data>
<data name="SoftwareSubvariantLabel" xml:space="preserve"><value>Subvariant</value></data>
<data name="SoftwareBarcodesLabel" xml:space="preserve"><value>Barcodes</value></data>
<data name="SoftwareProductCodesLabel" xml:space="preserve"><value>Product Codes</value></data>
</root>

View File

@@ -291,7 +291,7 @@ namespace Marechai.App
ApiClientBuilder.RegisterDefaultDeserializer<FormParseNodeFactory>();
if (string.IsNullOrEmpty(RequestAdapter.BaseUrl))
{
RequestAdapter.BaseUrl = "http://localhost:5099";
RequestAdapter.BaseUrl = "http://localhost:5097";
}
PathParameters.TryAdd("baseurl", RequestAdapter.BaseUrl);
}

View File

@@ -0,0 +1,48 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Software.ByLetter.Item;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System;
namespace Marechai.App.Software.ByLetter
{
/// <summary>
/// Builds and executes requests for operations under \software\by-letter
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class ByLetterRequestBuilder : BaseRequestBuilder
{
/// <summary>Gets an item from the Marechai.App.software.byLetter.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.App.Software.ByLetter.Item.WithCItemRequestBuilder"/></returns>
public global::Marechai.App.Software.ByLetter.Item.WithCItemRequestBuilder this[string position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
urlTplParams.Add("c", position);
return new global::Marechai.App.Software.ByLetter.Item.WithCItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByLetter.ByLetterRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public ByLetterRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-letter", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByLetter.ByLetterRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public ByLetterRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-letter", rawUrl)
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,92 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.App.Software.ByLetter.Item
{
/// <summary>
/// Builds and executes requests for operations under \software\by-letter\{c}
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithCItemRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByLetter.Item.WithCItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithCItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-letter/{c}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByLetter.Item.WithCItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithCItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-letter/{c}", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.App.Models.SoftwareDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.App.Models.SoftwareDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.App.Models.SoftwareDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.SoftwareDto>(requestInfo, global::Marechai.App.Models.SoftwareDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.App.Software.ByLetter.Item.WithCItemRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Software.ByLetter.Item.WithCItemRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Software.ByLetter.Item.WithCItemRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithCItemRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,61 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Software.ByPlatform.Item;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System;
namespace Marechai.App.Software.ByPlatform
{
/// <summary>
/// Builds and executes requests for operations under \software\by-platform
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class ByPlatformRequestBuilder : BaseRequestBuilder
{
/// <summary>Gets an item from the Marechai.App.software.byPlatform.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder"/></returns>
public global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder this[int position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
urlTplParams.Add("platformId", position);
return new global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>Gets an item from the Marechai.App.software.byPlatform.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder"/></returns>
[Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")]
public global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder this[string position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("platformId", position);
return new global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByPlatform.ByPlatformRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public ByPlatformRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-platform", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByPlatform.ByPlatformRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public ByPlatformRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-platform", rawUrl)
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,92 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.App.Software.ByPlatform.Item
{
/// <summary>
/// Builds and executes requests for operations under \software\by-platform\{platformId}
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithPlatformItemRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithPlatformItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-platform/{platformId}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithPlatformItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-platform/{platformId}", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.App.Models.SoftwareDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.App.Models.SoftwareDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.App.Models.SoftwareDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.SoftwareDto>(requestInfo, global::Marechai.App.Models.SoftwareDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Software.ByPlatform.Item.WithPlatformItemRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithPlatformItemRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,61 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Software.ByYear.Item;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System;
namespace Marechai.App.Software.ByYear
{
/// <summary>
/// Builds and executes requests for operations under \software\by-year
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class ByYearRequestBuilder : BaseRequestBuilder
{
/// <summary>Gets an item from the Marechai.App.software.byYear.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder"/></returns>
public global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder this[int position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
urlTplParams.Add("year", position);
return new global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>Gets an item from the Marechai.App.software.byYear.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder"/></returns>
[Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")]
public global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder this[string position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("year", position);
return new global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByYear.ByYearRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public ByYearRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-year", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByYear.ByYearRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public ByYearRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-year", rawUrl)
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,92 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.App.Software.ByYear.Item
{
/// <summary>
/// Builds and executes requests for operations under \software\by-year\{year}
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithYearItemRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithYearItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-year/{year}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithYearItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/by-year/{year}", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.App.Models.SoftwareDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.App.Models.SoftwareDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.App.Models.SoftwareDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.SoftwareDto>(requestInfo, global::Marechai.App.Models.SoftwareDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Software.ByYear.Item.WithYearItemRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithYearItemRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,91 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.App.Software.Count
{
/// <summary>
/// Builds and executes requests for operations under \software\count
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/count", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/count", rawUrl)
{
}
/// <returns>A <see cref="int"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<int?>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.App.Software.Count.CountRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Software.Count.CountRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Software.Count.CountRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,92 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.App.Software.Item.Companies
{
/// <summary>
/// Builds and executes requests for operations under \software\{-id}\companies
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CompaniesRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.Item.Companies.CompaniesRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CompaniesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/{%2Did}/companies", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.Item.Companies.CompaniesRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CompaniesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/{%2Did}/companies", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.App.Models.SoftwareCompanyRoleDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.App.Models.SoftwareCompanyRoleDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.App.Models.SoftwareCompanyRoleDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.SoftwareCompanyRoleDto>(requestInfo, global::Marechai.App.Models.SoftwareCompanyRoleDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.App.Software.Item.Companies.CompaniesRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Software.Item.Companies.CompaniesRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Software.Item.Companies.CompaniesRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CompaniesRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -1,6 +1,7 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Models;
using Marechai.App.Software.Item.Companies;
using Marechai.App.Software.Item.CompanyRoles;
using Marechai.App.Software.Item.Variants;
using Marechai.App.Software.Item.Versions;
@@ -20,6 +21,11 @@ namespace Marechai.App.Software.Item
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class ItemRequestBuilder : BaseRequestBuilder
{
/// <summary>The companies property</summary>
public global::Marechai.App.Software.Item.Companies.CompaniesRequestBuilder Companies
{
get => new global::Marechai.App.Software.Item.Companies.CompaniesRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The companyRoles property</summary>
public global::Marechai.App.Software.Item.CompanyRoles.CompanyRolesRequestBuilder CompanyRoles
{

View File

@@ -0,0 +1,91 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.App.Software.MaximumYear
{
/// <summary>
/// Builds and executes requests for operations under \software\maximum-year
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class MaximumYearRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.MaximumYear.MaximumYearRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public MaximumYearRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/maximum-year", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.MaximumYear.MaximumYearRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public MaximumYearRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/maximum-year", rawUrl)
{
}
/// <returns>A <see cref="int"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<int?>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.App.Software.MaximumYear.MaximumYearRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Software.MaximumYear.MaximumYearRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Software.MaximumYear.MaximumYearRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class MaximumYearRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,91 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.App.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.App.Software.MinimumYear
{
/// <summary>
/// Builds and executes requests for operations under \software\minimum-year
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class MinimumYearRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.MinimumYear.MinimumYearRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public MinimumYearRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/minimum-year", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Software.MinimumYear.MinimumYearRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public MinimumYearRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software/minimum-year", rawUrl)
{
}
/// <returns>A <see cref="int"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<int?>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.App.Software.MinimumYear.MinimumYearRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Software.MinimumYear.MinimumYearRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Software.MinimumYear.MinimumYearRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class MinimumYearRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -2,9 +2,15 @@
#pragma warning disable CS0618
using Marechai.App.Models;
using Marechai.App.Software.Barcodes;
using Marechai.App.Software.ByLetter;
using Marechai.App.Software.ByPlatform;
using Marechai.App.Software.ByYear;
using Marechai.App.Software.CompanyRoles;
using Marechai.App.Software.Count;
using Marechai.App.Software.Families;
using Marechai.App.Software.Item;
using Marechai.App.Software.MaximumYear;
using Marechai.App.Software.MinimumYear;
using Marechai.App.Software.OsCompatibility;
using Marechai.App.Software.Platforms;
using Marechai.App.Software.ProductCodes;
@@ -37,16 +43,46 @@ namespace Marechai.App.Software
{
get => new global::Marechai.App.Software.Barcodes.BarcodesRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The byLetter property</summary>
public global::Marechai.App.Software.ByLetter.ByLetterRequestBuilder ByLetter
{
get => new global::Marechai.App.Software.ByLetter.ByLetterRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The byPlatform property</summary>
public global::Marechai.App.Software.ByPlatform.ByPlatformRequestBuilder ByPlatform
{
get => new global::Marechai.App.Software.ByPlatform.ByPlatformRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The byYear property</summary>
public global::Marechai.App.Software.ByYear.ByYearRequestBuilder ByYear
{
get => new global::Marechai.App.Software.ByYear.ByYearRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The companyRoles property</summary>
public global::Marechai.App.Software.CompanyRoles.CompanyRolesRequestBuilder CompanyRoles
{
get => new global::Marechai.App.Software.CompanyRoles.CompanyRolesRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The count property</summary>
public global::Marechai.App.Software.Count.CountRequestBuilder Count
{
get => new global::Marechai.App.Software.Count.CountRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The families property</summary>
public global::Marechai.App.Software.Families.FamiliesRequestBuilder Families
{
get => new global::Marechai.App.Software.Families.FamiliesRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The maximumYear property</summary>
public global::Marechai.App.Software.MaximumYear.MaximumYearRequestBuilder MaximumYear
{
get => new global::Marechai.App.Software.MaximumYear.MaximumYearRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The minimumYear property</summary>
public global::Marechai.App.Software.MinimumYear.MinimumYearRequestBuilder MinimumYear
{
get => new global::Marechai.App.Software.MinimumYear.MinimumYearRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The osCompatibility property</summary>
public global::Marechai.App.Software.OsCompatibility.OsCompatibilityRequestBuilder OsCompatibility
{

View File

@@ -1,6 +1,6 @@
{
"descriptionHash": "1783B9AD175C772A685DC15EF6212E8E133A19BC2DE13AA6B4AB6AE83B1BD7A7DD9A29A6AC3FE5E4CC9D61F5786305F1E8E9F4C4B0A464CD7E206614BDD77C33",
"descriptionLocation": "http://localhost:5099/openapi/v1.json",
"descriptionHash": "2479308A8B6F7E9B13D049D54734CC69DAA33EAEA8B29B6ECE73CA63384B3619621B50C334E643A0D0B9140DAE38B22492CB10CE02B533DD9A8ED8284323F570",
"descriptionLocation": "http://localhost:5097/openapi/v1.json",
"lockFileVersion": "1.0.0",
"kiotaVersion": "1.29.0",
"clientClassName": "ApiClient",

View File

@@ -653,4 +653,22 @@ public class LocalizedStrings
public string PersonBooksLabel => _l["PersonBooksLabel"];
public string PersonDocumentsLabel => _l["PersonDocumentsLabel"];
public string PersonMagazinesLabel => _l["PersonMagazinesLabel"];
// Software user-facing pages
public string AllSoftwareButton => _l["AllSoftwareButton"];
public string SoftwareLabel => _l["SoftwareLabel"];
public string SoftwareErrorInfoBar_Title => _l["SoftwareErrorInfoBar_Title"];
public string SoftwareFamilyLabel => _l["SoftwareFamilyLabel"];
public string SoftwareCompaniesLabel => _l["SoftwareCompaniesLabel"];
public string SoftwareVersionsLabel => _l["SoftwareVersionsLabel"];
public string SoftwareIsOSLabel => _l["SoftwareIsOSLabel"];
public string SoftwareIsGameLabel => _l["SoftwareIsGameLabel"];
public string SoftwarePlatformLabel => _l["SoftwarePlatformLabel"];
public string SoftwareRegionLabel => _l["SoftwareRegionLabel"];
public string SoftwarePublisherLabel => _l["SoftwarePublisherLabel"];
public string SoftwareReleaseDateLabel => _l["SoftwareReleaseDateLabel"];
public string SoftwareVariantLabel => _l["SoftwareVariantLabel"];
public string SoftwareSubvariantLabel => _l["SoftwareSubvariantLabel"];
public string SoftwareBarcodesLabel => _l["SoftwareBarcodesLabel"];
public string SoftwareProductCodesLabel => _l["SoftwareProductCodesLabel"];
}

View File

@@ -0,0 +1,367 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Marechai.App.Models;
namespace Marechai.App.Services;
public class SoftwareBrowsingService
{
private readonly ApiClient _apiClient;
private readonly ILogger<SoftwareBrowsingService> _logger;
public SoftwareBrowsingService(ApiClient apiClient, ILogger<SoftwareBrowsingService> logger)
{
_apiClient = apiClient;
_logger = logger;
}
public async Task<int> GetSoftwareCountAsync()
{
try
{
_logger.LogInformation("Fetching software count from API");
int? result = await _apiClient.Software.Count.GetAsync();
int count = result ?? 0;
_logger.LogInformation("Successfully fetched software count: {Count}", count);
return count;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching software count from API");
return 0;
}
}
public async Task<int> GetMinimumYearAsync()
{
try
{
_logger.LogInformation("Fetching software minimum year from API");
int? result = await _apiClient.Software.MinimumYear.GetAsync();
int year = result ?? 0;
_logger.LogInformation("Successfully fetched software minimum year: {Year}", year);
return year;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching software minimum year from API");
return 0;
}
}
public async Task<int> GetMaximumYearAsync()
{
try
{
_logger.LogInformation("Fetching software maximum year from API");
int? result = await _apiClient.Software.MaximumYear.GetAsync();
int year = result ?? 0;
_logger.LogInformation("Successfully fetched software maximum year: {Year}", year);
return year;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching software maximum year from API");
return 0;
}
}
public async Task<List<SoftwareDto>> GetSoftwareByLetterAsync(char letter)
{
try
{
_logger.LogInformation("Fetching software starting with '{Letter}' from API", letter);
List<SoftwareDto> software = await _apiClient.Software.ByLetter[letter.ToString()].GetAsync();
if(software == null) return [];
_logger.LogInformation("Successfully fetched {Count} software starting with '{Letter}'",
software.Count,
letter);
return software;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching software by letter '{Letter}' from API", letter);
return [];
}
}
public async Task<List<SoftwareDto>> GetSoftwareByYearAsync(int year)
{
try
{
_logger.LogInformation("Fetching software from year {Year} from API", year);
List<SoftwareDto> software = await _apiClient.Software.ByYear[year].GetAsync();
if(software == null) return [];
_logger.LogInformation("Successfully fetched {Count} software from year {Year}", software.Count, year);
return software;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching software by year {Year} from API", year);
return [];
}
}
public async Task<List<SoftwareDto>> GetSoftwareByPlatformAsync(int platformId)
{
try
{
_logger.LogInformation("Fetching software for platform {PlatformId} from API", platformId);
List<SoftwareDto> software = await _apiClient.Software.ByPlatform[platformId].GetAsync();
if(software == null) return [];
_logger.LogInformation("Successfully fetched {Count} software for platform {PlatformId}",
software.Count,
platformId);
return software;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching software by platform {PlatformId} from API", platformId);
return [];
}
}
public async Task<List<SoftwareDto>> GetAllSoftwareAsync()
{
try
{
_logger.LogInformation("Fetching all software from API");
List<SoftwareDto> software = await _apiClient.Software.GetAsync();
if(software == null) return [];
_logger.LogInformation("Successfully fetched {Count} total software", software.Count);
return software;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching all software from API");
return [];
}
}
public async Task<SoftwareDto?> GetSoftwareByIdAsync(int softwareId)
{
try
{
_logger.LogInformation("Fetching software {SoftwareId} from API", softwareId);
SoftwareDto? software = await _apiClient.Software[softwareId].GetAsync();
if(software == null)
{
_logger.LogWarning("Software {SoftwareId} not found", softwareId);
return null;
}
_logger.LogInformation("Successfully fetched software {SoftwareId}: {Name}", softwareId, software.Name);
return software;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching software {SoftwareId} from API", softwareId);
return null;
}
}
public async Task<List<SoftwareVersionDto>> GetVersionsAsync(int softwareId)
{
try
{
_logger.LogInformation("Fetching versions for software {SoftwareId} from API", softwareId);
List<SoftwareVersionDto> versions = await _apiClient.Software[softwareId].Versions.GetAsync();
if(versions == null) return [];
_logger.LogInformation("Successfully fetched {Count} versions for software {SoftwareId}",
versions.Count,
softwareId);
return versions;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching versions for software {SoftwareId} from API", softwareId);
return [];
}
}
public async Task<List<SoftwareCompanyRoleDto>> GetCompaniesAsync(int softwareId)
{
try
{
_logger.LogInformation("Fetching companies for software {SoftwareId} from API", softwareId);
List<SoftwareCompanyRoleDto> companies =
await _apiClient.Software[softwareId].Companies.GetAsync();
if(companies == null) return [];
_logger.LogInformation("Successfully fetched {Count} companies for software {SoftwareId}",
companies.Count,
softwareId);
return companies;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching companies for software {SoftwareId} from API", softwareId);
return [];
}
}
public async Task<List<SoftwareReleaseDto>> GetReleasesByVersionAsync(int versionId)
{
try
{
_logger.LogInformation("Fetching releases for version {VersionId} from API", versionId);
List<SoftwareReleaseDto> releases =
await _apiClient.Software.Versions[versionId].Releases.GetAsync();
if(releases == null) return [];
_logger.LogInformation("Successfully fetched {Count} releases for version {VersionId}",
releases.Count,
versionId);
return releases;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching releases for version {VersionId} from API", versionId);
return [];
}
}
public async Task<SoftwareReleaseDto?> GetReleaseByIdAsync(int releaseId)
{
try
{
_logger.LogInformation("Fetching release {ReleaseId} from API", releaseId);
SoftwareReleaseDto? release = await _apiClient.Software.Releases[releaseId].GetAsync();
if(release == null)
{
_logger.LogWarning("Release {ReleaseId} not found", releaseId);
return null;
}
_logger.LogInformation("Successfully fetched release {ReleaseId}", releaseId);
return release;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching release {ReleaseId} from API", releaseId);
return null;
}
}
public async Task<List<SoftwareBarcodeDto>> GetBarcodesAsync(int releaseId)
{
try
{
_logger.LogInformation("Fetching barcodes for release {ReleaseId} from API", releaseId);
List<SoftwareBarcodeDto> barcodes =
await _apiClient.Software.Releases[releaseId].Barcodes.GetAsync();
if(barcodes == null) return [];
_logger.LogInformation("Successfully fetched {Count} barcodes for release {ReleaseId}",
barcodes.Count,
releaseId);
return barcodes;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching barcodes for release {ReleaseId} from API", releaseId);
return [];
}
}
public async Task<List<SoftwareProductCodeDto>> GetProductCodesAsync(int releaseId)
{
try
{
_logger.LogInformation("Fetching product codes for release {ReleaseId} from API", releaseId);
List<SoftwareProductCodeDto> productCodes =
await _apiClient.Software.Releases[releaseId].ProductCodes.GetAsync();
if(productCodes == null) return [];
_logger.LogInformation("Successfully fetched {Count} product codes for release {ReleaseId}",
productCodes.Count,
releaseId);
return productCodes;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching product codes for release {ReleaseId} from API", releaseId);
return [];
}
}
public async Task<List<SoftwarePlatformDto>> GetAllPlatformsAsync()
{
try
{
_logger.LogInformation("Fetching all software platforms from API");
List<SoftwarePlatformDto> platforms = await _apiClient.Software.Platforms.GetAsync();
if(platforms == null) return [];
_logger.LogInformation("Successfully fetched {Count} software platforms", platforms.Count);
return platforms;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching software platforms from API");
return [];
}
}
}

View File

@@ -0,0 +1,21 @@
namespace Marechai.App.Services;
public interface ISoftwareListFilterContext
{
SoftwareListFilterType FilterType { get; set; }
string FilterValue { get; set; }
}
public class SoftwareListFilterContext : ISoftwareListFilterContext
{
public SoftwareListFilterType FilterType { get; set; } = SoftwareListFilterType.All;
public string FilterValue { get; set; } = string.Empty;
}
public enum SoftwareListFilterType
{
All,
Letter,
Year,
Platform
}

View File

@@ -23,6 +23,7 @@
// Copyright © 2003-2026 Natalia Portillo
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
@@ -40,6 +41,102 @@ namespace Marechai.Server.Controllers;
[ApiController]
public class SoftwareController(MarechaiContext context) : ControllerBase
{
[HttpGet("count")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<int> GetSoftwareCountAsync() => context.Softwares.CountAsync();
[HttpGet("minimum-year")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<int> GetMinimumYearAsync() => context.SoftwareReleases
.Where(r => r.ReleaseDate.HasValue &&
r.ReleaseDate.Value.Year > 1000)
.MinAsync(r => r.ReleaseDate.Value.Year);
[HttpGet("maximum-year")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<int> GetMaximumYearAsync() => context.SoftwareReleases
.Where(r => r.ReleaseDate.HasValue &&
r.ReleaseDate.Value.Year > 1000)
.MaxAsync(r => r.ReleaseDate.Value.Year);
[HttpGet("by-letter/{c}")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<List<SoftwareDto>> GetSoftwareByLetterAsync(char c) => context.Softwares
.Where(s => EF.Functions.Like(s.Name, $"{c}%"))
.OrderBy(s => s.Name)
.Select(s => new SoftwareDto
{
Id = s.Id,
Name = s.Name,
FamilyId = s.FamilyId,
Family = s.Family.Name,
IsOperatingSystem = s.IsOperatingSystem,
IsGame = s.IsGame
})
.ToListAsync();
[HttpGet("by-year/{year:int}")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<List<SoftwareDto>> GetSoftwareByYearAsync(int year) => context.Softwares
.Where(s => s.Versions.Any(v => v.Releases.Any(r => r.ReleaseDate != null &&
r.ReleaseDate.Value.Year == year)))
.OrderBy(s => s.Name)
.Select(s => new SoftwareDto
{
Id = s.Id,
Name = s.Name,
FamilyId = s.FamilyId,
Family = s.Family.Name,
IsOperatingSystem = s.IsOperatingSystem,
IsGame = s.IsGame
})
.ToListAsync();
[HttpGet("by-platform/{platformId:ulong}")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<List<SoftwareDto>> GetSoftwareByPlatformAsync(ulong platformId) => context.Softwares
.Where(s => s.Versions.Any(v => v.Releases.Any(r => r.PlatformId == platformId)))
.OrderBy(s => s.Name)
.Select(s => new SoftwareDto
{
Id = s.Id,
Name = s.Name,
FamilyId = s.FamilyId,
Family = s.Family.Name,
IsOperatingSystem = s.IsOperatingSystem,
IsGame = s.IsGame
})
.ToListAsync();
[HttpGet("/software/{softwareId:ulong}/companies")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<List<SoftwareCompanyRoleDto>> GetCompaniesAsync(ulong softwareId) => context.SoftwareCompanyRoles
.Where(cr => cr.SoftwareId == softwareId)
.Select(cr => new SoftwareCompanyRoleDto
{
SoftwareId = cr.SoftwareId,
Software = cr.Software.Name,
CompanyId = cr.CompanyId,
Company = cr.Company.Name,
RoleId = cr.RoleId,
Role = cr.Role.Name
})
.ToListAsync();
[HttpGet]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]

View File

@@ -23,6 +23,7 @@
// Copyright © 2003-2026 Natalia Portillo
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;