feat: Add package management features and update checkout summary
- Added PackageService to handle package-related operations. - Introduced MyPackages and Packages pages for user package management. - Updated CheckoutSummary to display VAT calculations. - Enhanced OrderDetail to show financial details including VAT. - Added ChangePassword page for user profile management. - Implemented OrderTracking page for tracking order status. - Updated RouteConstants to include new routes for packages and password change.
This commit is contained in:
@@ -51,6 +51,8 @@ public static class ConfigureServices
|
|||||||
services.AddScoped<CategoryService>();
|
services.AddScoped<CategoryService>();
|
||||||
services.AddScoped<OrderService>();
|
services.AddScoped<OrderService>();
|
||||||
services.AddScoped<WalletService>();
|
services.AddScoped<WalletService>();
|
||||||
|
// Package service
|
||||||
|
services.AddScoped<PackageService>();
|
||||||
// New services for Club, Network, Commission
|
// New services for Club, Network, Commission
|
||||||
services.AddScoped<ClubMembershipService>();
|
services.AddScoped<ClubMembershipService>();
|
||||||
services.AddScoped<NetworkMembershipService>();
|
services.AddScoped<NetworkMembershipService>();
|
||||||
|
|||||||
201
src/FrontOffice.Main/Pages/Package/MyPackages.razor
Normal file
201
src/FrontOffice.Main/Pages/Package/MyPackages.razor
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
@attribute [Route(RouteConstants.Package.MyPackages)]
|
||||||
|
|
||||||
|
<PageTitle>پکیجهای من</PageTitle>
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Large" Class="py-8">
|
||||||
|
<!-- Breadcrumb -->
|
||||||
|
<MudBreadcrumbs Items="_breadcrumbItems" Class="mb-4" />
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<MudStack Class="mb-6">
|
||||||
|
<MudText Typo="Typo.h4">پکیجهای من</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Class="mud-text-secondary">
|
||||||
|
وضعیت پکیجهای خریداری شده و عضویت باشگاه مشتریان
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<MudStack AlignItems="AlignItems.Center" Class="py-16">
|
||||||
|
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large" />
|
||||||
|
<MudText Typo="Typo.body1" Class="mud-text-secondary mt-2">در حال بارگذاری...</MudText>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
else if (_userStatus == null || !_userStatus.HasPurchasedPackage)
|
||||||
|
{
|
||||||
|
<!-- No Package Purchased -->
|
||||||
|
<MudPaper Elevation="2" Class="pa-8 text-center">
|
||||||
|
<MudStack AlignItems="AlignItems.Center" Spacing="4">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.CardGiftcard" Size="Size.Large" Color="Color.Primary" Style="font-size: 80px;" />
|
||||||
|
<MudText Typo="Typo.h5">شما هنوز پکیجی خریداری نکردهاید</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Class="mud-text-secondary" Style="max-width: 500px;">
|
||||||
|
با خرید پکیج طلایی، به باشگاه مشتریان بپیوندید و از مزایای ویژه مانند کمیسیون هفتگی و شبکهسازی بهرهمند شوید.
|
||||||
|
</MudText>
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Size="Size.Large"
|
||||||
|
StartIcon="@Icons.Material.Filled.ShoppingCart"
|
||||||
|
OnClick="@(() => Navigation.NavigateTo(RouteConstants.Package.List))">
|
||||||
|
مشاهده پکیجها
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudGrid Spacing="4">
|
||||||
|
<!-- Package Status Card -->
|
||||||
|
<MudItem xs="12" md="6">
|
||||||
|
<MudPaper Elevation="2" Class="pa-6 h-100">
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Inventory2" Color="Color.Primary" />
|
||||||
|
<MudText Typo="Typo.h6">وضعیت پکیج</MudText>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudDivider />
|
||||||
|
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">نوع پکیج:</MudText>
|
||||||
|
<MudText Typo="Typo.body2">پکیج طلایی</MudText>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">روش خرید:</MudText>
|
||||||
|
<MudChip T="string" Size="Size.Small" Color="@GetPurchaseMethodColor(_userStatus.PurchaseMethod)">
|
||||||
|
@GetPurchaseMethodText(_userStatus.PurchaseMethod)
|
||||||
|
</MudChip>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@if (_userStatus.PurchaseDate.HasValue)
|
||||||
|
{
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">تاریخ خرید:</MudText>
|
||||||
|
<MudText Typo="Typo.body2">@_userStatus.PurchaseDate.Value.ToLocalTime().ToString("yyyy/MM/dd")</MudText>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">موجودی کیف پول:</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Success">@FormatPrice(_userStatus.WalletBalance)</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<!-- Club Membership Card -->
|
||||||
|
<MudItem xs="12" md="6">
|
||||||
|
<MudPaper Elevation="2" Class="pa-6 h-100">
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.CardMembership" Color="Color.Secondary" />
|
||||||
|
<MudText Typo="Typo.h6">عضویت باشگاه</MudText>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudDivider />
|
||||||
|
|
||||||
|
@if (_userStatus.IsClubMemberActive)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Success" Dense="true" Icon="@Icons.Material.Filled.CheckCircle">
|
||||||
|
عضویت باشگاه مشتریان شما فعال است
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Primary"
|
||||||
|
FullWidth="true"
|
||||||
|
StartIcon="@Icons.Material.Filled.AccountTree"
|
||||||
|
OnClick="@(() => Navigation.NavigateTo(RouteConstants.Network.Statistics))">
|
||||||
|
مشاهده شبکه
|
||||||
|
</MudButton>
|
||||||
|
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Success"
|
||||||
|
FullWidth="true"
|
||||||
|
StartIcon="@Icons.Material.Filled.Payments"
|
||||||
|
OnClick="@(() => Navigation.NavigateTo(RouteConstants.Commission.Dashboard))">
|
||||||
|
کمیسیونهای من
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
else if (_userStatus.CanActivateClubMembership)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Info" Dense="true" Icon="@Icons.Material.Filled.Info">
|
||||||
|
شما میتوانید عضویت باشگاه را فعال کنید
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
FullWidth="true"
|
||||||
|
Size="Size.Large"
|
||||||
|
StartIcon="@Icons.Material.Filled.CardMembership"
|
||||||
|
OnClick="@(() => Navigation.NavigateTo(RouteConstants.Club.Membership))">
|
||||||
|
فعالسازی باشگاه مشتریان
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Warning" Dense="true" Icon="@Icons.Material.Filled.Warning">
|
||||||
|
موجودی کیف پول شما برای فعالسازی کافی نیست
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||||
|
برای فعالسازی باشگاه مشتریان، موجودی کیف پول شما باید حداقل ۵۶ میلیون تومان باشد.
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<!-- Benefits Card -->
|
||||||
|
<MudItem xs="12">
|
||||||
|
<MudPaper Elevation="2" Class="pa-6">
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Stars" Color="Color.Warning" />
|
||||||
|
<MudText Typo="Typo.h6">مزایای پکیج طلایی</MudText>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudDivider />
|
||||||
|
|
||||||
|
<MudGrid Spacing="3">
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudPaper Outlined="true" Class="pa-4 text-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Group" Color="Color.Primary" Size="Size.Large" />
|
||||||
|
<MudText Typo="Typo.subtitle2" Class="mt-2">شبکهسازی نامحدود</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">دعوت دوستان و ساخت تیم</MudText>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudPaper Outlined="true" Class="pa-4 text-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Payments" Color="Color.Success" Size="Size.Large" />
|
||||||
|
<MudText Typo="Typo.subtitle2" Class="mt-2">کمیسیون هفتگی</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">دریافت سهم از فروش شبکه</MudText>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudPaper Outlined="true" Class="pa-4 text-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.ShoppingBag" Color="Color.Secondary" Size="Size.Large" />
|
||||||
|
<MudText Typo="Typo.subtitle2" Class="mt-2">فروشگاه تخفیفی</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">خرید با تخفیف ویژه</MudText>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<MudItem xs="12" sm="6" md="3">
|
||||||
|
<MudPaper Outlined="true" Class="pa-4 text-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Support" Color="Color.Info" Size="Size.Large" />
|
||||||
|
<MudText Typo="Typo.subtitle2" Class="mt-2">پشتیبانی ۲۴ ساعته</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">پشتیبانی اولویتدار</MudText>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
}
|
||||||
|
</MudContainer>
|
||||||
70
src/FrontOffice.Main/Pages/Package/MyPackages.razor.cs
Normal file
70
src/FrontOffice.Main/Pages/Package/MyPackages.razor.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using FrontOffice.Main.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace FrontOffice.Main.Pages.Package;
|
||||||
|
|
||||||
|
public partial class MyPackages : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject] private PackageService PackageService { get; set; } = default!;
|
||||||
|
|
||||||
|
private UserPackageStatusDto? _userStatus;
|
||||||
|
private bool _isLoading = true;
|
||||||
|
|
||||||
|
private List<BreadcrumbItem> _breadcrumbItems = new()
|
||||||
|
{
|
||||||
|
new BreadcrumbItem("صفحه اصلی", RouteConstants.Main.MainPage),
|
||||||
|
new BreadcrumbItem("پروفایل", RouteConstants.Profile.Index),
|
||||||
|
new BreadcrumbItem("پکیجهای من", null, disabled: true)
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadDataAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadDataAsync()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_userStatus = await PackageService.GetUserPackageStatusAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"Error loading user package status: {ex.Message}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetPurchaseMethodText(string? method)
|
||||||
|
{
|
||||||
|
return method switch
|
||||||
|
{
|
||||||
|
"DayaLoan" => "وام دایا",
|
||||||
|
"DirectPurchase" => "پرداخت مستقیم",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color GetPurchaseMethodColor(string? method)
|
||||||
|
{
|
||||||
|
return method switch
|
||||||
|
{
|
||||||
|
"DayaLoan" => Color.Info,
|
||||||
|
"DirectPurchase" => Color.Success,
|
||||||
|
_ => Color.Default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatPrice(long price)
|
||||||
|
{
|
||||||
|
return string.Format("{0:N0} تومان", price);
|
||||||
|
}
|
||||||
|
}
|
||||||
139
src/FrontOffice.Main/Pages/Package/Packages.razor
Normal file
139
src/FrontOffice.Main/Pages/Package/Packages.razor
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
@attribute [Route(RouteConstants.Package.List)]
|
||||||
|
|
||||||
|
<PageTitle>پکیجها</PageTitle>
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Large" Class="py-8">
|
||||||
|
<!-- Header -->
|
||||||
|
<MudStack Class="mb-6">
|
||||||
|
<MudText Typo="Typo.h4">پکیجهای سرمایهگذاری</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Class="mud-text-secondary">
|
||||||
|
با خرید پکیج طلایی، به باشگاه مشتریان بپیوندید و از مزایای ویژه بهرهمند شوید.
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<MudStack AlignItems="AlignItems.Center" Class="py-16">
|
||||||
|
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large" />
|
||||||
|
<MudText Typo="Typo.body1" Class="mud-text-secondary mt-2">در حال بارگذاری پکیجها...</MudText>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
else if (!_packages.Any())
|
||||||
|
{
|
||||||
|
<MudStack AlignItems="AlignItems.Center" Class="py-16">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Inventory2" Size="Size.Large" Color="Color.Default" />
|
||||||
|
<MudText Typo="Typo.h6" Class="mt-2">هیچ پکیجی یافت نشد</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">در حال حاضر پکیجی برای نمایش وجود ندارد.</MudText>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<!-- User Status Card (if logged in) -->
|
||||||
|
@if (_userStatus != null && _userStatus.HasPurchasedPackage)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Success" Class="mb-6" Icon="@Icons.Material.Filled.CheckCircle">
|
||||||
|
<MudText>
|
||||||
|
شما قبلاً پکیج طلایی را خریداری کردهاید.
|
||||||
|
@if (_userStatus.IsClubMemberActive)
|
||||||
|
{
|
||||||
|
<span>عضویت باشگاه شما فعال است.</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudLink Href="@RouteConstants.Club.Membership" Class="mx-2">فعالسازی باشگاه</MudLink>
|
||||||
|
}
|
||||||
|
</MudText>
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Packages Grid -->
|
||||||
|
<MudGrid Spacing="4">
|
||||||
|
@foreach (var package in _packages)
|
||||||
|
{
|
||||||
|
<MudItem xs="12" sm="6" md="4">
|
||||||
|
<MudCard Class="h-100 package-card" Elevation="3">
|
||||||
|
<!-- Package Image -->
|
||||||
|
<MudCardMedia Image="@package.ImageUrl"
|
||||||
|
Title="@package.Title"
|
||||||
|
Height="200" />
|
||||||
|
|
||||||
|
<MudCardContent>
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudText Typo="Typo.h5">@package.Title</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary" Style="min-height: 60px;">
|
||||||
|
@TruncateDescription(package.Description)
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<!-- Price -->
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mt-2">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Sell" Color="Color.Success" Size="Size.Small" />
|
||||||
|
<MudText Typo="Typo.h6" Color="Color.Success">@package.FormattedPrice</MudText>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<!-- Features Preview -->
|
||||||
|
<MudStack Spacing="1" Class="mt-2">
|
||||||
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
||||||
|
<MudText Typo="Typo.caption">عضویت در باشگاه مشتریان</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
||||||
|
<MudText Typo="Typo.caption">دریافت کمیسیون هفتگی</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
||||||
|
<MudText Typo="Typo.caption">شبکهسازی نامحدود</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</MudCardContent>
|
||||||
|
|
||||||
|
<MudCardActions Class="pa-4">
|
||||||
|
<MudButton Variant="Variant.Outlined"
|
||||||
|
Color="Color.Primary"
|
||||||
|
FullWidth="true"
|
||||||
|
OnClick="@(() => ViewPackageDetails(package.Id))">
|
||||||
|
مشاهده جزئیات
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
</MudGrid>
|
||||||
|
|
||||||
|
<!-- Info Section -->
|
||||||
|
<MudPaper Elevation="0" Class="pa-6 mt-8 rounded-xl" Style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
|
||||||
|
<MudGrid Spacing="4">
|
||||||
|
<MudItem xs="12" md="8">
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudText Typo="Typo.h5" Style="color: white;">چرا پکیج طلایی؟</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Style="color: rgba(255,255,255,0.9);">
|
||||||
|
با خرید پکیج طلایی، علاوه بر دسترسی به محصولات ویژه، میتوانید در شبکه فروش شرکت کنید
|
||||||
|
و از کمیسیونهای هفتگی بهرهمند شوید. هر چه شبکه شما گستردهتر، درآمد شما بیشتر!
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" md="4" Class="d-flex align-center justify-center">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Default"
|
||||||
|
Size="Size.Large"
|
||||||
|
StartIcon="@Icons.Material.Filled.HelpOutline"
|
||||||
|
OnClick="@(() => Navigation.NavigateTo(RouteConstants.FAQ.Index))">
|
||||||
|
سوالات متداول
|
||||||
|
</MudButton>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
</MudContainer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.package-card {
|
||||||
|
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
54
src/FrontOffice.Main/Pages/Package/Packages.razor.cs
Normal file
54
src/FrontOffice.Main/Pages/Package/Packages.razor.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using FrontOffice.Main.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace FrontOffice.Main.Pages.Package;
|
||||||
|
|
||||||
|
public partial class Packages : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject] private PackageService PackageService { get; set; } = default!;
|
||||||
|
|
||||||
|
private List<PackageDto> _packages = new();
|
||||||
|
private UserPackageStatusDto? _userStatus;
|
||||||
|
private bool _isLoading = true;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadDataAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadDataAsync()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Load packages
|
||||||
|
_packages = await PackageService.GetAllPackagesAsync();
|
||||||
|
|
||||||
|
// Load user status (if authenticated)
|
||||||
|
_userStatus = await PackageService.GetUserPackageStatusAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"Error loading packages: {ex.Message}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ViewPackageDetails(long packageId)
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo(RouteConstants.Package.Detail + packageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TruncateDescription(string description, int maxLength = 100)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(description)) return string.Empty;
|
||||||
|
if (description.Length <= maxLength) return description;
|
||||||
|
return description[..maxLength] + "...";
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/FrontOffice.Main/Pages/Profile/ChangePassword.razor
Normal file
70
src/FrontOffice.Main/Pages/Profile/ChangePassword.razor
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
@attribute [Route(RouteConstants.Profile.ChangePassword)]
|
||||||
|
|
||||||
|
<PageTitle>تغییر رمز عبور</PageTitle>
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Small" Class="py-6">
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||||
|
<MudText Typo="Typo.h5">تغییر رمز عبور</MudText>
|
||||||
|
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack" Href="@RouteConstants.Profile.Settings">بازگشت</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
@if (_success)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Success" Variant="Variant.Filled">
|
||||||
|
رمز عبور با موفقیت تغییر کرد
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudTextField T="string" @bind-Value="_currentPassword"
|
||||||
|
Label="رمز عبور فعلی"
|
||||||
|
InputType="InputType.Password"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="رمز عبور فعلی الزامی است"
|
||||||
|
Variant="Variant.Outlined" />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="_newPassword"
|
||||||
|
Label="رمز عبور جدید"
|
||||||
|
InputType="InputType.Password"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="رمز عبور جدید الزامی است"
|
||||||
|
HelperText="حداقل 8 کاراکتر شامل حروف و اعداد"
|
||||||
|
Variant="Variant.Outlined" />
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Value="_confirmPassword"
|
||||||
|
Label="تکرار رمز عبور جدید"
|
||||||
|
InputType="InputType.Password"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="تکرار رمز عبور الزامی است"
|
||||||
|
Variant="Variant.Outlined" />
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(_error))
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error" Variant="Variant.Outlined">@_error</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudStack Row="true" Justify="Justify.FlexEnd" Class="mt-2">
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
OnClick="ChangePasswordAsync"
|
||||||
|
Disabled="_saving"
|
||||||
|
StartIcon="@Icons.Material.Filled.Lock">
|
||||||
|
@if (_saving)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="me-2" />
|
||||||
|
<span>در حال ذخیره...</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>تغییر رمز عبور</span>
|
||||||
|
}
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
</MudStack>
|
||||||
|
</MudContainer>
|
||||||
77
src/FrontOffice.Main/Pages/Profile/ChangePassword.razor.cs
Normal file
77
src/FrontOffice.Main/Pages/Profile/ChangePassword.razor.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace FrontOffice.Main.Pages.Profile;
|
||||||
|
|
||||||
|
public partial class ChangePassword : ComponentBase
|
||||||
|
{
|
||||||
|
private string _currentPassword = string.Empty;
|
||||||
|
private string _newPassword = string.Empty;
|
||||||
|
private string _confirmPassword = string.Empty;
|
||||||
|
private string _error = string.Empty;
|
||||||
|
private bool _saving;
|
||||||
|
private bool _success;
|
||||||
|
|
||||||
|
private async Task ChangePasswordAsync()
|
||||||
|
{
|
||||||
|
_error = string.Empty;
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if (string.IsNullOrWhiteSpace(_currentPassword))
|
||||||
|
{
|
||||||
|
_error = "رمز عبور فعلی را وارد کنید";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_newPassword))
|
||||||
|
{
|
||||||
|
_error = "رمز عبور جدید را وارد کنید";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_newPassword.Length < 8)
|
||||||
|
{
|
||||||
|
_error = "رمز عبور جدید باید حداقل 8 کاراکتر باشد";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_newPassword != _confirmPassword)
|
||||||
|
{
|
||||||
|
_error = "رمز عبور جدید و تکرار آن یکسان نیستند";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentPassword == _newPassword)
|
||||||
|
{
|
||||||
|
_error = "رمز عبور جدید باید متفاوت از رمز فعلی باشد";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_saving = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO: Call UserContract.ChangePasswordAsync when available
|
||||||
|
// var request = new ChangePasswordRequest
|
||||||
|
// {
|
||||||
|
// CurrentPassword = _currentPassword,
|
||||||
|
// NewPassword = _newPassword
|
||||||
|
// };
|
||||||
|
// await UserContract.ChangePasswordAsync(request);
|
||||||
|
|
||||||
|
// Simulate API call
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
_success = true;
|
||||||
|
Snackbar.Add("رمز عبور با موفقیت تغییر کرد", Severity.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_error = ex.Message;
|
||||||
|
Snackbar.Add($"خطا در تغییر رمز عبور: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_saving = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,10 +101,24 @@
|
|||||||
}
|
}
|
||||||
</MudList>
|
</MudList>
|
||||||
<MudDivider Class="my-2" />
|
<MudDivider Class="my-2" />
|
||||||
|
|
||||||
|
@* نمایش جزئیات مالی با مالیات *@
|
||||||
|
<MudStack Spacing="1">
|
||||||
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
<MudText Typo="Typo.subtitle2">مبلغ قابل پرداخت</MudText>
|
<MudText Typo="Typo.body2">جمع کالاها:</MudText>
|
||||||
<MudText Typo="Typo.subtitle2" Color="Color.Primary">@FormatPrice(Cart.Total)</MudText>
|
<MudText Typo="Typo.body2">@FormatPrice(Cart.Total)</MudText>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">مالیات بر ارزش افزوده (۹%):</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">@FormatPrice(CalculateVAT())</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudDivider Class="my-1" />
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.subtitle1" Style="font-weight: bold;">مبلغ قابل پرداخت:</MudText>
|
||||||
|
<MudText Typo="Typo.subtitle1" Color="Color.Primary" Style="font-weight: bold;">@FormatPrice(Cart.Total + CalculateVAT())</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
<MudButton Disabled="@(!CanPlaceOrder)" Class="mt-3 w-100-mobile" Variant="Variant.Filled" Color="Color.Primary" OnClick="PlaceOrder" StartIcon="@Icons.Material.Filled.CheckCircle">ثبت سفارش</MudButton>
|
<MudButton Disabled="@(!CanPlaceOrder)" Class="mt-3 w-100-mobile" Variant="Variant.Filled" Color="Color.Primary" OnClick="PlaceOrder" StartIcon="@Icons.Material.Filled.CheckCircle">ثبت سفارش</MudButton>
|
||||||
}
|
}
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
|||||||
@@ -91,6 +91,11 @@ public partial class CheckoutSummary : ComponentBase
|
|||||||
|
|
||||||
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// محاسبه مالیات بر ارزش افزوده (۹%)
|
||||||
|
/// </summary>
|
||||||
|
private long CalculateVAT() => (long)(Cart.Total * 0.09);
|
||||||
|
|
||||||
private static string GetProductImageUrl(string? imageUrl)
|
private static string GetProductImageUrl(string? imageUrl)
|
||||||
=> string.IsNullOrWhiteSpace(imageUrl) ? "/images/product-placeholder.svg" : imageUrl;
|
=> string.IsNullOrWhiteSpace(imageUrl) ? "/images/product-placeholder.svg" : imageUrl;
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,29 @@ else
|
|||||||
</MudStack>
|
</MudStack>
|
||||||
</MudHidden>
|
</MudHidden>
|
||||||
<MudDivider Class="my-2" />
|
<MudDivider Class="my-2" />
|
||||||
<MudText Typo="Typo.h6" Align="Align.End">مبلغ کل: @FormatPrice(_order.FactorDetails.Sum(s=>s.UnitPrice.Value*s.Count.Value))</MudText>
|
|
||||||
|
@* نمایش جزئیات مالی *@
|
||||||
|
@{
|
||||||
|
var subtotal = _order.FactorDetails.Sum(s => s.UnitPrice.Value * s.Count.Value);
|
||||||
|
var vatAmount = (long)(subtotal * 0.09);
|
||||||
|
var totalWithVat = subtotal + vatAmount;
|
||||||
|
}
|
||||||
|
<MudStack Spacing="1" Class="pa-2" Style="background-color: var(--mud-palette-background-grey); border-radius: 8px;">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2">جمع کالاها:</MudText>
|
||||||
|
<MudText Typo="Typo.body2">@FormatPrice(subtotal)</MudText>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">مالیات بر ارزش افزوده (۹%):</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">@FormatPrice(vatAmount)</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudDivider Class="my-1" />
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.subtitle1" Style="font-weight: bold;">مبلغ قابل پرداخت:</MudText>
|
||||||
|
<MudText Typo="Typo.subtitle1" Color="Color.Primary" Style="font-weight: bold;">@FormatPrice(totalWithVat)</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
|
|||||||
102
src/FrontOffice.Main/Pages/Store/OrderTracking.razor
Normal file
102
src/FrontOffice.Main/Pages/Store/OrderTracking.razor
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
@using FrontOffice.BFF.UserOrder.Protobuf.Protos.UserOrder
|
||||||
|
@attribute [Route(RouteConstants.Store.OrderTracking + "{id:long}")]
|
||||||
|
|
||||||
|
<PageTitle>پیگیری سفارش</PageTitle>
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Medium" Class="py-6">
|
||||||
|
@if (_loading)
|
||||||
|
{
|
||||||
|
<MudStack AlignItems="AlignItems.Center">
|
||||||
|
<MudProgressCircular Indeterminate="true" Color="Color.Primary" />
|
||||||
|
<MudText Class="mt-2 mud-text-secondary">در حال بارگذاری...</MudText>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
else if (_order is null)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Warning">سفارش یافت نشد.</MudAlert>
|
||||||
|
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack"
|
||||||
|
Href="@RouteConstants.Store.Orders">
|
||||||
|
بازگشت به سفارشها
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||||
|
<MudText Typo="Typo.h5">پیگیری سفارش #@_order.Id</MudText>
|
||||||
|
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack"
|
||||||
|
Href="@(RouteConstants.Store.OrderDetail + id)">
|
||||||
|
جزئیات سفارش
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@* Timeline سفارش *@
|
||||||
|
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
|
||||||
|
<MudText Typo="Typo.h6" Class="mb-4">وضعیت سفارش</MudText>
|
||||||
|
|
||||||
|
<MudTimeline TimelinePosition="TimelinePosition.Start">
|
||||||
|
@foreach (var step in _trackingSteps)
|
||||||
|
{
|
||||||
|
<MudTimelineItem Color="@(step.IsCompleted ? Color.Success : (step.IsCurrent ? Color.Primary : Color.Default))"
|
||||||
|
Variant="@(step.IsCompleted || step.IsCurrent ? Variant.Filled : Variant.Outlined)"
|
||||||
|
Size="Size.Medium">
|
||||||
|
<ItemContent>
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.subtitle1"
|
||||||
|
Color="@(step.IsCompleted ? Color.Success : (step.IsCurrent ? Color.Primary : Color.Default))">
|
||||||
|
@step.Title
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">@step.Description</MudText>
|
||||||
|
@if (!string.IsNullOrEmpty(step.Date))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary mt-1">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Schedule" Size="Size.Small" Class="me-1" />
|
||||||
|
@step.Date
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</ItemContent>
|
||||||
|
</MudTimelineItem>
|
||||||
|
}
|
||||||
|
</MudTimeline>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
@* اطلاعات ارسال *@
|
||||||
|
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
|
||||||
|
<MudText Typo="Typo.h6" Class="mb-3">اطلاعات ارسال</MudText>
|
||||||
|
|
||||||
|
<MudGrid>
|
||||||
|
<MudItem xs="12" sm="6">
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">آدرس تحویل</MudText>
|
||||||
|
<MudText>@(_order.UserAddressText ?? "---")</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="6">
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary">روش پرداخت</MudText>
|
||||||
|
<MudText>@GetPaymentMethodText(_order.PaymentMethod)</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
@* پشتیبانی *@
|
||||||
|
<MudPaper Elevation="1" Class="pa-4 rounded-lg" Style="background-color: var(--mud-palette-background-grey);">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.HeadsetMic" Size="Size.Large" Color="Color.Primary" />
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.subtitle1">سوالی دارید؟</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||||
|
با پشتیبانی ما تماس بگیرید: ۰۲۱-۱۲۳۴۵۶۷۸
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudSpacer />
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Href="@RouteConstants.Contact.Index">
|
||||||
|
تماس با ما
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
</MudContainer>
|
||||||
125
src/FrontOffice.Main/Pages/Store/OrderTracking.razor.cs
Normal file
125
src/FrontOffice.Main/Pages/Store/OrderTracking.razor.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using FrontOffice.BFF.UserOrder.Protobuf.Protos.UserOrder;
|
||||||
|
using FrontOffice.Main.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace FrontOffice.Main.Pages.Store;
|
||||||
|
|
||||||
|
public partial class OrderTracking : ComponentBase
|
||||||
|
{
|
||||||
|
[Inject] private OrderService OrderService { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter] public long id { get; set; }
|
||||||
|
|
||||||
|
private GetUserOrderResponse? _order;
|
||||||
|
private bool _loading = true;
|
||||||
|
private List<TrackingStep> _trackingSteps = new();
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadOrderAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadOrderAsync()
|
||||||
|
{
|
||||||
|
_loading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await OrderService.GetOrderAsync(id);
|
||||||
|
if (response is not null)
|
||||||
|
{
|
||||||
|
_order = response;
|
||||||
|
BuildTrackingSteps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_loading = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildTrackingSteps()
|
||||||
|
{
|
||||||
|
if (_order is null) return;
|
||||||
|
|
||||||
|
// Build tracking steps based on PaymentStatus
|
||||||
|
var isPaid = _order.PaymentStatus == PaymentStatus.Success;
|
||||||
|
|
||||||
|
_trackingSteps = new List<TrackingStep>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Title = "ثبت سفارش",
|
||||||
|
Description = "سفارش شما با موفقیت ثبت شد",
|
||||||
|
IsCompleted = true,
|
||||||
|
Date = FormatDate(_order.PaymentDate)
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Title = "در انتظار پرداخت",
|
||||||
|
Description = "منتظر تایید پرداخت هستیم",
|
||||||
|
IsCompleted = isPaid,
|
||||||
|
IsCurrent = !isPaid,
|
||||||
|
Date = isPaid ? FormatDate(_order.PaymentDate) : ""
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Title = "تایید پرداخت",
|
||||||
|
Description = "پرداخت شما تایید شد",
|
||||||
|
IsCompleted = isPaid,
|
||||||
|
IsCurrent = false,
|
||||||
|
Date = isPaid ? FormatDate(_order.PaymentDate) : ""
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Title = "در حال پردازش",
|
||||||
|
Description = "سفارش شما در حال آمادهسازی است",
|
||||||
|
IsCompleted = false,
|
||||||
|
IsCurrent = isPaid,
|
||||||
|
Date = ""
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Title = "ارسال شده",
|
||||||
|
Description = "سفارش شما به پست تحویل داده شد",
|
||||||
|
IsCompleted = false,
|
||||||
|
IsCurrent = false,
|
||||||
|
Date = ""
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Title = "تحویل داده شده",
|
||||||
|
Description = "سفارش به دست شما رسید",
|
||||||
|
IsCompleted = false,
|
||||||
|
IsCurrent = false,
|
||||||
|
Date = ""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatDate(Google.Protobuf.WellKnownTypes.Timestamp? timestamp)
|
||||||
|
{
|
||||||
|
if (timestamp is null) return "";
|
||||||
|
var date = timestamp.ToDateTime();
|
||||||
|
var persianCalendar = new System.Globalization.PersianCalendar();
|
||||||
|
return $"{persianCalendar.GetYear(date)}/{persianCalendar.GetMonth(date):00}/{persianCalendar.GetDayOfMonth(date):00}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetPaymentMethodText(PaymentMethod method) => method switch
|
||||||
|
{
|
||||||
|
PaymentMethod.Ipg => "پرداخت آنلاین",
|
||||||
|
PaymentMethod.Wallet => "کیف پول",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
|
||||||
|
private class TrackingStep
|
||||||
|
{
|
||||||
|
public string Title { get; set; } = "";
|
||||||
|
public string Description { get; set; } = "";
|
||||||
|
public bool IsCompleted { get; set; }
|
||||||
|
public bool IsCurrent { get; set; }
|
||||||
|
public string Date { get; set; } = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/FrontOffice.Main/Utilities/PackageService.cs
Normal file
121
src/FrontOffice.Main/Utilities/PackageService.cs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
using FrontOffice.BFF.Package.Protobuf.Protos.Package;
|
||||||
|
|
||||||
|
namespace FrontOffice.Main.Utilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Package data transfer object for UI display
|
||||||
|
/// </summary>
|
||||||
|
public record PackageDto(
|
||||||
|
long Id,
|
||||||
|
string Title,
|
||||||
|
string Description,
|
||||||
|
string ImageUrl,
|
||||||
|
long Price)
|
||||||
|
{
|
||||||
|
public string FormattedPrice => string.Format("{0:N0} تومان", Price);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User's package purchase status
|
||||||
|
/// </summary>
|
||||||
|
public record UserPackageStatusDto(
|
||||||
|
bool HasPurchasedPackage,
|
||||||
|
string? PurchaseMethod, // DayaLoan, DirectPurchase, or null
|
||||||
|
bool IsClubMemberActive,
|
||||||
|
long WalletBalance,
|
||||||
|
bool CanActivateClubMembership,
|
||||||
|
DateTime? PurchaseDate);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service for managing packages in FrontOffice
|
||||||
|
/// </summary>
|
||||||
|
public class PackageService
|
||||||
|
{
|
||||||
|
private readonly PackageContract.PackageContractClient _client;
|
||||||
|
|
||||||
|
public PackageService(PackageContract.PackageContractClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all available packages
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<PackageDto>> GetAllPackagesAsync(CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new GetAllPackageByFilterRequest
|
||||||
|
{
|
||||||
|
PaginationState = new PaginationState
|
||||||
|
{
|
||||||
|
PageNumber = 1,
|
||||||
|
PageSize = 100
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await _client.GetAllPackageByFilterAsync(request, cancellationToken: ct);
|
||||||
|
|
||||||
|
return response.Models
|
||||||
|
.Select(m => new PackageDto(
|
||||||
|
m.Id,
|
||||||
|
m.Title,
|
||||||
|
m.Description,
|
||||||
|
UrlUtility.DownloadUrl + m.ImagePath,
|
||||||
|
m.Price))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"Error fetching packages: {ex.Message}");
|
||||||
|
#endif
|
||||||
|
return new List<PackageDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a single package by ID
|
||||||
|
/// </summary>
|
||||||
|
public async Task<PackageDto?> GetPackageByIdAsync(long id, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _client.GetPackageAsync(
|
||||||
|
new GetPackageRequest { Id = id },
|
||||||
|
cancellationToken: ct);
|
||||||
|
|
||||||
|
if (response == null) return null;
|
||||||
|
|
||||||
|
return new PackageDto(
|
||||||
|
response.Id,
|
||||||
|
response.Title,
|
||||||
|
response.Description,
|
||||||
|
UrlUtility.DownloadUrl + response.ImagePath,
|
||||||
|
response.Price);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine($"Error fetching package {id}: {ex.Message}");
|
||||||
|
#endif
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get user's package purchase status (Mock for now - needs BFF implementation)
|
||||||
|
/// </summary>
|
||||||
|
public Task<UserPackageStatusDto> GetUserPackageStatusAsync(CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
// TODO: Connect to GetUserPackageStatus RPC when available in FrontOffice.BFF
|
||||||
|
// For now, return a mock status
|
||||||
|
return Task.FromResult(new UserPackageStatusDto(
|
||||||
|
HasPurchasedPackage: false,
|
||||||
|
PurchaseMethod: null,
|
||||||
|
IsClubMemberActive: false,
|
||||||
|
WalletBalance: 0,
|
||||||
|
CanActivateClubMembership: false,
|
||||||
|
PurchaseDate: null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ public static class RouteConstants
|
|||||||
public const string Personal = "/profile/personal";
|
public const string Personal = "/profile/personal";
|
||||||
public const string Addresses = "/profile/addresses";
|
public const string Addresses = "/profile/addresses";
|
||||||
public const string Settings = "/profile/settings";
|
public const string Settings = "/profile/settings";
|
||||||
|
public const string ChangePassword = "/profile/change-password";
|
||||||
public const string Tree = "/profile/tree";
|
public const string Tree = "/profile/tree";
|
||||||
public const string Wallet = "/profile/wallet";
|
public const string Wallet = "/profile/wallet";
|
||||||
}
|
}
|
||||||
@@ -43,7 +44,10 @@ public static class RouteConstants
|
|||||||
|
|
||||||
public static class Package
|
public static class Package
|
||||||
{
|
{
|
||||||
|
public const string List = "/packages";
|
||||||
public const string Detail = "/package/";
|
public const string Detail = "/package/";
|
||||||
|
public const string MyPackages = "/my-packages";
|
||||||
|
public const string Purchase = "/purchase-package/";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class About
|
public static class About
|
||||||
@@ -74,6 +78,7 @@ public static class RouteConstants
|
|||||||
public const string CheckoutSummary = "/checkout-summary";
|
public const string CheckoutSummary = "/checkout-summary";
|
||||||
public const string Orders = "/orders";
|
public const string Orders = "/orders";
|
||||||
public const string OrderDetail = "/order/"; // usage: /order/{id}
|
public const string OrderDetail = "/order/"; // usage: /order/{id}
|
||||||
|
public const string OrderTracking = "/order-tracking/"; // usage: /order-tracking/{id}
|
||||||
public const string Categories = "/categories";
|
public const string Categories = "/categories";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user