@page "/users" @using Microsoft.AspNetCore.Authorization @rendermode @(new InteractiveServerRenderMode(prerender: false)) @attribute [Microsoft.AspNetCore.Authorization.Authorize] @inject ApiClientService ApiClient @inject IJSRuntime JS @using Microsoft.AspNetCore.Components.Authorization @inject AuthenticationStateProvider AuthStateProvider @inject MenuAuthorizationService MenuAuthorization @AppLocalization.PageTitle("users", "Users")

@AppLocalization.PageTitle("users", "Users")

@AppLocalization.PageDescription("users", "Manage people, status, and account details.")

@if (canCreateUser) { }
@if (isLoading) { } else {
@if (PagedUsers.Any()) { @foreach (var entry in PagedUsers.Select((user, index) => (user, index))) { var user = entry.user; } } else { }
@AppLocalization.Text("common.index", "No.") @AppLocalization.Text("common.user", "User") @AppLocalization.Text("field.email", "Email") @AppLocalization.Text("field.phone", "Phone") @AppLocalization.Text("common.role", "Role") @AppLocalization.Text("common.status", "Status") @AppLocalization.Text("common.actions", "Actions")
@AppLocalization.Number((currentPage - 1) * pageSize + entry.index + 1)
@(Content.GetUserFullName(user) is var displayName && displayName.Length > 0 ? displayName[0].ToString().ToUpper() : "")

@Content.GetUserFullName(user)

@@@user.Username

@user.Email @(!string.IsNullOrWhiteSpace(user.Phone) ? AppLocalization.LocalizeDigits(user.Phone) : "-") @{ var role = roles.FirstOrDefault(r => r.Id == user.RoleId); } @(role != null ? Content.GetRoleName(role) : $"{AppLocalization.Text("common.role", "Role")} #{user.RoleId}") @(user.IsActive ? AppLocalization.Text("status.active", "Active") : AppLocalization.Text("status.inactive", "Inactive"))
@if (canEditUser) { } @if (canDeleteUser) { }
}
@* ===== Create / Edit User Modal ===== *@ @if (showModal) {

@(isEditing ? AppLocalization.Text("user.editUser", "Edit User") : AppLocalization.Text("common.newUser", "New User"))

@if (isFirstNameError) {

@firstNameErrorMsg

} @if (isLastNameError) {

@lastNameErrorMsg

}
@if (isUsernameError) { }
@if (isUsernameError) {

@usernameErrorMsg

}
@if (isEmailError) { }
@if (isEmailError) {

@emailErrorMsg

}
@if (!isEditing) {
@if (isPasswordError) {

@passwordErrorMsg

}
}
@if (isEditing) {
} @if (!string.IsNullOrEmpty(errorMessage)) {

@errorMessage

}
} @* ===== Confirm Dialog ===== *@ @code { private bool isLoading = true; private bool showModal = false; private bool isEditing = false; private bool isSaving = false; private bool canCreateUser = false; private bool canEditUser = false; private bool canDeleteUser = false; private string? errorMessage; // Per-field validation errors private bool isFirstNameError = false; private string firstNameErrorMsg = ""; private bool isLastNameError = false; private string lastNameErrorMsg = ""; private bool isUsernameError = false; private string usernameErrorMsg = ""; private bool isEmailError = false; private string emailErrorMsg = ""; private bool isPasswordError = false; private string passwordErrorMsg = ""; // Confirm dialog state private bool showConfirmDialog = false; private string confirmTitle = ""; private string confirmMessage = ""; private string confirmButtonText = ""; private string confirmIcon = ""; private string confirmIconBg = ""; private string confirmIconText = ""; private string confirmButtonClass = ""; private Func? confirmAction; private List users = new(); private List roles = new(); private string? successMessage; private bool showPassword = false; // Search / filter private string searchInput = ""; private long filterRoleIdInput = 0; private string filterStatusInput = "all"; // Pagination state private int currentPage = 1; private int pageSize = 10; private int totalCount = 0; private int totalPages = 0; private int TotalPages => totalPages; private int TotalCount => totalCount; private IEnumerable PagedUsers => users; // Form fields private long editingId; private string formUsername = ""; private string formFirstName = ""; private string formLastName = ""; private string? formFirstNameMy; private string? formLastNameMy; private bool burmeseGenerated; private bool englishGenerated; private bool burmeseNeedsReview; private bool englishNeedsReview; private string formEmail = ""; private string formPassword = ""; private string? formPhone; private long formRoleId; private bool formIsActive = true; private void MoveUserNameToEnglish() { formFirstName = formFirstNameMy ?? string.Empty; formLastName = formLastNameMy ?? string.Empty; } private void MoveUserNameToBurmese() { formFirstNameMy = formFirstName; formLastNameMy = formLastName; } private void MarkGenerated(bool english) { if (english) { englishGenerated = true; englishNeedsReview = true; } else { burmeseGenerated = true; burmeseNeedsReview = true; } } private void SetGeneratedUserName(string? value, bool english) { var parts = (value ?? string.Empty).Split(' ', 2, StringSplitOptions.RemoveEmptyEntries); if (english) { formFirstName = parts.ElementAtOrDefault(0) ?? string.Empty; formLastName = parts.ElementAtOrDefault(1) ?? string.Empty; } else { formFirstNameMy = parts.ElementAtOrDefault(0); formLastNameMy = parts.ElementAtOrDefault(1); } MarkGenerated(english); } private Task GenerateUserEnglishName(TranslationResult result) { SetGeneratedUserName(result.TranslatedText, true); return Task.CompletedTask; } private Task GenerateUserBurmeseName(TranslationResult result) { SetGeneratedUserName(result.TranslatedText, false); return Task.CompletedTask; } protected override async Task OnInitializedAsync() { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); var user = authState.User; canCreateUser = await MenuAuthorization.HasAccessCodeAsync(user, "Users_Create"); canEditUser = await MenuAuthorization.HasAccessCodeAsync(user, "Users_Update"); canDeleteUser = await MenuAuthorization.HasAccessCodeAsync(user, "Users_Delete"); var client = ApiClient.CreateClient(); try { var usersTask = LoadUsersPageAsync(client); var rolesTask = client.GetFromJsonAsync>("Role", Serialization.CaseInsensitive); await System.Threading.Tasks.Task.WhenAll(usersTask, rolesTask); users = usersTask.Result?.Items ?? new(); totalCount = usersTask.Result?.TotalCount ?? 0; totalPages = usersTask.Result?.TotalPages ?? 0; currentPage = usersTask.Result?.Page ?? currentPage; pageSize = usersTask.Result?.PageSize ?? pageSize; roles = rolesTask.Result ?? new(); } catch (Exception ex) { Console.WriteLine($"Load error: {ex.Message}"); } finally { isLoading = false; } } protected override async Task OnAfterRenderAsync(bool firstRender) { await JS.InvokeVoidAsync("initIcons"); } private async Task?> LoadUsersPageAsync(HttpClient? client = null) { client ??= ApiClient.CreateClient(); return await client.GetFromJsonAsync>(BuildUsersUrl(), Serialization.CaseInsensitive); } // Pagination handlers private async Task HandlePageChanged(int page) { currentPage = page; await LoadUsers(); } private async Task HandlePageSizeChanged(int size) { pageSize = size; currentPage = 1; await LoadUsers(); } private async Task ApplySearch() { currentPage = 1; await LoadUsers(); } private async Task ResetSearch() { searchInput = ""; filterRoleIdInput = 0; filterStatusInput = "all"; currentPage = 1; await LoadUsers(); } private async Task LoadUsers() { var client = ApiClient.CreateClient(); var result = await LoadUsersPageAsync(client); users = result?.Items ?? new(); totalCount = result?.TotalCount ?? 0; totalPages = result?.TotalPages ?? 0; currentPage = result?.Page ?? currentPage; pageSize = result?.PageSize ?? pageSize; } private string BuildUsersUrl() { var query = new List { $"page={currentPage}", $"limit={pageSize}" }; if (!string.IsNullOrWhiteSpace(searchInput)) { query.Add($"search={Uri.EscapeDataString(searchInput.Trim())}"); } if (filterRoleIdInput > 0) { query.Add($"roleId={filterRoleIdInput}"); } if (!string.Equals(filterStatusInput, "all", StringComparison.OrdinalIgnoreCase)) { query.Add($"status={Uri.EscapeDataString(filterStatusInput)}"); } return $"User?{string.Join("&", query)}"; } private void OpenCreateModal() { isEditing = false; editingId = 0; formUsername = ""; formFirstName = ""; formLastName = ""; formFirstNameMy = null; formLastNameMy = null; burmeseGenerated = englishGenerated = burmeseNeedsReview = englishNeedsReview = false; formEmail = ""; formPassword = ""; formPhone = null; formRoleId = roles.FirstOrDefault()?.Id ?? 0; formIsActive = true; errorMessage = null; isFirstNameError = false; firstNameErrorMsg = ""; isLastNameError = false; lastNameErrorMsg = ""; isUsernameError = false; usernameErrorMsg = ""; isEmailError = false; emailErrorMsg = ""; isPasswordError = false; passwordErrorMsg = ""; showModal = true; } private void OpenEditModal(UserDto user) { isEditing = true; editingId = user.Id; formUsername = user.Username; formFirstName = user.FirstName; formLastName = user.LastName; formFirstNameMy = user.FirstNameMy; formLastNameMy = user.LastNameMy; burmeseGenerated = englishGenerated = burmeseNeedsReview = englishNeedsReview = false; formEmail = user.Email; formPhone = user.Phone; formRoleId = user.RoleId; formIsActive = user.IsActive; errorMessage = null; isFirstNameError = false; firstNameErrorMsg = ""; isLastNameError = false; lastNameErrorMsg = ""; isUsernameError = false; usernameErrorMsg = ""; isEmailError = false; emailErrorMsg = ""; isPasswordError = false; passwordErrorMsg = ""; showModal = true; } private void CloseModal() { showModal = false; errorMessage = null; isFirstNameError = false; isLastNameError = false; isUsernameError = false; isEmailError = false; isPasswordError = false; } private void RequestSaveConfirmation() { isFirstNameError = false; firstNameErrorMsg = ""; isLastNameError = false; lastNameErrorMsg = ""; isUsernameError = false; usernameErrorMsg = ""; isEmailError = false; emailErrorMsg = ""; isPasswordError = false; passwordErrorMsg = ""; bool hasError = false; var englishNameComplete = !string.IsNullOrWhiteSpace(formFirstName) && !string.IsNullOrWhiteSpace(formLastName); var burmeseNameComplete = !string.IsNullOrWhiteSpace(formFirstNameMy) && !string.IsNullOrWhiteSpace(formLastNameMy); if (!englishNameComplete && !burmeseNameComplete) { isFirstNameError = true; isLastNameError = true; firstNameErrorMsg = AppLocalization.Text("validation.completeNameRequired", "Enter a complete name in Burmese or English."); lastNameErrorMsg = firstNameErrorMsg; hasError = true; } if (string.IsNullOrWhiteSpace(formUsername)) { isUsernameError = true; usernameErrorMsg = AppLocalization.Text("validation.usernameRequired", "Username is required."); hasError = true; } else if (formUsername.Length < 3) { isUsernameError = true; usernameErrorMsg = AppLocalization.Text("validation.usernameMinLength", "Username must be at least 3 characters long."); hasError = true; } else if (!System.Text.RegularExpressions.Regex.IsMatch(formUsername, @"^[a-zA-Z0-9._]+$")) { isUsernameError = true; usernameErrorMsg = AppLocalization.Text("validation.usernameInvalidCharacters", "Username cannot contain spaces, and can only contain letters, numbers, underscores (_), and periods (.)"); hasError = true; } if (string.IsNullOrWhiteSpace(formEmail)) { isEmailError = true; emailErrorMsg = AppLocalization.Text("validation.emailRequired", "Email is required."); hasError = true; } if (!isEditing) { if (string.IsNullOrWhiteSpace(formPassword)) { isPasswordError = true; passwordErrorMsg = AppLocalization.Text("validation.passwordRequired", "Password is required."); hasError = true; } else if (formPassword.Length < 8) { isPasswordError = true; passwordErrorMsg = AppLocalization.Text("validation.passwordMinLengthRule", "Password must be at least 8 characters long."); hasError = true; } else if (!System.Text.RegularExpressions.Regex.IsMatch(formPassword, @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z0-9]).+$")) { isPasswordError = true; passwordErrorMsg = AppLocalization.Text("validation.passwordComplexityRule", "Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character."); hasError = true; } } if (hasError) return; if (isEditing) { confirmTitle = AppLocalization.Text("user.updateUser", "Update User?"); confirmMessage = AppLocalization.Text("user.updateUserConfirm", "Are you sure you want to update this user?"); confirmButtonText = AppLocalization.Text("action.update", "Update"); confirmIcon = "pencil"; confirmIconBg = "bg-amber-50"; confirmIconText = "text-amber-500"; confirmButtonClass = "bg-violet-600 hover:bg-violet-700"; } else { confirmTitle = AppLocalization.Text("user.createUser", "Create User?"); confirmMessage = AppLocalization.Text("user.createUserConfirm", "Are you sure you want to create this user?"); confirmButtonText = AppLocalization.Text("action.create", "Create"); confirmIcon = "plus-circle"; confirmIconBg = "bg-emerald-50"; confirmIconText = "text-emerald-500"; confirmButtonClass = "bg-violet-600 hover:bg-violet-700"; } confirmAction = SaveUser; showConfirmDialog = true; } private void RequestDeleteConfirmation(long id) { confirmTitle = AppLocalization.Text("user.deleteUser", "Delete User?"); confirmMessage = AppLocalization.Text("user.deleteUserConfirm", "Are you sure you want to delete this user? This action cannot be undone."); confirmButtonText = AppLocalization.Text("action.delete", "Delete"); confirmIcon = "trash-2"; confirmIconBg = "bg-rose-50"; confirmIconText = "text-rose-500"; confirmButtonClass = "bg-violet-600 hover:bg-violet-700"; confirmAction = () => DeleteUser(id); showConfirmDialog = true; } private async Task HandleConfirmAction() { showConfirmDialog = false; if (confirmAction != null) await confirmAction(); } private void CloseConfirmDialog() { showConfirmDialog = false; } private async Task SaveUser() { var englishNameComplete = !string.IsNullOrWhiteSpace(formFirstName) && !string.IsNullOrWhiteSpace(formLastName); var burmeseNameComplete = !string.IsNullOrWhiteSpace(formFirstNameMy) && !string.IsNullOrWhiteSpace(formLastNameMy); if (string.IsNullOrWhiteSpace(formUsername) || (!englishNameComplete && !burmeseNameComplete) || string.IsNullOrWhiteSpace(formEmail)) { errorMessage = AppLocalization.Text("validation.fillAllFields", "Please fill in all required fields."); return; } isSaving = true; errorMessage = null; var client = ApiClient.CreateClient(); try { if (isEditing) { var dto = new UpdateUserDto { Username = formUsername, FirstName = formFirstName, LastName = formLastName, FirstNameMy = formFirstNameMy, LastNameMy = formLastNameMy, Phone = formPhone, RoleId = formRoleId, IsActive = formIsActive }; var response = await client.PutAsJsonAsync($"User/{editingId}", dto); var res = await response.Content.ReadFromJsonAsync(Serialization.CaseInsensitive); if (res == null || !res.IsSuccess) { errorMessage = AppLocalization.Text("result.userSaveFailed", "Unable to save the user. Please try again."); return; } } else { if (string.IsNullOrWhiteSpace(formPassword)) { errorMessage = AppLocalization.Text("validation.passwordRequired", "Password is required."); isSaving = false; return; } if (formPassword.Length < 8) { errorMessage = AppLocalization.Text("validation.passwordMinLengthRule", "Password must be at least 8 characters long."); isSaving = false; return; } if (!System.Text.RegularExpressions.Regex.IsMatch(formPassword, @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z0-9]).+$")) { errorMessage = AppLocalization.Text("validation.passwordComplexityRule", "Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character."); isSaving = false; return; } var dto = new CreateUserDto { Username = formUsername, FirstName = formFirstName, LastName = formLastName, FirstNameMy = formFirstNameMy, LastNameMy = formLastNameMy, Email = formEmail, Password = formPassword, Phone = formPhone, RoleId = formRoleId }; var response = await client.PostAsJsonAsync("User", dto); var res = await response.Content.ReadFromJsonAsync>(Serialization.CaseInsensitive); if (res == null || !res.IsSuccess) { errorMessage = AppLocalization.Text("result.userSaveFailed", "Unable to save the user. Please try again."); return; } } CloseModal(); await LoadUsers(); successMessage = isEditing ? AppLocalization.Text("result.userUpdated", "User updated successfully!") : AppLocalization.Text("result.userCreated", "User created successfully!"); } catch (Exception ex) { Console.WriteLine($"Save user error: {ex.Message}"); errorMessage = AppLocalization.Text("result.userSaveFailed", "Unable to save the user. Please try again."); } finally { isSaving = false; } } private async Task DeleteUser(long id) { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); var currentUserIdStr = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; if (currentUserIdStr != null && long.TryParse(currentUserIdStr, out var currentUserId) && currentUserId == id) { await JS.InvokeVoidAsync("alert", AppLocalization.Text("user.cannotDeleteSelf", "You cannot delete your own account.")); return; } var client = ApiClient.CreateClient(); try { var response = await client.DeleteAsync($"User/{id}"); var res = await response.Content.ReadFromJsonAsync(Serialization.CaseInsensitive); if (res == null || !res.IsSuccess) { await JS.InvokeVoidAsync("alert", res?.ErrorMessage ?? AppLocalization.Text("result.failedToDeleteUser", "Failed to delete user.")); return; } await LoadUsers(); if (currentPage > TotalPages && TotalPages > 0) currentPage = TotalPages; successMessage = AppLocalization.Text("result.userDeleted", "User deleted successfully!"); } catch (Exception ex) { Console.WriteLine($"Delete error: {ex.Message}"); errorMessage = AppLocalization.Text("result.failedToDeleteUser", "Failed to delete user."); } } private async Task TogglePassword() { showPassword = !showPassword; await JS.InvokeVoidAsync("initIcons"); } }