samuel 1 year ago committed by sam
parent 98e6386d36
commit 39ed6a9ac7

@ -1,6 +1,6 @@
namespace AdminPanel.Models;
public record Team(string Name, string Picture, string MainColor, string SecondColor)
public record Team(uint Id, string Name, string Picture, string MainColor, string SecondColor)
{
}

@ -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());
}
}
}

@ -1,16 +1,40 @@
@page "/teams"
@using AdminPanel.Models
@using DataGridEditMode = MudBlazor.DataGridEditMode
<PageTitle>Teams Panel</PageTitle>
<MudPopover Open="@_isOpen" Fixed="true" Class="px-4 pt-4">
<h1>Team Panel</h1>
<MudPopover Open="@_isOpenAdd" Fixed="true" Class="px-4 pt-4">
<div class="d-flex flex-column">
<MudForm @ref="form" @bind-IsValid="@success" @bind-Errors="@errors">
<MudTextField T="string" Label="Name" Required="true" @bind-Value="FormName" RequiredError="Team's name is required!" />
<MudTextField T="string" Label="Picture" Required="true" @bind-Value="FormPicture" RequiredError="Picture is required!"/>
<MudTextField T="string" Label="MainColor" Required="true" @bind-Value="FormMainColor" RequiredError="Main color is required!"/>
<MudTextField T="string" Label="SecondaryColor" Required="true" @bind-Value="FormSecondaryColor" RequiredError="Secondary color is required!"/>
<MudForm @bind-IsValid="@success" @bind-Errors="@errors">
<MudTextField
T="string"
Label="Name"
Required="true"
@bind-Value="FormName"
RequiredError="Team's name is required!" />
<MudTextField
T="string"
Label="Picture"
Required="true"
@bind-Value="FormPicture"
RequiredError="Picture is required!"/>
<MudTextField
T="string"
Label="MainColor"
Required="true"
@bind-Value="FormMainColor"
RequiredError="Main color is required!"/>
<MudTextField
T="string"
Label="SecondaryColor"
Required="true"
@bind-Value="FormSecondaryColor"
RequiredError="Secondary color is required!"/>
<div class="d-flex justify-center">
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@AddTeamConfirmed" Class="ml-auto">Apply</MudButton>
</div>
@ -18,19 +42,65 @@
<MudButton OnClick="@ToggleOpen" Class="ml-auto mr-n3 mb-1" Color="Color.Error">Close</MudButton>
</div>
</MudPopover>
<MudCard>
<MudCardContent>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@ToggleOpen">Ajouter</MudButton>
</MudCardContent>
</MudCard>
<MudDataGrid T="Team" RowsPerPage="1" ServerData="GetTeamsFromServer">
<ToolBarContent style="display: flex; justify-content: space-evenly;">
<!--<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@ToggleOpen">Ajouter</MudButton>-->
<div style="display: flex; align-items: center">
<MudIconButton Icon="@Icons.Material.Filled.Add" OnClick="@ToggleOpen" Color="@Color.Success"/>
<MudText>Add Team</MudText>
</div>
<div style="display: flex; align-items: center">
<MudIconButton Icon="@Icons.Material.Filled.Remove" OnClick="@ToggleOpenConfirmation" Color="@Color.Error"/>
<MudText>Remove selection</MudText>
</div>
<!--<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@ToggleOpenConfirmation">Supprimer</MudButton>-->
<MudSpacer/>
<MudText>Teams</MudText>
<MudSpacer/>
</ToolBarContent>
</MudCard>
<MudPopover Open="@_isOpenConf" Fixed="true" Class="px-4 pt-4">
<div class="d-flex flex-column">
<Content>
<p>Are you sure you want to delete this team(s)?</p>
</Content>
<Footer>
<MudButton Variant="Variant.Outlined" Color="Color.Default" OnClick="@DeleteSelectedTeams">Confirm</MudButton>
<MudButton Variant="Variant.Outlined" Color="Color.Default" OnClick="@CloseToggleConf">Close</MudButton>
</Footer>
</div>
</MudPopover>
<MudDataGrid
T="Team"
RowsPerPage="4"
ServerData="GetTeamsFromServer"
@ref="Grid"
MultiSelection="true"
@bind-SelectedItems="SelectedTeams"
ReadOnly="false"
EditMode="DataGridEditMode.Form"
CommittedItemChanges="OnTeamUpdated">
<Columns>
<PropertyColumn Property="x => x.Name" />
<PropertyColumn Property="x => x.MainColor" />
<PropertyColumn Property="x => x.SecondColor" />
<PropertyColumn Property="x => x.Picture" />
<SelectColumn T="Team"/>
<PropertyColumn
Property="x => x.Name"/>
<PropertyColumn
Property="x => x.MainColor"/>
<PropertyColumn
Property="x => x.SecondColor"/>
<PropertyColumn
Property="x => x.Picture"/>
<TemplateColumn>
<CellTemplate>
<MudIconButton Size="Size.Small" Icon="@Icons.Material.Outlined.Edit" OnClick="@context.Actions.StartEditingItemAsync"/>
</CellTemplate>
</TemplateColumn>
</Columns>
<PagerContent>
<MudDataGridPager T="Team" PageSizeOptions="new []{1, 5, 10, 25, 50, 100}"/>
<MudDataGridPager T="Team" PageSizeOptions="new[] { 1, 2, 4, 10, 25, 50, 100 }"/>
</PagerContent>
</MudDataGrid>

