@page "/Members" @using BlazorFrontend.Services @using LibraryManagement.Shared.Models @using BlazorFrontend.Models @using Microsoft.AspNetCore.Authorization @attribute [Authorize(Roles = "Librarian")] @inject LibraryApiClient ApiClient @inject IJSRuntime JSRuntime @inject NavigationManager Navigation Members Directory @if (_currentView == ViewMode.List) {

Members Directory

Manage library members and administrator accounts. (@(_allUsers?.Count() ?? 0) total)

@if (_isLoading) {
} else if (_pagedResult != null) {
@foreach (var user in _pagedResult.Items) { }
Member Contact Role Status Actions
@(user.FullName?.Substring(0, Math.Min(2, user.FullName.Length)).ToUpper() ?? "U")
@if (!string.IsNullOrEmpty(user.StudentId)) {
ID: @user.StudentId
}
@user.Email
@(string.IsNullOrEmpty(user.PhoneNumber) ? "No phone" : user.PhoneNumber)
@user.Role @if (user.BanStatus == true) { Suspended } else if (user.IsActive) { Active } else { Inactive }
@* Pagination *@ @if (_pagedResult.TotalPages > 1) {

Showing @((_pagedResult.CurrentPage - 1) * _pagedResult.PageSize + 1)@(Math.Min(_pagedResult.CurrentPage * _pagedResult.PageSize, _pagedResult.TotalCount)) of @_pagedResult.TotalCount members

}
} } else if (_currentView == ViewMode.Details && _selectedUser != null) {
@(_selectedUser.User.FullName?.Substring(0, Math.Min(2, _selectedUser.User.FullName.Length)).ToUpper() ?? "U")

@_selectedUser.User.FullName

@_selectedUser.User.Role

Member Profile

@if (_selectedUser.User.BanStatus == true) { Suspended } else if (_selectedUser.User.IsActive) { Active }

Email

@_selectedUser.User.Email

Phone

@(string.IsNullOrEmpty(_selectedUser.User.PhoneNumber) ? "-" : _selectedUser.User.PhoneNumber)

Student ID

@(string.IsNullOrEmpty(_selectedUser.User.StudentId) ? "-" : _selectedUser.User.StudentId)

Joined

@_selectedUser.User.CreatedAt.ToString("MMM dd, yyyy")

Address

@(string.IsNullOrEmpty(_selectedUser.User.Address) ? "-" : _selectedUser.User.Address)

Library Wallet

Current Balance @_selectedUser.User.Balance.ToString("N0") MMK

Add Credits (Cash Deposit)

MMK

This will instantly add credits to the user's library wallet.

Manage Subscription

@if (_selectedUser.CurrentSubscription != null && _selectedUser.CurrentSubscription.IsActive) {
Active Plan

@_selectedUser.CurrentSubscription.MembershipType

Expires On
@_selectedUser.CurrentSubscription.ExpiryDate.ToString("MMM dd, yyyy")
} else {

No active subscription for this member.

}

Assign New Plan

@foreach (var plan in _selectedUser.AvailableMemberships) { var isCurrent = _selectedUser.CurrentSubscription?.IsActive == true && _selectedUser.CurrentSubscription.MembershipId == plan.Id;
@if (isCurrent) {
Current
}
@plan.Type

@plan.Price.ToString("N0") MMK / @plan.DurationMonths mo

@if (!isCurrent) { } else { }
}
} else if (_currentView == ViewMode.Create || _currentView == ViewMode.Edit) {

@(_currentView == ViewMode.Create ? "Add New Member" : "Edit Member Details")

@(_currentView == ViewMode.Create ? "Register a new member to the library system." : $"Update details for {_editUser?.FullName}.")

@if (_currentView == ViewMode.Create) {
} else if (_currentView == ViewMode.Edit) {
}
} @code { private enum ViewMode { List, Details, Create, Edit } private ViewMode _currentView = ViewMode.List; private IEnumerable? _allUsers; private PagedResult? _pagedResult; private bool _isLoading = true; private const int PageSize = 10; private MemberDetailsViewModel? _selectedUser; private UserCreateRequest _createRequest = new(); private UserUpdateRequest _updateRequest = new(); private UserDto? _editUser; private decimal _topUpAmount; protected override async Task OnInitializedAsync() { await LoadUsers(); } private async Task LoadUsers() { _isLoading = true; try { _allUsers = await ApiClient.GetUsersAsync(); ApplyPagination(1); } catch (Exception ex) { await JSRuntime.InvokeVoidAsync("showToast", "Error loading users: " + ex.Message, "error"); } finally { _isLoading = false; } } private void ApplyPagination(int page) { if (_allUsers == null) return; var totalCount = _allUsers.Count(); var totalPages = (int)Math.Ceiling(totalCount / (double)PageSize); page = Math.Max(1, Math.Min(page, Math.Max(1, totalPages))); _pagedResult = new PagedResult { Items = _allUsers.Skip((page - 1) * PageSize).Take(PageSize), CurrentPage = page, TotalPages = totalPages, TotalCount = totalCount, PageSize = PageSize }; } private void ChangePage(int page) { ApplyPagination(page); } private async Task ShowDetails(Guid userId) { _isLoading = true; try { var user = await ApiClient.GetUserAsync(userId); if (user == null) return; var memberships = await ApiClient.GetMembershipsAsync(); var subscription = await ApiClient.GetUserSubscriptionAsync(userId); _selectedUser = new MemberDetailsViewModel { User = user, AvailableMemberships = memberships.ToList(), CurrentSubscription = subscription }; _currentView = ViewMode.Details; } catch (Exception ex) { await JSRuntime.InvokeVoidAsync("showToast", "Error loading member details: " + ex.Message, "error"); } finally { _isLoading = false; } } private void ShowCreate() { _createRequest = new(); _currentView = ViewMode.Create; } private async Task ShowEdit(Guid userId) { _isLoading = true; try { _editUser = _allUsers?.FirstOrDefault(u => u.Id == userId); if (_editUser == null) { _editUser = await ApiClient.GetUserAsync(userId); } if (_editUser != null) { _updateRequest = new UserUpdateRequest { FullName = _editUser.FullName, PhoneNumber = _editUser.PhoneNumber, StudentId = _editUser.StudentId, Address = _editUser.Address }; _currentView = ViewMode.Edit; } } finally { _isLoading = false; } } private void BackToList() { _currentView = ViewMode.List; _selectedUser = null; _editUser = null; } private async Task HandleCreate() { try { var result = await ApiClient.CreateUserAsync(_createRequest); if (result != null) { await JSRuntime.InvokeVoidAsync("showToast", "Member created successfully!", "success"); await LoadUsers(); BackToList(); } else { await JSRuntime.InvokeVoidAsync("showToast", "Failed to create member.", "error"); } } catch (Exception ex) { await JSRuntime.InvokeVoidAsync("showToast", "Error: " + ex.Message, "error"); } } private async Task HandleUpdate() { if (_editUser == null) return; try { var result = await ApiClient.UpdateUserAsync(_editUser.Id, _updateRequest); if (result != null) { await JSRuntime.InvokeVoidAsync("showToast", "Member updated successfully!", "success"); await LoadUsers(); BackToList(); } } catch (Exception ex) { await JSRuntime.InvokeVoidAsync("showToast", "Error: " + ex.Message, "error"); } } private async Task DeleteUser() { if (_selectedUser == null) return; bool confirmed = await JSRuntime.InvokeAsync("confirm", "Are you sure you want to completely remove this member? This action cannot be undone."); if (!confirmed) return; try { var success = await ApiClient.DeleteUserAsync(_selectedUser.User.Id); if (success) { await JSRuntime.InvokeVoidAsync("showToast", "Member removed.", "success"); await LoadUsers(); BackToList(); } } catch (Exception ex) { await JSRuntime.InvokeVoidAsync("showToast", "Error: " + ex.Message, "error"); } } private async Task UpdateRole() { if (_selectedUser == null) return; var newRole = _selectedUser.User.Role == "Admin" ? "Member" : "Admin"; try { var success = await ApiClient.UpdateUserRoleAsync(_selectedUser.User.Id, newRole); if (success) { await JSRuntime.InvokeVoidAsync("showToast", $"Role updated to {newRole}.", "success"); await ShowDetails(_selectedUser.User.Id); // Refresh details } } catch (Exception ex) { await JSRuntime.InvokeVoidAsync("showToast", "Error: " + ex.Message, "error"); } } private async Task AssignSubscription(int membershipId, string planName) { if (_selectedUser == null) return; bool confirmed = await JSRuntime.InvokeAsync("confirm", $"Assign {planName} plan to this member?"); if (!confirmed) return; try { var request = new AdminSubscribeRequest { UserId = _selectedUser.User.Id, MembershipId = membershipId }; var result = await ApiClient.AdminSubscribeAsync(request); if (result != null) { await JSRuntime.InvokeVoidAsync("showToast", "Subscription assigned successfully.", "success"); await ShowDetails(_selectedUser.User.Id); // Refresh details } } catch (Exception ex) { await JSRuntime.InvokeVoidAsync("showToast", "Error: " + ex.Message, "error"); } } private async Task TopUpWallet() { if (_selectedUser == null || _topUpAmount <= 0) return; bool confirmed = await JSRuntime.InvokeAsync("confirm", $"Add {_topUpAmount:N0} MMK to {_selectedUser.User.FullName}'s wallet?"); if (!confirmed) return; try { var success = await ApiClient.TopUpWalletAsync(new TopUpRequest { UserId = _selectedUser.User.Id, Amount = _topUpAmount, Description = "Librarian Cash Top-up" }); if (success) { await JSRuntime.InvokeVoidAsync("showToast", "Wallet topped up successfully!", "success"); _topUpAmount = 0; await ShowDetails(_selectedUser.User.Id); // Refresh details to show new balance } else { await JSRuntime.InvokeVoidAsync("showToast", "Failed to top up wallet.", "error"); } } catch (Exception ex) { await JSRuntime.InvokeVoidAsync("showToast", "Error: " + ex.Message, "error"); } } }