Userverwaltung improved
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
@inherits LayoutComponentBase
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject NavigationManager Nav
|
||||
@inject timetracker.Data.UserNotificationService UserNotificationService
|
||||
@implements IDisposable
|
||||
@using System.Security.Claims
|
||||
|
||||
<MudThemeProvider Theme="_theme" />
|
||||
<MudPopoverProvider />
|
||||
@@ -29,6 +34,26 @@
|
||||
@code {
|
||||
private bool _drawerOpen = true;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
UserNotificationService.OnUserDeleted += HandleUserDeleted;
|
||||
}
|
||||
|
||||
private async Task HandleUserDeleted(int deletedUserId)
|
||||
{
|
||||
var state = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
var idClaim = state.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
if (idClaim != null && int.TryParse(idClaim, out var myId) && myId == deletedUserId)
|
||||
{
|
||||
await InvokeAsync(() => Nav.NavigateTo("/auth/logout", forceLoad: true));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
UserNotificationService.OnUserDeleted -= HandleUserDeleted;
|
||||
}
|
||||
|
||||
private readonly MudTheme _theme = new()
|
||||
{
|
||||
PaletteLight = new PaletteLight
|
||||
|
||||
@@ -25,5 +25,12 @@
|
||||
</MudNavMenu>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
<MudText Typo="Typo.caption" Class="px-4 py-2" Style="color: var(--mud-palette-text-disabled); text-align:center;">Version 1.1</MudText>
|
||||
<MudTooltip Text="Changelog anzeigen" Placement="Placement.Top">
|
||||
<MudNavMenu>
|
||||
<MudNavLink Href="/changelog" Icon="@Icons.Material.Filled.NewReleases"
|
||||
Style="color: var(--mud-palette-text-disabled); font-size:0.75rem;">
|
||||
Version 1.1
|
||||
</MudNavLink>
|
||||
</MudNavMenu>
|
||||
</MudTooltip>
|
||||
</div>
|
||||
@@ -4,6 +4,8 @@
|
||||
@inject AuthService AuthService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject timetracker.Data.UserNotificationService UserNotificationService
|
||||
@implements IDisposable
|
||||
|
||||
<PageTitle>Benutzerverwaltung – Timetracker</PageTitle>
|
||||
|
||||
@@ -122,10 +124,22 @@ else
|
||||
.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;
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
@page "/changelog"
|
||||
@rendermode InteractiveServer
|
||||
@attribute [Authorize]
|
||||
|
||||
<PageTitle>Changelog – Timetracker</PageTitle>
|
||||
|
||||
<MudStack Spacing="4">
|
||||
|
||||
@* ── Header ── *@
|
||||
<MudPaper Elevation="4" Class="pa-5 rounded-xl"
|
||||
Style="background: linear-gradient(135deg, #3F51B5 0%, #1A237E 100%); color:white;">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3">
|
||||
<MudIcon Icon="@Icons.Material.Filled.NewReleases" Style="color:white; font-size:2rem" />
|
||||
<MudStack Spacing="0">
|
||||
<MudText Typo="Typo.h5" Style="color:white; font-weight:700">Changelog</MudText>
|
||||
<MudText Typo="Typo.caption" Style="color:rgba(255,255,255,0.72)">Versionshistorie & Änderungen</MudText>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
|
||||
@foreach (var release in _releases)
|
||||
{
|
||||
<MudCard Elevation="2" Class="rounded-xl">
|
||||
<MudCardContent>
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2" Class="mb-3">
|
||||
<MudChip T="string" Color="@(release.IsLatest ? Color.Primary : Color.Default)"
|
||||
Variant="Variant.Filled" Size="Size.Medium" Style="font-weight:700">
|
||||
@release.Version
|
||||
</MudChip>
|
||||
@if (release.IsLatest)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Variant="Variant.Outlined" Size="Size.Small">
|
||||
Aktuell
|
||||
</MudChip>
|
||||
}
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">@release.Date</MudText>
|
||||
</MudStack>
|
||||
<MudDivider Class="mb-3" />
|
||||
<MudStack Spacing="1">
|
||||
@foreach (var entry in release.Entries)
|
||||
{
|
||||
<MudStack Row="true" AlignItems="AlignItems.Start" Spacing="2">
|
||||
<MudChip T="string" Color="@GetTagColor(entry.Tag)" Variant="Variant.Outlined"
|
||||
Size="Size.Small" Style="min-width:72px; justify-content:center; font-size:0.7rem;">
|
||||
@entry.Tag
|
||||
</MudChip>
|
||||
<MudText Typo="Typo.body2" Style="padding-top:2px">@entry.Text</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
}
|
||||
|
||||
</MudStack>
|
||||
|
||||
@code {
|
||||
private record ChangeEntry(string Tag, string Text);
|
||||
private record Release(string Version, string Date, bool IsLatest, List<ChangeEntry> Entries);
|
||||
|
||||
private static Color GetTagColor(string tag) => tag switch
|
||||
{
|
||||
"Neu" => Color.Success,
|
||||
"Fix" => Color.Error,
|
||||
"Upgrade" => Color.Info,
|
||||
_ => Color.Default
|
||||
};
|
||||
|
||||
private readonly List<Release> _releases =
|
||||
[
|
||||
new("1.1", "08.06.2026", true,
|
||||
[
|
||||
new("Neu", "Versionsnummer in der Navbar mit Link zum Changelog"),
|
||||
new("Neu", "Changelog-Seite"),
|
||||
new("Neu", "Live-Aktualisierung der Benutzerliste bei neuer Registrierung"),
|
||||
new("Neu", "Automatisches Abmelden gelöschter Benutzer"),
|
||||
new("Neu", "Benutzernamen in der Benutzerverwaltung umbenennen"),
|
||||
new("Upgrade", "Navbar: Benutzer und Abmelden-Button unten fixiert"),
|
||||
]),
|
||||
new("1.0", "20.05.2026", false,
|
||||
[
|
||||
new("Neu", "Erste Version des Timetrackers"),
|
||||
new("Neu", "Wochenübersicht mit Arbeitszeiten und Pausen"),
|
||||
new("Neu", "Monatsübersicht"),
|
||||
new("Neu", "Feiertage-Verwaltung"),
|
||||
new("Neu", "Urlaubs-Maximizer"),
|
||||
new("Neu", "Einstellungen"),
|
||||
new("Neu", "Benutzerverwaltung für Admins"),
|
||||
new("Neu", "Registrierung und Login"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
+5
-1
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace timetracker.Data;
|
||||
|
||||
public class AuthService(IDbContextFactory<TimetrackerDbContext> factory)
|
||||
public class AuthService(IDbContextFactory<TimetrackerDbContext> factory, UserNotificationService notifier)
|
||||
{
|
||||
public async Task<User?> LoginAsync(string username, string password)
|
||||
{
|
||||
@@ -31,6 +31,8 @@ public class AuthService(IDbContextFactory<TimetrackerDbContext> factory)
|
||||
{
|
||||
db.Users.Remove(user);
|
||||
await db.SaveChangesAsync();
|
||||
await notifier.NotifyUserDeletedAsync(userId);
|
||||
await notifier.NotifyUsersChangedAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +50,7 @@ public class AuthService(IDbContextFactory<TimetrackerDbContext> factory)
|
||||
|
||||
user.Username = newUsername;
|
||||
await db.SaveChangesAsync();
|
||||
await notifier.NotifyUsersChangedAsync();
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -65,6 +68,7 @@ public class AuthService(IDbContextFactory<TimetrackerDbContext> factory)
|
||||
var user = new User { Username = username, PasswordHash = hash, PasswordSalt = salt };
|
||||
db.Users.Add(user);
|
||||
await db.SaveChangesAsync();
|
||||
await notifier.NotifyUsersChangedAsync();
|
||||
return (user, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace timetracker.Data;
|
||||
|
||||
public class UserNotificationService
|
||||
{
|
||||
public event Func<Task>? OnUsersChanged;
|
||||
public event Func<int, Task>? OnUserDeleted;
|
||||
|
||||
public async Task NotifyUsersChangedAsync()
|
||||
{
|
||||
if (OnUsersChanged != null)
|
||||
await OnUsersChanged.Invoke();
|
||||
}
|
||||
|
||||
public async Task NotifyUserDeletedAsync(int userId)
|
||||
{
|
||||
if (OnUserDeleted != null)
|
||||
await OnUserDeleted.Invoke(userId);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ builder.Services.AddAuthorization(options =>
|
||||
});
|
||||
builder.Services.AddCascadingAuthenticationState();
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddSingleton<timetracker.Data.UserNotificationService>();
|
||||
builder.Services.AddScoped<AuthService>();
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user