diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/SelectSearch/SelectSearch.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/SelectSearch/SelectSearch.razor.cs
index bc2ac8e..44072ce 100644
--- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/SelectSearch/SelectSearch.razor.cs
+++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/SelectSearch/SelectSearch.razor.cs
@@ -15,6 +15,7 @@ public partial class SelectSearch
[Parameter] public string Placeholder { get; set; } = string.Empty;
[Parameter] public bool QuickAdd { get; set; } = false;
[Parameter] public Func? AddItem { get; set; }
+ [Parameter] public SelectType SelectType { get; set; } = SelectType.Multiple;
private SelectParams SelectParams = new();
private SearchInput? SearchInput;
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Icons.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Icons.cs
index d9059d3..2892160 100644
--- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Icons.cs
+++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Icons.cs
@@ -27,4 +27,16 @@ public static class Icons
public readonly static MarkupString Account = new(OpenBraket +
"" +
CloseBraket);
+
+ public readonly static MarkupString Bin = new(OpenBraket +
+ "" +
+ CloseBraket);
+
+ public readonly static MarkupString Pen = new(OpenBraket +
+ "" +
+ CloseBraket);
+
+ public readonly static MarkupString Check = new(OpenBraket +
+ "" +
+ CloseBraket);
}
diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs
index 17274ca..c4e7c03 100644
--- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs
+++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs
@@ -57,6 +57,7 @@ public class Translations (TranslationService translationService)
public string Roles => translationService.Translate(nameof(Roles));
public string ErrorFetchUsers => translationService.Translate(nameof(ErrorFetchUsers));
public string ErrorFetchRoles => translationService.Translate(nameof(ErrorFetchRoles));
+ public string MissingField => translationService.Translate(nameof(MissingField));
}
public static class ResourcesKey
@@ -122,4 +123,5 @@ public static class ResourcesKey
public static string Roles => _instance?.Roles ?? throw new InvalidOperationException("ResourcesKey.Roles is not initialized.");
public static string ErrorFetchUsers => _instance?.ErrorFetchUsers ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchUsers is not initialized.");
public static string ErrorFetchRoles => _instance?.ErrorFetchRoles ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchRoles is not initialized.");
+ public static string MissingField => _instance?.MissingField ?? throw new InvalidOperationException("ResourcesKey.MissingField is not initialized.");
}
\ No newline at end of file
diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs
index 60e1c34..e2ffd7a 100644
--- a/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs
+++ b/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs
@@ -4,7 +4,7 @@ namespace GameIdeas.Shared.Dto;
public class UserDto
{
- public int? Id { get; set; }
+ public string? Id { get; set; }
public string? Username { get; set; }
public string? Password { get; set; }
public RoleDto? Role { get; set; }
diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs
index 8fa1de7..f289c36 100644
--- a/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs
+++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs
@@ -1,6 +1,8 @@
-using GameIdeas.Shared.Dto;
+using GameIdeas.Shared.Constants;
+using GameIdeas.Shared.Dto;
using GameIdeas.WebAPI.Exceptions;
using GameIdeas.WebAPI.Services.Users;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace GameIdeas.WebAPI.Controllers;
@@ -36,4 +38,34 @@ public class UserController(
return StatusCode(500, e.Message);
}
}
+
+ [Authorize(Roles = GlobalConstants.ADMINISTRATOR)]
+ [HttpGet("Roles")]
+ public async Task>> GetRoles()
+ {
+ try
+ {
+ return Ok(await userService.GetRoles());
+ }
+ catch (Exception e)
+ {
+ logger.LogError(e, "Internal error while get roles");
+ return StatusCode(500, e.Message);
+ }
+ }
+
+ [Authorize(Roles = GlobalConstants.ADMINISTRATOR)]
+ [HttpGet]
+ public async Task> GetUsers([FromQuery] UserFilterDto filter)
+ {
+ try
+ {
+ return Ok(await userService.GetUsers(filter));
+ }
+ catch (Exception e)
+ {
+ logger.LogError(e, "Internal error while get users");
+ return StatusCode(500, e.Message);
+ }
+ }
}
diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json
index ce6d164..9ee40a8 100644
--- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json
+++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json
@@ -52,5 +52,6 @@
"UserLogoutFailed": "Déconnection de l'utilisateur échoué",
"Roles": "Rôles",
"ErrorFetchUsers": "Erreur lors de la récupération des utilisateurs",
- "ErrorFetchRoles": "Erreur lors de la récupération des rôles"
+ "ErrorFetchRoles": "Erreur lors de la récupération des rôles",
+ "MissingField": "Un champs est manquant"
}
\ No newline at end of file
diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/UserProfile.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/UserProfile.cs
new file mode 100644
index 0000000..12718ae
--- /dev/null
+++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/UserProfile.cs
@@ -0,0 +1,20 @@
+using AutoMapper;
+using GameIdeas.Shared.Dto;
+using GameIdeas.Shared.Model;
+using Microsoft.AspNetCore.Identity;
+
+namespace GameIdeas.WebAPI.Profiles;
+
+public class UserProfile : Profile
+{
+ public UserProfile()
+ {
+ CreateMap()
+ .ForMember(d => d.Id, o => o.MapFrom(s => s.Id))
+ .ForMember(d => d.Name, o => o.MapFrom(s => s.Name));
+
+ CreateMap()
+ .ForMember(d => d.Id, o => o.MapFrom(s => s.Id))
+ .ForMember(d => d.Username, o => o.MapFrom(s => s.UserName));
+ }
+}
diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs
index 90a6dbf..6b2a3b2 100644
--- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs
+++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs
@@ -72,7 +72,7 @@ public class GameReadService(GameIdeasContext context, IMapper mapper, ICategory
}
}
- private void ApplyFilter(ref IQueryable query, GameFilterDto filter)
+ private static void ApplyFilter(ref IQueryable query, GameFilterDto filter)
{
if (filter.PlatformIds != null)
{
diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserService.cs
index 484ece9..3e826a0 100644
--- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserService.cs
+++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserService.cs
@@ -5,4 +5,6 @@ namespace GameIdeas.WebAPI.Services.Users;
public interface IUserService
{
Task Login(UserDto user);
+ Task> GetRoles();
+ Task GetUsers(UserFilterDto filter);
}
diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserService.cs
index 65058a3..1b5dc5f 100644
--- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserService.cs
+++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserService.cs
@@ -1,9 +1,12 @@
-using GameIdeas.Resources;
+using AutoMapper;
+using GameIdeas.Resources;
using GameIdeas.Shared.Constants;
using GameIdeas.Shared.Dto;
using GameIdeas.Shared.Model;
+using GameIdeas.WebAPI.Context;
using GameIdeas.WebAPI.Exceptions;
using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
@@ -11,8 +14,95 @@ using System.Text;
namespace GameIdeas.WebAPI.Services.Users;
-public class UserService(UserManager userManager) : IUserService
+public class UserService(
+ UserManager userManager,
+ GameIdeasContext context,
+ IMapper mapper) : IUserService
{
+ public async Task> GetRoles()
+ {
+ var roles = await context.Roles
+ .ToListAsync();
+
+ return mapper.Map>(roles);
+ }
+
+ public async Task GetUsers(UserFilterDto filter)
+ {
+ var users = await context.Users
+ .OrderBy(u => u.UserName)
+ .ToListAsync();
+
+ var count = users.Count;
+ ApplyStaticFilter(ref users, filter);
+
+ var usersDto = mapper.Map>(users);
+ usersDto = await ApplyRoles(usersDto, filter);
+
+ return new UserListDto
+ {
+ Users = usersDto,
+ UsersCount = count
+ };
+ }
+
+ private async Task> ApplyRoles(IEnumerable users, UserFilterDto filter)
+ {
+ var userRolesQuery = context.UserRoles
+ .Where(ur => users.Select(u => u.Id).Contains(ur.UserId))
+ .AsQueryable();
+
+ var rolesQuery = context.Roles.AsQueryable();
+
+ if (filter.RoleIds != null)
+ {
+ userRolesQuery = userRolesQuery
+ .Where(ur => filter.RoleIds.Contains(ur.RoleId));
+
+ rolesQuery = rolesQuery.Where(role => filter.RoleIds.Contains(role.Id));
+ }
+
+ var roles = await rolesQuery.ToListAsync();
+ var userRoles = await userRolesQuery.ToListAsync();
+ users = users.Where(user => userRoles.Select(ur => ur.UserId).Contains(user.Id));
+
+ foreach (var user in users)
+ {
+ var currentRoleId = userRoles.FirstOrDefault(ur => ur.UserId == user.Id)?.RoleId;
+
+ if (currentRoleId == null)
+ {
+ continue;
+ }
+
+ var currentRole = roles.FirstOrDefault(r => r.Id == currentRoleId);
+ user.Role = mapper.Map(currentRole);
+ }
+
+ return users;
+ }
+
+ private static void ApplyStaticFilter(ref List users, UserFilterDto filter)
+ {
+ if (!string.IsNullOrWhiteSpace(filter.Name))
+ {
+ var keywords = filter.Name?
+ .Split([' '], StringSplitOptions.RemoveEmptyEntries)
+ .Select(k => k.Trim())
+ .ToArray() ?? [];
+
+ users = users
+ .Where(user => keywords.All(
+ kw => user.UserName?.Contains(kw, StringComparison.OrdinalIgnoreCase) ?? true
+ ))
+ .OrderBy(user => keywords.Min(kw =>
+ user.UserName?.IndexOf(kw, StringComparison.OrdinalIgnoreCase)
+ ))
+ .ThenBy(user => user.UserName?.Length)
+ .ToList();
+ }
+ }
+
public async Task Login(UserDto userDto)
{
if (userDto.Username == null || userDto.Password == null)