@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 @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 { Hauptdomain (SuperUser) } } @if (_editUserId == context.Id) { } else { @if (context.Username != "marc") { } } Keine Benutzer gefunden. } @code { private List _users = []; private List _tenants = []; private bool _loading = true; private int? _editUserId; private string _editUsername = ""; private int? _editTenantId; protected override async Task OnInitializedAsync() { var claim = (await AuthStateProvider.GetAuthenticationStateAsync()) .User.FindFirst(ClaimTypes.NameIdentifier); if (claim == null) return; UserNotificationService.OnUsersChanged += RefreshUsers; try { _users = await AuthService.GetAllUsersAsync(); _tenants = await Http.GetFromJsonAsync>("api/superadmin/tenants") ?? []; } catch (Exception ex) { Snackbar.Add($"Fehler beim Laden der Daten: {ex.Message}", Severity.Error); } _loading = false; } 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); } } } }