Compare commits
5 Commits
3d84e726db
...
kub-stage
| Author | SHA1 | Date | |
|---|---|---|---|
| 307302e33d | |||
| c243b59113 | |||
| aae4b2693d | |||
| 61fd805c75 | |||
| ef6d233bbc |
+1
-1
@@ -14,7 +14,7 @@
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
| 6.6 | `UserOrderContract.SubmitShopBuyOrder` | `UserOrderContract` | `/checkout-summary` | ثبت سفارش و پرداخت از کیف پول |
|
||||
| 6.7 | `OrderService.GetOrdersAsync` | `UserOrderContract.GetCustomerOrders` | `/orders` | لیست سفارشهای کاربر |
|
||||
| 6.8 | `OrderService.GetOrderAsync` | `UserOrderContract.GetCustomerOrder` | `/order/{id}`, `/order-tracking/{id}` | جزئیات یک سفارش |
|
||||
| 6.9 | `OrderService.CalculateOrderPVAsync` | `UserOrderContract.CalculateOrderPV` | `/order/{id}` | محاسبه PV سفارش |
|
||||
| 6.10 | `VATService.GetRateAsync` | `UserOrderContract` (via scope) | Store/Discount صفحات قیمت | نرخ مالیات بر ارزش افزوده (کش روزانه) |
|
||||
| 6.11 | `WalletService.GetBalancesAsync` | `UserWalletContract` | `/checkout-summary` | بررسی موجودی برای پرداخت سفارش |
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DateTimeConverterCL" Version="1.0.0" />
|
||||
<!-- Replace all FrontOffice.BFF protobuf packages with CMS protobuf -->
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.204" />
|
||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.210" />
|
||||
<!-- <ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />-->
|
||||
<PackageReference Include="MudBlazor" Version="8.14.0" />
|
||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
||||
|
||||
@@ -71,80 +71,6 @@
|
||||
</MudStack>
|
||||
}
|
||||
</MudPaper>
|
||||
|
||||
<!-- Address Selection -->
|
||||
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
|
||||
<MudText Typo="Typo.h5" Class="mb-4">انتخاب آدرس</MudText>
|
||||
|
||||
@if (_isLoadingAddresses)
|
||||
{
|
||||
<MudStack AlignItems="AlignItems.Center" Class="py-4">
|
||||
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Medium" />
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary mt-2">بارگذاری آدرسها...</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
else if (_addresses.Any())
|
||||
{
|
||||
<MudStack Spacing="2">
|
||||
@foreach (var address in _addresses)
|
||||
{
|
||||
<MudPaper Outlined="@(_selectedAddress?.Id != address.Id)"
|
||||
Elevation="@(_selectedAddress?.Id == address.Id ? 4 : 0)"
|
||||
Class="pa-3 rounded-xl cursor-pointer"
|
||||
Style="@(_selectedAddress?.Id == address.Id ? "border: 2px solid var(--mud-palette-primary);" : "")"
|
||||
@onclick="() => SetAddressAsDefault(address.Id)">
|
||||
<MudStack Spacing="1">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudCheckBox @bind-Value="address.IsDefault"
|
||||
Disabled="true"
|
||||
Color="Color.Primary" />
|
||||
<MudText Typo="Typo.subtitle2">@(address.Title)</MudText>
|
||||
@if (address.IsDefault)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Variant="Variant.Filled" Size="Size.Small">پیشفرض</MudChip>
|
||||
}
|
||||
@if (!address.IsDefault)
|
||||
{
|
||||
@if (_isSettingDefaultAddress && _settingDefaultAddressId == address.Id)
|
||||
{
|
||||
<MudProgressCircular Size="Size.Small" Color="Color.Primary" Indeterminate="true" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton Variant="Variant.Text"
|
||||
Color="Color.Primary"
|
||||
Size="Size.Small"
|
||||
OnClick="() => SetAddressAsDefault(address.Id)">
|
||||
تنظیم به عنوان پیشفرض
|
||||
</MudButton>
|
||||
}
|
||||
}
|
||||
</MudStack>
|
||||
<MudStack Row="true">
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">@(address.Address)</MudText>
|
||||
<MudSpacer />
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary">کد پستی: @(address.PostalCode)</MudText>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudStack AlignItems="AlignItems.Center" Class="py-4">
|
||||
<MudIcon Icon="@Icons.Material.Filled.LocationOff" Size="Size.Large" Color="Color.Default" />
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary mt-2">آدرسی ثبت نشده است.</MudText>
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
OnClick="() => Navigation.NavigateTo(RouteConstants.Profile.Index)"
|
||||
Class="mt-2">
|
||||
افزودن آدرس
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
</MudPaper>
|
||||
</MudStack>
|
||||
</MudItem>
|
||||
|
||||
@@ -241,7 +167,7 @@
|
||||
@if (!CanProceedToPayment)
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Error" Align="Align.Center">
|
||||
لطفاً پکیج و آدرس را انتخاب کنید.
|
||||
لطفاً پکیج را انتخاب کنید.
|
||||
</MudText>
|
||||
}
|
||||
</MudStack>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
using CMSMicroservice.Protobuf.Protos.Package;
|
||||
using CMSMicroservice.Protobuf.Protos.UserAddress;
|
||||
using FrontOffice.Main.Utilities;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
@@ -13,14 +11,10 @@ public partial class Checkout
|
||||
{
|
||||
[Inject] private PackageService PackageService { get; set; } = default!;
|
||||
[Inject] private PackageContract.PackageContractClient PackageClient { get; set; } = default!;
|
||||
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
|
||||
|
||||
[Parameter] public long? PackageId { get; set; }
|
||||
|
||||
private Pack? _selectedPackage;
|
||||
private List<CustomerAddressModel> _addresses = new();
|
||||
private CustomerAddressModel? _selectedAddress;
|
||||
private bool _isLoadingAddresses;
|
||||
private bool _isProcessingPayment;
|
||||
|
||||
// Discount code
|
||||
@@ -31,16 +25,11 @@ public partial class Checkout
|
||||
private long _discountAmount;
|
||||
private long _finalPrice;
|
||||
|
||||
// Address management
|
||||
private bool _isSettingDefaultAddress;
|
||||
private long? _settingDefaultAddressId;
|
||||
|
||||
private bool CanProceedToPayment => _selectedPackage != null && _selectedAddress != null;
|
||||
private bool CanProceedToPayment => _selectedPackage != null;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadPackageDetails();
|
||||
await LoadAddresses();
|
||||
}
|
||||
|
||||
private async Task LoadPackageDetails()
|
||||
@@ -72,60 +61,6 @@ public partial class Checkout
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadAddresses()
|
||||
{
|
||||
_isLoadingAddresses = true;
|
||||
try
|
||||
{
|
||||
var response = await UserAddressContract.GetCustomerAddressesAsync(request: new());
|
||||
if (response?.Models?.Any() == true)
|
||||
{
|
||||
_addresses = response.Models.ToList();
|
||||
// Select default address if available
|
||||
_selectedAddress = _addresses.FirstOrDefault(a => a.IsDefault) ?? _addresses.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
_addresses = new List<CustomerAddressModel>();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا در بارگذاری آدرسها: {ex.Message}", Severity.Error);
|
||||
_addresses = new List<CustomerAddressModel>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isLoadingAddresses = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetAddressAsDefault(long addressId)
|
||||
{
|
||||
if (_isSettingDefaultAddress) return;
|
||||
|
||||
_isSettingDefaultAddress = true;
|
||||
_settingDefaultAddressId = addressId;
|
||||
|
||||
try
|
||||
{
|
||||
await UserAddressContract.SetAddressAsDefaultAsync(new() { Id = addressId });
|
||||
await LoadAddresses(); // Reload addresses to reflect the change
|
||||
Snackbar.Add("آدرس پیشفرض با موفقیت تغییر یافت.", Severity.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا در تغییر آدرس پیشفرض: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isSettingDefaultAddress = false;
|
||||
_settingDefaultAddressId = null;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyDiscountCode()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_discountCode))
|
||||
@@ -157,7 +92,7 @@ public partial class Checkout
|
||||
_finalPrice = _selectedPackage!.Price;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception)
|
||||
{
|
||||
_discountMessage = "خطا در اعمال کد تخفیف.";
|
||||
_discountApplied = false;
|
||||
@@ -175,9 +110,9 @@ public partial class Checkout
|
||||
{
|
||||
if (_isProcessingPayment) return;
|
||||
|
||||
if (!CanProceedToPayment || _selectedPackage == null || _selectedAddress == null)
|
||||
if (!CanProceedToPayment || _selectedPackage == null)
|
||||
{
|
||||
Snackbar.Add("لطفاً پکیج و آدرس را انتخاب کنید.", Severity.Warning);
|
||||
Snackbar.Add("لطفاً پکیج را انتخاب کنید.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -219,9 +154,9 @@ public partial class Checkout
|
||||
|
||||
private async Task DayaLoanPayment()
|
||||
{
|
||||
if (_selectedAddress == null)
|
||||
if (_selectedPackage == null)
|
||||
{
|
||||
Snackbar.Add("لطفاً آدرس را انتخاب کنید.", Severity.Warning);
|
||||
Snackbar.Add("لطفاً پکیج را انتخاب کنید.", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -230,4 +165,4 @@ public partial class Checkout
|
||||
}
|
||||
|
||||
private record Pack(long Id, string Title, string Body, string Image, long Price, bool SupportsDirectPurchase = true, bool SupportsDayaPurchase = false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
else if (_addresses.Count == 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning">
|
||||
هیچ آدرسی ثبت نشده است. لطفاً از بخش پروفایل آدرس خود را اضافه کنید.
|
||||
هیچ آدرسی ثبت نشده است. میتوانید همینجا آدرس جدید اضافه کنید.
|
||||
</MudAlert>
|
||||
<MudButton Class="mt-2" Variant="Variant.Outlined" Href="/profile/addresses">افزودن آدرس</MudButton>
|
||||
<MudButton Class="mt-2" Variant="Variant.Outlined" Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
OnClick="OpenAddAddressDialog">افزودن آدرس</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -41,13 +43,25 @@
|
||||
<MudText Typo="Typo.subtitle2">@address.Title</MudText>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">@address.Address</MudText>
|
||||
</MudStack>
|
||||
@if (address.IsDefault)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Variant="Variant.Outlined">پیشفرض</MudChip>
|
||||
}
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
@if (address.IsDefault)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Variant="Variant.Outlined" Size="Size.Small">پیشفرض</MudChip>
|
||||
}
|
||||
<span @onclick:stopPropagation="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||
Size="Size.Small"
|
||||
Color="Color.Primary"
|
||||
aria-label="ویرایش آدرس"
|
||||
OnClick="@(() => OpenEditAddressDialog(address))" />
|
||||
</span>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudButton Class="mt-2" Variant="Variant.Text" Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
OnClick="OpenAddAddressDialog">افزودن آدرس جدید</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
</MudPaper>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using CMSMicroservice.Protobuf.Protos.UserAddress;
|
||||
using FrontOffice.Main.Pages.Profile.Components;
|
||||
using FrontOffice.Main.Utilities;
|
||||
using MudBlazor;
|
||||
|
||||
@@ -40,7 +41,11 @@ public partial class Checkout
|
||||
}
|
||||
await VAT.LoadAsync();
|
||||
await DiscountCart.EnsureInitializedAsync();
|
||||
await LoadAddresses();
|
||||
var userInfo = await AuthService.GetUserAuthInfo();
|
||||
if (userInfo.HasAddress)
|
||||
await LoadAddresses();
|
||||
else
|
||||
_addresses = new();
|
||||
}
|
||||
|
||||
private async Task LoadAddresses()
|
||||
@@ -57,12 +62,14 @@ public partial class Checkout
|
||||
else
|
||||
{
|
||||
_addresses = new();
|
||||
_selectedAddress = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا در بارگذاری آدرسها: {ex.Message}", Severity.Error);
|
||||
_addresses = new();
|
||||
_selectedAddress = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -71,6 +78,30 @@ public partial class Checkout
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenAddAddressDialog()
|
||||
{
|
||||
var dialog = await DialogService.ShowAsync<AddAddressDialog>("افزودن آدرس جدید");
|
||||
var result = await dialog.Result;
|
||||
if (result is { Canceled: false })
|
||||
{
|
||||
await AuthService.RefreshTokenAsync();
|
||||
await LoadAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenEditAddressDialog(CustomerAddressModel address)
|
||||
{
|
||||
var dialog = await DialogService.ShowAsync<EditAddressDialog>("ویرایش آدرس", new DialogParameters<EditAddressDialog>
|
||||
{
|
||||
{ x => x.Model, address }
|
||||
});
|
||||
var result = await dialog.Result;
|
||||
if (result is { Canceled: false })
|
||||
{
|
||||
await LoadAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PlaceOrder()
|
||||
{
|
||||
if (!CanPlaceOrder || _selectedAddress is null)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using CMSMicroservice.Protobuf.Protos.UserAddress;
|
||||
using FrontOffice.Main.Utilities;
|
||||
using CMSMicroservice.Protobuf.Protos.UserAddress;
|
||||
using FrontOffice.Main.Pages.Profile.Components;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -9,6 +9,7 @@ namespace FrontOffice.Main.Pages.Profile;
|
||||
public partial class Addresses : ComponentBase
|
||||
{
|
||||
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
|
||||
[Inject] private AuthService AuthService { get; set; } = default!;
|
||||
|
||||
private List<CustomerAddressModel> _addresses = new();
|
||||
private bool _isLoadingAddresses;
|
||||
@@ -42,7 +43,10 @@ public partial class Addresses : ComponentBase
|
||||
var dialog = await DialogService.ShowAsync<AddAddressDialog>("افزودن آدرس جدید");
|
||||
var result = await dialog.Result;
|
||||
if (result is not null && !result.Canceled)
|
||||
{
|
||||
await LoadAddresses();
|
||||
await AuthService.RefreshTokenAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenEditAddressDialog(CustomerAddressModel address)
|
||||
@@ -80,6 +84,7 @@ public partial class Addresses : ComponentBase
|
||||
await UserAddressContract.DeleteCustomerAddressAsync(new() { Id = id });
|
||||
Snackbar.Add("آدرس حذف شد.", Severity.Success);
|
||||
await LoadAddresses();
|
||||
await AuthService.RefreshTokenAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -110,7 +110,11 @@ public partial class OrganizationChart : IAsyncDisposable
|
||||
activationWeekNumber = n.ActivationWeekNumber,
|
||||
joinedAt = n.JoinedAt,
|
||||
referralCode = n.ReferralCode,
|
||||
packageName = n.PackageName ?? string.Empty
|
||||
packageName = n.PackageName ?? string.Empty,
|
||||
goldLeftLegTotal = n.GoldLeftLegTotal,
|
||||
goldRightLegTotal = n.GoldRightLegTotal,
|
||||
silverLeftLegTotal = n.SilverLeftLegTotal,
|
||||
silverRightLegTotal = n.SilverRightLegTotal
|
||||
}).ToArray();
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("OrgChart.init", "org-chart-container", flatData, _dotNetHelper);
|
||||
|
||||
@@ -333,6 +333,7 @@ public partial class Index
|
||||
if (!result.Canceled)
|
||||
{
|
||||
await LoadAddresses();
|
||||
await AuthService.RefreshTokenAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,6 +386,7 @@ public partial class Index
|
||||
});
|
||||
Snackbar.Add("آدرس با موفقیت حذف شد.", Severity.Success);
|
||||
await LoadAddresses();
|
||||
await AuthService.RefreshTokenAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -491,11 +493,6 @@ public partial class Index
|
||||
Snackbar.Add("لطفا اطلاعات شخصی خود را تکمیل کنید. (تاریخ تولد وارد نشده)", Severity.Error);
|
||||
return;
|
||||
}
|
||||
if (!_addresses.Any())
|
||||
{
|
||||
Snackbar.Add("آدرس محل سکونت شما الزامی است!", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var url = "https://dayadiamond.ir/profile/creditpurchase/?merchantcode=56146364";
|
||||
await JSRuntime.InvokeVoidAsync("open", url, "_blank");
|
||||
|
||||
@@ -58,8 +58,19 @@ public partial class WithdrawalRequests : ComponentBase
|
||||
try
|
||||
{
|
||||
_isSubmittingWithdrawal = true;
|
||||
_withdrawIban=_withdrawIban!.Trim().ToUpper().Replace("IR", "").Replace(" ", "");
|
||||
await WalletService.RequestWithdrawalAsync(_selectedPayout.Id, _withdrawMethod, "IR"+_withdrawIban);
|
||||
|
||||
string? normalizedIban = null;
|
||||
if (_withdrawMethod == WithdrawalMethodClient.Cash)
|
||||
{
|
||||
normalizedIban = NormalizeIranianIban(_withdrawIban);
|
||||
if (normalizedIban is null)
|
||||
{
|
||||
Snackbar.Add("فرمت شماره شبا معتبر نیست. باید IR و ۲۴ رقم باشد (خط تیره اختیاری است).", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await WalletService.RequestWithdrawalAsync(_selectedPayout.Id, _withdrawMethod, normalizedIban);
|
||||
Snackbar.Add("درخواست برداشت ثبت شد.", Severity.Success);
|
||||
|
||||
// بروزرسانی لیست
|
||||
@@ -125,4 +136,26 @@ public partial class WithdrawalRequests : ComponentBase
|
||||
1 => "الماس",
|
||||
_ => "-"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// فاصله و خط تیره را حذف میکند و شبا را به شکل IR + ۲۴ رقم برمیگرداند.
|
||||
/// </summary>
|
||||
private static string? NormalizeIranianIban(string? iban)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(iban))
|
||||
return null;
|
||||
|
||||
var normalized = iban.Trim().ToUpperInvariant()
|
||||
.Replace(" ", "", StringComparison.Ordinal)
|
||||
.Replace("-", "", StringComparison.Ordinal);
|
||||
|
||||
if (normalized.StartsWith("IR", StringComparison.Ordinal))
|
||||
normalized = normalized[2..];
|
||||
|
||||
normalized = "IR" + normalized;
|
||||
|
||||
return System.Text.RegularExpressions.Regex.IsMatch(normalized, @"^IR\d{24}$")
|
||||
? normalized
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
else if (_addresses.Count == 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning">
|
||||
هیچ آدرسی ثبت نشده است. لطفاً از بخش پروفایل آدرس خود را اضافه کنید.
|
||||
هیچ آدرسی ثبت نشده است. میتوانید همینجا آدرس جدید اضافه کنید.
|
||||
</MudAlert>
|
||||
<MudButton Class="mt-2" Variant="Variant.Outlined" Href="/profile/addresses">افزودن آدرس</MudButton>
|
||||
<MudButton Class="mt-2" Variant="Variant.Outlined" Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
OnClick="OpenAddAddressDialog">افزودن آدرس</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -40,13 +42,25 @@
|
||||
<MudText Typo="Typo.subtitle2">@address.Title</MudText>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">@address.Address</MudText>
|
||||
</MudStack>
|
||||
@if (address.IsDefault)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Variant="Variant.Outlined">پیشفرض</MudChip>
|
||||
}
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
@if (address.IsDefault)
|
||||
{
|
||||
<MudChip T="string" Color="Color.Success" Variant="Variant.Outlined" Size="Size.Small">پیشفرض</MudChip>
|
||||
}
|
||||
<span @onclick:stopPropagation="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||
Size="Size.Small"
|
||||
Color="Color.Primary"
|
||||
aria-label="ویرایش آدرس"
|
||||
OnClick="@(() => OpenEditAddressDialog(address))" />
|
||||
</span>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudButton Class="mt-2" Variant="Variant.Text" Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
OnClick="OpenAddAddressDialog">افزودن آدرس جدید</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
</MudPaper>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using CMSMicroservice.Protobuf.Protos.UserAddress;
|
||||
using CMSMicroservice.Protobuf.Protos.UserOrder;
|
||||
using FrontOffice.Main.Pages.Profile.Components;
|
||||
using FrontOffice.Main.Shared;
|
||||
using FrontOffice.Main.Utilities;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -35,7 +36,11 @@ public partial class CheckoutSummary : ComponentBase
|
||||
await AuthDialogService.ShowAuthDialogAsync();
|
||||
}
|
||||
await Cart.EnsureInitializedAsync();
|
||||
await LoadAddresses();
|
||||
var userInfo = await AuthService.GetUserAuthInfo();
|
||||
if (userInfo.HasAddress)
|
||||
await LoadAddresses();
|
||||
else
|
||||
_addresses = new();
|
||||
await LoadWalletBalance();
|
||||
}
|
||||
|
||||
@@ -68,12 +73,14 @@ public partial class CheckoutSummary : ComponentBase
|
||||
else
|
||||
{
|
||||
_addresses = new();
|
||||
_selectedAddress = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا در بارگذاری آدرسها: {ex.Message}", Severity.Error);
|
||||
_addresses = new();
|
||||
_selectedAddress = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -82,6 +89,30 @@ public partial class CheckoutSummary : ComponentBase
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenAddAddressDialog()
|
||||
{
|
||||
var dialog = await DialogService.ShowAsync<AddAddressDialog>("افزودن آدرس جدید");
|
||||
var result = await dialog.Result;
|
||||
if (result is { Canceled: false })
|
||||
{
|
||||
await AuthService.RefreshTokenAsync();
|
||||
await LoadAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenEditAddressDialog(CustomerAddressModel address)
|
||||
{
|
||||
var dialog = await DialogService.ShowAsync<EditAddressDialog>("ویرایش آدرس", new DialogParameters<EditAddressDialog>
|
||||
{
|
||||
{ x => x.Model, address }
|
||||
});
|
||||
var result = await dialog.Result;
|
||||
if (result is { Canceled: false })
|
||||
{
|
||||
await LoadAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PlaceOrder()
|
||||
{
|
||||
if (!CanPlaceOrder || _selectedAddress is null)
|
||||
|
||||
@@ -112,30 +112,6 @@ else
|
||||
</MudStack>
|
||||
}
|
||||
</MudStack>
|
||||
|
||||
@* نمایش PV سفارش *@
|
||||
@if (_pvData != null && _pvData.TotalPv > 0)
|
||||
{
|
||||
<MudDivider Class="my-2" />
|
||||
<MudStack Spacing="1" Class="pa-2" Style="background-color: var(--mud-palette-background-grey); border-radius: 8px;">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@Icons.Material.Filled.TrendingUp" Color="Color.Info" Size="Size.Small" />
|
||||
<MudText Typo="Typo.subtitle2" Color="Color.Info">امتیاز فروش (PV)</MudText>
|
||||
</MudStack>
|
||||
@foreach (var product in _pvData.Products)
|
||||
{
|
||||
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||
<MudText Typo="Typo.caption" Class="mud-text-secondary">@product.ProductTitle × @product.Quantity</MudText>
|
||||
<MudText Typo="Typo.caption">@string.Format("{0:N0}", product.TotalPv) PV</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
<MudDivider Class="my-1" />
|
||||
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||
<MudText Typo="Typo.body2" Class="fw-bold">جمع PV:</MudText>
|
||||
<MudText Typo="Typo.body2" Color="Color.Info" Class="fw-bold">@string.Format("{0:N0}", _pvData.TotalPv) PV</MudText>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
}
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
</MudContainer>
|
||||
|
||||
@@ -12,17 +12,12 @@ public partial class OrderDetail : ComponentBase
|
||||
[Parameter] public long id { get; set; }
|
||||
|
||||
private GetUserOrderResponse? _order;
|
||||
private CalculateOrderPVResponse? _pvData;
|
||||
private bool _loading;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_loading = true;
|
||||
_order = await OrderService.GetOrderAsync(id);
|
||||
if (_order != null)
|
||||
{
|
||||
_pvData = await OrderService.CalculateOrderPVAsync(id);
|
||||
}
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
@@ -52,4 +47,3 @@ public partial class OrderDetail : ComponentBase
|
||||
private static string GetProductImageUrl(string? imageUrl)
|
||||
=> string.IsNullOrWhiteSpace(imageUrl) ? "/images/product-placeholder.svg" : imageUrl.TrimStart('/');
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ public class AuthService
|
||||
var isSignMainContractStr = claims.FirstOrDefault(c => c.key == "IsSignMainContract").value;
|
||||
var hasPurchasedPackageStr = claims.FirstOrDefault(c => c.key == "HasPurchasedPackage").value;
|
||||
var isClubMemberActiveStr = claims.FirstOrDefault(c => c.key == "IsClubMemberActive").value;
|
||||
var hasAddressStr = claims.FirstOrDefault(c => c.key == "HasAddress").value;
|
||||
|
||||
_userAuthInfo.UserId = long.TryParse(userIdStr, out var userId) ? userId : 0;
|
||||
_userAuthInfo.FirstName = firstName ?? string.Empty;
|
||||
@@ -88,6 +89,7 @@ public class AuthService
|
||||
_userAuthInfo.IsSignMainContract = bool.TryParse(isSignMainContractStr, out var isSignMainContract) && isSignMainContract;
|
||||
_userAuthInfo.HasPurchasedPackage = bool.TryParse(hasPurchasedPackageStr, out var hasPurchased) && hasPurchased;
|
||||
_userAuthInfo.IsClubMemberActive = bool.TryParse(isClubMemberActiveStr, out var isClubActive) && isClubActive;
|
||||
_userAuthInfo.HasAddress = bool.TryParse(hasAddressStr, out var hasAddress) && hasAddress;
|
||||
}
|
||||
|
||||
private async Task<List<(string key, string value)>?> GetTokenClaimsAsync()
|
||||
|
||||
@@ -26,6 +26,18 @@ public class NetworkNodeDto
|
||||
/// نام پکیج موثر (مثلاً پکیج طلایی / نقرهای)
|
||||
/// </summary>
|
||||
public string? PackageName { get; set; }
|
||||
|
||||
/// <summary>آخرین امتیاز پای چپ پکیج طلایی</summary>
|
||||
public int GoldLeftLegTotal { get; set; }
|
||||
|
||||
/// <summary>آخرین امتیاز پای راست پکیج طلایی</summary>
|
||||
public int GoldRightLegTotal { get; set; }
|
||||
|
||||
/// <summary>آخرین امتیاز پای چپ پکیج نقرهای</summary>
|
||||
public int SilverLeftLegTotal { get; set; }
|
||||
|
||||
/// <summary>آخرین امتیاز پای راست پکیج نقرهای</summary>
|
||||
public int SilverRightLegTotal { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,6 +65,11 @@ public class FlatNetworkNodeDto
|
||||
/// نام پکیج موثر (مثلاً پکیج طلایی / نقرهای)
|
||||
/// </summary>
|
||||
public string? PackageName { get; set; }
|
||||
|
||||
public int GoldLeftLegTotal { get; set; }
|
||||
public int GoldRightLegTotal { get; set; }
|
||||
public int SilverLeftLegTotal { get; set; }
|
||||
public int SilverRightLegTotal { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,7 +109,11 @@ public class NetworkTreeDto
|
||||
ActivationWeekNumber = node.ActivationWeekNumber,
|
||||
JoinedAt = node.JoinedAt,
|
||||
ReferralCode = node.ReferralCode,
|
||||
PackageName = node.PackageName
|
||||
PackageName = node.PackageName,
|
||||
GoldLeftLegTotal = node.GoldLeftLegTotal,
|
||||
GoldRightLegTotal = node.GoldRightLegTotal,
|
||||
SilverLeftLegTotal = node.SilverLeftLegTotal,
|
||||
SilverRightLegTotal = node.SilverRightLegTotal
|
||||
};
|
||||
|
||||
result.Add(flatNode);
|
||||
|
||||
@@ -138,6 +138,10 @@ public class NetworkMembershipService
|
||||
IsClubActive = node.IsClubActive,
|
||||
ReferralCode = node.ReferralCode,
|
||||
PackageName = string.IsNullOrWhiteSpace(node.PackageName) ? null : node.PackageName,
|
||||
GoldLeftLegTotal = node.GoldLeftLegTotal,
|
||||
GoldRightLegTotal = node.GoldRightLegTotal,
|
||||
SilverLeftLegTotal = node.SilverLeftLegTotal,
|
||||
SilverRightLegTotal = node.SilverRightLegTotal,
|
||||
ActivationWeekNumber = node.ActivationWeekDefinitionId?.ToString(),
|
||||
JoinedAt = node.JoinedAt?.ToDateTime(),
|
||||
LeftChild = MapNodeFromProto(node.LeftChild),
|
||||
|
||||
@@ -97,22 +97,4 @@ public class OrderService
|
||||
return 0.10;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// محاسبه PV سفارش
|
||||
/// </summary>
|
||||
public async Task<CalculateOrderPVResponse?> CalculateOrderPVAsync(long orderId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _userOrderContractClient.CalculateOrderPVAsync(new CalculateOrderPVRequest
|
||||
{
|
||||
OrderId = orderId
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,5 @@ public class UserAuthInfo
|
||||
public bool IsSignMainContract { get; set; }
|
||||
public bool HasPurchasedPackage { get; set; }
|
||||
public bool IsClubMemberActive { get; set; }
|
||||
public bool HasAddress { get; set; }
|
||||
}
|
||||
@@ -72,10 +72,10 @@
|
||||
/* Compact Header */
|
||||
.node-header-compact {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
padding: 6px 8px;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #fff 100%);
|
||||
}
|
||||
|
||||
@@ -141,6 +141,22 @@
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
/* D3: طلایی / نقرهای — پای چپ و راست */
|
||||
.node-leg-scores {
|
||||
margin-top: 3px;
|
||||
line-height: 1.25;
|
||||
font-size: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.node-leg-scores .leg-score-line.gold {
|
||||
color: #b8860b;
|
||||
}
|
||||
|
||||
.node-leg-scores .leg-score-line.silver {
|
||||
color: #607d8b;
|
||||
}
|
||||
|
||||
/* Referral Code - only for club active members */
|
||||
.node-referral {
|
||||
display: flex;
|
||||
|
||||
@@ -37,8 +37,8 @@ window.OrgChart = {
|
||||
this.chart = new d3.OrgChart()
|
||||
.container('#' + containerId)
|
||||
.data(data)
|
||||
.nodeWidth((d) => 140)
|
||||
.nodeHeight((d) => 85)
|
||||
.nodeWidth((d) => 150)
|
||||
.nodeHeight((d) => 112)
|
||||
.childrenMargin((d) => 50)
|
||||
.compactMarginBetween((d) => 15)
|
||||
.compactMarginPair((d) => 15)
|
||||
@@ -80,6 +80,16 @@ window.OrgChart = {
|
||||
</div>`
|
||||
: '';
|
||||
|
||||
const gL = data.goldLeftLegTotal ?? 0;
|
||||
const gR = data.goldRightLegTotal ?? 0;
|
||||
const sL = data.silverLeftLegTotal ?? 0;
|
||||
const sR = data.silverRightLegTotal ?? 0;
|
||||
const legScoresHtml = `
|
||||
<div class="node-leg-scores" title="آخرین امتیاز هفتگی پای چپ / راست">
|
||||
<div class="leg-score-line gold">طلایی: چپ ${gL} · راست ${gR}</div>
|
||||
<div class="leg-score-line silver">نقرهای: چپ ${sL} · راست ${sR}</div>
|
||||
</div>`;
|
||||
|
||||
return `
|
||||
<div class="org-node-card ${positionClass} ${activeClass} ${clubActiveClass}" data-user-id="${data.id}">
|
||||
<div class="node-header-compact ${isRoot ? 'root' : ''}">
|
||||
@@ -88,6 +98,7 @@ window.OrgChart = {
|
||||
<div class="node-name-sm">${data.fullName || 'بدون نام'}</div>
|
||||
<div class="node-level-sm">L${data.level || 0}${!isRoot ? ' • ' + (data.position === 'Left' ? 'چپ' : 'راست') : ''}</div>
|
||||
${packageBadge}
|
||||
${legScoresHtml}
|
||||
${referralCodeHtml}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user