355 lines
15 KiB
Plaintext
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
} |