@page "/admin/users"
@rendermode InteractiveWebAssembly
@attribute [Authorize(Policy = "AdminOnly")]
@using System.Net.Http.Json
@using System.Security.Claims
@inject IAuthService AuthService
@inject ISnackbar Snackbar
@inject AuthenticationStateProvider AuthStateProvider
@inject IUserNotificationService UserNotificationService
@inject HttpClient Http
@inject IDialogService DialogService
@inject NavigationManager NavigationManager
@implements IDisposable
Benutzerverwaltung – Timetracker
@if (_loading)
{
Lade Benutzer…
}
else
{
@* ── Header ── *@
Benutzerverwaltung
@_users.Count Benutzer registriert
@* ── Tabelle ── *@
ID
Benutzername
Firma / Mandant
Aktionen
@context.Id
@if (_editUserId == context.Id)
{
}
else
{
@context.Username
@if (context.Username == "marc")
{
Admin
}
}
@if (_editUserId == context.Id)
{
Hauptdomain (System/Global)
@foreach (var tenant in _tenants)
{
@tenant.Name (@tenant.Subdomain)
}
}
else
{
@if (context.TenantId.HasValue)
{
var t = _tenants.FirstOrDefault(x => x.Id == context.TenantId.Value);
@(t?.Name ?? $"Firma #{context.TenantId}")
}
else
{
@(context.Username == "marc" ? "SuperAdmin" : "Einzelnutzer")
}
}
@if (_editUserId == context.Id)
{
}
else
{
@if (context.Username != "marc")
{
}
}
Keine Benutzer gefunden.
}
@code {
[PersistentState(AllowUpdates = true)]
public List? PersistentUsers { get; set; }
[PersistentState(AllowUpdates = true)]
public List? PersistentTenants { get; set; }
private List _users = [];
private List _tenants = [];
private bool _loading = true;
private int? _editUserId;
private string _editUsername = "";
private int? _editTenantId;
protected override async Task OnInitializedAsync()
{
if (PersistentUsers != null)
{
_users = PersistentUsers;
_tenants = PersistentTenants ?? [];
_loading = false;
}
var claim = (await AuthStateProvider.GetAuthenticationStateAsync())
.User.FindFirst(ClaimTypes.NameIdentifier);
if (claim == null) return;
UserNotificationService.OnUsersChanged += RefreshUsers;
if (PersistentUsers == null)
{
try
{
_users = await AuthService.GetAllUsersAsync();
_tenants = await Http.GetFromJsonAsync>("api/superadmin/tenants") ?? [];
PersistentUsers = _users;
PersistentTenants = _tenants;
_loading = false;
}
catch (Exception ex)
{
Snackbar.Add($"Fehler beim Laden der Daten: {ex.Message}", Severity.Error);
}
}
}
private async Task RefreshUsers()
{
_users = await AuthService.GetAllUsersAsync();
try
{
_tenants = await Http.GetFromJsonAsync>("api/superadmin/tenants") ?? [];
}
catch
{
// Ignore background refresh errors
}
await InvokeAsync(StateHasChanged);
}
public void Dispose()
{
UserNotificationService.OnUsersChanged -= RefreshUsers;
}
private void StartEdit(User user)
{
_editUserId = user.Id;
_editUsername = user.Username;
_editTenantId = user.TenantId;
}
private void CancelEdit()
{
_editUserId = null;
_editUsername = "";
_editTenantId = null;
}
private async Task SaveRename(User user)
{
var trimmed = _editUsername.Trim();
// 1. Rename if username changed
if (trimmed != user.Username)
{
var error = await AuthService.RenameUserAsync(user.Id, trimmed);
if (error != null)
{
Snackbar.Add(error, Severity.Error);
return;
}
user.Username = trimmed;
}
// 2. Move to tenant if tenant ID changed
if (_editTenantId != user.TenantId)
{
var error = await AuthService.AssignTenantAsync(user.Id, _editTenantId);
if (error != null)
{
Snackbar.Add(error, Severity.Error);
return;
}
user.TenantId = _editTenantId;
}
_editUserId = null;
_editUsername = "";
_editTenantId = null;
Snackbar.Add("Benutzerdaten erfolgreich aktualisiert.", Severity.Success);
}
private async Task DeleteUser(User user)
{
await AuthService.DeleteUserAsync(user.Id);
_users.Remove(user);
Snackbar.Add($"Benutzer \"{user.Username}\" gelöscht.", Severity.Info);
}
private async Task OpenResetPasswordDialog(User user)
{
var parameters = new DialogParameters
{
{ x => x.Username, user.Username }
};
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true };
var dialog = await DialogService.ShowAsync("Passwort zurücksetzen", parameters, options);
var result = await dialog.Result;
if (result != null && !result.Canceled && result.Data is string newPassword)
{
var error = await AuthService.ResetPasswordAsync(user.Id, newPassword);
if (error != null)
{
Snackbar.Add(error, Severity.Error);
}
else
{
Snackbar.Add($"Passwort für {user.Username} wurde erfolgreich zurückgesetzt.", Severity.Success);
}
}
}
private async Task DownloadUserPdf(User user)
{
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall, FullWidth = true };
var dialog = await DialogService.ShowAsync($"Stundenzettel für {user.Username}", options);
var result = await dialog.Result;
if (result != null && !result.Canceled && result.Data is MonthSelectorDialog.MonthSelectorResult data)
{
int year = data.Year;
int month = data.Month;
NavigationManager.NavigateTo($"api/tracker/export/month/pdf?userId={user.Id}&year={year}&month={month}", forceLoad: true);
}
}
}