1
0
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:
chylex 2022-10-28 04:19:15 +02:00
parent ab20e46af2
commit d57546bb71
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
4 changed files with 138 additions and 120 deletions

View File

@ -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);
}

View File

@ -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)));
}
}

View 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);
}
}

View File

@ -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));
}
}