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(); + } + } }