-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Admin | User role management (#829)
* Add users page * Implement role management
- Loading branch information
1 parent
a3e157b
commit 03e5f68
Showing
20 changed files
with
470 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication | ||
@using SSW.Rewards.ApiClient.Services | ||
@using SSW.Rewards.Shared.DTOs.Roles | ||
@using SSW.Rewards.Shared.DTOs.Users | ||
|
||
@inject IRoleService RoleService | ||
|
||
<MudDialog> | ||
<TitleContent> | ||
<MudText Typo="Typo.h6">Edit User Roles</MudText> | ||
</TitleContent> | ||
<DialogContent> | ||
@if (_loading) | ||
{ | ||
<MudProgressCircular Color="Color.Default" Indeterminate="true" /> | ||
} | ||
else | ||
{ | ||
<MudSelect T="string" @bind-SelectedValues="SelectedRoles" Label="Roles" MultiSelection="true"> | ||
@foreach (var item in _roles) | ||
{ | ||
<MudSelectItem T="string" Value="@item.Name">@item.Name</MudSelectItem> | ||
} | ||
</MudSelect> | ||
} | ||
</DialogContent> | ||
<DialogActions> | ||
<MudButton OnClick="Cancel">Cancel</MudButton> | ||
<MudButton | ||
Variant="Variant.Filled" | ||
Color="Color.Primary" | ||
OnClick="Submit">Save</MudButton> | ||
</DialogActions> | ||
</MudDialog> | ||
|
||
@code { | ||
private bool _loading = true; | ||
private IEnumerable<string> SelectedRoles { get; set; } = Enumerable.Empty<string>(); | ||
IEnumerable<RoleDto> _roles = Enumerable.Empty<RoleDto>(); | ||
|
||
[CascadingParameter] | ||
MudDialogInstance MudDialog { get; set; } | ||
|
||
[Parameter] | ||
public UserDto User { get; set; } | ||
|
||
protected override async Task OnInitializedAsync() | ||
{ | ||
try | ||
{ | ||
await FetchRoles(); | ||
} | ||
catch (AccessTokenNotAvailableException exception) | ||
{ | ||
exception.Redirect(); | ||
} | ||
} | ||
|
||
private async Task FetchRoles() | ||
{ | ||
_loading = true; | ||
_roles = await RoleService.GetRoles(); | ||
SelectedRoles = User.Roles.Select(r => r.Name); | ||
_loading = false; | ||
} | ||
|
||
private void Submit() | ||
{ | ||
User.Roles = _roles.Where(r => SelectedRoles.Contains(r.Name)).ToList(); | ||
MudDialog.Close(DialogResult.Ok(User)); | ||
} | ||
|
||
private void Cancel() => MudDialog?.Cancel(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
@page "/Users" | ||
@using SSW.Rewards.ApiClient.Services | ||
@using SSW.Rewards.Shared.DTOs.Users | ||
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication | ||
@using Icons = MudBlazor.Icons | ||
@using Microsoft.AspNetCore.Authorization | ||
@using SSW.Rewards.Admin.UI.Components | ||
|
||
@attribute [Authorize(Roles = "Admin")] | ||
|
||
@inject IUserAdminService UserAdminService | ||
@inject IDialogService DialogService | ||
@inject ISnackbar Snackbar | ||
|
||
<MudText Typo="Typo.h2">Users</MudText> | ||
<MudText Typo="Typo.body1">All users</MudText> | ||
|
||
<Table TItem="UserDto" | ||
Items="@(_vm.Users.Where(s => s.Name.Contains(_searchString, StringComparison.InvariantCultureIgnoreCase) || s.Email.Contains(_searchString, StringComparison.InvariantCultureIgnoreCase)).OrderBy(i => i.Name).ToList())" | ||
IsLoading="@_loading" | ||
TableTitle="Users"> | ||
<ToolbarContent> | ||
<MudTextField @bind-Value="_searchString" | ||
Placeholder="Search for a user" | ||
Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" | ||
Class="mt-0"/> | ||
</ToolbarContent> | ||
<HeadingTemplate> | ||
<MudTh Style="max-width: fit-content"> | ||
<MudTableSortLabel SortBy="new Func<UserDto, object>(x => x.Id)"> | ||
Id | ||
</MudTableSortLabel> | ||
</MudTh> | ||
<MudTh> | ||
<MudTableSortLabel SortBy="new Func<UserDto, object>(x => x.Name)" InitialDirection="SortDirection.Ascending"> | ||
Name | ||
</MudTableSortLabel> | ||
</MudTh> | ||
<MudTh> | ||
<MudTableSortLabel SortBy="new Func<UserDto, object>(x => x.Email)"> | ||
</MudTableSortLabel> | ||
</MudTh> | ||
<MudTh>Roles</MudTh> | ||
</HeadingTemplate> | ||
<RowTemplate> | ||
<MudTd DataLabel="Id">@context.Id</MudTd> | ||
<MudTd DataLabel="Name">@context.Name</MudTd> | ||
<MudTd DataLabel="Email">@context.Email</MudTd> | ||
<MudTd DataLabel="Roles"> | ||
<MudStack Row="true" Spacing="5" Style="align-items: center; width: 100%"> | ||
<MudText>@string.Join(", ", context.Roles.Select(x => x.Name).OrderBy(x => x))</MudText> | ||
<MudSpacer/> | ||
<MudIconButton | ||
Variant="Variant.Filled" | ||
Color="Color.Primary" | ||
OnClick="() => { EditRoles(context); }" | ||
Icon="@Icons.Material.Filled.Edit"/> | ||
</MudStack> | ||
</MudTd> | ||
</RowTemplate> | ||
</Table> | ||
|
||
@code { | ||
UsersViewModel _vm = new(); | ||
|
||
private bool _loading = true; | ||
private string _searchString = string.Empty; | ||
|
||
protected override async Task OnInitializedAsync() | ||
{ | ||
try | ||
{ | ||
await FetchData(); | ||
} | ||
catch (AccessTokenNotAvailableException exception) | ||
{ | ||
exception.Redirect(); | ||
} | ||
} | ||
|
||
private async Task FetchData() | ||
{ | ||
_loading = true; | ||
_vm = await UserAdminService.GetUsers(); | ||
_loading = false; | ||
} | ||
|
||
private async Task EditRoles(UserDto user) | ||
{ | ||
var dialog = DialogService.Show<SSW.Rewards.Admin.UI.Components.Dialogs.Users.EditUserRoles>("Edit User Roles", new DialogParameters { { "User", user } }); | ||
var result = await dialog.Result; | ||
if (!result.Canceled) | ||
{ | ||
try | ||
{ | ||
if (result.Data is UserDto userResult) | ||
{ | ||
await UserAdminService.UpdateUserRoles(userResult); | ||
StateHasChanged(); | ||
} | ||
|
||
Snackbar.Add("Roles updated", Severity.Success); | ||
} | ||
catch (Exception) | ||
{ | ||
Snackbar.Add("Failed to update roles", Severity.Error); | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System.Net.Http.Json; | ||
using SSW.Rewards.Shared.DTOs.Roles; | ||
|
||
namespace SSW.Rewards.ApiClient.Services; | ||
|
||
public interface IRoleService | ||
{ | ||
Task<IEnumerable<RoleDto>> GetRoles(CancellationToken cancellationToken = default); | ||
} | ||
|
||
public class RoleService : IRoleService | ||
{ | ||
private readonly HttpClient _httpClient; | ||
|
||
private const string _baseRoute = "api/Role/"; | ||
|
||
public RoleService(IHttpClientFactory httpClientFactory) | ||
{ | ||
_httpClient = httpClientFactory.CreateClient(Constants.AuthenticatedClient); | ||
} | ||
|
||
public async Task<IEnumerable<RoleDto>> GetRoles(CancellationToken cancellationToken) | ||
{ | ||
var result = await _httpClient.GetAsync($"{_baseRoute}GetRoles", cancellationToken); | ||
|
||
if (result.IsSuccessStatusCode) | ||
{ | ||
var response = await result.Content.ReadFromJsonAsync<IEnumerable<RoleDto>>(cancellationToken: cancellationToken); | ||
|
||
if (response is not null) | ||
{ | ||
return response; | ||
} | ||
} | ||
|
||
var responseContent = await result.Content.ReadAsStringAsync(cancellationToken); | ||
throw new Exception($"Failed to get roles: {responseContent}"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
using SSW.Rewards.Domain.Entities; | ||
| ||
|
||
namespace SSW.Rewards.Application.Common.Interfaces; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using AutoMapper.QueryableExtensions; | ||
using SSW.Rewards.Shared.DTOs.Roles; | ||
|
||
namespace SSW.Rewards.Application.Roles.Queries.GetRoles; | ||
|
||
public class GetRolesQuery : IRequest<IEnumerable<RoleDto>>; | ||
|
||
public class GetRolesQueryHandler : IRequestHandler<GetRolesQuery, IEnumerable<RoleDto>> | ||
{ | ||
private readonly IMapper _mapper; | ||
private readonly IApplicationDbContext _dbContext; | ||
|
||
public GetRolesQueryHandler(IMapper mapper, IApplicationDbContext dbContext) | ||
{ | ||
_mapper = mapper; | ||
_dbContext = dbContext; | ||
} | ||
|
||
public async Task<IEnumerable<RoleDto>> Handle(GetRolesQuery request, CancellationToken cancellationToken) | ||
{ | ||
return await _dbContext.Roles.ProjectTo<RoleDto>(_mapper.ConfigurationProvider).ToListAsync(cancellationToken); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using SSW.Rewards.Shared.DTOs.Roles; | ||
|
||
namespace SSW.Rewards.Application.Roles.Queries.GetRoles; | ||
public class Mapping : Profile | ||
{ | ||
public Mapping() | ||
{ | ||
CreateMap<Role, RoleDto>() | ||
.ForMember(dst => dst.Id, opt => opt.MapFrom(src => src.Id)) | ||
.ForMember(dst => dst.Name, opt => opt.MapFrom(src => src.Name)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.