Last active
April 11, 2025 20:30
-
-
Save sadukie/edb05737837d5c87c7bdaf25972f23e5 to your computer and use it in GitHub Desktop.
eShopOnWeb - User Management - UI
This file contains hidden or 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
using System.Threading.Tasks; | |
using BlazorAdmin.Models; | |
namespace BlazorAdmin.Interfaces; | |
public interface IUserManagementService | |
{ | |
Task<CreateUserResponse> Create(CreateUserRequest user); | |
Task<GetUserResponse> Update(UpdateUserRequest user); | |
Task Delete(string id); | |
Task<GetUserResponse> GetById(string id); | |
Task<GetUserResponse> GetByName(string userName); | |
Task<GetUserRolesResponse> GetRolesByUserId(string userId); | |
Task SaveRolesForUser(SaveRolesForUserRequest request); | |
Task<UserListResponse> List(); | |
} |
This file contains hidden or 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
namespace BlazorAdmin.Models; | |
public class CreateUserRequest | |
{ | |
public User User{ get; set; } | |
public CreateUserRequest() | |
{ | |
User = new(); | |
} | |
} |
This file contains hidden or 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
namespace BlazorAdmin.Models; | |
public class CreateUserResponse | |
{ | |
public string UserId { get; set; } | |
} |
This file contains hidden or 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
using System.ComponentModel.DataAnnotations; | |
namespace BlazorAdmin.Models; | |
public class DeleteUserRequest | |
{ | |
[Required(ErrorMessage = "The UserId field is required")] | |
public string UserId { get; set; } | |
} |
This file contains hidden or 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
namespace BlazorAdmin.Models; | |
public class GetUserResponse | |
{ | |
public User User { get; set; } | |
public GetUserResponse() | |
{ | |
User = new(); | |
} | |
} |
This file contains hidden or 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
using System.Collections.Generic; | |
namespace BlazorAdmin.Models; | |
public class GetUserRolesResponse | |
{ | |
public List<string> Roles { get; set; } | |
} |
This file contains hidden or 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
using System.Collections.Generic; | |
namespace BlazorAdmin.Models; | |
public class SaveRolesForUserRequest | |
{ | |
public string UserId { get; set; } | |
public List<string> RolesToAdd { get; set; } = []; | |
public List<string> RolesToRemove { get; set; } = []; | |
} |
This file contains hidden or 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
namespace BlazorAdmin.Models; | |
public class UpdateUserRequest | |
{ | |
public User User{ get; set; } | |
public UpdateUserRequest() | |
{ | |
User = new(); | |
} | |
} |
This file contains hidden or 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
using System; | |
using System.ComponentModel.DataAnnotations; | |
namespace BlazorAdmin.Models; | |
public class User | |
{ | |
public string Id { get; set; } | |
[Required] | |
[RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", ErrorMessage = "Invalid UserName.")] | |
public string UserName { get; set; } | |
[Required] | |
[RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", ErrorMessage = "Invalid email address format.")] | |
public string Email { get; set; } | |
public bool EmailConfirmed { get; set; } | |
public string PhoneNumber { get; set; } | |
public bool PhoneNumberConfirmed { get; set; } | |
public bool TwoFactorEnabled { get; set; } | |
public DateTimeOffset? LockoutEnd { get; set; } | |
} |
This file contains hidden or 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
using System.Collections.Generic; | |
namespace BlazorAdmin.Models; | |
public class UserListResponse | |
{ | |
public List<User> Users { get; set; } = []; | |
} |
This file contains hidden or 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
builder.Services.AddScoped<IUserManagementService, UserManagementService>(); |
This file contains hidden or 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
using System.Threading.Tasks; | |
using BlazorAdmin.Interfaces; | |
using BlazorAdmin.Models; | |
using Microsoft.Extensions.Logging; | |
namespace BlazorAdmin.Services; | |
public class UserManagementService(HttpService httpService, ILogger<IUserManagementService> logger) : IUserManagementService | |
{ | |
public async Task<CreateUserResponse> Create(CreateUserRequest user) | |
{ | |
var response = await httpService.HttpPost<CreateUserResponse>($"users", user); | |
return response; | |
} | |
public async Task Delete(string userId) | |
{ | |
await httpService.HttpDelete($"users/{userId}"); | |
} | |
public async Task<GetUserResponse> Update(UpdateUserRequest user) | |
{ | |
return await httpService.HttpPut<GetUserResponse>($"users", user); | |
} | |
public async Task<GetUserResponse> GetById(string userId) | |
{ | |
return await httpService.HttpGet<GetUserResponse>($"users/{userId}"); | |
} | |
public async Task<GetUserRolesResponse> GetRolesByUserId(string userId) | |
{ | |
return await httpService.HttpGet<GetUserRolesResponse>($"users/{userId}/roles"); | |
} | |
public async Task<GetUserResponse> GetByName(string userName) { | |
return await httpService.HttpGet<GetUserResponse>($"users/name/{userName}"); | |
} | |
public async Task<UserListResponse> List() | |
{ | |
logger.LogInformation("Fetching users"); | |
return await httpService.HttpGet<UserListResponse>($"users"); | |
} | |
public async Task SaveRolesForUser(SaveRolesForUserRequest request) | |
{ | |
await httpService.HttpPut($"users/{request.UserId}/roles",request); | |
} | |
} |
This file contains hidden or 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
public async Task<HttpStatusCode> HttpPut(string uri, object dataToSend) | |
{ | |
var content = ToJson(dataToSend); | |
var result = await _httpClient.PutAsync($"{_apiUrl}{uri}", content); | |
if (!result.IsSuccessStatusCode) | |
{ | |
_toastService.ShowToast("Error", ToastLevel.Error); | |
return result.StatusCode; | |
} | |
_toastService.ShowToast($"Updated successfully!", ToastLevel.Success); | |
return result.StatusCode; | |
} |
This file contains hidden or 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
@using BlazorAdmin.Interfaces | |
@using BlazorAdmin.Models | |
@using Microsoft.AspNetCore.Identity | |
@inject ILogger<Create> Logger | |
@inject IJSRuntime JSRuntime | |
@inject IUserManagementService UserManagementService | |
@inject IRoleManagementService RoleManagementService | |
@namespace BlazorAdmin.Pages.UserPage | |
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
@if (_item == null) | |
{ | |
<Spinner></Spinner> | |
} | |
else | |
{ | |
<EditForm Model="_item.User" OnValidSubmit="@CreateClick"> | |
<DataAnnotationsValidator /> | |
<div class="modal-header"> | |
<h5 class="modal-title" id="exampleModalLabel">Create</h5> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
<div class="container"> | |
<div class="row"> | |
<div class="col-md-12"> | |
<!-- User Information --> | |
<div class="form-group row"> | |
<label for="UserName" class="control-label col-md-6">Username</label> | |
<div class="col-md-6"> | |
<InputText id="UserName" class="form-control" @bind-Value="_item.User.UserName" /> | |
<ValidationMessage For="@(() => _item.User.UserName)" class="text-danger" /> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label for="Email" class="control-label col-md-6">Email</label> | |
<div class="col-md-6"> | |
<InputText id="Email" class="form-control" @bind-Value="_item.User.Email" /> | |
<ValidationMessage For="@(() => _item.User.Email)" class="text-danger" /> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label for="PhoneNumber" class="control-label col-md-6">Phone Number</label> | |
<div class="col-md-6"> | |
<InputText id="PhoneNumber" class="form-control" @bind-Value="_item.User.PhoneNumber" /> | |
<ValidationMessage For="@(() => _item.User.PhoneNumber)" class="text-danger" /> | |
</div> | |
</div> | |
<div class="form-group row form-check"> | |
<div class="col-md-6"> | |
<InputCheckbox id="EmailConfirmed" class="form-check-input" @bind-Value="_item.User.EmailConfirmed" /> | |
<label for="EmailConfirmed" class="form-check-label">Email Confirmed</label> | |
</div> | |
</div> | |
<div class="form-group row form-check"> | |
<div class="col-md-6"> | |
<InputCheckbox id="TwoFactorEnabled" class="form-check-input" @bind-Value="_item.User.TwoFactorEnabled" /> | |
<label for="TwoFactorEnabled" class="form-check-label">Two-Factor Authentication Enabled</label> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label for="LockoutEnd" class="control-label col-md-6">Lockout Until</label> | |
<div class="col-md-6"> | |
<InputDate id="LockoutEnd" class="form-control" @bind-Value="_item.User.LockoutEnd" /> | |
<ValidationMessage For="@(() => _item.User.LockoutEnd)" class="text-danger" /> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<h4>Roles</h4> | |
@if (_roles == null || _roles.Count == 0) | |
{ | |
<p>No roles available.</p> | |
} | |
else | |
{ | |
@foreach (var role in _roles) | |
{ | |
<div class="form-group row form-check"> | |
<div class="col-md-6"> | |
<input type="checkbox" id="@role" class="form-check-input" @onchange="(e) => UpdateRoleSelectedState(role.Name, (bool)((ChangeEventArgs)e).Value)" /> | |
<label class="form-check-label" for="@role">@role</label> | |
</div> | |
</div> | |
} | |
} | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button> | |
<button type="submit" class="btn btn-primary"> | |
Create | |
</button> | |
</div> | |
</EditForm> | |
} | |
</div> | |
</div> | |
</div> | |
@if (_showCreateModal) | |
{ | |
<div class="modal-backdrop fade show"></div> | |
} | |
@code { | |
[Parameter] | |
public EventCallback<string> OnSaveClick { get; set; } | |
private string _modalDisplay = "none;"; | |
private string _modalClass = ""; | |
private bool _showCreateModal = false; | |
private CreateUserRequest _item; | |
private Dictionary<string, bool> _selectedRoles = new(); | |
private List<IdentityRole> _roles; | |
private async Task CreateClick() | |
{ | |
if (_item.User != null) | |
{ | |
var result = await UserManagementService.Create(_item); | |
var checkedRoles = _selectedRoles.Where(r => r.Value).Select(r => r.Key).ToList(); | |
if (checkedRoles.Count > 0) | |
{ | |
SaveRolesForUserRequest rolesForUserRequest = new SaveRolesForUserRequest() | |
{ | |
UserId = result.UserId, | |
RolesToAdd = checkedRoles | |
}; | |
await UserManagementService.SaveRolesForUser(rolesForUserRequest); | |
} | |
if (result != null) | |
{ | |
Logger.LogInformation("Created User {userId}", result.UserId); | |
await OnSaveClick.InvokeAsync(null); | |
await Close(); | |
} | |
} | |
} | |
public async Task Open() | |
{ | |
Logger.LogInformation("Now loading... /Users/Create"); | |
await new Css(JSRuntime).HideBodyOverflow(); | |
_item = new CreateUserRequest(); | |
_roles = (await RoleManagementService.List()).Roles; | |
_modalDisplay = "block;"; | |
_modalClass = "Show"; | |
_showCreateModal = true; | |
StateHasChanged(); | |
} | |
private void UpdateRoleSelectedState(string role, bool isChecked) | |
{ | |
_selectedRoles[role] = isChecked; | |
} | |
private async Task Close() | |
{ | |
await new Css(JSRuntime).ShowBodyOverflow(); | |
_modalDisplay = "none"; | |
_modalClass = ""; | |
_showCreateModal = false; | |
} | |
} |
This file contains hidden or 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
@inject ILogger<Delete> Logger | |
@inject IJSRuntime JSRuntime | |
@inject IUserManagementService UserManagementService | |
@inherits BlazorAdmin.Helpers.BlazorComponent | |
@namespace BlazorAdmin.Pages.UserPage | |
@using BlazorAdmin.Interfaces | |
@using BlazorAdmin.Models | |
@using Microsoft.AspNetCore.Identity | |
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
@if (_userId is null || _userName is null) | |
{ | |
<Spinner></Spinner> | |
} | |
else | |
{ | |
<div class="modal-header"> | |
<h5 class="modal-title" id="exampleModalLabel">Delete @_userName</h5> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
<div class="container"> | |
<div class="row"> | |
<p> | |
Are you sure you want to <strong class="text-danger">DELETE</strong> this user: <strong>@_userName</strong>? | |
</p> | |
</div> | |
</div> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button> | |
<button class="btn btn-danger" @onclick="() => DeleteClick(_userId)"> | |
Delete | |
</button> | |
</div> | |
} | |
</div> | |
</div> | |
</div> | |
@if (_showDeleteModal) | |
{ | |
<div class="modal-backdrop fade show"></div> | |
} | |
@code { | |
[Parameter] | |
public EventCallback<string> OnSaveClick { get; set; } | |
private string _modalDisplay = "none;"; | |
private string _modalClass = ""; | |
private bool _showDeleteModal = false; | |
private string _userId = ""; | |
private string _userName = ""; | |
private async Task DeleteClick(string id) | |
{ | |
await UserManagementService.Delete(id); | |
Logger.LogInformation("Deleted user Id: {id}", id); | |
await OnSaveClick.InvokeAsync(null); | |
await Close(); | |
} | |
public async Task Open(string id, string userName) | |
{ | |
await new Css(JSRuntime).HideBodyOverflow(); | |
_userId = id; | |
_userName = userName; | |
_modalDisplay = "block;"; | |
_modalClass = "Show"; | |
_showDeleteModal = true; | |
StateHasChanged(); | |
} | |
private async Task Close() | |
{ | |
await new Css(JSRuntime).ShowBodyOverflow(); | |
_modalDisplay = "none"; | |
_modalClass = ""; | |
_showDeleteModal = false; | |
} | |
} |
This file contains hidden or 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
@inject ILogger<Edit> Logger | |
@inject IJSRuntime JSRuntime | |
@inject IUserManagementService UserManagementService | |
@inject IRoleManagementService RoleManagementService | |
@inherits BlazorAdmin.Helpers.BlazorComponent | |
@namespace BlazorAdmin.Pages.UserPage | |
@using BlazorAdmin.Interfaces | |
@using BlazorAdmin.Models | |
@using Microsoft.AspNetCore.Identity | |
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
@if (_item == null) | |
{ | |
<Spinner></Spinner> | |
} | |
else | |
{ | |
<EditForm Model="_item" OnValidSubmit="@SaveClick"> | |
<div class="modal-header"> | |
<h5 class="modal-title" id="exampleModalLabel">Edit @_item.UserName</h5> | |
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> | |
<span aria-hidden="true">×</span> | |
</button> | |
</div> | |
<div class="modal-body"> | |
<div class="container"> | |
<div class="row"> | |
<div class="col-md-12"> | |
<!-- User Information --> | |
<div class="form-group row"> | |
<label for="UserName" class="control-label col-md-6">Username</label> | |
<div class="col-md-6"> | |
<InputText id="UserName" class="form-control" @bind-Value="_item.UserName" /> | |
<ValidationMessage For="@(() => _item.UserName)" class="text-danger" /> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label for="Email" class="control-label col-md-6">Email</label> | |
<div class="col-md-6"> | |
<InputText id="Email" class="form-control" @bind-Value="_item.Email" /> | |
<ValidationMessage For="@(() => _item.Email)" class="text-danger" /> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label for="PhoneNumber" class="control-label col-md-6">Phone Number</label> | |
<div class="col-md-6"> | |
<InputText id="PhoneNumber" class="form-control" @bind-Value="_item.PhoneNumber" /> | |
<ValidationMessage For="@(() => _item.PhoneNumber)" class="text-danger" /> | |
</div> | |
</div> | |
<div class="form-group row form-check"> | |
<div class="col-md-6"> | |
<InputCheckbox id="EmailConfirmed" class="form-check-input" @bind-Value="_item.EmailConfirmed" /> | |
<label for="EmailConfirmed" class="form-check-label">Email Confirmed</label> | |
</div> | |
</div> | |
<div class="form-group row form-check"> | |
<div class="col-md-6"> | |
<InputCheckbox id="TwoFactorEnabled" class="form-check-input" @bind-Value="_item.TwoFactorEnabled" /> | |
<label for="TwoFactorEnabled" class="form-check-label">Two-Factor Authentication Enabled</label> | |
</div> | |
</div> | |
<div class="form-group row"> | |
<label for="LockoutEnd" class="control-label col-md-6">Lockout Until</label> | |
<div class="col-md-6"> | |
<InputDate id="LockoutEnd" class="form-control" @bind-Value="_item.LockoutEnd" /> | |
<ValidationMessage For="@(() => _item.LockoutEnd)" class="text-danger" /> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<h4>Roles</h4> | |
@if (_roles == null || _roles.Count == 0) | |
{ | |
<p>No roles available.</p> | |
} | |
else | |
{ | |
@foreach (var role in _roles) | |
{ | |
<div class="form-group row form-check"> | |
<div class="col-md-6"> | |
<input type="checkbox" id="@role" class="form-check-input" checked="@_selectedRoles[role.Name]" @onchange="(e) => UpdateRoleSelectedState(role.Name, (bool)((ChangeEventArgs)e).Value)" /> | |
<label class="form-check-label" for="@role">@role</label> | |
</div> | |
</div> | |
} | |
} | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button> | |
<button type="submit" class="btn btn-primary"> | |
Save | |
</button> | |
</div> | |
</EditForm> | |
} | |
</div> | |
</div> | |
</div> | |
@if (_showEditModal) | |
{ | |
<div class="modal-backdrop fade show"></div> | |
} | |
@code { | |
[Parameter] | |
public EventCallback<string> OnSaveClick { get; set; } | |
private string _modalDisplay = "none;"; | |
private string _modalClass = ""; | |
private bool _showEditModal = false; | |
private User _item = new User(); | |
private Dictionary<string, bool> _selectedRoles = new(); | |
private Dictionary<string, bool> _startingRoles = new(); | |
private List<IdentityRole> _roles; | |
private async Task SaveClick() | |
{ | |
var updateRequest = new UpdateUserRequest() | |
{ | |
User = _item | |
}; | |
await UserManagementService.Update(updateRequest); | |
var checkedRoles = _selectedRoles.Where(r => r.Value).Select(r => r.Key).ToList(); | |
var startingRoles = _startingRoles.Where(r => r.Value).Select(r => r.Key).ToList(); | |
var rolesToRemove = startingRoles.Except(checkedRoles).ToList(); | |
var rolesToAdd = checkedRoles.Except(startingRoles).ToList(); | |
if (rolesToAdd.Any() || rolesToRemove.Any()) | |
{ | |
SaveRolesForUserRequest rolesForUserRequest = new SaveRolesForUserRequest() | |
{ | |
UserId = _item.Id, | |
RolesToAdd = rolesToAdd, | |
RolesToRemove = rolesToRemove | |
}; | |
await UserManagementService.SaveRolesForUser(rolesForUserRequest); | |
} | |
Logger.LogInformation("Updated User Id: {userId}", _item.Id); | |
await OnSaveClick.InvokeAsync(null); | |
await Close(); | |
} | |
public async Task Open(string id) | |
{ | |
Logger.LogInformation("Now loading... /Users/Edit/{Id}", id); | |
await new Css(JSRuntime).HideBodyOverflow(); | |
_item = (await UserManagementService.GetById(id)).User; | |
_roles = (await RoleManagementService.List()).Roles; | |
var userRoles = await UserManagementService.GetRolesByUserId(id); | |
foreach (var role in _roles) | |
{ | |
var checkedStatus = userRoles.Roles.Contains(role.Name); | |
_selectedRoles[role.Name] = checkedStatus; | |
_startingRoles[role.Name] = checkedStatus; | |
} | |
Logger.LogInformation("Acquired user {id}", _item.Id); | |
_modalDisplay = "block;"; | |
_modalClass = "Show"; | |
_showEditModal = true; | |
StateHasChanged(); | |
} | |
private void UpdateRoleSelectedState(string role, bool isChecked) | |
{ | |
_selectedRoles[role] = isChecked; | |
} | |
private async Task Close() | |
{ | |
await new Css(JSRuntime).ShowBodyOverflow(); | |
_modalDisplay = "none"; | |
_modalClass = ""; | |
_showEditModal = false; | |
} | |
} |
This file contains hidden or 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
@page "/users" | |
@attribute [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)] | |
@inherits BlazorAdmin.Helpers.BlazorComponent | |
@namespace BlazorAdmin.Pages.UserPage | |
<PageTitle>eShopOnWeb Admin: Manage Users</PageTitle> | |
<h1>Manage Users</h1> | |
@if (_users == null) | |
{ | |
<Spinner></Spinner> | |
} | |
else | |
{ | |
<p class="esh-link-wrapper"> | |
<button class="btn btn-primary" @onclick="@(CreateClick)"> | |
Create New | |
</button> | |
</p> | |
<table class="table table-striped table-hover"> | |
<thead> | |
<tr> | |
<th>Email</th> | |
<th>Actions</th> | |
</tr> | |
</thead> | |
<tbody class="cursor-pointer"> | |
@foreach (var user in _users) | |
{ | |
<tr> | |
<td>@user.Email</td> | |
<td> | |
@if (_currentUser is null || _currentUser.Identity is null) | |
{ | |
<p>Unknown current user</p> | |
} else if (user.UserName != _currentUser.Identity.Name && user.UserName != "[email protected]") | |
{ | |
<button @onclick="@(() => EditClick(user.Id))" @onclick:stopPropagation="true" class="btn btn-primary"> | |
Edit | |
</button> | |
<button @onclick="@(() => DeleteClick(user.Id,user.UserName))" @onclick:stopPropagation="true" class="btn btn-danger"> | |
Delete | |
</button> | |
} | |
</td> | |
</tr> | |
} | |
</tbody> | |
</table> | |
<Create OnSaveClick="ReloadUsers" @ref="CreateComponent"></Create> | |
<Delete OnSaveClick="ReloadUsers" @ref="DeleteComponent"></Delete> | |
<Edit OnSaveClick="ReloadUsers" @ref="EditComponent"></Edit> | |
} |
This file contains hidden or 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
using System.Collections.Generic; | |
using System.Security.Claims; | |
using System.Threading.Tasks; | |
using BlazorAdmin.Helpers; | |
using BlazorAdmin.Interfaces; | |
using BlazorAdmin.Models; | |
using Microsoft.AspNetCore.Components.Authorization; | |
using Microsoft.Extensions.Logging; | |
namespace BlazorAdmin.Pages.UserPage; | |
public partial class List : BlazorComponent | |
{ | |
[Microsoft.AspNetCore.Components.Inject] | |
public IUserManagementService UserManagementService{ get; set; } | |
[Microsoft.AspNetCore.Components.Inject] | |
ILogger<List> Logger { get; set; } | |
[Microsoft.AspNetCore.Components.Inject] | |
public AuthenticationStateProvider AuthProvider { get; set; } | |
private List<User> _users = []; | |
private ClaimsPrincipal _currentUser = null; | |
private Create CreateComponent { get; set; } | |
private Delete DeleteComponent { get; set; } | |
private Edit EditComponent { get; set; } | |
protected override async Task OnInitializedAsync() | |
{ | |
await base.OnInitializedAsync(); | |
var getCurrentClaim = await AuthProvider.GetAuthenticationStateAsync(); | |
_currentUser = getCurrentClaim.User; | |
Logger.LogInformation("Current User: {0}", _currentUser.Identity.Name); | |
} | |
protected override async Task OnAfterRenderAsync(bool firstRender) | |
{ | |
if (firstRender) | |
{ | |
var response = await UserManagementService.List(); | |
_users = response.Users; | |
CallRequestRefresh(); | |
} | |
await base.OnAfterRenderAsync(firstRender); | |
} | |
private async Task CreateClick() | |
{ | |
await CreateComponent.Open(); | |
} | |
private async Task EditClick(string id) | |
{ | |
Logger.LogInformation("Edit User {id}", id); | |
await EditComponent.Open(id); | |
} | |
private async Task DeleteClick(string id, string userName) | |
{ | |
Logger.LogInformation("Displaying delete confirmation for User {id}", id); | |
await DeleteComponent.Open(id, userName); | |
} | |
private async Task ReloadUsers() | |
{ | |
var usersCall = await UserManagementService.List(); | |
_users = usersCall.Users; | |
StateHasChanged(); | |
} | |
} |
Comments are disabled for this gist.