diff --git a/src/Koogle.Application/Services/UserService.cs b/src/Koogle.Application/Services/UserService.cs
index 6122a94..04489f2 100644
--- a/src/Koogle.Application/Services/UserService.cs
+++ b/src/Koogle.Application/Services/UserService.cs
@@ -613,6 +613,9 @@ public class UserService : IUserService
await _appDb.SaveChangesAsync(ct);
+ // Assign default Viewer role to new member
+ await AssignClubRoleAsync(userProfileId, clubId, "Viewer", approvedById, ct);
+
// Send notification to user
var identityUser = await _userManager.FindByIdAsync(membership.UserProfile.IdentityUserId.ToString());
if (identityUser?.Email != null)
diff --git a/src/Koogle.Web/Components/App.razor b/src/Koogle.Web/Components/App.razor
index 30bab9b..18b6580 100644
--- a/src/Koogle.Web/Components/App.razor
+++ b/src/Koogle.Web/Components/App.razor
@@ -25,7 +25,14 @@
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
- private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account")
- ? new InteractiveServerRenderMode(prerender: false)
- : new InteractiveServerRenderMode(prerender: true);
+ private IComponentRenderMode? RenderModeForPage
+ {
+ get
+ {
+ var path = HttpContext.Request.Path;
+ var disablePrerender = path.StartsWithSegments("/Account")
+ || path.StartsWithSegments("/settings");
+ return new InteractiveServerRenderMode(prerender: !disablePrerender);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Koogle.Web/Components/Pages/Admin/ClubMemberRolesDialog.razor b/src/Koogle.Web/Components/Pages/Admin/ClubMemberRolesDialog.razor
new file mode 100644
index 0000000..8cbd523
--- /dev/null
+++ b/src/Koogle.Web/Components/Pages/Admin/ClubMemberRolesDialog.razor
@@ -0,0 +1,132 @@
+@using Koogle.Application.DTOs
+@using Koogle.Application.Interfaces
+@using Koogle.Domain.Enums
+
+@inject IUserService UserService
+@inject ISnackbar Snackbar
+
+
+
+ Rollen fuer @User.DisplayName
+
+
+
+ @foreach (var role in _availableRoles)
+ {
+ var hasRole = _currentRoles.Contains(role);
+
+ @GetRoleDisplayName(role)
+
+ }
+
+
+
+
+
+
+ Aus Verein entfernen
+
+
+
+ Schliessen
+
+
+
+@code {
+ [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ [Parameter] public UserDto User { get; set; } = null!;
+ [Parameter] public Guid ClubId { get; set; }
+ [Parameter] public Guid CurrentUserProfileId { get; set; }
+
+ // Available roles for club-admin (no SuperAdmin)
+ private readonly string[] _availableRoles = { UserRole.Viewer, UserRole.Editor, UserRole.Admin };
+ private List _currentRoles = new();
+ private bool _isProcessing;
+ private bool _hasChanges;
+
+ protected override void OnInitialized()
+ {
+ var membership = User.ClubMemberships.FirstOrDefault(m => m.ClubId == ClubId);
+ _currentRoles = membership?.Roles.ToList() ?? new List();
+ }
+
+ private static string GetRoleDisplayName(string role) => role switch
+ {
+ UserRole.Viewer => "Betrachter",
+ UserRole.Editor => "Bearbeiter",
+ UserRole.Admin => "Administrator",
+ _ => role
+ };
+
+ private async Task ToggleRole(string role, bool currentlyHasRole)
+ {
+ _isProcessing = true;
+ try
+ {
+ bool success;
+ if (currentlyHasRole)
+ {
+ success = await UserService.RemoveClubRoleAsync(User.ProfileId, ClubId, role);
+ }
+ else
+ {
+ success = await UserService.AssignClubRoleAsync(User.ProfileId, ClubId, role, CurrentUserProfileId);
+ }
+
+ if (success)
+ {
+ if (currentlyHasRole)
+ _currentRoles.Remove(role);
+ else
+ _currentRoles.Add(role);
+
+ _hasChanges = true;
+ Snackbar.Add(currentlyHasRole ? "Rolle entfernt" : "Rolle hinzugefuegt", Severity.Success);
+ StateHasChanged();
+ }
+ else
+ {
+ Snackbar.Add("Fehler beim Aendern der Rolle", Severity.Error);
+ }
+ }
+ finally
+ {
+ _isProcessing = false;
+ }
+ }
+
+ private async Task RemoveFromClub()
+ {
+ _isProcessing = true;
+ try
+ {
+ var success = await UserService.RemoveUserFromClubAsync(User.ProfileId, ClubId);
+ if (success)
+ {
+ _hasChanges = true;
+ Snackbar.Add("Aus Verein entfernt", Severity.Success);
+ MudDialog.Close(DialogResult.Ok(_hasChanges));
+ }
+ else
+ {
+ Snackbar.Add("Fehler beim Entfernen", Severity.Error);
+ }
+ }
+ finally
+ {
+ _isProcessing = false;
+ }
+ }
+
+ private void Close() => MudDialog.Close(DialogResult.Ok(_hasChanges));
+}
diff --git a/src/Koogle.Web/Components/Pages/Settings.razor b/src/Koogle.Web/Components/Pages/Settings.razor
index 76714c3..257e27b 100644
--- a/src/Koogle.Web/Components/Pages/Settings.razor
+++ b/src/Koogle.Web/Components/Pages/Settings.razor
@@ -196,6 +196,61 @@ else
+
+
+
+
+ @if (_isLoadingMembers)
+ {
+
+ }
+ else if (!_clubMembers.Any())
+ {
+ Keine Mitglieder vorhanden.
+ }
+ else
+ {
+
+
+ Name
+ E-Mail
+ Rollen
+ Aktionen
+
+
+ @context.DisplayName
+ @context.Identity.Email
+
+ @{
+ var membership = context.ClubMemberships.FirstOrDefault(m => m.ClubId == CurrentClubContext.ClubId);
+ if (membership != null && membership.Roles.Any())
+ {
+ @foreach (var role in membership.Roles)
+ {
+
+ @GetRoleDisplayName(role)
+
+ }
+ }
+ else
+ {
+ Keine Rolle
+ }
+ }
+
+
+
+
+
+
+
+
+ }
+
+
+
}
@@ -204,9 +259,11 @@ else
private bool _isLoading = true;
private bool _isLoadingInvitations = true;
private bool _isLoadingPending = true;
+ private bool _isLoadingMembers = true;
private int _activeTabIndex = 0;
private List _invitations = new();
private List _pendingMemberships = new();
+ private List _clubMembers = new();
protected override async Task OnInitializedAsync()
{
@@ -217,19 +274,34 @@ else
private async Task LoadAllDataAsync()
{
_isLoading = true;
+ _isLoadingInvitations = true;
+ _isLoadingPending = true;
+ _isLoadingMembers = true;
+
try
{
var clubId = CurrentClubContext.ClubId;
if (clubId != Guid.Empty)
{
_club = await ClubService.GetByIdAsync(clubId);
- await LoadInvitationsAsync();
- await LoadPendingMembershipsAsync();
+ _isLoading = false;
+
+ _invitations = (await ClubService.GetInvitationsByClubAsync(clubId)).ToList();
+ _isLoadingInvitations = false;
+
+ _pendingMemberships = (await UserService.GetPendingMembershipsAsync(clubId)).ToList();
+ _isLoadingPending = false;
+
+ _clubMembers = (await UserService.GetUsersByClubAsync(clubId)).ToList();
+ _isLoadingMembers = false;
}
}
finally
{
_isLoading = false;
+ _isLoadingInvitations = false;
+ _isLoadingPending = false;
+ _isLoadingMembers = false;
}
}
@@ -267,6 +339,23 @@ else
}
}
+ private async Task LoadMembersAsync()
+ {
+ _isLoadingMembers = true;
+ try
+ {
+ var clubId = CurrentClubContext.ClubId;
+ if (clubId != Guid.Empty)
+ {
+ _clubMembers = (await UserService.GetUsersByClubAsync(clubId)).ToList();
+ }
+ }
+ finally
+ {
+ _isLoadingMembers = false;
+ }
+ }
+
private void ClearError()
{
Dispatcher.Dispatch(new ClearClubErrorAction());
@@ -461,4 +550,47 @@ else
}
}
}
+
+ private static string GetRoleDisplayName(string role) => role switch
+ {
+ UserRole.Viewer => "Betrachter",
+ UserRole.Editor => "Bearbeiter",
+ UserRole.Admin => "Administrator",
+ _ => role
+ };
+
+ private static Color GetRoleColor(string role) => role switch
+ {
+ UserRole.Admin => Color.Error,
+ UserRole.Editor => Color.Warning,
+ UserRole.Viewer => Color.Info,
+ _ => Color.Default
+ };
+
+ private async Task OpenMemberRolesDialog(UserDto user)
+ {
+ var currentUser = await UserService.GetCurrentUserAsync();
+ if (currentUser == null)
+ {
+ Snackbar.Add("Nicht angemeldet", Severity.Error);
+ return;
+ }
+
+ var parameters = new DialogParameters
+ {
+ { "User", user },
+ { "ClubId", CurrentClubContext.ClubId },
+ { "CurrentUserProfileId", currentUser.ProfileId }
+ };
+
+ var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small };
+ var dialog = await DialogService.ShowAsync("Rollen verwalten", parameters, options);
+ var result = await dialog.Result;
+
+ if (result != null && !result.Canceled && result.Data is bool hasChanges && hasChanges)
+ {
+ await LoadMembersAsync();
+ StateHasChanged();
+ }
+ }
}