Files
timetracker/timetracker.Client/Components/Pages/AdminUsers.razor
T
2026-06-08 16:24:51 +02:00

177 lines
7.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@page "/admin/users"
@rendermode InteractiveWebAssembly
@attribute [Authorize(Policy = "AdminOnly")]
@inject IAuthService AuthService
@inject ISnackbar Snackbar
@inject AuthenticationStateProvider AuthStateProvider
@inject IUserNotificationService UserNotificationService
@implements IDisposable
<PageTitle>Benutzerverwaltung Timetracker</PageTitle>
@if (_loading)
{
<MudStack AlignItems="AlignItems.Center" Class="mt-16" Spacing="3">
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large" />
<MudText Color="Color.Secondary">Lade Benutzer…</MudText>
</MudStack>
}
else
{
<MudStack Spacing="4">
@* ── Header ── *@
<MudPaper Elevation="4" Class="pa-5 rounded-xl"
Style="background: linear-gradient(135deg, #B71C1C 0%, #7F0000 100%); color:white;">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3">
<MudIcon Icon="@Icons.Material.Filled.AdminPanelSettings" Style="color:white; font-size:2rem" />
<MudStack Spacing="0">
<MudText Typo="Typo.h5" Style="color:white; font-weight:700">Benutzerverwaltung</MudText>
<MudText Typo="Typo.caption" Style="color:rgba(255,255,255,0.72)">
@_users.Count Benutzer registriert
</MudText>
</MudStack>
</MudStack>
</MudPaper>
@* ── Tabelle ── *@
<MudCard Elevation="3" Class="rounded-xl">
<MudCardContent Class="pa-0">
<MudTable Items="_users" Hover="true" Striped="true" Dense="false"
SortLabel="Sortieren">
<HeaderContent>
<MudTh><MudTableSortLabel SortBy="new Func<User, object>(u => u.Id)">ID</MudTableSortLabel></MudTh>
<MudTh><MudTableSortLabel SortBy="new Func<User, object>(u => u.Username)" InitialDirection="SortDirection.Ascending">Benutzername</MudTableSortLabel></MudTh>
<MudTh Style="text-align:right">Aktionen</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>
<MudText Typo="Typo.body2" Color="Color.Secondary">@context.Id</MudText>
</MudTd>
<MudTd>
@if (_editUserId == context.Id)
{
<MudTextField @bind-Value="_editUsername"
Variant="Variant.Outlined"
Margin="Margin.Dense"
Immediate="true"
Style="max-width:220px"
OnKeyDown="@(async e => { if (e.Key == "Enter") await SaveRename(context); })" />
}
else
{
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudIcon Icon="@Icons.Material.Filled.AccountCircle"
Color="@(context.Username == "marc" ? Color.Error : Color.Default)"
Size="Size.Small" />
<MudText Style="@(context.Username == "marc" ? "font-weight:700" : "")">
@context.Username
</MudText>
@if (context.Username == "marc")
{
<MudChip T="string" Size="Size.Small" Color="Color.Error" Variant="Variant.Outlined">Admin</MudChip>
}
</MudStack>
}
</MudTd>
<MudTd Style="text-align:right">
@if (_editUserId == context.Id)
{
<MudIconButton Icon="@Icons.Material.Filled.Check"
Color="Color.Success"
Size="Size.Small"
OnClick="@(() => SaveRename(context))" />
<MudIconButton Icon="@Icons.Material.Filled.Close"
Color="Color.Default"
Size="Size.Small"
OnClick="CancelEdit" />
}
else
{
<MudIconButton Icon="@Icons.Material.Filled.Edit"
Color="Color.Primary"
Size="Size.Small"
OnClick="@(() => StartEdit(context))" />
@if (context.Username != "marc")
{
<MudIconButton Icon="@Icons.Material.Filled.DeleteOutline"
Color="Color.Error"
Size="Size.Small"
OnClick="@(() => DeleteUser(context))" />
}
}
</MudTd>
</RowTemplate>
<NoRecordsContent>
<MudText Color="Color.Secondary" Class="pa-4">Keine Benutzer gefunden.</MudText>
</NoRecordsContent>
</MudTable>
</MudCardContent>
</MudCard>
</MudStack>
}
@code {
private List<User> _users = [];
private bool _loading = true;
private int? _editUserId;
private string _editUsername = "";
protected override async Task OnInitializedAsync()
{
var claim = (await AuthStateProvider.GetAuthenticationStateAsync())
.User.FindFirst(ClaimTypes.NameIdentifier);
if (claim == null) return;
UserNotificationService.OnUsersChanged += RefreshUsers;
_users = await AuthService.GetAllUsersAsync();
_loading = false;
}
private async Task RefreshUsers()
{
_users = await AuthService.GetAllUsersAsync();
await InvokeAsync(StateHasChanged);
}
public void Dispose()
{
UserNotificationService.OnUsersChanged -= RefreshUsers;
}
private void StartEdit(User user)
{
_editUserId = user.Id;
_editUsername = user.Username;
}
private void CancelEdit()
{
_editUserId = null;
_editUsername = "";
}
private async Task SaveRename(User user)
{
var trimmed = _editUsername.Trim();
var error = await AuthService.RenameUserAsync(user.Id, trimmed);
if (error != null)
{
Snackbar.Add(error, Severity.Error);
return;
}
user.Username = trimmed;
_editUserId = null;
_editUsername = "";
Snackbar.Add($"Benutzer umbenannt zu \"{trimmed}\".", 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);
}
}