From 21404dc81318c8d43b1c6c0370ac648580acef84 Mon Sep 17 00:00:00 2001 From: Samuel BERION Date: Wed, 31 Jan 2024 19:31:26 +0100 Subject: [PATCH] Add teams administration panel (#3) Co-authored-by: samuel Co-authored-by: sam Reviewed-on: https://codefirst.iut.uca.fr/git/IQBall/Server-Panel/pulls/3 Co-authored-by: Samuel BERION Co-committed-by: Samuel BERION --- AdminPanel.csproj | 11 ++++ Components/UserComponent.razor | 2 +- Models/Team.cs | 6 ++ Pages/DisplayUtils.cs | 24 ++++++++ Pages/TeamListPanel.razor | 102 +++++++++++++++++++++++++++++- Pages/TeamListPanel.razor.cs | 109 +++++++++++++++++++++++++++++++++ Pages/UserListPanel.razor.cs | 23 ++----- Program.cs | 6 +- Services/ErrorsUtils.cs | 27 ++++++++ Services/HttpTeamService.cs | 50 +++++++++++++++ Services/HttpUsersService.cs | 31 ++-------- Services/ITeamService.cs | 12 ++++ Services/ServiceException.cs | 7 ++- Shared/NavMenu.razor | 9 ++- wwwroot/index.html | 1 + 15 files changed, 365 insertions(+), 55 deletions(-) create mode 100644 Models/Team.cs create mode 100644 Pages/DisplayUtils.cs create mode 100644 Pages/TeamListPanel.razor.cs create mode 100644 Services/ErrorsUtils.cs create mode 100644 Services/HttpTeamService.cs create mode 100644 Services/ITeamService.cs diff --git a/AdminPanel.csproj b/AdminPanel.csproj index d5bc199..030c8ad 100644 --- a/AdminPanel.csproj +++ b/AdminPanel.csproj @@ -18,6 +18,9 @@ + + + @@ -27,4 +30,12 @@ + + + + + + + + diff --git a/Components/UserComponent.razor b/Components/UserComponent.razor index b33f1f9..c3562ea 100644 --- a/Components/UserComponent.razor +++ b/Components/UserComponent.razor @@ -1,5 +1,4 @@ 
- @if (@User.IsAdmin) {

Administrator @User.Name

@@ -8,6 +7,7 @@ {

@User.Name

} +

email: @User.Email

id: @User.Id

diff --git a/Models/Team.cs b/Models/Team.cs new file mode 100644 index 0000000..b4f7f20 --- /dev/null +++ b/Models/Team.cs @@ -0,0 +1,6 @@ +namespace AdminPanel.Models; + +public record Team(uint Id, string Name, string Picture, string MainColor, string SecondColor) +{ + +} \ No newline at end of file diff --git a/Pages/DisplayUtils.cs b/Pages/DisplayUtils.cs new file mode 100644 index 0000000..fd30ce9 --- /dev/null +++ b/Pages/DisplayUtils.cs @@ -0,0 +1,24 @@ +using System.Text; +using AdminPanel.Services; +using MudBlazor; + +namespace AdminPanel.Pages; + +public class DisplayUtils +{ + public static void ShowErrors(ServiceException e, ISnackbar snackbar) + { + foreach (var erronedArgument in e.ArgumentMessages) + { + var sb = new StringBuilder(erronedArgument.Key); + sb.Append(" :"); + + foreach (var message in erronedArgument.Value) + { + sb.Append("\n\t-"); + sb.Append(message); + } + snackbar.Add(sb.ToString()); + } + } +} \ No newline at end of file diff --git a/Pages/TeamListPanel.razor b/Pages/TeamListPanel.razor index de789a1..813635a 100644 --- a/Pages/TeamListPanel.razor +++ b/Pages/TeamListPanel.razor @@ -1,10 +1,106 @@ @page "/teams" +@using AdminPanel.Models +@using DataGridEditMode = MudBlazor.DataGridEditMode Teams Panel -

TeamListPanel

+

Team Panel

-@code { -} + +
+ + + + + +
+ Apply +
+
+ Close +
+
+ + + + +
+ + Add Team +
+
+ + Remove selection +
+ + + Teams + +
+
+ + + +
+ +

Are you sure you want to delete this team(s)?

+
+
+ Confirm + Close +
+
+
+ + + + + + + + + + + + + + + + + + diff --git a/Pages/TeamListPanel.razor.cs b/Pages/TeamListPanel.razor.cs new file mode 100644 index 0000000..32f2af8 --- /dev/null +++ b/Pages/TeamListPanel.razor.cs @@ -0,0 +1,109 @@ +using System.ComponentModel.DataAnnotations; +using System.Text; +using AdminPanel.Models; +using AdminPanel.Services; +using Blazorise.Extensions; +using Microsoft.AspNetCore.Components; +using MudBlazor; + +namespace AdminPanel.Pages; + +public partial class TeamListPanel +{ + [Inject] private ISnackbar Snackbar { get; init; } + [Inject] private ITeamService TeamService { get; init; } + private bool _isOpenAdd; + private bool _isOpenConf; + private bool _success; + private string[] _errors = { }; + + [StringLength(1,ErrorMessage = "Nombre de caractères minimal : 1 ")] + private string? FormName { get; set; } + + [RegularExpression(@"(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}(\.[a-zA-Z0-9]{2,})?", ErrorMessage = "L'image doit être le lien d'une image sur le web")] + private string? FormPicture { get; set; } + + [RegularExpression(@"(/#[0-9a-f]{6})",ErrorMessage = "La couleur principale doit être un code hexadécimal")] + private string? FormMainColor { get; set; } + + [RegularExpression(@"(/#[0-9a-f]{6})",ErrorMessage = "La couleur secondaire doit être un code hexadécimal")] + private string? FormSecondaryColor { get; set; } + private MudDataGrid? Grid { get; set; } + private HashSet SelectedTeams { get; set; } = new(); + + private void UnBindForm() + { + FormName = null; + FormPicture = null; + FormMainColor = null; + FormSecondaryColor = null; + } + + private async Task> GetTeamsFromServer(GridState state) + { + var (count, teams) = await TeamService.ListTeam((uint)(state.Page * state.PageSize), (uint)state.PageSize); + return new GridData + { + TotalItems = (int)count, + Items = teams + }; + } + + private void ToggleOpen() + { + _isOpenAdd = !(_isOpenAdd); + } + + private void ToggleOpenConfirmation() + { + _isOpenConf = !(_isOpenConf); + } + + private async Task AddTeam() + { + try + { + await TeamService.AddTeam(FormName!, FormPicture!, FormMainColor!, FormSecondaryColor!); + } + catch (ServiceException err) + { + DisplayUtils.ShowErrors(err,Snackbar); + } + } + + private async void AddTeamConfirmed() + { + await AddTeam(); + ToggleOpen(); + await Grid!.ReloadServerData(); + UnBindForm(); + } + + private async void DeleteSelectedTeams() + { + if (!SelectedTeams.IsNullOrEmpty()) + { + var ids = SelectedTeams.ToList().ConvertAll(team => team.Id); + await TeamService.DeleteTeams(ids); + await Grid!.ReloadServerData(); + } + CloseToggleConf(); + } + + private void CloseToggleConf() + { + _isOpenConf = false; + } + + private async void OnTeamUpdated(Team t) + { + try + { + await TeamService.UpdateTeam(t); + } + catch (ServiceException err) + { + DisplayUtils.ShowErrors(err,Snackbar); + } + } +} \ No newline at end of file diff --git a/Pages/UserListPanel.razor.cs b/Pages/UserListPanel.razor.cs index f4eff47..3b3d51d 100644 --- a/Pages/UserListPanel.razor.cs +++ b/Pages/UserListPanel.razor.cs @@ -52,7 +52,7 @@ namespace AdminPanel.Pages } catch (ServiceException err) { - ShowErrors(err); + DisplayUtils.ShowErrors(err,Snackbar); } } @@ -70,25 +70,10 @@ namespace AdminPanel.Pages } catch (ServiceException err) { - ShowErrors(err); - } - } - - private void ShowErrors(ServiceException e) - { - foreach (var erronedArgument in e.ArgumentMessages) - { - var sb = new StringBuilder(erronedArgument.Key); - sb.Append(" :"); - - foreach (var message in erronedArgument.Value) - { - sb.Append("\n\t-"); - sb.Append(message); - } - Snackbar.Add(sb.ToString()); + DisplayUtils.ShowErrors(err,Snackbar); } } + private async void RemoveSelection(MouseEventArgs e) { @@ -101,7 +86,7 @@ namespace AdminPanel.Pages } catch (ServiceException err) { - ShowErrors(err); + DisplayUtils.ShowErrors(err,Snackbar); } } diff --git a/Program.cs b/Program.cs index 91dd8e2..ae3bdd6 100644 --- a/Program.cs +++ b/Program.cs @@ -1,5 +1,4 @@ using AdminPanel; -using AdminPanel.Pages; using AdminPanel.Services; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; @@ -8,7 +7,6 @@ using MudBlazor.Services; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); builder.RootComponents.Add("head::after"); - builder.Services .AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); @@ -21,8 +19,10 @@ var client = new HttpClient builder.Services.AddScoped(sp => new HttpUsersService(client)); //builder.Services.AddScoped(); +builder.Services.AddScoped(sp => new HttpTeamService(client)); builder.Services.AddMudServices(); -await builder.Build().RunAsync(); \ No newline at end of file +await builder.Build().RunAsync(); + diff --git a/Services/ErrorsUtils.cs b/Services/ErrorsUtils.cs new file mode 100644 index 0000000..62c3e8c --- /dev/null +++ b/Services/ErrorsUtils.cs @@ -0,0 +1,27 @@ +using System.Net; +using System.Net.Http.Json; + +namespace AdminPanel.Services; + +public class ErrorsUtils +{ + private record ServerErrorMessageDto(string? Field, string? Error, string Message); + + public static async Task EnsureResponseIsOk(HttpResponseMessage response) + { + if (response.StatusCode == HttpStatusCode.OK) + { + return; + } + + var content = await response.Content.ReadFromJsonAsync(); + var messages = content! + .GroupBy(e => e.Field ?? e.Error!) + .ToDictionary( + g => g.Key, + g => g.ToList().ConvertAll(e => e.Message) + ); + + throw new ServiceException("Server refused request", messages); + } +} \ No newline at end of file diff --git a/Services/HttpTeamService.cs b/Services/HttpTeamService.cs new file mode 100644 index 0000000..6f38f26 --- /dev/null +++ b/Services/HttpTeamService.cs @@ -0,0 +1,50 @@ +using System.Net.Http.Json; +using AdminPanel.Models; + +namespace AdminPanel.Services; + +public class HttpTeamService : ITeamService +{ + + private readonly HttpClient _client; + + public HttpTeamService(HttpClient client) + { + this._client = client; + } + + private record ListTeamResponse(uint TotalCount, List Teams); + + public async Task<(uint, List)> ListTeam(uint from, uint count) + { + var httpResponse = await _client.GetAsync($"/api/admin/list-team?start={from}&n={count}"); + httpResponse.EnsureSuccessStatusCode(); + var response = await httpResponse.Content.ReadFromJsonAsync()!; + + return (response.TotalCount, response.Teams); + } + + private record AddTeamDTORequest(string Name, string Picture, string MainColor, string SecondaryColor); + + public async Task AddTeam(string name, string picture, string mainColor, string secondaryColor) + { + var httpResponse = await _client.PostAsJsonAsync($"/api/admin/add-team", new AddTeamDTORequest(name,picture,mainColor,secondaryColor)); + await ErrorsUtils.EnsureResponseIsOk(httpResponse); + } + + private record DeleteTeamsDTORequest(List Teams); + public async Task DeleteTeams(List teams) + { + var httpResponse = await _client.PostAsJsonAsync($"/api/admin/delete-teams", new DeleteTeamsDTORequest(teams)); + await ErrorsUtils.EnsureResponseIsOk(httpResponse); + + } + + private record UpdateTeamDTORequest(uint Id, string Name, string Picture, string MainColor, string SecondaryColor); + public async Task UpdateTeam(Team team) + { + var httpResponse = await _client.PostAsJsonAsync($"/api/admin/team/{team.Id}/update", + new UpdateTeamDTORequest(team.Id, team.Name, team.Picture, team.MainColor, team.SecondColor) ); + await ErrorsUtils.EnsureResponseIsOk(httpResponse); + } +} \ No newline at end of file diff --git a/Services/HttpUsersService.cs b/Services/HttpUsersService.cs index 824fefd..3582edc 100644 --- a/Services/HttpUsersService.cs +++ b/Services/HttpUsersService.cs @@ -13,35 +13,14 @@ public class HttpUsersService : IUsersService { this.Client = client; } - - private record ServerErrorMessageDto(string? Field, string? Error, string Message); - - private async Task EnsureResponseIsOk(HttpResponseMessage response) - { - - if (response.StatusCode == HttpStatusCode.OK) - { - return; - } - - var content = await response.Content.ReadFromJsonAsync(); - var messages = content! - .GroupBy(e => e.Field ?? e.Error!) - .ToDictionary( - g => g.Key, - g => g.ToList().ConvertAll(e => e.Message) - ); - - throw new ServiceException("Server refused request", messages); - } - + private record ListUsersResponseDto(List Users, uint TotalCount); public async Task<(uint, List)> ListUsers(uint from, uint len, string? searchString = null) { var httpResponse = await Client.GetAsync($"/api/admin/list-users?start={from}&n={len}&search={searchString ?? ""}"); - await EnsureResponseIsOk(httpResponse); + await ErrorsUtils.EnsureResponseIsOk(httpResponse); var response = await httpResponse.Content.ReadFromJsonAsync() ?? throw new Exception("Received non-parseable json from server"); @@ -58,7 +37,7 @@ public class HttpUsersService : IUsersService var httpResponse = await Client.PostAsJsonAsync("/api/admin/user/add", new AddUserRequestDto(username, email, password, isAdmin)); - await EnsureResponseIsOk(httpResponse); + await ErrorsUtils.EnsureResponseIsOk(httpResponse); var response = await httpResponse.Content.ReadFromJsonAsync() ?? throw new Exception("Received non-parseable json from server"); @@ -79,7 +58,7 @@ public class HttpUsersService : IUsersService var httpResponse = await Client.PostAsJsonAsync("/api/admin/user/remove-all", new RemoveUsersRequestDto(userIds.ToArray())); - await EnsureResponseIsOk(httpResponse); + await ErrorsUtils.EnsureResponseIsOk(httpResponse); } private record UpdateUserRequestDto(string Email, string Username, bool IsAdmin); @@ -88,6 +67,6 @@ public class HttpUsersService : IUsersService { var httpResponse = await Client.PostAsJsonAsync($"/api/admin/user/{user.Id}/update", new UpdateUserRequestDto(user.Email, user.Name, user.IsAdmin)); - await EnsureResponseIsOk(httpResponse); + await ErrorsUtils.EnsureResponseIsOk(httpResponse); } } \ No newline at end of file diff --git a/Services/ITeamService.cs b/Services/ITeamService.cs new file mode 100644 index 0000000..163d883 --- /dev/null +++ b/Services/ITeamService.cs @@ -0,0 +1,12 @@ +using AdminPanel.Models; + +namespace AdminPanel.Services; + +public interface ITeamService +{ + public Task<(uint, List)> ListTeam(uint from, uint count); + public Task AddTeam(string name, string picture, string mainColor, string secondaryColor); + public Task DeleteTeams(List teams); + public Task UpdateTeam(Team team); + +} \ No newline at end of file diff --git a/Services/ServiceException.cs b/Services/ServiceException.cs index 8dbcb00..853efac 100644 --- a/Services/ServiceException.cs +++ b/Services/ServiceException.cs @@ -1,6 +1,9 @@ +using System.Net; +using System.Net.Http.Json; + namespace AdminPanel.Services; -class ServiceException : Exception +public class ServiceException : Exception { public Dictionary> ArgumentMessages { get; init; } @@ -15,4 +18,6 @@ class ServiceException : Exception { ArgumentMessages = arguments; } + + } \ No newline at end of file diff --git a/Shared/NavMenu.razor b/Shared/NavMenu.razor index 51db3dd..fedc92f 100644 --- a/Shared/NavMenu.razor +++ b/Shared/NavMenu.razor @@ -15,8 +15,13 @@ + diff --git a/wwwroot/index.html b/wwwroot/index.html index 74fb073..7d18a24 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -16,6 +16,7 @@ +