diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs index b737d80..9673b84 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs @@ -22,4 +22,19 @@ public static class GameHelper _ => "--yellow", }; } + + public static string GetFormatedStorageSpace(double? storageValue) + { + if (storageValue == null) + { + return string.Empty; + } + + return storageValue switch + { + >= 1000000 => $"{storageValue / 1000000:f1} To", + >= 1000 => $"{storageValue / 1000:f1} Go", + _ => $"{storageValue:f1} Mo" + }; + } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameBase.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameBase.cs index efc56cf..14e4339 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameBase.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameBase.cs @@ -12,19 +12,4 @@ public class GameBase : ComponentBase { NavigationManager.NavigateTo($"/Games/Detail/{GameDto.Id}"); } - - protected string GetFormatedStorageSpace() - { - if (GameDto.StorageSpace == null) - { - return string.Empty; - } - - return GameDto.StorageSpace switch - { - >= 1000000 => $"{GameDto.StorageSpace / 1000000:f1} To", - >= 1000 => $"{GameDto.StorageSpace / 1000:f1} Go", - _ => $"{GameDto.StorageSpace:f1} Mo" - }; - } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameRow.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameRow.razor index a2309f2..b31104c 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameRow.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameRow.razor @@ -28,7 +28,7 @@ } - @GetFormatedStorageSpace() + @GameHelper.GetFormatedStorageSpace(GameDto.StorageSpace)
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor index 460ecf6..1657943 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor @@ -22,5 +22,11 @@ + + + + @ResourcesKey.LastAdd
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor.cs index be03b6b..ac79f65 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/AdvancedGameFilter.razor.cs @@ -1,4 +1,7 @@ +using GameIdeas.BlazorApp.Helpers; +using GameIdeas.BlazorApp.Pages.Games.Header; using GameIdeas.BlazorApp.Shared.Components.Select.Models; +using GameIdeas.Resources; using GameIdeas.Shared.Dto; using Microsoft.AspNetCore.Components; @@ -11,9 +14,43 @@ public partial class AdvancedGameFilter [Parameter] public EventCallback GameFilterChanged { get; set; } private readonly SelectTheme Theme = SelectTheme.AdvancedFilter; + private readonly List StorageSpaces = [ + new() { MaxSize = 100 }, + new() { MinSize = 100, MaxSize = 1000 }, + new() { MinSize = 1000, MaxSize = 5000 }, + new() { MinSize = 5000, MaxSize = 10000 }, + new() { MinSize = 10000, MaxSize = 20000 }, + new() { MinSize = 20000, MaxSize = 40000 }, + new() { MinSize = 40000, MaxSize = 100000 }, + new() { MinSize = 100000 }, + ]; private async Task HandleValueChanged() { await GameFilterChanged.InvokeAsync(GameFilter); } + + private static string GetStorageSpaceLabel(StorageSpaceDto storageSpace) + { + if (storageSpace.MinSize == null && storageSpace.MaxSize != null) + { + return string.Format(ResourcesKey.MinStorageSpaceFormat, + GameHelper.GetFormatedStorageSpace(storageSpace.MinSize)); + } + + if (storageSpace.MinSize != null && storageSpace.MaxSize == null) + { + return string.Format(ResourcesKey.MaxStorageSpaceFormat, + GameHelper.GetFormatedStorageSpace(storageSpace.MaxSize)); + } + + if (storageSpace.MinSize != null && storageSpace.MaxSize != null) + { + return string.Format(ResourcesKey.MinMaxStorageSpaceFormat, + GameHelper.GetFormatedStorageSpace(storageSpace.MinSize), + GameHelper.GetFormatedStorageSpace(storageSpace.MaxSize)); + } + + throw new ArgumentNullException(ResourcesKey.ErrorStorageSpaceLabel); + } } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilterParams.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilterParams.cs index 1fc1f1b..87c02c1 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilterParams.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Filter/GameFilterParams.cs @@ -1,5 +1,6 @@ - -namespace GameIdeas.Shared.Dto; +using GameIdeas.Shared.Dto; + +namespace GameIdeas.BlazorApp.Pages.Games.Filter; public class GameFilterParams { @@ -14,6 +15,5 @@ public class GameFilterParams public int MinInterest { get; set; } = 1; public int MaxInterest { get; set; } = 5; public List? ReleaseYears { get; set; } - public int? MinStorageSize { get; set; } - public int? MaxStorageSize { get; set; } + public List? StorageSpaces { get; set; } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor index 369b8bf..e49ff7f 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor @@ -40,5 +40,5 @@ - + diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.cs index 00c14b7..a8b0ae3 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.cs @@ -1,3 +1,4 @@ +using GameIdeas.BlazorApp.Pages.Games.Filter; using GameIdeas.BlazorApp.Pages.Games.Gateways; using GameIdeas.BlazorApp.Shared.Components.Popup; using GameIdeas.BlazorApp.Shared.Models; @@ -28,7 +29,7 @@ public partial class Game GameFilter.SortProperty= Filter.GameFilter.GameProperties .First(gp => gp.PropertyName == nameof(GameIdeas.Shared.Model.Game.Title)); - await HandleFetchDatas(true); + await HandleFetchDatas(); await base.OnInitializedAsync(); } @@ -49,11 +50,11 @@ public partial class Game { ManualAddPopup?.Close(); } - private async Task HandleFetchDatas(bool loadCategories = false) + private async Task HandleFetchDatas(bool loadCategories = true, bool displayLoader = true) { try { - IsLoading = true; + IsLoading = displayLoader; if (loadCategories) Categories = await GameGateway.FetchCategories(); @@ -72,6 +73,6 @@ public partial class Game private async Task HandleFilterChanged(GameFilterParams args) { GameFilter = args; - await HandleFetchDatas(false); + await HandleFetchDatas(loadCategories: false, displayLoader: false); } } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs index 0cbf89b..e2823fb 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs @@ -1,4 +1,5 @@ -using GameIdeas.BlazorApp.Services; +using GameIdeas.BlazorApp.Pages.Games.Filter; +using GameIdeas.BlazorApp.Services; using GameIdeas.BlazorApp.Shared.Constants; using GameIdeas.BlazorApp.Shared.Exceptions; using GameIdeas.Resources; @@ -44,16 +45,13 @@ public class GameGateway(IHttpClientService httpClientService) : IGameGateway Title = filterParams.Title, MaxInterest = filterParams.MaxInterest, MinInterest = filterParams.MinInterest, - MaxStorageSize = filterParams.MaxStorageSize, - MinStorageSize = filterParams.MinStorageSize, - ReleaseYears = filterParams.ReleaseYears, + StorageSpaces = filterParams.StorageSpaces, DeveloperIds = filterParams.Developers?.Select(d => d.Id ?? 0).ToList(), PublisherIds = filterParams.Publishers?.Select(d => d.Id ?? 0).ToList(), PlatformIds = filterParams.Platforms?.Select(d => d.Id ?? 0).ToList(), PropertyIds = filterParams.Properties?.Select(d => d.Id ?? 0).ToList(), + ReleaseYears = filterParams.ReleaseYears, TagIds = filterParams.Tags?.Select(d => d.Id ?? 0).ToList(), - SortType = filterParams.SortType?.SortType, - SortPropertyName = filterParams.SortProperty?.PropertyName }; var result = await httpClientService.FetchDataAsync>(Endpoints.Game.Fetch(filter)); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs index cbb8439..d78155f 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs @@ -1,4 +1,5 @@ -using GameIdeas.Shared.Dto; +using GameIdeas.BlazorApp.Pages.Games.Filter; +using GameIdeas.Shared.Dto; namespace GameIdeas.BlazorApp.Pages.Games.Gateways; diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Search/SearchInput.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Search/SearchInput.razor.cs index d9e250a..f6521df 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Search/SearchInput.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Search/SearchInput.razor.cs @@ -21,7 +21,7 @@ public partial class SearchInput Text = string.Empty; Timer = new() { - Interval = 1000, + Interval = 500, AutoReset = false, }; diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs index c4a23c0..288e791 100644 --- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs +++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs @@ -44,6 +44,10 @@ public class Translations (TranslationService translationService) public string ErrorFetchGames => translationService.Translate(nameof(ErrorFetchGames)); public string Ascending => translationService.Translate(nameof(Ascending)); public string Descending => translationService.Translate(nameof(Descending)); + public string ErrorStorageSpaceLabel => translationService.Translate(nameof(ErrorStorageSpaceLabel)); + public string MinStorageSpaceFormat => translationService.Translate(nameof(MinStorageSpaceFormat)); + public string MaxStorageSpaceFormat => translationService.Translate(nameof(MaxStorageSpaceFormat)); + public string MinMaxStorageSpaceFormat => translationService.Translate(nameof(MinMaxStorageSpaceFormat)); } public static class ResourcesKey @@ -96,4 +100,8 @@ public static class ResourcesKey public static string ErrorFetchGames => _instance?.ErrorFetchGames ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchGames is not initialized."); public static string Ascending => _instance?.Ascending ?? throw new InvalidOperationException("ResourcesKey.Ascending is not initialized."); public static string Descending => _instance?.Descending ?? throw new InvalidOperationException("ResourcesKey.Descending is not initialized."); + public static string ErrorStorageSpaceLabel => _instance?.ErrorStorageSpaceLabel ?? throw new InvalidOperationException("ResourcesKey.ErrorStorageSpaceLabel is not initialized."); + public static string MinStorageSpaceFormat => _instance?.MinStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MinStorageSpaceFormat is not initialized."); + public static string MaxStorageSpaceFormat => _instance?.MaxStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MaxStorageSpaceFormat is not initialized."); + public static string MinMaxStorageSpaceFormat => _instance?.MinMaxStorageSpaceFormat ?? throw new InvalidOperationException("ResourcesKey.MinMaxStorageSpaceFormat is not initialized."); } \ No newline at end of file diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/CategoriesDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/CategoriesDto.cs index 4d6583f..0136301 100644 --- a/src/GameIdeas/GameIdeas.Shared/Dto/CategoriesDto.cs +++ b/src/GameIdeas/GameIdeas.Shared/Dto/CategoriesDto.cs @@ -7,4 +7,5 @@ public class CategoriesDto public List? Tags { get; set; } public List? Developers { get; set; } public List? Publishers { get; set; } + public List? ReleaseYears { get; set; } } diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/GameFilterDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/GameFilterDto.cs index 7c1fcb2..ef6f3fe 100644 --- a/src/GameIdeas/GameIdeas.Shared/Dto/GameFilterDto.cs +++ b/src/GameIdeas/GameIdeas.Shared/Dto/GameFilterDto.cs @@ -13,9 +13,8 @@ public class GameFilterDto public List? TagIds { get; set; } public List? PublisherIds { get; set; } public List? DeveloperIds { get; set; } - public int MinInterest { get; set; } = 1; - public int MaxInterest { get; set; } = 5; + public int? MinInterest { get; set; } + public int? MaxInterest { get; set; } public List? ReleaseYears { get; set; } - public int? MinStorageSize { get; set; } - public int? MaxStorageSize { get; set; } + public List? StorageSpaces { get; set; } } diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/StorageSpaceDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/StorageSpaceDto.cs new file mode 100644 index 0000000..1e0f96c --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Dto/StorageSpaceDto.cs @@ -0,0 +1,7 @@ +namespace GameIdeas.Shared.Dto; + +public class StorageSpaceDto +{ + public int? MinSize { get; set; } + public int? MaxSize { get; set; } +} \ No newline at end of file diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json index 9d63308..c0bffdf 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json @@ -39,5 +39,9 @@ "Unknown": "Inconnu", "ErrorFetchGames": "Erreur lors de la récupération des jeux", "Ascending": "Ascendant", - "Descending": "Descendant" + "Descending": "Descendant", + "ErrorStorageSpaceLabel": "Erreur lors de la génération des label de l'espace de stockage", + "MinStorageSpaceFormat": "Jusqu'à {0}", + "MaxStorageSpaceFormat": "Plus de {0}", + "MinMaxStorageSpaceFormat": "{0} à {1}" } \ No newline at end of file diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Categories/CategoryService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Categories/CategoryService.cs index 4d7c05d..11562ac 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Categories/CategoryService.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Categories/CategoryService.cs @@ -15,6 +15,9 @@ public class CategoryService(GameIdeasContext context, IMapper mapper) : ICatego var tags = await context.Tags.ToListAsync(); var developers = await context.Developers.ToListAsync(); var publishers = await context.Publishers.ToListAsync(); + var releaseYears = await context.Games + .Where(game => game.ReleaseDate != null) + .Select(game => game.ReleaseDate!.Value.Year).Distinct().ToListAsync(); return new() { @@ -22,7 +25,8 @@ public class CategoryService(GameIdeasContext context, IMapper mapper) : ICatego Properties = mapper.Map>(properties), Tags = mapper.Map>(tags), Developers = mapper.Map>(developers), - Publishers = mapper.Map>(publishers) + Publishers = mapper.Map>(publishers), + ReleaseYears = mapper.Map>(releaseYears) }; } } diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs index 16e80cd..def9582 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs @@ -5,6 +5,7 @@ using GameIdeas.Shared.Exceptions; using GameIdeas.Shared.Model; using GameIdeas.WebAPI.Context; using Microsoft.EntityFrameworkCore; +using System.Linq; using System.Linq.Expressions; namespace GameIdeas.WebAPI.Services.Games; @@ -31,7 +32,7 @@ public class GameReadService(GameIdeasContext context, IMapper mapper) : IGameRe if (!string.IsNullOrWhiteSpace(filter.Title)) { - games = ApplySearchGameFilter(games, filter).ToList(); + games = [.. ApplySearchGameFilter(games, filter)]; } return mapper.Map>(games); @@ -81,6 +82,52 @@ public class GameReadService(GameIdeasContext context, IMapper mapper) : IGameRe query = query.Where(game => filter.PlatformIds.All(plat => game.GamePlatforms.Any(gp => gp.PlatformId == plat))); } + + if (filter.PropertyIds != null) + { + query = query.Where(game => filter.PropertyIds.All(prop => + game.GameProperties.Any(gp => gp.PropertyId == prop))); + } + + if (filter.TagIds != null) + { + query = query.Where(game => filter.TagIds.All(tag => + game.GameTags.Any(gt => gt.TagId == tag))); + } + + if (filter.PublisherIds != null) + { + query = query.Where(game => filter.PublisherIds.All(pub => + game.GamePublishers.Any(gp => gp.PublisherId == pub))); + } + + if (filter.DeveloperIds != null) + { + query = query.Where(game => filter.DeveloperIds.All(dev => + game.GameDevelopers.Any(gd => gd.DeveloperId == dev))); + } + + if (filter.MinInterest != null) + { + query = query.Where(game => game.Interest >= filter.MinInterest); + } + + if (filter.MaxInterest != null) + { + query = query.Where(game => game.Interest <= filter.MaxInterest); + } + + if (filter.StorageSpaces != null) + { + query = query.Where(game => filter.StorageSpaces.Where(stor => + stor.MinSize <= game.StorageSpace && stor.MaxSize > game.StorageSpace).Count() != 0); + } + + if (filter.ReleaseYears != null) + { + query = query.Where(game => game.ReleaseDate != null && + filter.ReleaseYears.Contains(game.ReleaseDate.Value.Year)); + } } private static IEnumerable ApplySearchGameFilter(IEnumerable query, GameFilterDto filter)