mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2025-05-09 14:34:10 +02:00
Refactor and tweak design of Users table and edit dialogs on web
This commit is contained in:
parent
ab20e46af2
commit
d57546bb71
Server/Phantom.Server.Web
@ -4,6 +4,7 @@
|
||||
@using Phantom.Server.Services.Users
|
||||
@attribute [Authorize(Permission.ViewUsersPolicy)]
|
||||
@inject UserManager<IdentityUser> UserManager
|
||||
@inject PermissionManager PermissionManager
|
||||
@inject IdentityLookup IdentityLookup
|
||||
|
||||
<h1>Users</h1>
|
||||
@ -12,48 +13,59 @@
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#add-user">Add User...</button>
|
||||
</PermissionView>
|
||||
|
||||
<table class="table align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<Column Width="315px">Identifier</Column>
|
||||
<Column Width="125px; 40%">Username</Column>
|
||||
<Column Width="125px; 60%">Roles</Column>
|
||||
<PermissionView Permission="Permission.EditUsers">
|
||||
<Column Width="175px">Actions</Column>
|
||||
</PermissionView>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var user in allUsers) {
|
||||
<tr>
|
||||
<td>
|
||||
<code class="text-uppercase">@user.Id</code>
|
||||
</td>
|
||||
<td>@user.UserName</td>
|
||||
<td>@(userRoles.TryGetValue(user.Id, out var roles) ? roles : "-")</td>
|
||||
<PermissionView Permission="Permission.EditUsers">
|
||||
<td>
|
||||
@if (IdentityLookup.GetAuthenticatedUserId(context.User) != user.Id) {
|
||||
<button class="btn btn-primary btn-sm" @onclick="() => userRolesDialog.Show(user)">Edit Roles</button>
|
||||
<button class="btn btn-danger btn-sm" @onclick="() => userDeleteDialog.Show(user)">Delete...</button>
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
@{ var canEdit = PermissionManager.CheckPermission(context.User, Permission.EditUsers); }
|
||||
<table class="table align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<Column Width="315px">Identifier</Column>
|
||||
<Column Width="125px; 40%">Username</Column>
|
||||
<Column Width="125px; 60%">Roles</Column>
|
||||
@if (canEdit) {
|
||||
<Column Width="175px">Actions</Column>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var user in allUsers) {
|
||||
var isMe = IdentityLookup.GetAuthenticatedUserId(context.User) == user.Id;
|
||||
<tr>
|
||||
<td>
|
||||
<code class="text-uppercase">@user.Id</code>
|
||||
</td>
|
||||
@if (isMe) {
|
||||
<td class="fw-semibold">@user.UserName</td>
|
||||
}
|
||||
</td>
|
||||
</PermissionView>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
else {
|
||||
<td>@user.UserName</td>
|
||||
}
|
||||
<td>@(userRoles.TryGetValue(user.Id, out var roles) ? roles : "-")</td>
|
||||
@if (canEdit) {
|
||||
<td>
|
||||
@if (!isMe) {
|
||||
<button class="btn btn-primary btn-sm" @onclick="() => userRolesDialog.Show(user)">Edit Roles</button>
|
||||
<button class="btn btn-danger btn-sm" @onclick="() => userDeleteDialog.Show(user)">Delete...</button>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<PermissionView Permission="Permission.EditUsers">
|
||||
<UserAddDialog ModalId="add-user" UserAdded="OnUserAdded" />
|
||||
<UserRolesDialog @ref="userRolesDialog" ModalId="manage-user-roles" UserRolesChanged="OnUserRolesChanged" />
|
||||
<UserDeleteDialog @ref="userDeleteDialog" ModalId="delete-user" UserDeleted="OnUserDeleted" />
|
||||
<UserRolesDialog @ref="userRolesDialog" ModalId="manage-user-roles" UserModified="OnUserRolesChanged" />
|
||||
<UserDeleteDialog @ref="userDeleteDialog" ModalId="delete-user" UserModified="OnUserDeleted" />
|
||||
</PermissionView>
|
||||
|
||||
@code {
|
||||
|
||||
private ImmutableArray<IdentityUser> allUsers = ImmutableArray<IdentityUser>.Empty;
|
||||
private readonly Dictionary<string, string> userRoles = new ();
|
||||
private readonly Dictionary<string, string> userRoles = new();
|
||||
|
||||
private UserRolesDialog userRolesDialog = null!;
|
||||
private UserDeleteDialog userDeleteDialog = null!;
|
||||
@ -72,11 +84,12 @@
|
||||
var roles = await UserManager.GetRolesAsync(user);
|
||||
if (roles.Count > 0) {
|
||||
userRoles[user.Id] = string.Join(", ", roles);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
userRoles.Remove(user.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnUserAdded(IdentityUser user) {
|
||||
allUsers = allUsers.Add(user);
|
||||
}
|
||||
|
@ -1,63 +1,31 @@
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using Phantom.Server.Services.Audit
|
||||
@inherits PhantomComponent
|
||||
@inject IJSRuntime Js;
|
||||
@inherits UserEditDialogBase
|
||||
@inject UserManager<IdentityUser> UserManager
|
||||
@inject AuditLog AuditLog
|
||||
|
||||
<Modal Id="@ModalId" TitleText="Delete User">
|
||||
<Body>
|
||||
You are about to delete the user <strong>@usernameToDelete</strong>.<br>
|
||||
You are about to delete the user <strong class="fw-semibold">@EditedUserName</strong>.<br>
|
||||
This action cannot be undone.
|
||||
</Body>
|
||||
<Footer>
|
||||
<FormSubmitError Model="submitModel" />
|
||||
<FormButtonSubmit Model="submitModel" Label="Delete User" type="button" class="btn btn-danger" @onclick="DeleteUser" />
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => userToDelete = null">Cancel</button>
|
||||
<FormSubmitError Model="SubmitModel" />
|
||||
<FormButtonSubmit Model="SubmitModel" Label="Delete User" type="button" class="btn btn-danger" @onclick="Submit" />
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="OnClosed">Cancel</button>
|
||||
</Footer>
|
||||
</Modal>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter, EditorRequired]
|
||||
public string ModalId { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<IdentityUser> UserDeleted { get; set; }
|
||||
|
||||
private readonly FormButtonSubmit.SubmitModel submitModel = new();
|
||||
|
||||
private IdentityUser? userToDelete = null;
|
||||
private string usernameToDelete = string.Empty; // Not reset when the modal is closed to prevent re-rendering modal body.
|
||||
|
||||
public async Task Show(IdentityUser user) {
|
||||
userToDelete = user;
|
||||
usernameToDelete = user.UserName ?? $"<{user.Id}>";
|
||||
await Js.InvokeVoidAsync("showModal", ModalId);
|
||||
}
|
||||
|
||||
private async Task DeleteUser() {
|
||||
await submitModel.StartSubmitting();
|
||||
|
||||
if (!await CheckPermission(Permission.EditUsers)) {
|
||||
submitModel.StopSubmitting("You do not have permission to delete users.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (userToDelete == null) {
|
||||
submitModel.StopSubmitting("Invalid user.");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await UserManager.DeleteAsync(userToDelete);
|
||||
protected override async Task DoEdit(IdentityUser user) {
|
||||
var result = await UserManager.DeleteAsync(user);
|
||||
if (result.Succeeded) {
|
||||
await AuditLog.AddUserDeletedEvent(userToDelete);
|
||||
await UserDeleted.InvokeAsync(userToDelete);
|
||||
await Js.InvokeVoidAsync("closeModal", ModalId);
|
||||
submitModel.StopSubmitting();
|
||||
await AuditLog.AddUserDeletedEvent(user);
|
||||
await OnEditSuccess();
|
||||
}
|
||||
else {
|
||||
submitModel.StopSubmitting(string.Join("\n", result.Errors.Select(static error => error.Description)));
|
||||
OnEditFailure(string.Join("\n", result.Errors.Select(static error => error.Description)));
|
||||
}
|
||||
}
|
||||
|
||||
|
68
Server/Phantom.Server.Web/Shared/UserEditDialogBase.cs
Normal file
68
Server/Phantom.Server.Web/Shared/UserEditDialogBase.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.JSInterop;
|
||||
using Phantom.Server.Web.Base;
|
||||
using Phantom.Server.Web.Components.Forms;
|
||||
using Phantom.Server.Web.Identity.Data;
|
||||
|
||||
namespace Phantom.Server.Web.Shared;
|
||||
|
||||
public abstract class UserEditDialogBase : PhantomComponent {
|
||||
[Inject]
|
||||
public IJSRuntime Js { get; set; } = null!;
|
||||
|
||||
[Parameter, EditorRequired]
|
||||
public string ModalId { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<IdentityUser> UserModified { get; set; }
|
||||
|
||||
protected readonly FormButtonSubmit.SubmitModel SubmitModel = new();
|
||||
|
||||
private IdentityUser? EditedUser { get; set; } = null;
|
||||
protected string EditedUserName { get; private set; } = string.Empty;
|
||||
|
||||
internal async Task Show(IdentityUser user) {
|
||||
EditedUser = user;
|
||||
EditedUserName = user.UserName ?? $"<{user.Id}>";
|
||||
await BeforeShown(user);
|
||||
|
||||
StateHasChanged();
|
||||
await Js.InvokeVoidAsync("showModal", ModalId);
|
||||
}
|
||||
|
||||
protected virtual Task BeforeShown(IdentityUser user) {
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected void OnClosed() {
|
||||
EditedUser = null;
|
||||
}
|
||||
|
||||
protected async Task Submit() {
|
||||
await SubmitModel.StartSubmitting();
|
||||
|
||||
if (!await CheckPermission(Permission.EditUsers)) {
|
||||
SubmitModel.StopSubmitting("You do not have permission to edit users.");
|
||||
}
|
||||
else if (EditedUser == null) {
|
||||
SubmitModel.StopSubmitting("Invalid user.");
|
||||
}
|
||||
else {
|
||||
await DoEdit(EditedUser);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Task DoEdit(IdentityUser user);
|
||||
|
||||
protected async Task OnEditSuccess() {
|
||||
await UserModified.InvokeAsync(EditedUser);
|
||||
await Js.InvokeVoidAsync("closeModal", ModalId);
|
||||
SubmitModel.StopSubmitting();
|
||||
OnClosed();
|
||||
}
|
||||
|
||||
protected void OnEditFailure(string message) {
|
||||
SubmitModel.StopSubmitting(message);
|
||||
}
|
||||
}
|
@ -2,15 +2,14 @@
|
||||
@using System.Collections.Immutable
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using Phantom.Server.Services.Audit
|
||||
@inherits PhantomComponent
|
||||
@inject IJSRuntime Js;
|
||||
@inherits UserEditDialogBase
|
||||
@inject UserManager<IdentityUser> UserManager
|
||||
@inject RoleManager<IdentityRole> RoleManager
|
||||
@inject AuditLog AuditLog
|
||||
|
||||
<Modal Id="@ModalId" TitleText="Manage User Roles">
|
||||
<Body>
|
||||
Roles for user: <strong>@username</strong><br>
|
||||
Roles for user: <strong class="fw-semibold">@EditedUserName</strong><br>
|
||||
@for (var index = 0; index < items.Count; index++) {
|
||||
var item = items[index];
|
||||
<div class="mt-1">
|
||||
@ -20,30 +19,17 @@
|
||||
}
|
||||
</Body>
|
||||
<Footer>
|
||||
<FormSubmitError Model="submitModel" />
|
||||
<FormButtonSubmit Model="submitModel" Label="Save Roles" type="button" class="btn btn-success" @onclick="UpdateUser" />
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => userToEdit = null">Cancel</button>
|
||||
<FormSubmitError Model="SubmitModel" />
|
||||
<FormButtonSubmit Model="SubmitModel" Label="Save Roles" type="button" class="btn btn-success" @onclick="Submit" />
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="OnClosed">Cancel</button>
|
||||
</Footer>
|
||||
</Modal>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter, EditorRequired]
|
||||
public string ModalId { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<IdentityUser> UserRolesChanged { get; set; }
|
||||
|
||||
private readonly FormButtonSubmit.SubmitModel submitModel = new();
|
||||
|
||||
private IdentityUser? userToEdit = null;
|
||||
private string username = string.Empty;
|
||||
private List<RoleItem> items = new();
|
||||
|
||||
public async Task Show(IdentityUser user) {
|
||||
this.userToEdit = user;
|
||||
this.username = user.UserName ?? $"<{user.Id}>";
|
||||
|
||||
protected override async Task BeforeShown(IdentityUser user) {
|
||||
var userRoles = await GetUserRoles(user);
|
||||
this.items = RoleManager.Roles
|
||||
.Select(static role => role.Name)
|
||||
@ -51,25 +37,10 @@
|
||||
.WhereNotNull()
|
||||
.Select(role => new RoleItem(role, userRoles.Contains(role)))
|
||||
.ToList();
|
||||
|
||||
StateHasChanged();
|
||||
await Js.InvokeVoidAsync("showModal", ModalId);
|
||||
}
|
||||
|
||||
private async Task UpdateUser() {
|
||||
await submitModel.StartSubmitting();
|
||||
|
||||
if (!await CheckPermission(Permission.EditUsers)) {
|
||||
submitModel.StopSubmitting("You do not have permission to edit users.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (userToEdit == null) {
|
||||
submitModel.StopSubmitting("Invalid user.");
|
||||
return;
|
||||
}
|
||||
|
||||
var userRoles = await GetUserRoles(userToEdit);
|
||||
protected override async Task DoEdit(IdentityUser user) {
|
||||
var userRoles = await GetUserRoles(user);
|
||||
var addedToRoles = new List<string>();
|
||||
var removedFromRoles = new List<string>();
|
||||
var errors = new List<string>();
|
||||
@ -82,7 +53,7 @@
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = shouldHaveRole ? await UserManager.AddToRoleAsync(userToEdit, roleName) : await UserManager.RemoveFromRoleAsync(userToEdit, roleName);
|
||||
var result = shouldHaveRole ? await UserManager.AddToRoleAsync(user, roleName) : await UserManager.RemoveFromRoleAsync(user, roleName);
|
||||
if (result.Succeeded) {
|
||||
var modifiedList = shouldHaveRole ? addedToRoles : removedFromRoles;
|
||||
modifiedList.Add(roleName);
|
||||
@ -93,13 +64,11 @@
|
||||
}
|
||||
|
||||
if (errors.Count == 0) {
|
||||
await AuditLog.AddUserRolesChangedEvent(userToEdit, addedToRoles, removedFromRoles);
|
||||
await UserRolesChanged.InvokeAsync(userToEdit);
|
||||
await Js.InvokeVoidAsync("closeModal", ModalId);
|
||||
submitModel.StopSubmitting();
|
||||
await AuditLog.AddUserRolesChangedEvent(user, addedToRoles, removedFromRoles);
|
||||
await OnEditSuccess();
|
||||
}
|
||||
else {
|
||||
submitModel.StopSubmitting(string.Join("\n", errors));
|
||||
OnEditFailure(string.Join("\n", errors));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user