Add Users And account administration panel #1
Merged
maxime.batista
merged 5 commits from users
into master
1 year ago
@ -1,32 +1,95 @@
|
||||
@page "/users"
|
||||
|
||||
|
||||
@using AdminPanel.Components;
|
||||
@using AdminPanel.Models;
|
||||
@using AdminPanel.Models
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
|
||||
<PageTitle>User Panel</PageTitle>
|
||||
|
||||
<h1>User Panel</h1>
|
||||
|
||||
<div class="users-div">
|
||||
@if (Users == null)
|
||||
{
|
||||
<p>Fetching Data...</p>
|
||||
}
|
||||
else
|
||||
foreach (User user in Users)
|
||||
{
|
||||
<div class="user-cell">
|
||||
<UserComponent User=@user/>
|
||||
<Leaflet>
|
||||
<Header>
|
||||
see more
|
||||
</Header>
|
||||
|
||||
<Body>
|
||||
<p>Ratio</p>
|
||||
</Body>
|
||||
</Leaflet>
|
||||
<div id="users-div">
|
||||
|
||||
<MudOverlay Visible="IsAddingUser" DarkBackground>
|
||||
<MudForm id="add-account-form">
|
||||
<MudTextField T="string"
|
||||
Label="Name"
|
||||
@bind-Value="FormAccountName"
|
||||
Required
|
||||
Validation="@(VerifyLength(6, 256))"/>
|
||||
<MudTextField T="string"
|
||||
Label="Email"
|
||||
@bind-Value="FormAccountEmail"
|
||||
Required
|
||||
Validation="@(new EmailAddressAttribute() { ErrorMessage = "The email address is invalid" })"/>
|
||||
<MudTextField T="string"
|
||||
InputType="InputType.Password"
|
||||
Label="Password"
|
||||
@bind-Value="FormAccountPassword"
|
||||
Validation="@(VerifyLength(6, 256))"
|
||||
Required/>
|
||||
<MudCheckBox T="bool" Label="Is Administrator" @bind-Value="FormAccountIsAdmin"/>
|
||||
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Secondary" OnClick="@(() => IsAddingUser = false)">Cancel</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="ConfirmAddAccount">Add Account</MudButton>
|
||||
</MudForm>
|
||||
</MudOverlay>
|
||||
|
||||
|
||||
<MudDataGrid
|
||||
T="User"
|
||||
@ref="Grid"
|
||||
ServerData="LoadUsersFromServer"
|
||||
MultiSelection="true"
|
||||
ColumnResizeMode="ResizeMode.Column"
|
||||
RowsPerPage="10"
|
||||
ReadOnly="false"
|
||||
EditMode="DataGridEditMode.Form"
|
||||
@bind-SelectedItems="SelectedItems"
|
||||
CommittedItemChanges="OnAccountUpdated">
|
||||
|
||||
<ToolBarContent>
|
||||
<div style="display: flex; align-items: center">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Add" OnClick="@(() => IsAddingUser = true)" Color="@Color.Success" Class="add-item-btn"/>
|
||||
<MudText Align="Align.Center">Add Account</MudText>
|
||||
</div>
|
||||
}
|
||||
<div style="display: flex; align-items: center">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Remove" OnClick="RemoveSelection" Color="@Color.Error" Class="remove-item-btn"/>
|
||||
<MudText Align="Align.Center">Remove Selection</MudText>
|
||||
</div>
|
||||
<MudSpacer/>
|
||||
<MudText>Accounts</MudText>
|
||||
<MudSpacer/>
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Value="SearchString"
|
||||
OnKeyDown="@ValidateSearch"
|
||||
Placeholder="Search an email or a name"
|
||||
AdornmentIcon="@Icons.Material.Filled.Search"
|
||||
Immediate
|
||||
IconSize="Size.Medium"/>
|
||||
</ToolBarContent>
|
||||
|
||||
<Columns>
|
||||
<SelectColumn T="User"/>
|
||||
<PropertyColumn Property="x => x.Name" Required/>
|
||||
<PropertyColumn Property="x => x.Email" Title="Email Address" Required/>
|
||||
<TemplateColumn Title="Is Administrator">
|
||||
<CellTemplate>
|
||||
<MudCheckBox Value="@context.Item!.IsAdmin" ReadOnly/>
|
||||
</CellTemplate>
|
||||
<EditTemplate>
|
||||
<MudText>Is Administrator :</MudText>
|
||||
<MudCheckBox @bind-Value="context.Item.IsAdmin"/>
|
||||
</EditTemplate>
|
||||
</TemplateColumn>
|
||||
<PropertyColumn Property="x => x.Id" IsEditable="false"/>
|
||||
<TemplateColumn>
|
||||
<CellTemplate>
|
||||
<MudIconButton Size="Size.Small" Icon="@Icons.Material.Outlined.Edit" OnClick="@context.Actions.StartEditingItemAsync"/>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</Columns>
|
||||
<PagerContent>
|
||||
<MudDataGridPager T="User" PageSizeOptions="new[] { 1, 2, 4, 10, 25, 50, 100 }"/>
|
||||
</PagerContent>
|
||||
</MudDataGrid>
|
||||
</div>
|
@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
#add-account-form {
|
||||
background-color: white;
|
||||
visibility: hidden;
|
||||
color: #ffba00;
|
||||
}
|
||||
|
@ -1,11 +1,28 @@
|
||||
using AdminPanel;
|
||||
using AdminPanel.Pages;
|
||||
using AdminPanel.Services;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using MudBlazor.Services;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
builder.Services
|
||||
.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
// builder.Logging.SetMinimumLevel(LogLevel.Debug);
|
||||
|
||||
var client = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri("http://localhost:8080")
|
||||
};
|
||||
|
||||
builder.Services.AddScoped<IUsersService>(sp => new HttpUsersService(client));
|
||||
//builder.Services.AddScoped<IUsersService, UsersServiceStub>();
|
||||
|
||||
builder.Services.AddMudServices();
|
||||
|
||||
|
||||
await builder.Build().RunAsync();
|
@ -0,0 +1,93 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using AdminPanel.Models;
|
||||
|
||||
namespace AdminPanel.Services;
|
||||
|
||||
public class HttpUsersService : IUsersService
|
||||
{
|
||||
private HttpClient Client { get; }
|
||||
|
||||
public HttpUsersService(HttpClient client)
|
||||
{
|
||||
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);
|
||||
|
||||
var response = await httpResponse.Content.ReadFromJsonAsync<ListUsersResponseDto>()
|
||||
?? throw new Exception("Received non-parseable json from server");
|
||||
|
||||
return (response.TotalCount, response.Users);
|
||||
}
|
||||
|
||||
private record AddUserRequestDto(string Username, string Email, string Password, bool IsAdmin);
|
||||
|
||||
private record AddUserResponseDto(uint Id);
|
||||
|
||||
public async Task<User> AddUser(string username, string email, string password, bool isAdmin)
|
||||
{
|
||||
var httpResponse = await Client.PostAsJsonAsync("/api/admin/user/add",
|
||||
new AddUserRequestDto(username, email, password, isAdmin));
|
||||
|
||||
await EnsureResponseIsOk(httpResponse);
|
||||
|
||||
var response = await httpResponse.Content.ReadFromJsonAsync<AddUserResponseDto>()
|
||||
?? throw new Exception("Received non-parseable json from server");
|
||||
|
||||
return new User
|
||||
{
|
||||
Name = username,
|
||||
Email = email,
|
||||
IsAdmin = isAdmin,
|
||||
Id = response.Id
|
||||
};
|
||||
}
|
||||
|
||||
private record RemoveUsersRequestDto(uint[] Identifiers);
|
||||
|
||||
public async Task RemoveUsers(IEnumerable<uint> userIds)
|
||||
{
|
||||
var httpResponse =
|
||||
await Client.PostAsJsonAsync("/api/admin/user/remove-all", new RemoveUsersRequestDto(userIds.ToArray()));
|
||||
|
||||
await EnsureResponseIsOk(httpResponse);
|
||||
}
|
||||
|
||||
private record UpdateUserRequestDto(string Email, string Username, bool IsAdmin);
|
||||
|
||||
public async Task UpdateUser(User user)
|
||||
{
|
||||
var httpResponse = await Client.PostAsJsonAsync($"/api/admin/user/{user.Id}/update",
|
||||
new UpdateUserRequestDto(user.Email, user.Name, user.IsAdmin));
|
||||
await EnsureResponseIsOk(httpResponse);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
namespace AdminPanel.Services;
|
||||
|
||||
class ServiceException : Exception
|
||||
{
|
||||
public Dictionary<string, List<string>> ArgumentMessages { get; init; }
|
||||
|
||||
|
||||
|
||||
public ServiceException(string? message, Dictionary<string, List<string>> arguments) : base(message)
|
||||
{
|
||||
ArgumentMessages = arguments;
|
||||
}
|
||||
|
||||
public ServiceException(string? message, Dictionary<string, List<string>> arguments, Exception? innerException) : base(message, innerException)
|
||||
{
|
||||
ArgumentMessages = arguments;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue