Merge pull request 'UserManagement' (#8) from UserManagement into main
Some checks failed
Build ARM64 Linux / build-arm64 (push) Successful in 5m27s
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 9m55s
Build Linux x64 / build-linux-x64 (push) Successful in 5m21s
Build Windows x64 / build-windows-x64 (push) Successful in 6m31s
Build Docker Linux ARM64 Release / build-docker-linux-arm64-release (release) Failing after 1m4s

Reviewed-on: CouchLog/CouchLog#8
This commit was merged in pull request #8.
This commit is contained in:
Henry Trumme
2025-12-16 22:21:26 +01:00
15 changed files with 278 additions and 24 deletions

View File

@@ -0,0 +1,7 @@
@page "/AdminSettings"
<h3>Index</h3>
@code {
}

View File

@@ -0,0 +1,177 @@
@page "/AdminSettings/UserManagement"
@rendermode InteractiveServer
@using CouchLog.Data
@using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Components.QuickGrid
@inject ApplicationDbContext CouchLogDB
@inject UserManager<ApplicationUser> UserManager
@inject RoleManager<IdentityRole> RoleManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager NavigationManager
<div class="d-flex align-items-center justify-content-between">
<h3 class="mb-0">UserManagement</h3>
<button type="button"
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#exampleModal">
Add User
</button>
</div>
<div>
<QuickGrid Items="@gridUsers.AsQueryable()">
<PropertyColumn Title="#" Property="@(u => u.Index)" />
<PropertyColumn Title="Username" Property="@(u => u.UserName)" />
<PropertyColumn Title="Role" Property="@(u => u.Role)" />
<TemplateColumn Title="">
@if(context.UserId != currentUserId)
{
<button class="btn btn-danger btn-sm" @onclick="() => DeleteUser(context)">Delete</button>
}
</TemplateColumn>
</QuickGrid>
</div>
<!-- #region UserCreate Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" style="color: black">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" style="color: black" id="exampleModalLabel">Create User</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- 1. Username Input mit Binding und Bootstrap Klasse -->
<div class="mb-3">
<label for="newUsernameInput" class="form-label">Username:</label>
<input type="text" class="form-control" id="newUsernameInput" @bind="newUsername" />
</div>
<!-- 2. Select mit Binding -->
<div class="mb-3">
<label class="form-label">Role:</label>
<select class="form-select" @bind="newUserRoleId">
<!-- WICHTIG: Eine leere Option als Standard, damit man weiß, ob etwas gewählt wurde -->
<option value="" selected disabled>Bitte Rolle wählen...</option>
@foreach (var role in roles)
{
<option value="@role.Id">@role.Name</option>
}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" @onclick="CreateUserAsync">Create User</button>
</div>
</div>
</div>
</div>
<!-- #endregion -->
@code {
string newUsername = "";
string newUserRoleId = "";
List<UserGridItem> gridUsers = new();
List<IdentityRole> roles = new();
string? currentUserId;
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
var users = await CouchLogDB.Users.ToListAsync();
roles = await CouchLogDB.Roles.ToListAsync();
int index = 1;
gridUsers.Clear();
foreach (var user in users)
{
var role = await UserManager.GetRolesAsync(user);
gridUsers.Add(new UserGridItem
{
Index = index++,
UserId = user.Id,
UserName = user.UserName!,
Role = role.FirstOrDefault() ?? "-"
});
}
}
public class UserGridItem
{
public int Index { get; set; }
public string UserId { get; set; } = "";
public string UserName { get; set; } = "";
public string Role { get; set; } = "";
}
async Task DeleteUser(UserGridItem user)
{
if (user.UserId == currentUserId)
return;
var identityUser = await UserManager.FindByIdAsync(user.UserId);
if (identityUser is null)
return;
var result = await UserManager.DeleteAsync(identityUser);
if (result.Succeeded)
{
gridUsers.Remove(user);
StateHasChanged();
}
else
{
// Optional: Fehler anzeigen
}
}
private async Task CreateUserAsync()
{
// Validierung
if (string.IsNullOrWhiteSpace(newUsername) || string.IsNullOrWhiteSpace(newUserRoleId))
{
// Fehlermeldung anzeigen oder abbrechen
Console.WriteLine("Bitte Benutzername und Rolle angeben.");
return;
}
// Hier deine Logik zum Erstellen des Users
var roleIdToUse = newUserRoleId;
var usernameToUse = newUsername;
ApplicationUser newUser = new ApplicationUser
{
UserName = newUsername,
EmailConfirmed = true
};
var result = await UserManager.CreateAsync(newUser, "NewPassword123!");
IdentityRole roleName = roles.FirstOrDefault(r => r.Id == newUserRoleId);
if(result.Succeeded)
{
await UserManager.AddToRoleAsync(newUser, roleName.Name);
}
// Modal schließen oder Formular zurücksetzen
newUsername = "";
newUserRoleId = "";
NavigationManager.NavigateTo(NavigationManager.Uri, true);
}
}

