Add user manager page (#22)
Reviewed-on: #22
This commit was merged in pull request #22.
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
@using Blazored.FluentValidation
|
||||
|
||||
<EditForm EditContext="EditContext" OnSubmit="HandleLoginSubmit">
|
||||
<FluentValidationValidator Validator="Validator" />
|
||||
<div class="login-form">
|
||||
<div class="login-field">
|
||||
<div class="input-title">@ResourcesKey.EnterUsername</div>
|
||||
<InputText class="input-text"
|
||||
@bind-Value="UserDto.Username" />
|
||||
</div>
|
||||
<div class="login-field">
|
||||
<div class="input-title">@ResourcesKey.EnterPassword</div>
|
||||
<InputText class="input-text" type="password"
|
||||
@bind-Value="UserDto.Password" />
|
||||
</div>
|
||||
<div class="login-field">
|
||||
<button class="login-button" type="submit" disabled="@IsLoading">
|
||||
@if (IsLoading)
|
||||
{
|
||||
<div class="loading"></div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ResourcesKey.Login
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
@@ -0,0 +1,44 @@
|
||||
using GameIdeas.BlazorApp.Pages.UserMenu.Gateways;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.UserMenu.Components;
|
||||
|
||||
public partial class Login
|
||||
{
|
||||
[Parameter] public IAuthGateway AuthGateway { get; set; } = default!;
|
||||
|
||||
private EditContext? EditContext;
|
||||
private UserDto UserDto = new();
|
||||
private bool IsLoading = false;
|
||||
private LoginValidator Validator = new();
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
EditContext = new EditContext(UserDto);
|
||||
}
|
||||
|
||||
private async Task HandleLoginSubmit()
|
||||
{
|
||||
if (EditContext?.Validate() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
await AuthGateway.Login(UserDto);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
UserDto.Password = string.Empty;
|
||||
EditContext?.Validate();
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px 8px;
|
||||
gap: 20px;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.login-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
::deep .input-text {
|
||||
background: var(--input-selected);
|
||||
border: 2px solid var(--input-selected);
|
||||
border-radius: var(--small-radius);
|
||||
padding: 6px;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
::deep .input-text:focus-visible {
|
||||
border: 2px solid var(--violet) !important;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
background: var(--violet);
|
||||
border: none;
|
||||
border-radius: 100px;
|
||||
height: 32px;
|
||||
color: var(--white);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-button:hover {
|
||||
background: var(--violet-selected);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-button:disabled {
|
||||
background: var(--violet-selected);
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.loading {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid rgba(0, 0, 0, 0.2);
|
||||
border-top-color: var(--white);
|
||||
animation: loading 1s linear infinite;
|
||||
justify-self: center;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using FluentValidation;
|
||||
using GameIdeas.Shared.Dto;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.UserMenu.Components;
|
||||
|
||||
public class LoginValidator : AbstractValidator<UserDto>
|
||||
{
|
||||
public LoginValidator()
|
||||
{
|
||||
RuleFor(dto => dto.Username)
|
||||
.NotNull()
|
||||
.NotEmpty();
|
||||
|
||||
RuleFor(dto => dto.Password)
|
||||
.NotNull()
|
||||
.NotEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using GameIdeas.BlazorApp.Services;
|
||||
using GameIdeas.BlazorApp.Shared.Constants;
|
||||
using GameIdeas.BlazorApp.Shared.Exceptions;
|
||||
using GameIdeas.Resources;
|
||||
using GameIdeas.Shared.Dto;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.UserMenu.Gateways;
|
||||
|
||||
public class AuthGateway(IHttpClientService httpClient,
|
||||
AuthenticationStateProvider stateProvider) : IAuthGateway
|
||||
{
|
||||
public async Task<bool> Login(UserDto userDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var token = await httpClient.PostAsync<TokenDto>(Endpoints.Auth.Login, userDto);
|
||||
await ((JwtAuthenticationStateProvider)stateProvider).NotifyUserAuthenticationAsync(token!.Token!);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new AuthenticationUserException(ResourcesKey.UserLoginFailed);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Logout()
|
||||
{
|
||||
try
|
||||
{
|
||||
await ((JwtAuthenticationStateProvider)stateProvider).NotifyUserLogoutAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new AuthenticationUserException(ResourcesKey.UserLogoutFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using GameIdeas.Shared.Dto;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.UserMenu.Gateways;
|
||||
|
||||
public interface IAuthGateway
|
||||
{
|
||||
Task<bool> Login(UserDto userDto);
|
||||
Task Logout();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
@using GameIdeas.BlazorApp.Pages.UserMenu.Components
|
||||
@using GameIdeas.BlazorApp.Shared.Components.BackdropFilter
|
||||
@using GameIdeas.BlazorApp.Shared.Constants
|
||||
@using GameIdeas.Shared.Constants
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
|
||||
<div class="menu">
|
||||
<div class="icon" @onclick=HandleAccountClicked>
|
||||
@Icons.Account
|
||||
</div>
|
||||
<div class="container">
|
||||
@if (ContentVisile)
|
||||
{
|
||||
<div class="content">
|
||||
<AuthorizeView Roles="@GlobalConstants.ADMIN_MEMBER">
|
||||
<Authorized>
|
||||
<div class="menu-element">
|
||||
@ResourcesKey.CategoriesManager
|
||||
</div>
|
||||
<span class="line"></span>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<AuthorizeView Roles="@GlobalConstants.ADMINISTRATOR">
|
||||
<Authorized>
|
||||
<a class="menu-element" href="/Users">
|
||||
@ResourcesKey.UserManager
|
||||
</a>
|
||||
<span class="line"></span>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<div class="menu-element" @onclick="HandleLogoutClicked">
|
||||
@ResourcesKey.Logout
|
||||
</div>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<Login AuthGateway="AuthGateway" />
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BackdropFilter AllowBodyScroll=true CloseOnClick=true Color="BackdropFilterColor.Transparent"
|
||||
IsVisible="ContentVisile" OnClick="HandleBackdropFilterClicked" />
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using GameIdeas.BlazorApp.Pages.UserMenu.Gateways;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GameIdeas.BlazorApp.Pages.UserMenu;
|
||||
|
||||
public partial class UserMenu
|
||||
{
|
||||
[Inject] private IAuthGateway AuthGateway { get; set; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
|
||||
|
||||
private bool ContentVisile = false;
|
||||
|
||||
private async Task HandleLogoutClicked()
|
||||
{
|
||||
ContentVisile = false;
|
||||
await AuthGateway.Logout();
|
||||
NavigationManager.NavigateTo("/Games");
|
||||
}
|
||||
|
||||
private void HandleAccountClicked()
|
||||
{
|
||||
ContentVisile = true;
|
||||
}
|
||||
|
||||
private void HandleBackdropFilterClicked()
|
||||
{
|
||||
ContentVisile = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
.menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon ::deep svg {
|
||||
fill: var(--line);
|
||||
}
|
||||
|
||||
.container {
|
||||
right: 0;
|
||||
position: absolute;
|
||||
margin-top: 4px;
|
||||
z-index: var(--index-dropdown);
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
border-radius: var(--big-radius);
|
||||
border: 2px solid var(--input-selected);
|
||||
background: var(--dropdown-content);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 0 6px;
|
||||
border-bottom: 2px solid var(--input-selected);
|
||||
}
|
||||
|
||||
.menu-element {
|
||||
color: var(--white);
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
padding: 0 20px;
|
||||
align-content: center;
|
||||
text-wrap: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-element:hover {
|
||||
background: var(--input-selected)
|
||||
}
|
||||
Reference in New Issue
Block a user