feature/apply-filter #18

Merged
Egamorf merged 11 commits from feature/apply-filter into main 2025-04-20 15:43:24 +02:00
13 changed files with 67 additions and 58 deletions
Showing only changes of commit 436fe3b050 - Show all commits

View File

@@ -25,8 +25,8 @@
<SelectSearch TItem="int" Placeholder="@ResourcesKey.ReleaseDate" GetLabel="@(p => p.ToString())" <SelectSearch TItem="int" Placeholder="@ResourcesKey.ReleaseDate" GetLabel="@(p => p.ToString())"
@bind-Values=GameFilter.ReleaseYears @bind-Values:after=HandleValueChanged Theme="Theme" Items="Categories?.ReleaseYears" /> @bind-Values=GameFilter.ReleaseYears @bind-Values:after=HandleValueChanged Theme="Theme" Items="Categories?.ReleaseYears" />
<SelectSearch TItem="StorageSpaceDto" Placeholder="@ResourcesKey.StorageSize" GetLabel="@GetStorageSpaceLabel" <SelectSearch TItem="int" Placeholder="@ResourcesKey.StorageSize" GetLabel="@GetStorageSpaceLabel"
@bind-Values=GameFilter.StorageSpaces @bind-Values:after=HandleValueChanged Theme="Theme" Items="StorageSpaces" /> @bind-Values=GameFilter.StorageSpaceIds @bind-Values:after=HandleValueChanged Theme="Theme" Items="@(Categories?.StorageSpaces?.Select(stor => stor.Id).ToList())" />
<span class="title">@ResourcesKey.LastAdd</span> <span class="title">@ResourcesKey.LastAdd</span>
</div> </div>

View File

@@ -14,34 +14,27 @@ public partial class AdvancedGameFilter
[Parameter] public EventCallback<GameFilterParams> GameFilterChanged { get; set; } [Parameter] public EventCallback<GameFilterParams> GameFilterChanged { get; set; }
private readonly SelectTheme Theme = SelectTheme.AdvancedFilter; private readonly SelectTheme Theme = SelectTheme.AdvancedFilter;
private readonly List<StorageSpaceDto> 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() private async Task HandleValueChanged()
{ {
await GameFilterChanged.InvokeAsync(GameFilter); await GameFilterChanged.InvokeAsync(GameFilter);
} }
private static string GetStorageSpaceLabel(StorageSpaceDto storageSpace) private string GetStorageSpaceLabel(int storageSpaceId)
{ {
var storageSpace = Categories?.StorageSpaces?.FirstOrDefault(c => c.Id == storageSpaceId)
?? throw new ArgumentNullException(ResourcesKey.ErrorStorageSpaceLabel);
if (storageSpace.MinSize == null && storageSpace.MaxSize != null) if (storageSpace.MinSize == null && storageSpace.MaxSize != null)
{ {
return string.Format(ResourcesKey.MinStorageSpaceFormat, return string.Format(ResourcesKey.MinStorageSpaceFormat,
GameHelper.GetFormatedStorageSpace(storageSpace.MinSize)); GameHelper.GetFormatedStorageSpace(storageSpace.MaxSize));
} }
if (storageSpace.MinSize != null && storageSpace.MaxSize == null) if (storageSpace.MinSize != null && storageSpace.MaxSize == null)
{ {
return string.Format(ResourcesKey.MaxStorageSpaceFormat, return string.Format(ResourcesKey.MaxStorageSpaceFormat,
GameHelper.GetFormatedStorageSpace(storageSpace.MaxSize)); GameHelper.GetFormatedStorageSpace(storageSpace.MinSize));
} }
if (storageSpace.MinSize != null && storageSpace.MaxSize != null) if (storageSpace.MinSize != null && storageSpace.MaxSize != null)

View File

@@ -12,8 +12,8 @@ public class GameFilterParams
public List<TagDto>? Tags { get; set; } public List<TagDto>? Tags { get; set; }
public List<PublisherDto>? Publishers { get; set; } public List<PublisherDto>? Publishers { get; set; }
public List<DeveloperDto>? Developers { get; set; } public List<DeveloperDto>? Developers { get; set; }
public int? MinInterest { get; set; } public int MinInterest { get; set; } = 1;
public int? MaxInterest { get; set; } public int MaxInterest { get; set; } = 5;
public List<int>? ReleaseYears { get; set; } public List<int>? ReleaseYears { get; set; }
public List<StorageSpaceDto>? StorageSpaces { get; set; } public List<int>? StorageSpaceIds { get; set; }
} }

View File

@@ -45,7 +45,7 @@ public class GameGateway(IHttpClientService httpClientService) : IGameGateway
Title = filterParams.Title, Title = filterParams.Title,
MaxInterest = filterParams.MaxInterest, MaxInterest = filterParams.MaxInterest,
MinInterest = filterParams.MinInterest, MinInterest = filterParams.MinInterest,
StorageSpaces = filterParams.StorageSpaces, StorageSpaces = filterParams.StorageSpaceIds,
DeveloperIds = filterParams.Developers?.Select(d => d.Id ?? 0).ToList(), DeveloperIds = filterParams.Developers?.Select(d => d.Id ?? 0).ToList(),
PublisherIds = filterParams.Publishers?.Select(d => d.Id ?? 0).ToList(), PublisherIds = filterParams.Publishers?.Select(d => d.Id ?? 0).ToList(),
PlatformIds = filterParams.Platforms?.Select(d => d.Id ?? 0).ToList(), PlatformIds = filterParams.Platforms?.Select(d => d.Id ?? 0).ToList(),

View File

@@ -6,10 +6,10 @@ namespace GameIdeas.BlazorApp.Shared.Components.SliderRange;
public partial class SliderRange public partial class SliderRange
{ {
[Parameter] public SliderRangeParams Params { get; set; } = new(); [Parameter] public SliderRangeParams Params { get; set; } = new();
[Parameter] public int? Max { get; set; } [Parameter] public int Max { get; set; }
[Parameter] public EventCallback<int?> MaxChanged { get; set; } [Parameter] public EventCallback<int> MaxChanged { get; set; }
[Parameter] public int? Min { get; set; } [Parameter] public int Min { get; set; }
[Parameter] public EventCallback<int?> MinChanged { get; set; } [Parameter] public EventCallback<int> MinChanged { get; set; }
private async Task HandleSlideTwoInput() private async Task HandleSlideTwoInput()
{ {
@@ -33,12 +33,12 @@ public partial class SliderRange
private string FillColor() private string FillColor()
{ {
var percent1 = (double)(Min! - Params.Min) / (Params.Max - Params.Min) * 100; var percent1 = (double)(Min - Params.Min) / (Params.Max - Params.Min) * 100;
var percent2 = (double)(Max! - Params.Min) / (Params.Max - Params.Min) * 100; var percent2 = (double)(Max - Params.Min) / (Params.Max - Params.Min) * 100;
return $"background: linear-gradient(to right, var(--line) {percent1}% , var(--violet) {percent1}% , var(--violet) {percent2}%, var(--line) {percent2}%)"; return $"background: linear-gradient(to right, var(--line) {percent1}% , var(--violet) {percent1}% , var(--violet) {percent2}%, var(--line) {percent2}%)";
} }
private string StatusColor(int? value) private string StatusColor(int value)
{ {
string str = "--thumb-color: var({0});"; string str = "--thumb-color: var({0});";

View File

@@ -8,4 +8,5 @@ public class CategoriesDto
public List<DeveloperDto>? Developers { get; set; } public List<DeveloperDto>? Developers { get; set; }
public List<PublisherDto>? Publishers { get; set; } public List<PublisherDto>? Publishers { get; set; }
public List<int>? ReleaseYears { get; set; } public List<int>? ReleaseYears { get; set; }
public List<StorageSpaceDto>? StorageSpaces { get; set; }
} }

View File

@@ -16,5 +16,5 @@ public class GameFilterDto
public int? MinInterest { get; set; } public int? MinInterest { get; set; }
public int? MaxInterest { get; set; } public int? MaxInterest { get; set; }
public List<int>? ReleaseYears { get; set; } public List<int>? ReleaseYears { get; set; }
public List<StorageSpaceDto>? StorageSpaces { get; set; } public List<int>? StorageSpaces { get; set; }
} }

View File

@@ -4,4 +4,5 @@ public class StorageSpaceDto
{ {
public int? MinSize { get; set; } public int? MinSize { get; set; }
public int? MaxSize { get; set; } public int? MaxSize { get; set; }
public int Id { get; set; }
} }

View File

@@ -1,5 +1,5 @@
using GameIdeas.Shared.Dto; using GameIdeas.Shared.Dto;
using GameIdeas.WebAPI.Services.Interfaces; using GameIdeas.WebAPI.Services.Categories;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace GameIdeas.WebAPI.Controllers; namespace GameIdeas.WebAPI.Controllers;

View File

@@ -1,11 +1,8 @@
using GameIdeas.Resources; using GameIdeas.Resources;
using GameIdeas.WebAPI.Context; using GameIdeas.WebAPI.Context;
using GameIdeas.WebAPI.Profiles;
using GameIdeas.WebAPI.Services.Categories; using GameIdeas.WebAPI.Services.Categories;
using GameIdeas.WebAPI.Services.Games; using GameIdeas.WebAPI.Services.Games;
using GameIdeas.WebAPI.Services.Interfaces;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
var services = builder.Services; var services = builder.Services;

View File

@@ -1,13 +1,23 @@
using AutoMapper; using AutoMapper;
using GameIdeas.Shared.Dto; using GameIdeas.Shared.Dto;
using GameIdeas.WebAPI.Context; using GameIdeas.WebAPI.Context;
using GameIdeas.WebAPI.Services.Interfaces;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace GameIdeas.WebAPI.Services.Categories; namespace GameIdeas.WebAPI.Services.Categories;
public class CategoryService(GameIdeasContext context, IMapper mapper) : ICategoryService public class CategoryService(GameIdeasContext context, IMapper mapper) : ICategoryService
{ {
public List<StorageSpaceDto> GetStorageSpaces() => [
new() { Id = 1, MaxSize = 100 },
new() { Id = 2, MinSize = 100, MaxSize = 1000 },
new() { Id = 3, MinSize = 1000, MaxSize = 5000 },
new() { Id = 4, MinSize = 5000, MaxSize = 10000 },
new() { Id = 5, MinSize = 10000, MaxSize = 20000 },
new() { Id = 6, MinSize = 20000, MaxSize = 40000 },
new() { Id = 7, MinSize = 40000, MaxSize = 100000 },
new() { Id = 8, MinSize = 100000 },
];
public async Task<CategoriesDto> GetCategories() public async Task<CategoriesDto> GetCategories()
{ {
var platforms = await context.Platforms.ToListAsync(); var platforms = await context.Platforms.ToListAsync();
@@ -26,7 +36,8 @@ public class CategoryService(GameIdeasContext context, IMapper mapper) : ICatego
Tags = mapper.Map<List<TagDto>>(tags), Tags = mapper.Map<List<TagDto>>(tags),
Developers = mapper.Map<List<DeveloperDto>>(developers), Developers = mapper.Map<List<DeveloperDto>>(developers),
Publishers = mapper.Map<List<PublisherDto>>(publishers), Publishers = mapper.Map<List<PublisherDto>>(publishers),
ReleaseYears = mapper.Map<List<int>>(releaseYears) ReleaseYears = mapper.Map<List<int>>(releaseYears),
StorageSpaces = GetStorageSpaces()
}; };
} }
} }

