From 290ad790452df994415f8d7e6b95c19ac97b8937 Mon Sep 17 00:00:00 2001 From: Oscar Veldman Date: Wed, 31 Jan 2024 18:17:25 +0100 Subject: [PATCH] Add user overview in the admin module --- .../UserManagers/GetUsersResponse.cs | 1 + .../Application/GetUsersUseCase.cs | 4 ++ .../Infrastructure/IUserRepository.cs | 1 + .../Infrastructure/UserRepository.cs | 11 +++++- .../Identity/GetUsersEndpointTests.cs | 1 + .../Application/Users/IUserService.cs | 8 ++++ .../Application/Users/UserService.cs | 28 ++++++++++++++ .../Application/Users/Users.razor | 19 ++++++++++ .../Application/Users/Users.razor.cs | 38 +++++++++++++++++++ .../WebAssemblyHostBuilderExtensions.cs | 12 ++++++ .../Layout/NavMenu.razor | 6 ++- .../Layout/NavMenu.razor.css | 4 ++ MadWorld/MadWorld.Frontend.Admin/Program.cs | 2 + .../MadWorld.Frontend.Admin/_Imports.razor | 3 ++ 14 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 MadWorld/MadWorld.Frontend.Admin/Application/Users/IUserService.cs create mode 100644 MadWorld/MadWorld.Frontend.Admin/Application/Users/UserService.cs create mode 100644 MadWorld/MadWorld.Frontend.Admin/Application/Users/Users.razor create mode 100644 MadWorld/MadWorld.Frontend.Admin/Application/Users/Users.razor.cs create mode 100644 MadWorld/MadWorld.Frontend.Admin/Extensions/WebAssemblyHostBuilderExtensions.cs diff --git a/MadWorld/MadWorld.Backend.Identity.Contracts/UserManagers/GetUsersResponse.cs b/MadWorld/MadWorld.Backend.Identity.Contracts/UserManagers/GetUsersResponse.cs index eb0cc8e..f3ceda3 100644 --- a/MadWorld/MadWorld.Backend.Identity.Contracts/UserManagers/GetUsersResponse.cs +++ b/MadWorld/MadWorld.Backend.Identity.Contracts/UserManagers/GetUsersResponse.cs @@ -2,5 +2,6 @@ namespace MadWorld.Backend.Identity.Contracts.UserManagers; public sealed class GetUsersResponse { + public int TotalCount { get; set; } public IReadOnlyList Users { get; set; } = Array.Empty(); } \ No newline at end of file diff --git a/MadWorld/MadWorld.Backend.Identity/Application/GetUsersUseCase.cs b/MadWorld/MadWorld.Backend.Identity/Application/GetUsersUseCase.cs index 3c7ed40..a4d450d 100644 --- a/MadWorld/MadWorld.Backend.Identity/Application/GetUsersUseCase.cs +++ b/MadWorld/MadWorld.Backend.Identity/Application/GetUsersUseCase.cs @@ -1,5 +1,6 @@ using MadWorld.Backend.Identity.Application.Mappers; using MadWorld.Backend.Identity.Contracts.UserManagers; +using MadWorld.Backend.Identity.Domain.Users; using MadWorld.Backend.Identity.Infrastructure; namespace MadWorld.Backend.Identity.Application; @@ -17,9 +18,12 @@ public GetUsersResponse GetUsers(int page) { ArgumentOutOfRangeException.ThrowIfNegative(page); + var totalCount = _repository.CountUsers(); var users = _repository.GetUsers(page); + return new GetUsersResponse() { + TotalCount = totalCount, Users = users.ToDto() }; } diff --git a/MadWorld/MadWorld.Backend.Identity/Infrastructure/IUserRepository.cs b/MadWorld/MadWorld.Backend.Identity/Infrastructure/IUserRepository.cs index c9bf8e7..07ac731 100644 --- a/MadWorld/MadWorld.Backend.Identity/Infrastructure/IUserRepository.cs +++ b/MadWorld/MadWorld.Backend.Identity/Infrastructure/IUserRepository.cs @@ -4,6 +4,7 @@ namespace MadWorld.Backend.Identity.Infrastructure; public interface IUserRepository { + int CountUsers(); List GetUsers(int page); Task AddRefreshToken(RefreshToken token); Task DeleteExpiredRefreshTokens(); diff --git a/MadWorld/MadWorld.Backend.Identity/Infrastructure/UserRepository.cs b/MadWorld/MadWorld.Backend.Identity/Infrastructure/UserRepository.cs index 20af33f..c8fc144 100644 --- a/MadWorld/MadWorld.Backend.Identity/Infrastructure/UserRepository.cs +++ b/MadWorld/MadWorld.Backend.Identity/Infrastructure/UserRepository.cs @@ -6,8 +6,8 @@ namespace MadWorld.Backend.Identity.Infrastructure; public class UserRepository : IUserRepository { - private int TakeAmount = 10; - + private const int TakeAmount = 10; + private readonly UserDbContext _context; private readonly IDateTimeProvider _dateTimeProvider; @@ -17,6 +17,13 @@ public UserRepository(UserDbContext context, IDateTimeProvider dateTimeProvider) _dateTimeProvider = dateTimeProvider; } + public int CountUsers() + { + return _context + .Users + .Count(); + } + public List GetUsers(int page) { return _context diff --git a/MadWorld/MadWorld.Backend.IntegrationTests/Identity/GetUsersEndpointTests.cs b/MadWorld/MadWorld.Backend.IntegrationTests/Identity/GetUsersEndpointTests.cs index a5ea94d..6f49ca0 100644 --- a/MadWorld/MadWorld.Backend.IntegrationTests/Identity/GetUsersEndpointTests.cs +++ b/MadWorld/MadWorld.Backend.IntegrationTests/Identity/GetUsersEndpointTests.cs @@ -52,6 +52,7 @@ public async Task Execute_WhenRequestIsValid_ShouldReturnValidResponse() response.EnsureSuccessStatusCode(); var result = await response.Content .ReadFromJsonAsync(); + result!.TotalCount.ShouldBe(13); result!.Users.Count.ShouldBe(10); result!.Users[1].Email.ShouldBe($"test0@test.com"); } diff --git a/MadWorld/MadWorld.Frontend.Admin/Application/Users/IUserService.cs b/MadWorld/MadWorld.Frontend.Admin/Application/Users/IUserService.cs new file mode 100644 index 0000000..25646f0 --- /dev/null +++ b/MadWorld/MadWorld.Frontend.Admin/Application/Users/IUserService.cs @@ -0,0 +1,8 @@ +using MadWorld.Backend.Identity.Contracts.UserManagers; + +namespace MadWorld.Frontend.Admin.Application.Users; + +public interface IUserService +{ + Task GetUsers(int page); +} \ No newline at end of file diff --git a/MadWorld/MadWorld.Frontend.Admin/Application/Users/UserService.cs b/MadWorld/MadWorld.Frontend.Admin/Application/Users/UserService.cs new file mode 100644 index 0000000..f037d83 --- /dev/null +++ b/MadWorld/MadWorld.Frontend.Admin/Application/Users/UserService.cs @@ -0,0 +1,28 @@ +using System.Net.Http.Json; +using MadWorld.Backend.Identity.Contracts.UserManagers; +using MadWorld.Shared.Blazor.Common; + +namespace MadWorld.Frontend.Admin.Application.Users; + +public class UserService : IUserService +{ + private const string Endpoint = "UserManager"; + + private readonly HttpClient _client; + public UserService(IHttpClientFactory clientFactory) + { + _client = clientFactory.CreateClient(ApiTypes.Identity); + } + + public async Task GetUsers(int page) + { + var response = await _client.GetAsync($"{Endpoint}/Users?page={page}"); + + if (!response.IsSuccessStatusCode) + { + return new GetUsersResponse(); + } + + return await response.Content.ReadFromJsonAsync() ?? new GetUsersResponse(); + } +} \ No newline at end of file diff --git a/MadWorld/MadWorld.Frontend.Admin/Application/Users/Users.razor b/MadWorld/MadWorld.Frontend.Admin/Application/Users/Users.razor new file mode 100644 index 0000000..38b6957 --- /dev/null +++ b/MadWorld/MadWorld.Frontend.Admin/Application/Users/Users.razor @@ -0,0 +1,19 @@ +@using MadWorld.Backend.Identity.Contracts.UserManagers; + +@page "/Users" +
+

All Users

+
+
+ + + + + +
\ No newline at end of file diff --git a/MadWorld/MadWorld.Frontend.Admin/Application/Users/Users.razor.cs b/MadWorld/MadWorld.Frontend.Admin/Application/Users/Users.razor.cs new file mode 100644 index 0000000..285b399 --- /dev/null +++ b/MadWorld/MadWorld.Frontend.Admin/Application/Users/Users.razor.cs @@ -0,0 +1,38 @@ +using MadWorld.Backend.Identity.Contracts.UserManagers; +using Microsoft.AspNetCore.Components; +using Radzen; + +namespace MadWorld.Frontend.Admin.Application.Users; + +public partial class Users +{ + [Inject] + public IUserService UserService { get; set; } = null!; + + private GetUsersResponse _response = new(); + + private int _currentPage = 0; + + protected override async Task OnInitializedAsync() + { + await LoadUsers(); + + await base.OnInitializedAsync(); + } + + private Task LoadData(LoadDataArgs _) => Task.CompletedTask; + + private async Task OnPage(PagerEventArgs args) + { + if (_currentPage != args.PageIndex) + { + _currentPage = args.PageIndex; + await LoadUsers(); + } + } + + private async Task LoadUsers() + { + _response = await UserService.GetUsers(_currentPage); + } +} \ No newline at end of file diff --git a/MadWorld/MadWorld.Frontend.Admin/Extensions/WebAssemblyHostBuilderExtensions.cs b/MadWorld/MadWorld.Frontend.Admin/Extensions/WebAssemblyHostBuilderExtensions.cs new file mode 100644 index 0000000..f109271 --- /dev/null +++ b/MadWorld/MadWorld.Frontend.Admin/Extensions/WebAssemblyHostBuilderExtensions.cs @@ -0,0 +1,12 @@ +using MadWorld.Frontend.Admin.Application.Users; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +namespace MadWorld.Frontend.Admin.Extensions; + +public static class WebAssemblyHostBuilderExtensions +{ + public static void AddApplication(this WebAssemblyHostBuilder builder) + { + builder.Services.AddScoped(); + } +} \ No newline at end of file diff --git a/MadWorld/MadWorld.Frontend.Admin/Layout/NavMenu.razor b/MadWorld/MadWorld.Frontend.Admin/Layout/NavMenu.razor index 826942e..894a9c0 100644 --- a/MadWorld/MadWorld.Frontend.Admin/Layout/NavMenu.razor +++ b/MadWorld/MadWorld.Frontend.Admin/Layout/NavMenu.razor @@ -14,6 +14,11 @@ Home + @@ -26,5 +31,4 @@ { collapseNavMenu = !collapseNavMenu; } - } \ No newline at end of file diff --git a/MadWorld/MadWorld.Frontend.Admin/Layout/NavMenu.razor.css b/MadWorld/MadWorld.Frontend.Admin/Layout/NavMenu.razor.css index 881d128..7a77181 100644 --- a/MadWorld/MadWorld.Frontend.Admin/Layout/NavMenu.razor.css +++ b/MadWorld/MadWorld.Frontend.Admin/Layout/NavMenu.razor.css @@ -25,6 +25,10 @@ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); } +.bi-people-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-people-fill' viewBox='0 0 16 16'%3E%3Cpath d='M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6m-5.784 6A2.24 2.24 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.3 6.3 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5'/%3E%3C/svg%3E"); +} + .bi-plus-square-fill-nav-menu { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); } diff --git a/MadWorld/MadWorld.Frontend.Admin/Program.cs b/MadWorld/MadWorld.Frontend.Admin/Program.cs index 38d44be..9293e4b 100644 --- a/MadWorld/MadWorld.Frontend.Admin/Program.cs +++ b/MadWorld/MadWorld.Frontend.Admin/Program.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using MadWorld.Frontend.Admin; +using MadWorld.Frontend.Admin.Extensions; using MadWorld.Shared.Blazor.Authentications; using MadWorld.Shared.Blazor.Common; @@ -10,6 +11,7 @@ builder.AddCommon(); builder.AddAuthentication(); +builder.AddApplication(); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); diff --git a/MadWorld/MadWorld.Frontend.Admin/_Imports.razor b/MadWorld/MadWorld.Frontend.Admin/_Imports.razor index f9f078c..8bcfefc 100644 --- a/MadWorld/MadWorld.Frontend.Admin/_Imports.razor +++ b/MadWorld/MadWorld.Frontend.Admin/_Imports.razor @@ -7,5 +7,8 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop +@using Radzen; +@using Radzen.Blazor; + @using MadWorld.Frontend.Admin @using MadWorld.Frontend.Admin.Layout \ No newline at end of file