Implemented member management of roles. Moved users controller
This commit is contained in:
114
Moonlight.Frontend/UI/Admin/Modals/ManageRoleMembersDialog.razor
Normal file
114
Moonlight.Frontend/UI/Admin/Modals/ManageRoleMembersDialog.razor
Normal file
@@ -0,0 +1,114 @@
|
||||
@using LucideBlazor
|
||||
@using Moonlight.Shared.Http.Responses
|
||||
@using Moonlight.Shared.Http.Responses.Admin
|
||||
@using Moonlight.Shared.Http.Responses.Users
|
||||
@using ShadcnBlazor.Buttons
|
||||
@using ShadcnBlazor.DataGrids
|
||||
@using ShadcnBlazor.Dialogs
|
||||
@using ShadcnBlazor.Extras.Comboboxes
|
||||
@using ShadcnBlazor.Extras.Common
|
||||
@using ShadcnBlazor.Labels
|
||||
@using ShadcnBlazor.Tabels
|
||||
|
||||
@inherits ShadcnBlazor.Extras.Dialogs.DialogBase
|
||||
|
||||
@inject HttpClient HttpClient
|
||||
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Manage members of @Role.Name
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Manage members of this role
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-2 mt-5">
|
||||
<Label>Add new member</Label>
|
||||
<div class="flex justify-start gap-1">
|
||||
<Combobox @bind-Value="SelectedUser"
|
||||
ClassName="w-[200px]"
|
||||
FieldPlaceholder="Select user"
|
||||
SearchPlaceholder="Search user"
|
||||
ValueSelector="dto => dto.Username"
|
||||
Source="LoadUsersAsync"/>
|
||||
<WButtom OnClick="AddAsync" Variant="ButtonVariant.Outline" Size="ButtonSize.Icon">
|
||||
<PlusIcon/>
|
||||
</WButtom>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<DataGrid @ref="Grid"
|
||||
TGridItem="UserDto"
|
||||
ColumnVisibility="false"
|
||||
EnableSearch="true"
|
||||
EnableLiveSearch="true"
|
||||
Loader="LoadAsync">
|
||||
<PropertyColumn Field="dto => dto.Username"/>
|
||||
<PropertyColumn Field="dto => dto.Email"/>
|
||||
<TemplateColumn>
|
||||
<CellTemplate>
|
||||
<TableCell>
|
||||
<WButtom OnClick="_ => RemoveAsync(context)" Variant="ButtonVariant.Destructive" Size="ButtonSize.Icon">
|
||||
<TrashIcon/>
|
||||
</WButtom>
|
||||
</TableCell>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</DataGrid>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public RoleDto Role { get; set; }
|
||||
|
||||
private UserDto? SelectedUser;
|
||||
private DataGrid<UserDto> Grid;
|
||||
|
||||
private async Task<DataGridResponse<UserDto>> LoadAsync(DataGridRequest<UserDto> request)
|
||||
{
|
||||
var query = $"?startIndex={request.StartIndex}&length={request.Length}";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.SearchTerm))
|
||||
query += $"&searchTerm={request.SearchTerm}";
|
||||
|
||||
var response = await HttpClient.GetFromJsonAsync<PagedData<UserDto>>(
|
||||
$"api/admin/roles/{Role.Id}/members{query}"
|
||||
);
|
||||
|
||||
return new DataGridResponse<UserDto>(response!.Data, response.TotalLength);
|
||||
}
|
||||
|
||||
private async Task<UserDto[]> LoadUsersAsync(string? searchTerm)
|
||||
{
|
||||
var query = "?startIndex=0&length=100";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
query += $"&searchTerm={searchTerm}";
|
||||
|
||||
var response = await HttpClient.GetFromJsonAsync<PagedData<UserDto>>(
|
||||
$"api/admin/roles/{Role.Id}/members/available{query}"
|
||||
);
|
||||
|
||||
return response!.Data;
|
||||
}
|
||||
|
||||
private async Task AddAsync()
|
||||
{
|
||||
if (SelectedUser == null)
|
||||
return;
|
||||
|
||||
await HttpClient.PutAsync($"api/admin/roles/{Role.Id}/members/{SelectedUser.Id}", null);
|
||||
|
||||
SelectedUser = null;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await Grid.RefreshAsync();
|
||||
}
|
||||
|
||||
private async Task RemoveAsync(UserDto user)
|
||||
{
|
||||
await HttpClient.DeleteAsync($"api/admin/roles/{Role.Id}/members/{user.Id}");
|
||||
await Grid.RefreshAsync();
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent Value="users">
|
||||
<u />
|
||||
<Users />
|
||||
</TabsContent>
|
||||
<TabsContent Value="roles">
|
||||
<Roles />
|
||||
|
||||
@@ -35,7 +35,18 @@
|
||||
<div class="mt-3">
|
||||
<DataGrid @ref="Grid" TGridItem="RoleDto" Loader="LoadAsync" PageSize="10" ClassName="bg-card">
|
||||
<PropertyColumn Field="u => u.Id"/>
|
||||
<PropertyColumn IsFilterable="true" Identifier="@nameof(RoleDto.Name)" Field="r => r.Name"/>
|
||||
<TemplateColumn Title="Name" IsFilterable="true" Identifier="@nameof(RoleDto.Name)">
|
||||
<HeadTemplate>
|
||||
<TableHead>Name</TableHead>
|
||||
</HeadTemplate>
|
||||
<CellTemplate>
|
||||
<TableCell>
|
||||
<a class="text-primary" href="#" @onclick="_ => MembersAsync(context)" @onclick:preventDefault>
|
||||
@context.Name
|
||||
</a>
|
||||
</TableCell>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
<PropertyColumn Title="Description" Field="r => r.Description"/>
|
||||
<PropertyColumn Title="Members" Field="r => r.MemberCount"/>
|
||||
<TemplateColumn>
|
||||
@@ -45,19 +56,27 @@
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Slot Context="dropdownSlot">
|
||||
<Button Size="ButtonSize.IconSm" Variant="ButtonVariant.Ghost" @attributes="dropdownSlot">
|
||||
<Button Size="ButtonSize.IconSm" Variant="ButtonVariant.Ghost"
|
||||
@attributes="dropdownSlot">
|
||||
<EllipsisIcon/>
|
||||
</Button>
|
||||
</Slot>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent SideOffset="2">
|
||||
<DropdownMenuItem OnClick="() => MembersAsync(context)">
|
||||
Members
|
||||
<DropdownMenuShortcut>
|
||||
<UsersRoundIcon/>
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem OnClick="() => EditAsync(context)">
|
||||
Edit
|
||||
<DropdownMenuShortcut>
|
||||
<PenIcon/>
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem OnClick="() => DeleteAsync(context)" Variant="DropdownMenuItemVariant.Destructive">
|
||||
<DropdownMenuItem OnClick="() => DeleteAsync(context)"
|
||||
Variant="DropdownMenuItemVariant.Destructive">
|
||||
Delete
|
||||
<DropdownMenuShortcut>
|
||||
<TrashIcon/>
|
||||
@@ -100,7 +119,7 @@
|
||||
request,
|
||||
Constants.SerializerOptions
|
||||
);
|
||||
|
||||
|
||||
await ToastService.SuccessAsync("Role creation", $"Role {request.Name} has been successfully created");
|
||||
await Grid.RefreshAsync();
|
||||
};
|
||||
@@ -119,13 +138,18 @@
|
||||
request,
|
||||
Constants.SerializerOptions
|
||||
);
|
||||
|
||||
|
||||
await ToastService.SuccessAsync("Role update", $"Role {request.Name} has been successfully updated");
|
||||
await Grid.RefreshAsync();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private async Task MembersAsync(RoleDto role)
|
||||
{
|
||||
await DialogService.LaunchAsync<ManageRoleMembersDialog>(parameters => { parameters[nameof(ManageRoleMembersDialog.Role)] = role; }, model => { model.ClassName = "sm:max-w-xl"; });
|
||||
}
|
||||
|
||||
private async Task DeleteAsync(RoleDto role)
|
||||
{
|
||||
await AlertDialogService.ConfirmDangerAsync(
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<div class="mt-3">
|
||||
<DataGrid @ref="Grid" TGridItem="UserDto" Loader="LoadAsync" PageSize="10" ClassName="bg-card">
|
||||
<PropertyColumn HeadClassName="text-left" CellClassName="text-left" Field="u => u.Id"/>
|
||||
<PropertyColumn HeadClassName="text-left" CellClassName="text-left" IsFilterable="true"
|
||||
@@ -83,7 +83,7 @@
|
||||
var filterOptions = request.Filters.Count > 0 ? new FilterOptions(request.Filters) : null;
|
||||
|
||||
var response = await HttpClient.GetFromJsonAsync<PagedData<UserDto>>(
|
||||
$"api/users{query}&filterOptions={filterOptions}",
|
||||
$"api/admin/users{query}&filterOptions={filterOptions}",
|
||||
Constants.SerializerOptions
|
||||
);
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
parameters[nameof(CreateUserDialog.OnSubmit)] = async (CreateUserDto dto) =>
|
||||
{
|
||||
await HttpClient.PostAsJsonAsync(
|
||||
"/api/users",
|
||||
"/api/admin/users",
|
||||
dto,
|
||||
Constants.SerializerOptions
|
||||
);
|
||||
@@ -120,7 +120,7 @@
|
||||
parameters[nameof(CreateUserDialog.OnSubmit)] = async (UpdateUserDto dto) =>
|
||||
{
|
||||
await HttpClient.PatchAsJsonAsync(
|
||||
$"/api/users/{user.Id}",
|
||||
$"/api/admin/users/{user.Id}",
|
||||
dto
|
||||
);
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
"Do you really want to delete this user? This action cannot be undone",
|
||||
async () =>
|
||||
{
|
||||
await HttpClient.DeleteAsync($"api/users/{user.Id}");
|
||||
await HttpClient.DeleteAsync($"api/admin/users/{user.Id}");
|
||||
await ToastService.SuccessAsync("User deletion", $"Successfully deleted user {user.Username}");
|
||||
|
||||
await Grid.RefreshAsync();
|
||||
|
||||
Reference in New Issue
Block a user