View File

@@ -0,0 +1,2 @@
@using CouchLog.Components.AdminSettings.Shared
@layout AdminSettingsLayout

View File

@@ -0,0 +1,17 @@
@inherits LayoutComponentBase
@layout CouchLog.Components.Layout.MainLayout
<h1>Manage CouchLog</h1>
<div>
<h2></h2>
<hr />
<div class="row">
<div class="col-lg-3">
<AdminSettingsNavMenu />
</div>
<div class="col-lg-9">
@Body
</div>
</div>
</div>

View File

@@ -0,0 +1,18 @@
@using Microsoft.AspNetCore.Identity
@using CouchLog.Data
@inject SignInManager<ApplicationUser> SignInManager
<ul class="nav nav-pills flex-column">
<li class="nav-item">
<NavLink class="nav-link" href="/AdminSettings/UserManagement" Match="NavLinkMatch.All">User Management</NavLink>
</li>
<!--
<li class="nav-item">
<NavLink class="nav-link" href=""></NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href=""></NavLink>
</li>
-->
</ul>

View File

@@ -6,10 +6,6 @@
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>

View File

@@ -1,7 +0,0 @@
@page "/AdminSettings"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>Admin Settings</PageTitle>

View File

@@ -16,6 +16,7 @@
<PageTitle>GlobalList</PageTitle>
<div class="container-fluid mt-4">
<!-- #region CreateEntity -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Global List</h2>
<button class="btn btn-info" type="button" @onclick="ToogleCollapseNewGlobalEntity">@(isCollapseNewGlobalEntityOpen ? "X" : "Add Entity")</button>
@@ -59,13 +60,17 @@
</EditForm>
</div>
</div>
<!-- #endregion -->
<!-- #region Show Entitys -->
<div class="row">
@foreach (var Entity in GlobalEntities)
{
<div name="Enity-Container" class="col-12 col-md-6 col-lg-3 mb-4 Entity-Container">
<div name="Entity-Container-Card" class="Entity-Container-Card">
<div class="Entity-Container-Menu-Button">
<button type="button" class="Entity-Container-Menu-Button" data-bs-toogle="modal" data-bs-target="@Entity.Id">&#8942;</button>
<div class="Entity-Container-Menu-Button dropdown ms-1">
<button type="button" class="Entity-Container-Menu-Button btn menu-btn" data-bs-toggle="modal" data-bs-target="#modal-@Entity.Id">&#8942;</button>
</div>
<div class="modal fade" id="@Entity.Id" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
@@ -103,8 +108,26 @@
</div>
</div>
</div>
<div class="modal fade" id="modal-@Entity.Id" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Optionen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<button class="btn btn-danger" type="button" @onclick="() => DeleteEntity(Entity)">Delete Entity</button>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
</div>
</div>
</div>
</div>
}
</div>
<!-- #endregion -->
</div>
@code
@@ -268,4 +291,24 @@
GenreIds.Add(GenreId);
}
}
private async Task DeleteEntity(GlobalEntity entity)
{
if(entity.PicturePath != null)
{
try
{
File.Delete(Path.Combine("wwwroot", entity.PicturePath));
}
catch(Exception)
{
}
}
CouchLogDB.GlobalEntities.Remove(entity);
await CouchLogDB.SaveChangesAsync();
NavigationManager.NavigateTo(NavigationManager.Uri, true);
}
}

View File

@@ -58,21 +58,22 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="10.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.1" />
<PackageReference Include="Microsoft.Build" Version="18.0.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="5.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="5.0.0" />
<PackageReference Include="Microsoft.DotNet.Scaffolding.Shared" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0">
<PackageReference Include="Microsoft.DotNet.Scaffolding.Shared" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36511.14
# Visual Studio Version 18
VisualStudioVersion = 18.1.11304.174 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CouchLog", "CouchLog.csproj", "{4FBF15C3-5FC5-4605-9EBC-3F7DA1ADC47A}"
EndProject

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB