Initial commit

This commit is contained in:
2026-06-02 10:51:14 +02:00
committed by kawa
commit d19092c84e
182 changed files with 13757 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
@page "/admin/users"
@attribute [Microsoft.AspNetCore.Authorization.Authorize]
@inject IUserService UserService
@inject IUserContextAccessor UserContext
@inject NavigationManager Nav
@rendermode InteractiveServer
@using SharepointToolbox.Web.Core.Models
@using SharepointToolbox.Web.Services.Auth
@using SharepointToolbox.Web.Services.Session
<h1 class="page-title">User Management</h1>
<p class="page-subtitle">Manage technician accounts and roles. Auto-provisioned on first OIDC login.</p>
@if (!UserContext.IsAuthenticated || UserContext.Role != UserRole.Admin)
{
<div class="alert alert-error">Access denied. Admin role required.</div>
return;
}
@if (!string.IsNullOrEmpty(_message))
{
<div class="alert @(_isError ? "alert-error" : "alert-info")">@_message</div>
}
@if (_users.Count == 0)
{
<div class="alert alert-info">No users provisioned yet.</div>
}
else
{
<div class="card" style="overflow-x:auto">
<table style="width:100%;border-collapse:collapse">
<thead>
<tr style="border-bottom:2px solid var(--border)">
<th style="text-align:left;padding:8px">User</th>
<th style="text-align:left;padding:8px">Email</th>
<th style="text-align:left;padding:8px">Role</th>
<th style="text-align:left;padding:8px">Last Login</th>
<th style="text-align:left;padding:8px">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in _users)
{
<tr style="border-bottom:1px solid var(--border)">
<td style="padding:8px">@user.DisplayName</td>
<td style="padding:8px">@user.Email</td>
<td style="padding:8px">
<select class="form-input" style="width:130px"
value="@user.Role"
@onchange="e => OnRoleChange(user, e)"
disabled="@(user.Email == UserContext.Email)">
@foreach (var role in Enum.GetValues<UserRole>())
{
<option value="@role" selected="@(user.Role == role)">@role</option>
}
</select>
</td>
<td style="padding:8px">@(user.LastLogin?.ToString("yyyy-MM-dd HH:mm") ?? "Never")</td>
<td style="padding:8px">
@if (user.Email != UserContext.Email)
{
<button class="btn btn-danger btn-sm" @onclick="() => DeleteUserAsync(user)">Remove</button>
}
else
{
<span class="chip chip-green">You</span>
}
</td>
</tr>
}
</tbody>
</table>
</div>
}
@code {
private List<AppUser> _users = new();
private string _message = string.Empty;
private bool _isError;
protected override async Task OnInitializedAsync()
{
_users = (await UserService.GetAllAsync()).ToList();
}
private async Task OnRoleChange(AppUser user, ChangeEventArgs e)
{
if (!Enum.TryParse<UserRole>(e.Value?.ToString(), out var newRole)) return;
try
{
await UserService.UpdateRoleAsync(user.Id, newRole);
user.Role = newRole;
_message = $"Role updated for {user.DisplayName}.";
_isError = false;
}
catch (Exception ex)
{
_message = $"Error: {ex.Message}";
_isError = true;
}
}
private async Task DeleteUserAsync(AppUser user)
{
await UserService.DeleteAsync(user.Id);
_users.Remove(user);
_message = $"User {user.DisplayName} removed.";
_isError = false;
}
}