From 9d0d5ec599ee1b98e7f08b42238289d0c994ad49 Mon Sep 17 00:00:00 2001 From: Egamorf Date: Mon, 21 Apr 2025 19:58:06 +0200 Subject: [PATCH 01/11] Renaming folder --- .../Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs | 8 ++------ .../Pages/Games/Header/GameHeader.razor | 2 +- .../Pages/{User => UserMenu}/Components/Login.razor | 0 .../Pages/{User => UserMenu}/Components/Login.razor.cs | 4 ++-- .../Pages/{User => UserMenu}/Components/Login.razor.css | 0 .../Pages/{User => UserMenu}/Components/LoginValidator.cs | 2 +- .../Pages/{User => UserMenu}/Gateways/AuthGateway.cs | 2 +- .../Pages/{User => UserMenu}/Gateways/IAuthGateway.cs | 2 +- .../Pages/{User => UserMenu}/UserMenu.razor | 2 +- .../Pages/{User => UserMenu}/UserMenu.razor.cs | 6 ++---- .../Pages/{User => UserMenu}/UserMenu.razor.css | 0 src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs | 2 +- 12 files changed, 12 insertions(+), 18 deletions(-) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/Components/Login.razor (100%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/Components/Login.razor.cs (89%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/Components/Login.razor.css (100%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/Components/LoginValidator.cs (85%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/Gateways/AuthGateway.cs (95%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/Gateways/IAuthGateway.cs (69%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/UserMenu.razor (96%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/UserMenu.razor.cs (73%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/{User => UserMenu}/UserMenu.razor.css (100%) diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs index 181281d..f33ea26 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Helpers/GameHelper.cs @@ -13,12 +13,8 @@ public static class GameHelper throw new ArgumentNullException(nameof(authState), "Authentication state missing"); } - var userId = authState.User.FindFirstValue(ClaimTypes.Sid); - - if (userId == null) - { - throw new ArgumentNullException(nameof(authState), "user state missing"); - } + var userId = authState.User.FindFirstValue(ClaimTypes.Sid) + ?? throw new ArgumentNullException(nameof(authState), "user state missing"); game.CreationUserId = userId; game.CreationDate = DateTime.Now; diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor index 0516759..e51f11b 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor @@ -1,5 +1,5 @@ @using GameIdeas.BlazorApp.Pages.Games -@using GameIdeas.BlazorApp.Pages.User +@using GameIdeas.BlazorApp.Pages.UserMenu @using GameIdeas.BlazorApp.Shared.Components.Select @using GameIdeas.BlazorApp.Shared.Components.Select.Models @using GameIdeas.BlazorApp.Shared.Models diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/Login.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor similarity index 100% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/Login.razor rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/Login.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor.cs similarity index 89% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/Login.razor.cs rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor.cs index da55a33..9d935ab 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/Login.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor.cs @@ -1,9 +1,9 @@ -using GameIdeas.BlazorApp.Pages.User.Gateways; +using GameIdeas.BlazorApp.Pages.UserMenu.Gateways; using GameIdeas.Shared.Dto; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; -namespace GameIdeas.BlazorApp.Pages.User.Components; +namespace GameIdeas.BlazorApp.Pages.UserMenu.Components; public partial class Login { diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/Login.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor.css similarity index 100% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/Login.razor.css rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor.css diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/LoginValidator.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/LoginValidator.cs similarity index 85% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/LoginValidator.cs rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/LoginValidator.cs index 13c7046..8ff7aaa 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Components/LoginValidator.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/LoginValidator.cs @@ -1,7 +1,7 @@ using FluentValidation; using GameIdeas.Shared.Dto; -namespace GameIdeas.BlazorApp.Pages.User.Components; +namespace GameIdeas.BlazorApp.Pages.UserMenu.Components; public class LoginValidator : AbstractValidator { diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Gateways/AuthGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Gateways/AuthGateway.cs similarity index 95% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Gateways/AuthGateway.cs rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Gateways/AuthGateway.cs index ea2de5c..4753e96 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Gateways/AuthGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Gateways/AuthGateway.cs @@ -5,7 +5,7 @@ using GameIdeas.Resources; using GameIdeas.Shared.Dto; using Microsoft.AspNetCore.Components.Authorization; -namespace GameIdeas.BlazorApp.Pages.User.Gateways; +namespace GameIdeas.BlazorApp.Pages.UserMenu.Gateways; public class AuthGateway(IHttpClientService httpClient, AuthenticationStateProvider stateProvider) : IAuthGateway diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Gateways/IAuthGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Gateways/IAuthGateway.cs similarity index 69% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Gateways/IAuthGateway.cs rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Gateways/IAuthGateway.cs index c3d2620..9ded24c 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/Gateways/IAuthGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Gateways/IAuthGateway.cs @@ -1,6 +1,6 @@ using GameIdeas.Shared.Dto; -namespace GameIdeas.BlazorApp.Pages.User.Gateways; +namespace GameIdeas.BlazorApp.Pages.UserMenu.Gateways; public interface IAuthGateway { diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/UserMenu.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor similarity index 96% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/UserMenu.razor rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor index 49c6910..b46f17a 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/UserMenu.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor @@ -1,4 +1,4 @@ -@using GameIdeas.BlazorApp.Pages.User.Components +@using GameIdeas.BlazorApp.Pages.UserMenu.Components @using GameIdeas.BlazorApp.Shared.Components.BackdropFilter @using GameIdeas.BlazorApp.Shared.Constants @using GameIdeas.Shared.Constants diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/UserMenu.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.cs similarity index 73% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/UserMenu.razor.cs rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.cs index 33e6c07..cd40676 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/UserMenu.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.cs @@ -1,9 +1,7 @@ -using GameIdeas.BlazorApp.Pages.User.Gateways; -using GameIdeas.BlazorApp.Services; +using GameIdeas.BlazorApp.Pages.UserMenu.Gateways; using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Authorization; -namespace GameIdeas.BlazorApp.Pages.User; +namespace GameIdeas.BlazorApp.Pages.UserMenu; public partial class UserMenu { diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/UserMenu.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.css similarity index 100% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/User/UserMenu.razor.css rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.css diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs index 647dc15..4edf1f7 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs @@ -2,7 +2,7 @@ using System.Net.Http.Json; using Blazored.LocalStorage; using GameIdeas.BlazorApp; using GameIdeas.BlazorApp.Pages.Games.Gateways; -using GameIdeas.BlazorApp.Pages.User.Gateways; +using GameIdeas.BlazorApp.Pages.UserMenu.Gateways; using GameIdeas.BlazorApp.Services; using GameIdeas.Resources; using Microsoft.AspNetCore.Components.Authorization; -- 2.39.5 From cec1d551af76615090595c549a2964cddf229a00 Mon Sep 17 00:00:00 2001 From: Egamorf Date: Mon, 21 Apr 2025 21:16:12 +0200 Subject: [PATCH 02/11] Initial page --- .../Pages/Games/{Game.razor => Games.razor} | 0 .../Games/{Game.razor.cs => Games.razor.cs} | 2 +- .../Games/{Game.razor.css => Games.razor.css} | 0 .../Pages/Games/Header/GameHeader.razor | 35 +++++++++------- .../Pages/Games/Header/GameHeader.razor.cs | 5 ++- .../Pages/UserMenu/UserMenu.razor | 4 +- .../Pages/UserMenu/UserMenu.razor.css | 2 + .../Pages/Users/Users.razor | 42 +++++++++++++++++++ .../Pages/Users/Users.razor.cs | 16 +++++++ .../Pages/Users/Users.razor.css | 1 + src/GameIdeas/GameIdeas.Shared/Dto/RoleDto.cs | 7 ++++ src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs | 2 +- 12 files changed, 94 insertions(+), 22 deletions(-) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/{Game.razor => Games.razor} (100%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/{Game.razor.cs => Games.razor.cs} (98%) rename src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/{Game.razor.css => Games.razor.css} (100%) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css create mode 100644 src/GameIdeas/GameIdeas.Shared/Dto/RoleDto.cs diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor similarity index 100% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs similarity index 98% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.cs rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs index a8b0ae3..d165867 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Components; namespace GameIdeas.BlazorApp.Pages.Games; -public partial class Game +public partial class Games { [Inject] private IGameGateway GameGateway { get; set; } = default!; diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.css similarity index 100% rename from src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Game.razor.css rename to src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor.css diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor index e51f11b..95627b2 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor @@ -17,25 +17,28 @@ @ChildContent + + + } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs index 36b8e79..790268e 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs @@ -8,7 +8,8 @@ namespace GameIdeas.BlazorApp.Pages.Games.Header; public partial class GameHeader : ComponentBase { - [Parameter] public EventCallback AddTypeChanged { get; set; } + [Parameter] public bool DisplayAdd { get; set; } = true; + [Parameter] public EventCallback? AddTypeChanged { get; set; } [Parameter] public RenderFragment? ChildContent { get; set; } @@ -39,6 +40,6 @@ public partial class GameHeader : ComponentBase private async Task HandleAddTypeClicked(IEnumerable> values) { SelectListAdd?.Close(); - await AddTypeChanged.InvokeAsync(values.FirstOrDefault().Key); + await AddTypeChanged?.InvokeAsync(values.FirstOrDefault().Key)!; } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor index b46f17a..336d471 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor @@ -23,9 +23,9 @@ - + diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.css index e8a4918..8c8cdbb 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/UserMenu.razor.css @@ -36,6 +36,8 @@ } .menu-element { + color: var(--white); + text-decoration: none; height: 32px; padding: 0 20px; align-content: center; diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor new file mode 100644 index 0000000..880af56 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor @@ -0,0 +1,42 @@ +@page "/Users" +@using GameIdeas.BlazorApp.Pages.Games.Header +@using GameIdeas.BlazorApp.Layouts +@using GameIdeas.BlazorApp.Shared.Components.Popup +@using GameIdeas.BlazorApp.Shared.Components.Search +@using GameIdeas.BlazorApp.Shared.Components.SelectSearch +@using GameIdeas.Shared.Dto + +@layout MainLayout + +@ResourcesKey.GamesIdeas + + +
+ + +
+
+ +
+
+ @if (!IsLoading) + { + @foreach (var user in UsersDto) + { + + } + } + else + { + @for (int i = 0; i < 20; i++) + { + + } + } + +
+
+ + + + \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs new file mode 100644 index 0000000..339a8fd --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs @@ -0,0 +1,16 @@ +using GameIdeas.BlazorApp.Shared.Components.Popup; +using GameIdeas.Shared.Dto; + +namespace GameIdeas.BlazorApp.Pages.Users; + +public partial class Users +{ + private Popup? Popup; + private bool IsLoading = false; + private IEnumerable UsersDto = []; + + private void HandleBackdropClicked() + { + Popup?.Close(); + } +} \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/RoleDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/RoleDto.cs new file mode 100644 index 0000000..021d7d5 --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Dto/RoleDto.cs @@ -0,0 +1,7 @@ +namespace GameIdeas.Shared.Dto; + +public class RoleDto +{ + public string Id { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; +} diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs index 11d6da0..60e1c34 100644 --- a/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs +++ b/src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs @@ -7,5 +7,5 @@ public class UserDto public int? Id { get; set; } public string? Username { get; set; } public string? Password { get; set; } - public string? RoleId { get; set; } + public RoleDto? Role { get; set; } } -- 2.39.5 From ea9fc001f332a5d807fe5d01849a39350ec139ff Mon Sep 17 00:00:00 2001 From: Egamorf Date: Mon, 21 Apr 2025 21:38:44 +0200 Subject: [PATCH 03/11] Add style to users page --- .../Pages/Games/Header/GameHeader.razor.cs | 4 ++-- .../GameIdeas.BlazorApp/Pages/Users/Users.razor | 4 ++-- .../GameIdeas.BlazorApp/Pages/Users/Users.razor.css | 11 ++++++++++- .../GameIdeas.Resources/CreateStaticResourceKey.cs | 2 ++ .../Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json | 3 ++- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs index 790268e..df01ce4 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs @@ -9,7 +9,7 @@ namespace GameIdeas.BlazorApp.Pages.Games.Header; public partial class GameHeader : ComponentBase { [Parameter] public bool DisplayAdd { get; set; } = true; - [Parameter] public EventCallback? AddTypeChanged { get; set; } + [Parameter] public EventCallback AddTypeChanged { get; set; } [Parameter] public RenderFragment? ChildContent { get; set; } @@ -40,6 +40,6 @@ public partial class GameHeader : ComponentBase private async Task HandleAddTypeClicked(IEnumerable> values) { SelectListAdd?.Close(); - await AddTypeChanged?.InvokeAsync(values.FirstOrDefault().Key)!; + await AddTypeChanged.InvokeAsync(values.FirstOrDefault().Key); } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor index 880af56..1ea8a7d 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor @@ -12,8 +12,8 @@
- - + +
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css index 5f28270..d839b81 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css @@ -1 +1,10 @@ - \ No newline at end of file +.header-content { + display: flex; + flex-direction: row; + gap: 8px; +} + +::deep.header-content>* { + box-sizing: border-box; + width: 200px; +} diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs index d2494b2..303659a 100644 --- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs +++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs @@ -54,6 +54,7 @@ public class Translations (TranslationService translationService) public string UserUnauthorized => translationService.Translate(nameof(UserUnauthorized)); public string UserLoginFailed => translationService.Translate(nameof(UserLoginFailed)); public string UserLogoutFailed => translationService.Translate(nameof(UserLogoutFailed)); + public string Roles => translationService.Translate(nameof(Roles)); } public static class ResourcesKey @@ -116,4 +117,5 @@ public static class ResourcesKey public static string UserUnauthorized => _instance?.UserUnauthorized ?? throw new InvalidOperationException("ResourcesKey.UserUnauthorized is not initialized."); public static string UserLoginFailed => _instance?.UserLoginFailed ?? throw new InvalidOperationException("ResourcesKey.UserLoginFailed is not initialized."); public static string UserLogoutFailed => _instance?.UserLogoutFailed ?? throw new InvalidOperationException("ResourcesKey.UserLogoutFailed is not initialized."); + public static string Roles => _instance?.Roles ?? throw new InvalidOperationException("ResourcesKey.Roles is not initialized."); } \ 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 7296e28..9b9e68f 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json @@ -49,5 +49,6 @@ "InvalidToken": "Le token JWT est invalide", "UserUnauthorized": "Utilisateur non authorisé", "UserLoginFailed": "Authentification de l'utilisateur échoué", - "UserLogoutFailed": "Déconnection de l'utilisateur échoué" + "UserLogoutFailed": "Déconnection de l'utilisateur échoué", + "Roles": "Rôles" } \ No newline at end of file -- 2.39.5 From 6d109d00e609c3af755d86b613127fb2c1c047c1 Mon Sep 17 00:00:00 2001 From: Egamorf Date: Tue, 22 Apr 2025 21:29:50 +0200 Subject: [PATCH 04/11] Add gateway and filter --- .../Pages/Games/Gateways/GameGateway.cs | 2 +- .../Pages/Users/Gateways/IUserGateway.cs | 8 +++++ .../Pages/Users/Gateways/UserGateway.cs | 29 +++++++++++++++++++ .../Shared/Constants/Endpoints.cs | 11 +++++-- .../Exceptions/UserNotFoundException.cs | 3 ++ .../GameIdeas.Shared/Dto/UserFilterDto.cs | 8 +++++ .../GameIdeas.Shared/Dto/UserListDto.cs | 7 +++++ 7 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/UserNotFoundException.cs create mode 100644 src/GameIdeas/GameIdeas.Shared/Dto/UserFilterDto.cs create mode 100644 src/GameIdeas/GameIdeas.Shared/Dto/UserListDto.cs 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 21148a3..6494a4f 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs @@ -59,7 +59,7 @@ public class GameGateway(IHttpClientService httpClientService) : IGameGateway } catch (Exception) { - throw new CategoryNotFoundException(ResourcesKey.ErrorFetchGames); + throw new GameNotFoundException(ResourcesKey.ErrorFetchGames); } } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs new file mode 100644 index 0000000..0b9be82 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs @@ -0,0 +1,8 @@ +using GameIdeas.Shared.Dto; + +namespace GameIdeas.BlazorApp.Pages.Users.Gateways; + +public interface IUserGateway +{ + Task GetUsers(UserFilterParams filterParams); +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs new file mode 100644 index 0000000..c9120e7 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs @@ -0,0 +1,29 @@ +using GameIdeas.BlazorApp.Services; +using GameIdeas.BlazorApp.Shared.Constants; +using GameIdeas.BlazorApp.Shared.Exceptions; +using GameIdeas.Resources; +using GameIdeas.Shared.Dto; + +namespace GameIdeas.BlazorApp.Pages.Users.Gateways; + +public class UserGateway(IHttpClientService httpClient) : IUserGateway +{ + public async Task GetUsers(UserFilterParams filterParams) + { + try + { + UserFilterDto filter = new() + { + + }; + + var url = Endpoints.User.Fetch(filter); + return await httpClient.FetchDataAsync(url) + ?? throw new InvalidOperationException(ResourcesKey.ErrorFetchUsers); + } + catch (Exception) + { + throw new UserNotFoundException(ResourcesKey.ErrorFetchUsers); + } + } +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs index 0c45383..65bc47c 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs @@ -7,17 +7,22 @@ public static class Endpoints { public static class Game { - public static readonly string Create = "api/Game/Create"; + public const string Create = "api/Game/Create"; public static string Fetch(GameFilterDto filter) => $"api/Game?{UrlHelper.BuildUrlParams(filter)}"; } public static class Category { - public static readonly string AllCategories = "api/Category/All"; + public const string AllCategories = "api/Category/All"; } public static class Auth { - public static readonly string Login = "api/User/Login"; + public const string Login = "api/User/Login"; + } + + public static class User + { + public static string Fetch(UserFilterDto filter) => $"api/User?{UrlHelper.BuildUrlParams(filter)}"; } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/UserNotFoundException.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/UserNotFoundException.cs new file mode 100644 index 0000000..11ef9aa --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/UserNotFoundException.cs @@ -0,0 +1,3 @@ +namespace GameIdeas.BlazorApp.Shared.Exceptions; + +public class UserNotFoundException(string message) : Exception(message); diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/UserFilterDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/UserFilterDto.cs new file mode 100644 index 0000000..2d8eed3 --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Dto/UserFilterDto.cs @@ -0,0 +1,8 @@ +namespace GameIdeas.Shared.Dto; + +public class UserFilterDto +{ + public int CurrentPage { get; set; } + public string? Name { get; set; } + public IEnumerable? RoleIds { get; set; } +} diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/UserListDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/UserListDto.cs new file mode 100644 index 0000000..40c80bc --- /dev/null +++ b/src/GameIdeas/GameIdeas.Shared/Dto/UserListDto.cs @@ -0,0 +1,7 @@ +namespace GameIdeas.Shared.Dto; + +public class UserListDto +{ + public IEnumerable? Users { get; set; } + public int UsersCount { get; set; } +} -- 2.39.5 From 851f0b5af18be4f198027febadeabb6478d1b610 Mon Sep 17 00:00:00 2001 From: Egamorf Date: Wed, 23 Apr 2025 02:01:34 +0200 Subject: [PATCH 05/11] Add filter and gateway logics --- .../Pages/Users/Filters/UserFilterParams.cs | 9 +++++ .../Pages/Users/Gateways/IUserGateway.cs | 6 ++-- .../Pages/Users/Gateways/UserGateway.cs | 22 ++++++++++-- .../Pages/Users/Users.razor | 7 ++-- .../Pages/Users/Users.razor.cs | 36 +++++++++++++++++-- .../Shared/Constants/Endpoints.cs | 1 + .../Exceptions/RoleNotFoundException.cs | 3 ++ .../CreateStaticResourceKey.cs | 4 +++ .../GameIdeas.WebAPI/Files/GameIdeas.fr.json | 4 ++- 9 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Filters/UserFilterParams.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/RoleNotFoundException.cs diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Filters/UserFilterParams.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Filters/UserFilterParams.cs new file mode 100644 index 0000000..227354d --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Filters/UserFilterParams.cs @@ -0,0 +1,9 @@ +using GameIdeas.Shared.Dto; + +namespace GameIdeas.BlazorApp.Pages.Users.Filters; + +public class UserFilterParams +{ + public string? Name { get; set; } + public List? Roles { get; set; } +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs index 0b9be82..42350e8 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs @@ -1,8 +1,10 @@ -using GameIdeas.Shared.Dto; +using GameIdeas.BlazorApp.Pages.Users.Filters; +using GameIdeas.Shared.Dto; namespace GameIdeas.BlazorApp.Pages.Users.Gateways; public interface IUserGateway { - Task GetUsers(UserFilterParams filterParams); + Task GetUsers(UserFilterParams filterParams, int currentPage); + Task> GetRoles(); } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs index c9120e7..b6e7a15 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs @@ -1,4 +1,5 @@ -using GameIdeas.BlazorApp.Services; +using GameIdeas.BlazorApp.Pages.Users.Filters; +using GameIdeas.BlazorApp.Services; using GameIdeas.BlazorApp.Shared.Constants; using GameIdeas.BlazorApp.Shared.Exceptions; using GameIdeas.Resources; @@ -8,13 +9,28 @@ namespace GameIdeas.BlazorApp.Pages.Users.Gateways; public class UserGateway(IHttpClientService httpClient) : IUserGateway { - public async Task GetUsers(UserFilterParams filterParams) + public async Task> GetRoles() + { + try + { + return await httpClient.FetchDataAsync>(Endpoints.User.Roles) + ?? throw new InvalidOperationException(ResourcesKey.ErrorFetchRoles); + } + catch (Exception) + { + throw new RoleNotFoundException(ResourcesKey.ErrorFetchRoles); + } + } + + public async Task GetUsers(UserFilterParams filterParams, int currentPage) { try { UserFilterDto filter = new() { - + CurrentPage = currentPage, + Name = filterParams.Name, + RoleIds = filterParams.Roles?.Select(r => r.Id) }; var url = Endpoints.User.Fetch(filter); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor index 1ea8a7d..4b34129 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor @@ -12,8 +12,9 @@
- - + +
@@ -21,7 +22,7 @@
@if (!IsLoading) { - @foreach (var user in UsersDto) + @foreach (var user in UserList.Users ?? []) { } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs index 339a8fd..08b6dea 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs @@ -1,16 +1,46 @@ +using GameIdeas.BlazorApp.Pages.Users.Filters; +using GameIdeas.BlazorApp.Pages.Users.Gateways; using GameIdeas.BlazorApp.Shared.Components.Popup; using GameIdeas.Shared.Dto; +using Microsoft.AspNetCore.Components; namespace GameIdeas.BlazorApp.Pages.Users; public partial class Users { + [Inject] private IUserGateway UserGateway { get; set; } = default!; + private Popup? Popup; private bool IsLoading = false; - private IEnumerable UsersDto = []; + private UserFilterParams FilterParams = new(); + private UserListDto UserList = new(); + private IEnumerable Roles = []; + private int CurrentPage = 1; - private void HandleBackdropClicked() + protected override async Task OnInitializedAsync() { - Popup?.Close(); + await FetchData(); + await base.OnInitializedAsync(); + } + + private async Task FetchData(bool fetchRoles = true) + { + try + { + IsLoading = true; + + if (fetchRoles) + Roles = await UserGateway.GetRoles(); + + UserList = await UserGateway.GetUsers(FilterParams, CurrentPage); + } + catch (Exception) + { + throw; + } + finally + { + IsLoading = false; + } } } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs index 65bc47c..46d63d5 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs @@ -24,5 +24,6 @@ public static class Endpoints public static class User { public static string Fetch(UserFilterDto filter) => $"api/User?{UrlHelper.BuildUrlParams(filter)}"; + public const string Roles = "api/User/Roles"; } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/RoleNotFoundException.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/RoleNotFoundException.cs new file mode 100644 index 0000000..392bea3 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/RoleNotFoundException.cs @@ -0,0 +1,3 @@ +namespace GameIdeas.BlazorApp.Shared.Exceptions; + +public class RoleNotFoundException(string message) : Exception(message); diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs index 303659a..17274ca 100644 --- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs +++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs @@ -55,6 +55,8 @@ public class Translations (TranslationService translationService) public string UserLoginFailed => translationService.Translate(nameof(UserLoginFailed)); public string UserLogoutFailed => translationService.Translate(nameof(UserLogoutFailed)); public string Roles => translationService.Translate(nameof(Roles)); + public string ErrorFetchUsers => translationService.Translate(nameof(ErrorFetchUsers)); + public string ErrorFetchRoles => translationService.Translate(nameof(ErrorFetchRoles)); } public static class ResourcesKey @@ -118,4 +120,6 @@ public static class ResourcesKey public static string UserLoginFailed => _instance?.UserLoginFailed ?? throw new InvalidOperationException("ResourcesKey.UserLoginFailed is not initialized."); public static string UserLogoutFailed => _instance?.UserLogoutFailed ?? throw new InvalidOperationException("ResourcesKey.UserLogoutFailed is not initialized."); 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."); } \ 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 9b9e68f..ce6d164 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json @@ -50,5 +50,7 @@ "UserUnauthorized": "Utilisateur non authorisé", "UserLoginFailed": "Authentification de l'utilisateur échoué", "UserLogoutFailed": "Déconnection de l'utilisateur échoué", - "Roles": "Rôles" + "Roles": "Rôles", + "ErrorFetchUsers": "Erreur lors de la récupération des utilisateurs", + "ErrorFetchRoles": "Erreur lors de la récupération des rôles" } \ No newline at end of file -- 2.39.5 From 065de43d6c48dc1f8999b93759f3c321ba7d1593 Mon Sep 17 00:00:00 2001 From: Egamorf Date: Sat, 26 Apr 2025 21:03:58 +0200 Subject: [PATCH 06/11] Get users and role and add user row --- .../Pages/Users/Components/UserRow.razor | 35 +++++++ .../Pages/Users/Components/UserRow.razor.cs | 62 ++++++++++++ .../Pages/Users/Components/UserRow.razor.css | 14 +++ .../Pages/Users/Components/UserValidator.cs | 23 +++++ .../Pages/Users/Users.razor | 7 +- .../Pages/Users/Users.razor.cs | 8 ++ .../Pages/Users/Users.razor.css | 10 ++ .../Client/GameIdeas.BlazorApp/Program.cs | 2 + .../SelectSearch/SelectSearch.razor | 2 +- .../SelectSearch/SelectSearch.razor.cs | 1 + .../Shared/Constants/Icons.cs | 12 +++ .../CreateStaticResourceKey.cs | 2 + src/GameIdeas/GameIdeas.Shared/Dto/UserDto.cs | 2 +- .../Controllers/UserController.cs | 34 ++++++- .../GameIdeas.WebAPI/Files/GameIdeas.fr.json | 3 +- .../GameIdeas.WebAPI/Profiles/UserProfile.cs | 20 ++++ .../Services/Games/GameReadService.cs | 2 +- .../Services/Users/IUserService.cs | 2 + .../Services/Users/UserService.cs | 94 ++++++++++++++++++- 19 files changed, 325 insertions(+), 10 deletions(-) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.cs create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.css create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserValidator.cs create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/UserProfile.cs diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor new file mode 100644 index 0000000..763feb1 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor @@ -0,0 +1,35 @@ +@using GameIdeas.BlazorApp.Shared.Components.Select +@using GameIdeas.BlazorApp.Shared.Components.Select.Models +@using GameIdeas.BlazorApp.Shared.Constants +@using GameIdeas.Shared.Dto + + +
+
+ @Icons.Account +
+
+ +
+
+ +
+
+ +
+
+ + @if (!CanEdit) + { + + } + else + { + + } +
+
+
\ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.cs new file mode 100644 index 0000000..c8eaca8 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.cs @@ -0,0 +1,62 @@ +using GameIdeas.BlazorApp.Shared.Components.Select; +using GameIdeas.BlazorApp.Shared.Components.Select.Models; +using GameIdeas.Shared.Dto; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Forms; + +namespace GameIdeas.BlazorApp.Pages.Users.Components; + +public partial class UserRow +{ + [Parameter] public UserDto User { get; set; } = new(); + [Parameter] public List Roles { get; set; } = []; + [Parameter] public bool CanEdit { get; set; } = false; + [Parameter] public EventCallback OnRemove { get; set; } + [Parameter] public EventCallback OnSubmit { get; set; } + + private SelectParams SelectRoleParams = new(); + private EditContext? EditContext; + + protected override void OnInitialized() + { + EditContext = new(User); + base.OnInitialized(); + } + + protected override void OnParametersSet() + { + SelectRoleParams = new() + { + GetItemLabel = role => role.Name, + Items = Roles + }; + + base.OnParametersSet(); + } + + private void HandleValuesChanged(IEnumerable roles) + { + User.Role = roles.FirstOrDefault(); + } + + private async Task HandleSubmitClicked() + { + if (EditContext?.Validate() == false) + { + return; + } + + CanEdit = false; + await OnSubmit.InvokeAsync(User); + } + + + private void HandleEditClicked() + { + CanEdit = true; + } + private async Task HandleRemoveClicked() + { + await OnRemove.InvokeAsync(User); + } +} \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.css new file mode 100644 index 0000000..5c0ab4a --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.css @@ -0,0 +1,14 @@ +.row { + height: 64px; + display: grid; + grid-template-columns: 48px 1fr 1fr 1fr auto; + grid-gap: 8px; + padding: 0 8px; + background: var(--input-secondary); + box-shadow: var(--drop-shadow); + border-radius: var(--big-radius); +} + +.row > * { + align-content: center; +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserValidator.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserValidator.cs new file mode 100644 index 0000000..7550c83 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserValidator.cs @@ -0,0 +1,23 @@ +using FluentValidation; +using GameIdeas.Resources; +using GameIdeas.Shared.Dto; + +namespace GameIdeas.BlazorApp.Pages.Users.Components; + +public class UserValidator : AbstractValidator +{ + public UserValidator() + { + RuleFor(user => user.Username) + .NotEmpty() + .WithMessage(ResourcesKey.MissingField); + + RuleFor(user => user.Password) + .NotEmpty() + .WithMessage(ResourcesKey.MissingField); + + RuleFor(user => user.Role) + .NotEmpty() + .WithMessage(ResourcesKey.MissingField); + } +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor index 4b34129..673f280 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor @@ -1,8 +1,10 @@ @page "/Users" @using GameIdeas.BlazorApp.Pages.Games.Header @using GameIdeas.BlazorApp.Layouts +@using GameIdeas.BlazorApp.Pages.Users.Components @using GameIdeas.BlazorApp.Shared.Components.Popup @using GameIdeas.BlazorApp.Shared.Components.Search +@using GameIdeas.BlazorApp.Shared.Components.Select.Models @using GameIdeas.BlazorApp.Shared.Components.SelectSearch @using GameIdeas.Shared.Dto @@ -14,7 +16,7 @@
+ Items="Roles.ToList()" GetLabel="@(role => role.Name)" Theme="SelectTheme.Filter" />
@@ -24,7 +26,7 @@ { @foreach (var user in UserList.Users ?? []) { - + } } else @@ -34,7 +36,6 @@ } } -
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs index 08b6dea..19212ea 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs @@ -43,4 +43,12 @@ public partial class Users IsLoading = false; } } + private Task HandleSubmitUser(UserDto args) + { + throw new NotImplementedException(); + } + private Task HandleRemoveUser(UserDto args) + { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css index d839b81..068faa8 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css @@ -8,3 +8,13 @@ box-sizing: border-box; width: 200px; } + +.container { + padding: 20px 200px; +} + +@media screen and (max-width: 1000px) { + .container { + padding: 20px 20px; + } +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs index 4edf1f7..261e04c 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Program.cs @@ -3,6 +3,7 @@ using Blazored.LocalStorage; using GameIdeas.BlazorApp; using GameIdeas.BlazorApp.Pages.Games.Gateways; using GameIdeas.BlazorApp.Pages.UserMenu.Gateways; +using GameIdeas.BlazorApp.Pages.Users.Gateways; using GameIdeas.BlazorApp.Services; using GameIdeas.Resources; using Microsoft.AspNetCore.Components.Authorization; @@ -37,6 +38,7 @@ services.AddScoped(); services.AddScoped(); services.AddScoped(); +services.AddScoped(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/SelectSearch/SelectSearch.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/SelectSearch/SelectSearch.razor index 35c1a8a..383fdb8 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/SelectSearch/SelectSearch.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/SelectSearch/SelectSearch.razor @@ -5,7 +5,7 @@ @typeparam TItem - +
- +
- - @if (!CanEdit) - { - - } - else - { - - } + + +
\ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.cs index c8eaca8..6cd0411 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.cs @@ -53,8 +53,9 @@ public partial class UserRow private void HandleEditClicked() { - CanEdit = true; + CanEdit = !CanEdit; } + private async Task HandleRemoveClicked() { await OnRemove.InvokeAsync(User); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.css index 5c0ab4a..30e2589 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRow.razor.css @@ -12,3 +12,64 @@ .row > * { align-content: center; } + +input[disabled], input, input::placeholder { + color: var(--white); +} + +input, .role-label { + border: none; + outline: none; + background: none; + height: 28px; + padding: 0 10px; + box-sizing: border-box; + border-radius: var(--small-radius); + border: solid 1px var(--line); + background: rgb(0, 0, 0, 0.2); +} + + input[disabled], .disabled { + border: none; + background: none; + } + +.role-label { + display: block; + align-content: center; + width: fit-content; +} + +.buttons { + display: flex; + flex-direction: row; + gap: 8px; + height:auto; +} + +.buttons > * { + border: none; + outline: none; + margin: auto; + height: 28px; + width: 28px; + background: none; + border-radius: var(--small-radius); + padding: 2px; +} + +.buttons > *:hover, .selected { + background: var(--input-selected) +} + +.remove ::deep svg { + fill: var(--red); +} + +.edit ::deep svg { + fill: var(--yellow); +} + +.submit ::deep svg { + fill: var(--green); +} diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Search/SearchInput.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Search/SearchInput.razor index 5c1e7f6..410062d 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Search/SearchInput.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Search/SearchInput.razor @@ -2,8 +2,7 @@ @using GameIdeas.Shared.Constants
- Date: Sun, 27 Apr 2025 15:02:54 +0200 Subject: [PATCH 08/11] complete style and add skeleton --- .../Pages/Games/Header/GameHeader.razor | 4 +- .../Pages/Games/Header/GameHeader.razor.cs | 5 -- .../Pages/UserMenu/Components/Login.razor | 2 +- .../Pages/UserMenu/Components/Login.razor.cs | 2 +- .../Pages/Users/Components/UserRow.razor | 2 +- .../Pages/Users/Components/UserRow.razor.cs | 15 ++++ .../Pages/Users/Components/UserRow.razor.css | 10 ++- .../Users/Components/UserRowSkeleton.razor | 12 ++++ .../Components/UserRowSkeleton.razor.css | 69 +++++++++++++++++++ .../Pages/Users/Users.razor | 2 +- .../Pages/Users/Users.razor.cs | 2 +- .../Pages/Users/Users.razor.css | 10 ++- .../Select/Components/SelectRow.razor.css | 11 ++- .../Components/Select/Helpers/SelectHelper.cs | 1 + .../Components/Select/Models/SelectTheme.cs | 3 +- .../Shared/Components/Select/Select.razor.css | 8 ++- 16 files changed, 139 insertions(+), 19 deletions(-) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRowSkeleton.razor create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Components/UserRowSkeleton.razor.css diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor index 95627b2..8f02472 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor @@ -10,9 +10,9 @@ @inherits ComponentBase
-
+ Game Ideas -
+ @ChildContent diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs index df01ce4..3249a21 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Header/GameHeader.razor.cs @@ -32,11 +32,6 @@ public partial class GameHeader : ComponentBase base.OnInitialized(); } - private void HandleIconClicked() - { - throw new NotImplementedException(); - } - private async Task HandleAddTypeClicked(IEnumerable> values) { SelectListAdd?.Close(); diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor index 444b7aa..c9044f0 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor @@ -1,7 +1,7 @@ @using Blazored.FluentValidation - + diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs index 19212ea..8a7677f 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.cs @@ -40,7 +40,7 @@ public partial class Users } finally { - IsLoading = false; + IsLoading = false; } } private Task HandleSubmitUser(UserDto args) diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css index 068faa8..18bbb60 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor.css @@ -4,15 +4,21 @@ gap: 8px; } -::deep.header-content>* { +::deep .search-container .select-container { box-sizing: border-box; - width: 200px; + max-width: 200px; } .container { padding: 20px 200px; } +.content { + display: flex; + flex-direction: column; + gap: 20px; +} + @media screen and (max-width: 1000px) { .container { padding: 20px 20px; diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Components/SelectRow.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Components/SelectRow.razor.css index 133a7f1..5651f0a 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Components/SelectRow.razor.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Components/SelectRow.razor.css @@ -42,4 +42,13 @@ .navigation .selected { display: none; - } \ No newline at end of file + } + +/***** Single Theme *****/ +.single { + padding: 4px 8px; +} + +.single .selected { + display: none; +} \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Helpers/SelectHelper.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Helpers/SelectHelper.cs index 862ceda..a327106 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Helpers/SelectHelper.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Helpers/SelectHelper.cs @@ -13,6 +13,7 @@ public static class SelectHelper SelectTheme.Filter => "filter", SelectTheme.AdvancedFilter => "advanced-filter", SelectTheme.Creation => "creation", + SelectTheme.Single => "single", _ => string.Empty }; } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Models/SelectTheme.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Models/SelectTheme.cs index 63d38ba..c5af5a2 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Models/SelectTheme.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Models/SelectTheme.cs @@ -6,5 +6,6 @@ public enum SelectTheme Sort, Filter, AdvancedFilter, - Creation + Creation, + Single } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Select.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Select.razor.css index 90c1264..eec629c 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Select.razor.css +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Components/Select/Select.razor.css @@ -14,6 +14,7 @@ z-index: var(--index-dropdown); border-radius: var(--small-radius); width: 100%; + box-shadow: var(--drop-shadow); } .content { @@ -23,7 +24,6 @@ flex-direction: column; animation-name: fade-in; animation-duration: 0.2s; - box-shadow: var(--drop-shadow); } .line { @@ -87,7 +87,7 @@ border-bottom: 2px solid var(--input-selected); } -/***** Sort Theme *****/ +/***** Creation Theme *****/ .creation .content { border-radius: var(--small-radius); box-sizing: border-box; @@ -100,3 +100,7 @@ border-bottom: 2px solid var(--input-selected); } +/***** Single Theme *****/ +.single { + border: none; +} -- 2.39.5 From 63622bd299a8e0b3ffad8ce1ad41716f94734b10 Mon Sep 17 00:00:00 2001 From: Egamorf Date: Sun, 27 Apr 2025 17:09:15 +0200 Subject: [PATCH 09/11] Add user service and controller --- .../Pages/Users/Gateways/IUserGateway.cs | 3 + .../Pages/Users/Gateways/UserGateway.cs | 39 +++++++++ .../Shared/Constants/Endpoints.cs | 4 + .../Exceptions/UserCreationException.cs | 3 + .../CreateStaticResourceKey.cs | 6 ++ .../Controllers/UserController.cs | 54 +++++++++++- .../GameIdeas.WebAPI/Files/GameIdeas.fr.json | 7 +- .../Server/GameIdeas.WebAPI/Program.cs | 3 +- .../{IUserService.cs => IUserReadService.cs} | 2 +- .../Services/Users/IUserWriteService.cs | 10 +++ .../{UserService.cs => UserReadService.cs} | 4 +- .../Services/Users/UserWriteService.cs | 83 +++++++++++++++++++ 12 files changed, 208 insertions(+), 10 deletions(-) create mode 100644 src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/UserCreationException.cs rename src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/{IUserService.cs => IUserReadService.cs} (86%) create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserWriteService.cs rename src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/{UserService.cs => UserReadService.cs} (98%) create mode 100644 src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserWriteService.cs diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs index 42350e8..0aeb332 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/IUserGateway.cs @@ -7,4 +7,7 @@ public interface IUserGateway { Task GetUsers(UserFilterParams filterParams, int currentPage); Task> GetRoles(); + Task CreateUser(UserDto user); + Task UpdateUser(UserDto user); + Task DeleteUser(string userId); } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs index b6e7a15..81161c1 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Gateways/UserGateway.cs @@ -9,6 +9,32 @@ namespace GameIdeas.BlazorApp.Pages.Users.Gateways; public class UserGateway(IHttpClientService httpClient) : IUserGateway { + public async Task CreateUser(UserDto user) + { + try + { + return await httpClient.PostAsync(Endpoints.User.Create, user) + ?? throw new InvalidOperationException(ResourcesKey.ErrorCreateUser); + } + catch (Exception) + { + throw new UserCreationException(ResourcesKey.ErrorCreateUser); + } + } + + public async Task DeleteUser(string userId) + { + try + { + return await httpClient.DeleteAsync(Endpoints.User.Delete(userId)) + ?? throw new InvalidOperationException(ResourcesKey.ErrorDeleteUser); + } + catch (Exception) + { + throw new UserCreationException(ResourcesKey.ErrorDeleteUser); + } + } + public async Task> GetRoles() { try @@ -42,4 +68,17 @@ public class UserGateway(IHttpClientService httpClient) : IUserGateway throw new UserNotFoundException(ResourcesKey.ErrorFetchUsers); } } + + public async Task UpdateUser(UserDto user) + { + try + { + return await httpClient.PutAsync(Endpoints.User.Update(user.Id ?? string.Empty), user) + ?? throw new InvalidOperationException(ResourcesKey.ErrorUpdateUser); + } + catch (Exception) + { + throw new UserCreationException(ResourcesKey.ErrorUpdateUser); + } + } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs index 46d63d5..53c7180 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs @@ -25,5 +25,9 @@ public static class Endpoints { public static string Fetch(UserFilterDto filter) => $"api/User?{UrlHelper.BuildUrlParams(filter)}"; public const string Roles = "api/User/Roles"; + public const string Create = "api/User/Create"; + public static string Delete(string userId) => $"api/User/Delete/{userId}"; + public static string Update(string userId) => $"api/User/Update/{userId}"; + } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/UserCreationException.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/UserCreationException.cs new file mode 100644 index 0000000..4e4f945 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Exceptions/UserCreationException.cs @@ -0,0 +1,3 @@ +namespace GameIdeas.BlazorApp.Shared.Exceptions; + +public class UserCreationException(string message) : Exception(message); diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs index c4e7c03..71ca1ab 100644 --- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs +++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs @@ -58,6 +58,9 @@ public class Translations (TranslationService translationService) public string ErrorFetchUsers => translationService.Translate(nameof(ErrorFetchUsers)); public string ErrorFetchRoles => translationService.Translate(nameof(ErrorFetchRoles)); public string MissingField => translationService.Translate(nameof(MissingField)); + public string ErrorCreateUser => translationService.Translate(nameof(ErrorCreateUser)); + public string ErrorUpdateUser => translationService.Translate(nameof(ErrorUpdateUser)); + public string ErrorDeleteUser => translationService.Translate(nameof(ErrorDeleteUser)); } public static class ResourcesKey @@ -124,4 +127,7 @@ public static class ResourcesKey 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."); + public static string ErrorCreateUser => _instance?.ErrorCreateUser ?? throw new InvalidOperationException("ResourcesKey.ErrorCreateUser is not initialized."); + public static string ErrorUpdateUser => _instance?.ErrorUpdateUser ?? throw new InvalidOperationException("ResourcesKey.ErrorUpdateUser is not initialized."); + public static string ErrorDeleteUser => _instance?.ErrorDeleteUser ?? throw new InvalidOperationException("ResourcesKey.ErrorDeleteUser is not initialized."); } \ No newline at end of file diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs index f289c36..ba76f3d 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Controllers/UserController.cs @@ -10,7 +10,8 @@ namespace GameIdeas.WebAPI.Controllers; [ApiController] [Route("api/[controller]")] public class UserController( - IUserService userService, + IUserReadService userReadService, + IUserWriteService userWriteService, ILoggerFactory loggerFactory) : Controller { private readonly ILogger logger = loggerFactory.CreateLogger(); @@ -20,7 +21,7 @@ public class UserController( { try { - return Ok(await userService.Login(model)); + return Ok(await userReadService.Login(model)); } catch (UserInvalidException e) { @@ -45,7 +46,7 @@ public class UserController( { try { - return Ok(await userService.GetRoles()); + return Ok(await userReadService.GetRoles()); } catch (Exception e) { @@ -60,7 +61,7 @@ public class UserController( { try { - return Ok(await userService.GetUsers(filter)); + return Ok(await userReadService.GetUsers(filter)); } catch (Exception e) { @@ -68,4 +69,49 @@ public class UserController( return StatusCode(500, e.Message); } } + + [Authorize(Roles = GlobalConstants.ADMINISTRATOR)] + [HttpPost("Create")] + public async Task> CreateUser([FromBody] UserDto user) + { + try + { + return Created("/Create", await userWriteService.CreateUser(user)); + } + catch (Exception e) + { + logger.LogError(e, "Internal error while create user"); + return StatusCode(500, e.Message); + } + } + + [Authorize(Roles = GlobalConstants.ADMINISTRATOR)] + [HttpPut("Update/{userId}")] + public async Task> UpdateUser(string userId, [FromBody] UserDto user) + { + try + { + return Created("/Update", await userWriteService.UpdateUser(userId, user)); + } + catch (Exception e) + { + logger.LogError(e, "Internal error while update user"); + return StatusCode(500, e.Message); + } + } + + [Authorize(Roles = GlobalConstants.ADMINISTRATOR)] + [HttpDelete("Delete/{userId}")] + public async Task> DeleteUser(string userId) + { + try + { + return Created("/Delete", await userWriteService.DeleteUser(userId)); + } + catch (Exception e) + { + logger.LogError(e, "Internal error while delete user"); + 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 9ee40a8..4ce3ca9 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json @@ -34,7 +34,7 @@ "RequestFailedStatusFormat": "Erreur lors de la réponse, code {0}", "ErrorFetchCategories": "Erreur lors de la récupération des catégories", "PlaceholderAdd": "Ajouter un nouveau", - "ErrorCreateGame": "Erreur lors de la Création d'un jeu", + "ErrorCreateGame": "Erreur lors de la création d'un jeu", "InvalidTitle": "Le titre est incorrect", "InvalidInterest": "L'interêt est incorrect", "Unknown": "Inconnu", @@ -53,5 +53,8 @@ "Roles": "Rôles", "ErrorFetchUsers": "Erreur lors de la récupération des utilisateurs", "ErrorFetchRoles": "Erreur lors de la récupération des rôles", - "MissingField": "Un champs est manquant" + "MissingField": "Un champs est manquant", + "ErrorCreateUser": "Erreur lors de la création d'un utilisateur", + "ErrorUpdateUser": "Erreur lors de la mise à jour d'un utilisateur", + "ErrorDeleteUser": "Erreur lors de la suppression d'un utilisateur" } \ No newline at end of file diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Program.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Program.cs index a110899..1bd9aa5 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Program.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Program.cs @@ -64,7 +64,8 @@ services.AddAuthorization(); services.AddSingleton(); services.AddSingleton(); -services.AddScoped(); +services.AddScoped(); +services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserReadService.cs similarity index 86% rename from src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserService.cs rename to src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserReadService.cs index 3e826a0..cd8ac81 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserService.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserReadService.cs @@ -2,7 +2,7 @@ namespace GameIdeas.WebAPI.Services.Users; -public interface IUserService +public interface IUserReadService { Task Login(UserDto user); Task> GetRoles(); diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserWriteService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserWriteService.cs new file mode 100644 index 0000000..ce5d70a --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/IUserWriteService.cs @@ -0,0 +1,10 @@ +using GameIdeas.Shared.Dto; + +namespace GameIdeas.WebAPI.Services.Users; + +public interface IUserWriteService +{ + Task CreateUser(UserDto user); + Task UpdateUser(string userId, UserDto user); + Task DeleteUser(string userId); +} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserReadService.cs similarity index 98% rename from src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserService.cs rename to src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserReadService.cs index 1b5dc5f..f60d4bb 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserService.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserReadService.cs @@ -14,10 +14,10 @@ using System.Text; namespace GameIdeas.WebAPI.Services.Users; -public class UserService( +public class UserReadService( UserManager userManager, GameIdeasContext context, - IMapper mapper) : IUserService + IMapper mapper) : IUserReadService { public async Task> GetRoles() { diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserWriteService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserWriteService.cs new file mode 100644 index 0000000..404a883 --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Users/UserWriteService.cs @@ -0,0 +1,83 @@ +using AutoMapper; +using GameIdeas.Resources; +using GameIdeas.Shared.Dto; +using GameIdeas.Shared.Model; +using GameIdeas.WebAPI.Exceptions; +using Microsoft.AspNetCore.Identity; + +namespace GameIdeas.WebAPI.Services.Users; + +public class UserWriteService( + UserManager userManager) : IUserWriteService +{ + public async Task CreateUser(UserDto user) + { + if (user.Username == null || + user.Password == null || + user.Role == null) + { + throw new UserInvalidException(ResourcesKey.MissingField); + } + + User userToCreate = new() { UserName = user.Username }; + + var result = await userManager.CreateAsync(userToCreate, user.Password); + + if (result.Succeeded) + { + await userManager.AddToRoleAsync(userToCreate, user.Role.Name); + } + else + { + throw new UserInvalidException(string.Join("; ", result.Errors)); + } + + return userToCreate.Id; + } + + public async Task DeleteUser(string userId) + { + if (userId == null) + { + throw new ArgumentNullException(ResourcesKey.MissingField); + } + + var user = await userManager.FindByIdAsync(userId) + ?? throw new UserInvalidException("User not found"); + + await userManager.DeleteAsync(user); + return userId; + } + + public async Task UpdateUser(string userId, UserDto user) + { + if (userId == null) + { + throw new ArgumentNullException(ResourcesKey.MissingField); + } + + var userToUpdate = await userManager.FindByIdAsync(userId) + ?? throw new UserInvalidException("User not found"); + + if (user.Username != null) + { + userToUpdate.UserName = user.Username; + await userManager.UpdateAsync(userToUpdate); + } + + if (user.Password != null) + { + await userManager.RemovePasswordAsync(userToUpdate); + await userManager.AddPasswordAsync(userToUpdate, user.Password); + } + + if (user.Role != null) + { + var roles = await userManager.GetRolesAsync(userToUpdate); + await userManager.RemoveFromRolesAsync(userToUpdate, roles); + await userManager.AddToRoleAsync(userToUpdate, user.Role.Name); + } + + return userToUpdate.Id; + } +} -- 2.39.5 From 757e9db08db400da52faa8dc04b569759d88d652 Mon Sep 17 00:00:00 2001 From: Egamorf Date: Sun, 27 Apr 2025 19:56:17 +0200 Subject: [PATCH 10/11] Crud on users --- .../Pages/UserMenu/Components/Login.razor | 2 +- .../Pages/UserMenu/UserMenu.razor.cs | 4 +- .../Pages/Users/Components/UserRow.razor | 21 +++--- .../Pages/Users/Components/UserRow.razor.cs | 11 +-- .../Pages/Users/Components/UserRow.razor.css | 14 +++- .../Pages/Users/Components/UserValidator.cs | 31 ++++++++- .../Pages/Users/Gateways/IUserGateway.cs | 6 +- .../Pages/Users/Gateways/UserGateway.cs | 12 ++-- .../Pages/Users/Users.razor | 8 ++- .../Pages/Users/Users.razor.cs | 69 +++++++++++++++++-- .../Pages/Users/Users.razor.css | 8 ++- .../Constants/GlobalConstants.cs | 2 + src/GameIdeas/GameIdeas.Shared/Dto/IdDto.cs | 6 ++ .../Controllers/UserController.cs | 15 ++-- .../20250420160158_SeedDefaultUser.cs | 4 +- .../Server/GameIdeas.WebAPI/Program.cs | 11 +++ .../Services/Users/UserWriteService.cs | 2 +- 17 files changed, 182 insertions(+), 44 deletions(-) create mode 100644 src/GameIdeas/GameIdeas.Shared/Dto/IdDto.cs diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor index c9044f0..e7be564 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/UserMenu/Components/Login.razor @@ -10,7 +10,7 @@