u
This commit is contained in:
52
src/FrontOffice.Main/Pages/Profile/AddressDialog.razor
Normal file
52
src/FrontOffice.Main/Pages/Profile/AddressDialog.razor
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h4" Align="Align.Center">@(IsEdit ? "ویرایش آدرس" : "افزودن آدرس جدید")</MudText>
|
||||
</TitleContent>
|
||||
<DialogContent>
|
||||
<MudForm @ref="_form" Model="_request" Validation="@(_validator.ValidateValue)">
|
||||
<MudStack Spacing="3">
|
||||
<MudTextField @bind-Value="_request.Title"
|
||||
For="@(() => _request.Title)"
|
||||
Label="عنوان آدرس"
|
||||
Variant="Variant.Outlined"
|
||||
Required="true"
|
||||
RequiredError="عنوان آدرس الزامی است." />
|
||||
|
||||
<MudTextField @bind-Value="_request.Address"
|
||||
For="@(() => _request.Address)"
|
||||
Label="آدرس کامل"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="3"
|
||||
Required="true"
|
||||
RequiredError="آدرس الزامی است." />
|
||||
|
||||
<MudTextField @bind-Value="_request.PostalCode"
|
||||
For="@(() => _request.PostalCode)"
|
||||
Label="کد پستی"
|
||||
Variant="Variant.Outlined"
|
||||
InputType="InputType.Text"
|
||||
Required="true"
|
||||
RequiredError="کد پستی الزامی است." />
|
||||
|
||||
<MudTextField @bind-Value="_request.CityId"
|
||||
For="@(() => _request.CityId)"
|
||||
Label="شناسه شهر"
|
||||
Variant="Variant.Outlined"
|
||||
InputType="InputType.Number"
|
||||
Required="true"
|
||||
RequiredError="شهر الزامی است." />
|
||||
</MudStack>
|
||||
</MudForm>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton Variant="Variant.Text" OnClick="Cancel" Disabled="_isSaving">لغو</MudButton>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
OnClick="SaveAddress"
|
||||
Disabled="_isSaving"
|
||||
FullWidth="true">
|
||||
@(IsEdit ? "ویرایش آدرس" : "افزودن آدرس")
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
64
src/FrontOffice.Main/Pages/Profile/AddressDialog.razor.cs
Normal file
64
src/FrontOffice.Main/Pages/Profile/AddressDialog.razor.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using FrontOffice.BFF.UserAddress.Protobuf.Protos.UserAddress;
|
||||
using FrontOffice.BFF.UserAddress.Protobuf.Validator;
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
using Severity = MudBlazor.Severity;
|
||||
|
||||
namespace FrontOffice.Main.Pages.Profile;
|
||||
|
||||
public partial class AddressDialog : ComponentBase
|
||||
{
|
||||
[CascadingParameter] private MudDialogInstance MudDialog { get; set; } = default!;
|
||||
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
|
||||
|
||||
[Parameter] public GetAllUserAddressByFilterResponseModel? Model { get; set; }
|
||||
[Parameter] public bool IsEdit { get; set; }
|
||||
|
||||
private MudForm? _form;
|
||||
private readonly UpdateUserAddressRequestValidator _validator = new();
|
||||
private bool _isSaving;
|
||||
private UpdateUserAddressRequest _request = new();
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
if (Model != null)
|
||||
_request = Model.Adapt<UpdateUserAddressRequest>();
|
||||
}
|
||||
private async Task SaveAddress()
|
||||
{
|
||||
if (_form == null) return;
|
||||
|
||||
await _form.Validate();
|
||||
if (!_form.IsValid) return;
|
||||
|
||||
_isSaving = true;
|
||||
try
|
||||
{
|
||||
if (IsEdit)
|
||||
{
|
||||
await UserAddressContract.UpdateUserAddressAsync(_request);
|
||||
Snackbar.Add("آدرس با موفقیت ویرایش شد.", Severity.Success);
|
||||
MudDialog.Close(DialogResult.Ok(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
var createRequest = _request.Adapt<CreateNewUserAddressRequest>();
|
||||
var response = await UserAddressContract.CreateNewUserAddressAsync(createRequest);
|
||||
Snackbar.Add("آدرس با موفقیت اضافه شد.", Severity.Success);
|
||||
MudDialog.Close(DialogResult.Ok(true));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isSaving = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void Cancel() => MudDialog.Cancel();
|
||||
}
|
||||
@@ -64,7 +64,7 @@
|
||||
<!-- Personal Information Tab -->
|
||||
<MudTabPanel Text="اطلاعات شخصی" Icon="@Icons.Material.Filled.Person">
|
||||
<div class="pa-4">
|
||||
<MudForm @ref="_personalForm" Model="_userProfile" Validation="@((Func<object, IEnumerable<FluentValidation.Results.ValidationFailure>>)((model) => _personalValidator.Validate((UserProfile)model).Errors))">
|
||||
<MudForm @ref="_personalForm" Model="_userProfile" Validation="@(_personalValidator.ValidateValue)">
|
||||
<MudGrid Spacing="3">
|
||||
<MudItem xs="12" md="6">
|
||||
<MudTextField @bind-Value="_updateUserRequest.FirstName"
|
||||
@@ -107,6 +107,82 @@
|
||||
</div>
|
||||
</MudTabPanel>
|
||||
|
||||
<!-- Address Management Tab -->
|
||||
<MudTabPanel Text="آدرسها" Icon="@Icons.Material.Filled.LocationOn">
|
||||
<div class="pa-4">
|
||||
<MudStack Spacing="4">
|
||||
<!-- Add New Address Button -->
|
||||
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.subtitle1" Class="fw-600">آدرسهای شما</MudText>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
OnClick="OpenAddAddressDialog">
|
||||
افزودن آدرس جدید
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
<!-- Address List -->
|
||||
@if (_isLoadingAddresses)
|
||||
{
|
||||
<MudStack AlignItems="AlignItems.Center" Class="py-8">
|
||||
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large" />
|
||||
<MudText Typo="Typo.body1" Class="mud-text-secondary mt-2">در حال بارگذاری آدرسها...</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
else if (_addresses.Any())
|
||||
{
|
||||
<MudStack Spacing="3">
|
||||
@foreach (var address in _addresses)
|
||||
{
|
||||
<MudPaper Elevation="2" Class="pa-4 rounded-xl mud-theme-surface">
|
||||
<MudStack Spacing="3">
|
||||
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Start">
|
||||
<MudStack Spacing="1">
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.h6" Class="fw-600">@address.Title</MudText>
|
||||
@if (address.IsDefault)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Variant="Variant.Filled" Size="Size.Small">پیشفرض</MudChip>
|
||||
}
|
||||
</MudStack>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">@address.Address</MudText>
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary">کد پستی: @address.PostalCode</MudText>
|
||||
</MudStack>
|
||||
|
||||
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Size="Size.Small" AnchorOrigin="Origin.TopRight">
|
||||
<MudMenuItem OnClick="@(() => OpenEditAddressDialog(address))">ویرایش</MudMenuItem>
|
||||
@if (!address.IsDefault)
|
||||
{
|
||||
<MudMenuItem OnClick="@(() => SetAsDefaultAddress(address.Id))">تنظیم به عنوان پیشفرض</MudMenuItem>
|
||||
}
|
||||
<MudDivider />
|
||||
<MudMenuItem OnClick="@(() => DeleteAddress(address.Id))" Class="mud-text-error">حذف</MudMenuItem>
|
||||
</MudMenu>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudStack AlignItems="AlignItems.Center" Class="py-8">
|
||||
<MudIcon Icon="@Icons.Material.Filled.LocationOff" Size="Size.Large" Color="Color.Default" />
|
||||
<MudText Typo="Typo.body1" Class="mud-text-secondary mt-2">هنوز آدرسی ثبت نکردهاید.</MudText>
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
OnClick="OpenAddAddressDialog"
|
||||
Class="mt-2">
|
||||
افزودن اولین آدرس
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
</MudStack>
|
||||
</div>
|
||||
</MudTabPanel>
|
||||
|
||||
<!-- Account Settings Tab -->
|
||||
<MudTabPanel Text="تنظیمات حساب" Icon="@Icons.Material.Filled.Settings">
|
||||
<div class="pa-4">
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using FluentValidation;
|
||||
using FrontOffice.BFF.User.Protobuf.Protos.User;
|
||||
using FrontOffice.BFF.User.Protobuf.Validator;
|
||||
using FrontOffice.BFF.UserAddress.Protobuf.Protos.UserAddress;
|
||||
using FrontOffice.BFF.UserAddress.Protobuf.Validator;
|
||||
using FrontOffice.Main.Utilities;
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -13,9 +16,11 @@ namespace FrontOffice.Main.Pages.Profile;
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private UserContract.UserContractClient UserContract { get; set; } = default!;
|
||||
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
|
||||
|
||||
private GetUserResponse _userProfile = new();
|
||||
private UpdateUserRequest _updateUserRequest = new();
|
||||
private readonly UpdateUserRequestValidator _personalValidator = new();
|
||||
|
||||
private MudForm? _personalForm;
|
||||
|
||||
@@ -24,14 +29,22 @@ public partial class Index
|
||||
|
||||
private string _copyMessage = string.Empty;
|
||||
|
||||
private readonly UserProfileValidator _personalValidator = new();
|
||||
private DateTime? _date;
|
||||
|
||||
// Address management
|
||||
private List<GetAllUserAddressByFilterResponseModel> _addresses = new();
|
||||
private bool _isLoadingAddresses;
|
||||
private CreateNewUserAddressRequest _newAddressRequest = new();
|
||||
private UpdateUserAddressRequest _editAddressRequest = new();
|
||||
private MudForm? _addressForm;
|
||||
private readonly CreateNewUserAddressRequestValidator _addressValidator = new();
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
if (firstRender)
|
||||
{
|
||||
await LoadUserProfile();
|
||||
await LoadAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +65,6 @@ public partial class Index
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
|
||||
private async Task SavePersonalInfo()
|
||||
{
|
||||
if (_personalForm is null) return;
|
||||
@@ -82,12 +94,6 @@ public partial class Index
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelPersonalChanges()
|
||||
{
|
||||
// TODO: Reset form to original values
|
||||
Snackbar.Add("تغییرات لغو شد.", Severity.Info);
|
||||
}
|
||||
|
||||
|
||||
private async Task SaveSettings()
|
||||
{
|
||||
@@ -95,10 +101,7 @@ public partial class Index
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
await SavePersonalInfo();
|
||||
|
||||
Snackbar.Add("تنظیمات با موفقیت ذخیره شد.", Severity.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -153,50 +156,104 @@ public partial class Index
|
||||
}
|
||||
}
|
||||
|
||||
public class UserProfile
|
||||
// Address management methods
|
||||
private async Task LoadAddresses()
|
||||
{
|
||||
[Required(ErrorMessage = "نام الزامی است")]
|
||||
public string? FirstName { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "نام خانوادگی الزامی است")]
|
||||
public string? LastName { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "ایمیل الزامی است")]
|
||||
[EmailAddress(ErrorMessage = "فرمت ایمیل صحیح نیست")]
|
||||
public string? Email { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "شماره موبایل الزامی است")]
|
||||
public string? PhoneNumber { get; set; }
|
||||
|
||||
public string? NationalCode { get; set; }
|
||||
public string? BirthDate { get; set; }
|
||||
public string? Address { get; set; }
|
||||
|
||||
// Read-only fields
|
||||
public string? JoinDate { get; set; }
|
||||
public string? LastLogin { get; set; }
|
||||
public int TotalReferrals { get; set; }
|
||||
public string? Level { get; set; }
|
||||
}
|
||||
public class AccountSettings
|
||||
{
|
||||
public bool EmailNotifications { get; set; }
|
||||
public bool SmsNotifications { get; set; }
|
||||
public bool PushNotifications { get; set; }
|
||||
public bool ProfileVisibility { get; set; }
|
||||
public bool ShowOnlineStatus { get; set; }
|
||||
public string? Language { get; set; }
|
||||
public string? Theme { get; set; }
|
||||
}
|
||||
|
||||
public class UserProfileValidator : AbstractValidator<UserProfile>
|
||||
{
|
||||
public UserProfileValidator()
|
||||
_isLoadingAddresses = true;
|
||||
try
|
||||
{
|
||||
RuleFor(x => x.FirstName).NotEmpty().WithMessage("نام الزامی است");
|
||||
RuleFor(x => x.LastName).NotEmpty().WithMessage("نام خانوادگی الزامی است");
|
||||
RuleFor(x => x.Email).NotEmpty().EmailAddress().WithMessage("ایمیل معتبر نیست");
|
||||
RuleFor(x => x.PhoneNumber).NotEmpty().WithMessage("شماره موبایل الزامی است");
|
||||
var response = await UserAddressContract.GetAllUserAddressByFilterAsync(request: new());
|
||||
if (response?.Models?.Any() == true)
|
||||
{
|
||||
_addresses = response.Models.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
_addresses = new List<GetAllUserAddressByFilterResponseModel>();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا در بارگذاری آدرسها: {ex.Message}", Severity.Error);
|
||||
_addresses = new List<GetAllUserAddressByFilterResponseModel>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isLoadingAddresses = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenAddAddressDialog()
|
||||
{
|
||||
_newAddressRequest = new CreateNewUserAddressRequest();
|
||||
var dialog = await DialogService.ShowAsync<AddressDialog>("افزودن آدرس جدید", new DialogParameters<AddressDialog>
|
||||
{
|
||||
{ x => x.IsEdit, false }
|
||||
});
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled)
|
||||
{
|
||||
await LoadAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenEditAddressDialog(GetAllUserAddressByFilterResponseModel address)
|
||||
{
|
||||
var dialog = await DialogService.ShowAsync<AddressDialog>("ویرایش آدرس", new DialogParameters<AddressDialog>
|
||||
{
|
||||
{ x => x.Model, address },
|
||||
{ x => x.IsEdit, true }
|
||||
});
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled)
|
||||
{
|
||||
await LoadAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetAsDefaultAddress(long addressId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UserAddressContract.SetAddressAsDefaultAsync(request: new()
|
||||
{
|
||||
Id = addressId
|
||||
});
|
||||
Snackbar.Add("آدرس پیشفرض با موفقیت تغییر کرد.", Severity.Success);
|
||||
await LoadAddresses();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا در تغییر آدرس پیشفرض: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteAddress(long addressId)
|
||||
{
|
||||
var result = await DialogService.ShowMessageBox(
|
||||
"تأیید حذف",
|
||||
"آیا از حذف این آدرس اطمینان دارید؟",
|
||||
yesText: "حذف",
|
||||
cancelText: "لغو");
|
||||
|
||||
if (result == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UserAddressContract.DeleteUserAddressAsync(request: new()
|
||||
{
|
||||
Id = addressId
|
||||
});
|
||||
Snackbar.Add("آدرس با موفقیت حذف شد.", Severity.Success);
|
||||
await LoadAddresses();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا در حذف آدرس: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user