View File

@@ -1,8 +1,9 @@
using GameIdeas.Shared.Dto; using GameIdeas.Shared.Dto;
namespace GameIdeas.WebAPI.Services.Interfaces; namespace GameIdeas.WebAPI.Services.Categories;
public interface ICategoryService public interface ICategoryService
{ {
List<StorageSpaceDto> GetStorageSpaces();
Task<CategoriesDto> GetCategories(); Task<CategoriesDto> GetCategories();
} }

View File

@@ -4,13 +4,13 @@ using GameIdeas.Shared.Dto;
using GameIdeas.Shared.Exceptions; using GameIdeas.Shared.Exceptions;
using GameIdeas.Shared.Model; using GameIdeas.Shared.Model;
using GameIdeas.WebAPI.Context; using GameIdeas.WebAPI.Context;
using GameIdeas.WebAPI.Services.Categories;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
namespace GameIdeas.WebAPI.Services.Games; namespace GameIdeas.WebAPI.Services.Games;
public class GameReadService(GameIdeasContext context, IMapper mapper) : IGameReadService public class GameReadService(GameIdeasContext context, IMapper mapper, ICategoryService categoryService) : IGameReadService
{ {
public async Task<IEnumerable<GameDto>> GetGames(GameFilterDto filter) public async Task<IEnumerable<GameDto>> GetGames(GameFilterDto filter)
{ {
@@ -30,10 +30,7 @@ public class GameReadService(GameIdeasContext context, IMapper mapper) : IGameRe
.Take(GlobalConstants.NUMBER_PER_PAGE) .Take(GlobalConstants.NUMBER_PER_PAGE)
.ToListAsync(); .ToListAsync();
if (!string.IsNullOrWhiteSpace(filter.Title)) ApplyStaticFilter(ref games, filter);
{
games = [.. ApplySearchGameFilter(games, filter)];
}
return mapper.Map<IEnumerable<GameDto>>(games); return mapper.Map<IEnumerable<GameDto>>(games);
} }
@@ -75,7 +72,7 @@ public class GameReadService(GameIdeasContext context, IMapper mapper) : IGameRe
} }
} }
private static void ApplyFilter(ref IQueryable<Game> query, GameFilterDto filter) private void ApplyFilter(ref IQueryable<Game> query, GameFilterDto filter)
{ {
if (filter.PlatformIds != null) if (filter.PlatformIds != null)
{ {
@@ -117,12 +114,6 @@ public class GameReadService(GameIdeasContext context, IMapper mapper) : IGameRe
query = query.Where(game => game.Interest <= filter.MaxInterest); 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) if (filter.ReleaseYears != null)
{ {
query = query.Where(game => game.ReleaseDate != null && query = query.Where(game => game.ReleaseDate != null &&
@@ -130,22 +121,36 @@ public class GameReadService(GameIdeasContext context, IMapper mapper) : IGameRe
} }
} }
private static IEnumerable<Game> ApplySearchGameFilter(IEnumerable<Game> query, GameFilterDto filter) private void ApplyStaticFilter(ref List<Game> games, GameFilterDto filter)
{ {
var keywords = filter.Title? if (!string.IsNullOrWhiteSpace(filter.Title))
{
var keywords = filter.Title?
.Split([' '], StringSplitOptions.RemoveEmptyEntries) .Split([' '], StringSplitOptions.RemoveEmptyEntries)
.Select(k => k.Trim()) .Select(k => k.Trim())
.ToArray() ?? []; .ToArray() ?? [];
query = query games = games
.Where(game => keywords.All( .Where(game => keywords.All(
kw => game.Title.Contains(kw, StringComparison.OrdinalIgnoreCase) kw => game.Title.Contains(kw, StringComparison.OrdinalIgnoreCase)
)) ))
.OrderBy(game => keywords.Min(kw => .OrderBy(game => keywords.Min(kw =>
game.Title.IndexOf(kw, StringComparison.OrdinalIgnoreCase) game.Title.IndexOf(kw, StringComparison.OrdinalIgnoreCase)
)) ))
.ThenBy(game => game.Title.Length); .ThenBy(game => game.Title.Length)
.ToList();
return query; return;
}
if (filter.StorageSpaces != null)
{
var storageSpaces = categoryService.GetStorageSpaces().Where(stor => filter.StorageSpaces.Contains(stor.Id));
games = games
.Where(game => storageSpaces.Any(stor =>
(stor.MinSize ?? int.MinValue) <= game.StorageSpace && (stor.MaxSize ?? int.MaxValue) > game.StorageSpace))
.ToList();
}
} }
} }