RobotNet/RobotNet.IdentityServer/Components/Account/Pages/Infor.razor
2025-10-15 15:15:53 +07:00

355 lines
15 KiB
Plaintext

@rendermode InteractiveServer
@using Microsoft.AspNetCore.Identity
@using RobotNet.IdentityServer.Data
@using MudBlazor
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components
@using Microsoft.EntityFrameworkCore
@using System.Threading
@using RobotNet.IdentityServer.Services
@using System.Text.RegularExpressions
@using System.ComponentModel.DataAnnotations
@inherits LayoutComponentBase
@inject RobotNet.IdentityServer.Services.UserImageService UserImageService
@inject RobotNet.IdentityServer.Services.UserInfoService UserInfoService
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject UserManager<ApplicationUser> UserManager
@inject RoleManager<ApplicationRole> RoleManager
@inject ISnackbar Snackbar
@inject IDialogService DialogService
@inject NavigationManager NavigationManager
<MudDialogProvider />
<MudSnackbarProvider />
<div class="d-flex justify-content-center align-items-center" style="height: 90vh; overflow-y: auto;">
<MudContainer MaxWidth="MaxWidth.Medium" Class="py-8">
@if (userInfo != null)
{
<MudCard Elevation="3" Class="rounded-lg">
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.h5" Class="mb-0">Thông tin cá nhân</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">Quản lý thông tin hồ sơ của bạn</MudText>
</CardHeaderContent>
<CardHeaderActions>
<MudChip T="string" Color="Color.Primary" Size="Size.Small" Label="true">@string.Join(", ", userRoles)</MudChip>
</CardHeaderActions>
</MudCardHeader>
<MudCardContent>
<MudGrid>
<MudItem xs="12" md="4" Class="d-flex flex-column align-items-center">
<div class="position-relative d-flex justify-content-center my-3">
<MudImage Class="rounded-circle"
Style="width:150px; height:150px; object-fit:cover"
Src="@avatarUrl" />
<MudIconButton Icon="@Icons.Material.Filled.Edit"
Color="Color.Default"
Size="Size.Small"
OnClick="ChangeAvatar"
Style="position:absolute; bottom:0; right:calc(50% - 60px); background-color:white" />
</div>
<MudText Typo="Typo.h6" Class="mt-3 text-center">@userInfo.FullName</MudText>
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="text-center">
ID: @(userInfo.Id.Length > 10 ? userInfo.Id.Substring(0, 10) + "..." : userInfo.Id)
</MudText>
</MudItem>
<MudItem xs="12" md="8">
<MudPaper Elevation="0" Class="pa-4">
<MudForm @ref="form" Model="userInfo">
<MudTextField Label="Tên người dùng"
@bind-Value="userInfo.UserName"
Variant="Variant.Outlined"
Disabled="true"
HelperText="Tên người dùng không thể thay đổi"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Person"
FullWidth="true" />
<MudTextField Label="Họ và tên"
@bind-Value="userInfo.FullName"
Variant="Variant.Outlined"
Required="true"
RequiredError="Họ và tên là bắt buộc"
@onfocus="EnableButtons"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Badge"
FullWidth="true" />
<MudTextField Label="Email"
@bind-Value="userInfo.Email"
Variant="Variant.Outlined"
Required="true"
RequiredError="Email là bắt buộc"
@onfocus="EnableButtons"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Email"
FullWidth="true" />
<MudTextField Label="Số điện thoại"
@bind-Value="userInfo.PhoneNumber"
Variant="Variant.Outlined"
@onfocus="EnableButtons"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Phone"
FullWidth="true" />
</MudForm>
@if (!isButtonDisabled)
{
<MudPaper Class="d-flex gap-3 justify-end py-2 px-0" Elevation="0">
<MudButton Variant="Variant.Filled"
StartIcon="@Icons.Material.Filled.Cancel"
Color="Color.Error"
OnClick="ResetFields"
Size="Size.Medium">
Hủy
</MudButton>
<MudButton Variant="Variant.Filled"
StartIcon="@Icons.Material.Filled.Save"
Color="Color.Primary"
OnClick="SaveUserInfo"
Size="Size.Medium">
Lưu thay đổi
</MudButton>
</MudPaper>
}
</MudPaper>
</MudItem>
</MudGrid>
</MudCardContent>
</MudCard>
}
else
{
<MudCard Elevation="3" Class="rounded-lg pa-8">
<MudCardContent Class="d-flex flex-column align-items-center justify-center">
<MudIcon Icon="@Icons.Material.Filled.ErrorOutline" Color="Color.Error" Size="Size.Large" Class="mb-4" />
<MudText Typo="Typo.h5" Class="mb-2">Vui lòng đăng nhập</MudText>
<MudText Typo="Typo.body1" Class="text-center mb-4">
Bạn cần đăng nhập để xem và chỉnh sửa thông tin cá nhân.
</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(() => NavigationManager.NavigateTo("/Account/Login"))">
Đăng nhập ngay
</MudButton>
</MudCardContent>
</MudCard>
}
</MudContainer>
</div>
<MudDialog @bind-Visible="ChangeAvatarVisible">
<DialogContent>
<div class="d-flex flex-column align-items-center text-center px-2">
<h5 class="mb-2">Thay đổi ảnh hồ sơ</h5>
<MudText Typo="Typo.caption" Class="mb-3">
Ảnh hồ sơ giúp người khác nhận ra bạn và xác nhận rằng bạn đã đăng nhập.
</MudText>
<div class="rounded-circle overflow-hidden mb-3"
style="width: 130px; height: 130px; border: 2px solid #ccc;">
<MudImage Src="@avatarPreview"
Alt="avatar preview"
Style="width: 100%; height: 100%; object-fit: cover;" />
</div>
<InputFile OnChange="HandleSelected" accept="image/*">
<MudButton Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.Edit" Color="Color.Primary">
Thay đổi
</MudButton>
</InputFile>
</div>
</DialogContent>
<DialogActions>
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="ConfirmChangeAvatar">
Xác nhận
</MudButton>
<MudButton Variant="Variant.Text" OnClick="@(() => ChangeAvatarVisible = false)">
Hủy
</MudButton>
</DialogActions>
</MudDialog>
@code {
MudForm? form;
private string? avatarPreview;
private string? avatarUrl;
private IBrowserFile? selectedFile;
private bool ChangeAvatarVisible = false;
private bool isButtonDisabled = true;
private string originalFullName = "";
private string originalEmail = "";
private string originalPhoneNumber = "";
private string originalUserName = "";
private ApplicationUser? userInfo;
private List<string> userRoles = new List<string>();
private void EnableButtons()
{
isButtonDisabled = false;
}
private void ChangeAvatar()
{
ChangeAvatarVisible = true;
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
var authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authenticationState.User;
if (user?.Identity?.IsAuthenticated == true)
{
userInfo = await UserManager.GetUserAsync(user);
if (userInfo != null)
{
userRoles = (await UserManager.GetRolesAsync(userInfo)).ToList();
originalUserName = userInfo.UserName?? string.Empty;
originalFullName = userInfo.FullName?? string.Empty;
originalEmail = userInfo.Email?? string.Empty;
originalPhoneNumber = userInfo.PhoneNumber ?? string.Empty;
if (userInfo.AvatarImage != null)
{
avatarUrl = $"data:{userInfo.AvatarContentType};base64,{Convert.ToBase64String(userInfo.AvatarImage)}";
avatarPreview = avatarUrl;
}
else
{
avatarUrl = "/uploads/avatars/anh.jpg";
avatarPreview = avatarUrl;
}
}
}
else
{
Snackbar.Add("Vui lòng đăng nhập để tiếp tục", Severity.Error);
}
}
private async Task HandleSelected(InputFileChangeEventArgs e)
{
selectedFile = e.File;
const long maxSize = 5 * 1024 * 1024;
if (selectedFile.Size > maxSize)
{
Snackbar.Add("⚠️ Ảnh bạn chọn vượt quá 5MB. Vui lòng chọn ảnh nhỏ hơn.", Severity.Warning);
avatarPreview = avatarUrl;
selectedFile = null;
return;
}
try
{
(byte[] buffer, string contentType) = await UserImageService.ResizeAndConvertAsync(selectedFile.OpenReadStream());
avatarPreview = $"data:{contentType};base64,{Convert.ToBase64String(buffer)}";
if (userInfo != null)
{
userInfo.AvatarImage = buffer;
userInfo.AvatarContentType = selectedFile.ContentType;
}
}
catch (Exception ex)
{
Snackbar.Add($"❌ Lỗi khi đọc ảnh: {ex.Message}", Severity.Error);
avatarPreview = avatarUrl;
}
}
private async Task ConfirmChangeAvatar()
{
if (userInfo != null && userInfo.AvatarImage != null)
{
var result = await UserManager.UpdateAsync(userInfo);
if (result.Succeeded)
{
avatarUrl = avatarPreview;
ChangeAvatarVisible = false;
await Task.Delay(200);
await UserInfoService.NotifyUserInfoChanged();
StateHasChanged();
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
Snackbar.Add("Cập nhật ảnh đại diện thành công!", Severity.Success);
}
else
{
Snackbar.Add("Lỗi khi cập nhật avatar.", Severity.Error);
}
}
}
private void ResetFields()
{
if (userInfo == null) return;
userInfo.FullName = originalFullName;
userInfo.Email = originalEmail;
userInfo.PhoneNumber = originalPhoneNumber;
userInfo.UserName = originalUserName;
isButtonDisabled = true;
}
private async Task SaveUserInfo()
{
if (userInfo != null)
{
try
{
var result = await UserManager.UpdateAsync(userInfo);
if (result.Succeeded)
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var userInfo = await UserManager.GetUserAsync(authState.User);
if(userInfo != null)
{
originalFullName = userInfo.FullName ?? string.Empty;
originalEmail = userInfo.Email ?? string.Empty;
originalPhoneNumber = userInfo.PhoneNumber ?? string.Empty;
originalUserName = userInfo.UserName ?? string.Empty;
}
isButtonDisabled = true;
await Task.Delay(200);
await UserInfoService.NotifyUserInfoChanged();
StateHasChanged();
Snackbar.Add("Thông tin đã được cập nhật!", Severity.Success);
}
else
{
Snackbar.Add("Lỗi khi cập nhật thông tin.", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Error while saving user information: {ex.Message}", Severity.Error);
}
}
}
}