@ -1,6 +1,8 @@
using System.Net.Http.Json;
using System.ComponentModel.DataAnnotations;
using System.Text;
using AdminPanel.Models;
using AdminPanel.Services;
using Blazorise.Extensions;
using Microsoft.AspNetCore.Components;
using MudBlazor;
@ -8,45 +10,100 @@ namespace AdminPanel.Pages;
public partial class TeamListPanel
{
[Inject]
private ITeamService TeamService { get; init; }
private bool _isOpen;
private MudForm form;
bool success;
string[] errors = { };
[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; }
private string? FormMainColor { get; set; }
private string? FormSecondaryColor { 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<Team>? Grid { get; set; }
private HashSet<Team> SelectedTeams { get; set; } = new();
private void UnBindForm()
{
FormName = null;
FormPicture = null;
FormMainColor = null;
FormSecondaryColor = null;
}
private async Task<GridData<Team>> GetTeamsFromServer(GridState<Team> state)
{
var (count, teams) = await TeamService.ListTeam((uint)(state.Page * state.PageSize), (uint)state.PageSize);
return new GridData<Team>
{
TotalItems = (int) count,
TotalItems = (int)count,
Items = teams
};
}
private void ToggleOpen()
{
_isOpen = !(_isOpen);
_isOpenAdd = !(_isOpenAdd);
}
private void ToggleOpenConfirmation()
{
_isOpenConf = !(_isOpenConf);
}
private async void AddTeam()
private async Task AddTeam()
{
await TeamService.AddTeam(FormName!,FormPicture!,FormMainColor!,FormSecondaryColor!);
try
{
await TeamService.AddTeam(FormName!, FormPicture!, FormMainColor!, FormSecondaryColor!);
}
catch (ServiceException err)
{
DisplayUtils.ShowErrors(err,Snackbar);
}
}
private void AddTeamConfirmed()
private async void AddTeamConfirmed()
{
AddTeam();
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);
}
}
}

@ -52,7 +52,7 @@ namespace AdminPanel.Pages
}
catch (ServiceException err)
{
ShowErrors(err);
DisplayUtils.ShowErrors(err,Snackbar);
}
}
@ -70,27 +70,11 @@ namespace AdminPanel.Pages
}
catch (ServiceException err)
{
ShowErrors(err);
DisplayUtils.ShowErrors(err,Snackbar);
}
}
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());
}
}
private async void RemoveSelection(MouseEventArgs e)
{
var users = SelectedItems.ToList().ConvertAll(u => u.Id);
@ -102,7 +86,7 @@ namespace AdminPanel.Pages
}
catch (ServiceException err)
{
ShowErrors(err);
DisplayUtils.ShowErrors(err,Snackbar);
}
}

@ -19,6 +19,7 @@ var client = new HttpClient
builder.Services.AddScoped<IUsersService>(sp => new HttpUsersService(client));
//builder.Services.AddScoped<IUsersService, UsersServiceStub>();
builder.Services.AddScoped<ITeamService>(sp => new HttpTeamService(client));
builder.Services.AddMudServices();

@ -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<ServerErrorMessageDto[]>();
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);
}
}

@ -24,11 +24,27 @@ public class HttpTeamService : ITeamService
return (response.TotalCount, response.Teams);
}
private record AddTeamRequest(string Name, string Picture, string MainColor, string SecondaryColor);
private record AddTeamDTORequest(string Name, string Picture, string MainColor, string SecondaryColor);
public async Task AddTeam(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 AddTeamRequest(name,Picture,mainColor,secondaryColor));
httpResponse.EnsureSuccessStatusCode();
var httpResponse = await _client.PostAsJsonAsync($"/api/admin/add-team", new AddTeamDTORequest(name,picture,mainColor,secondaryColor));
await ErrorsUtils.EnsureResponseIsOk(httpResponse);
}
private record DeleteTeamsDTORequest(List<uint> Teams);
public async Task DeleteTeams(List<uint> 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);
}
}

@ -14,34 +14,13 @@ 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<ServerErrorMessageDto[]>();
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<User> Users, uint TotalCount);
public async Task<(uint, List<User>)> 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<ListUsersResponseDto>()
?? 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<AddUserResponseDto>()
?? 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);
}
}

@ -5,6 +5,8 @@ namespace AdminPanel.Services;
public interface ITeamService
{
public Task<(uint, List<Team>)> ListTeam(uint from, uint count);
public Task AddTeam(string name, string Picture, string mainColor, string secondaryColor);
public Task AddTeam(string name, string picture, string mainColor, string secondaryColor);
public Task DeleteTeams(List<uint> teams);
public Task UpdateTeam(Team team);
}

@ -1,6 +1,9 @@
using System.Net;
using System.Net.Http.Json;
namespace AdminPanel.Services;
class ServiceException : Exception
public class ServiceException : Exception
{
public Dictionary<string, List<string>> ArgumentMessages { get; init; }
@ -15,4 +18,6 @@ class ServiceException : Exception
{
ArgumentMessages = arguments;
}
}

@ -4,11 +4,6 @@
<MudDialogProvider/>
<MudSnackbarProvider/>
<MudThemeProvider/>
<MudDialogProvider/>
<MudSnackbarProvider/>
<div class="page">
<div class="sidebar">
<NavMenu />

Loading…
Cancel
Save