diff --git a/src/FrontOffice.Main/Pages/Profile/Index.razor b/src/FrontOffice.Main/Pages/Profile/Index.razor index f01a2b8..10dc3eb 100644 --- a/src/FrontOffice.Main/Pages/Profile/Index.razor +++ b/src/FrontOffice.Main/Pages/Profile/Index.razor @@ -119,7 +119,7 @@ لینک دعوت شما هنوز فعال نشده است - برای فعال‌سازی لینک دعوت، ابتدا پکیج پایه را تهیه کنید و سپس در باشگاه مشتریان عضو شوید. + برای فعال‌سازی لینک دعوت، ابتدا یکی از پکیج‌ها را تهیه کنید و سپس در باشگاه مشتریان عضو شوید. از مزایای ویژه عضویت بهره‌مند شده و با فعالیت در زمینه توسعه فروشگاه‌ها پاداش دریافت کنید. @@ -130,7 +130,7 @@ Class="rounded-pill" StartIcon="@Icons.Material.Filled.ShoppingCart" OnClick="OpenPurchaseOptions"> - شروع فرآیند تامین اعتبار + تهیه پکیج } @@ -195,88 +195,4 @@ -@* ── Purchase Options Dialog ── *@ - - - - - خرید پکیج پایه ۵۶ میلیون تومان - - - - برای خرید پکیج پایه، یکی از روش‌های زیر را انتخاب کنید: - - - - - - پرداخت مستقیم - - پرداخت آنی از طریق درگاه بانکی - @if (_purchaseCycleCount > 0) - { - - @if (_isProcessingPayment) - { - - در حال انتقال به درگاه... - } - else - { - پرداخت با کارت بانکی - } - - } - else - { - - @if (_isProcessingPayment) - { - - در حال انتقال به درگاه... - } - else - { - پرداخت با کارت بانکی - } - - } - - - - @if (_purchaseCycleCount == 0) - { - - - - - اعتبار الماسی دایا - - تأمین اعتبار از طریق خرید توکن الماس و دریافت وام - - تأمین اعتبار الماسی - - - - } - else - { - - از دور دوم به بعد، خرید پکیج فقط از طریق درگاه بانکی امکان‌پذیر است. - - } - - - بستن - - - - diff --git a/src/FrontOffice.Main/Pages/Profile/Index.razor.cs b/src/FrontOffice.Main/Pages/Profile/Index.razor.cs index 727ebcc..49820af 100644 --- a/src/FrontOffice.Main/Pages/Profile/Index.razor.cs +++ b/src/FrontOffice.Main/Pages/Profile/Index.razor.cs @@ -42,8 +42,7 @@ public partial class Index private bool _isClubMemberActive; private bool CanShowReferralLink => _hasPurchasedPackage && _isClubMemberActive; - // Purchase Options Bottom Sheet - private bool _showPurchaseBottomSheet; + // Purchase flow private bool _isProcessingPayment; private int _purchaseCycleCount; // تعداد دورهای خرید — اگر > 0 فقط IPG مجاز @@ -394,53 +393,65 @@ public partial class Index } } - private void OpenPurchaseOptions() + private async Task OpenPurchaseOptions() { - _showPurchaseBottomSheet = true; + var options = new DialogOptions + { + MaxWidth = MaxWidth.Small, + FullWidth = true, + CloseOnEscapeKey = true, + BackdropClick = true + }; + + var parameters = new DialogParameters + { + { x => x.PurchaseCycleCount, _purchaseCycleCount } + }; + + var dialog = await DialogService.ShowAsync( + string.Empty, parameters, options); + var result = await dialog.Result; + + if (result.Canceled) return; + + if (result.Data is Shared.PackagePurchaseDialog.PackagePurchaseResult purchase) + { + switch (purchase.Method) + { + case Shared.PackagePurchaseDialog.PaymentMethodType.DirectPayment: + await ProcessDirectPayment(purchase.Package.Id); + break; + case Shared.PackagePurchaseDialog.PaymentMethodType.DayaLoan: + await ProcessDayaLoanPayment(); + break; + } + } } - private void ClosePurchaseOptions() - { - _showPurchaseBottomSheet = false; - } - - private async Task DirectPayment() + private async Task ProcessDirectPayment(long packageId) { if (_isProcessingPayment) return; - + _isProcessingPayment = true; - _showPurchaseBottomSheet = false; - + StateHasChanged(); + try { - // Get available packages and pick the first one - var packages = await PackageContract.GetCustomerPackagesAsync(new GetCustomerPackagesRequest()); - var selectedPackage = packages?.Models?.FirstOrDefault(); - if (selectedPackage == null) - { - Snackbar.Add("پکیجی برای خرید یافت نشد", Severity.Error); - return; - } - - // Call CMS to initiate purchase (UserId from JWT, PackageId explicit) var callbackUrl = $"{Navigation.BaseUri}profile/payment-callback"; - + var response = await PackageContract.CustomerPurchasePackageAsync(new CustomerPurchasePackageRequest { - PackageId = selectedPackage.Id, + PackageId = packageId, CallbackUrl = callbackUrl, PurchaseMethod = PurchaseMethodEnum.PurchaseMethodGateway }); - + if (!response.Success) { Snackbar.Add(response.Message ?? "خطا در آغاز فرآیند پرداخت", Severity.Error); return; } - - // Redirect to payment gateway - // ZarinPal appends ?Authority=...&Status=... to callback URL - // PaymentCallback uses Authority+Status to verify via CustomerVerifyPackagePurchase + if (!string.IsNullOrEmpty(response.PaymentGatewayUrl)) { Navigation.NavigateTo(response.PaymentGatewayUrl, forceLoad: true); @@ -461,41 +472,34 @@ public partial class Index } } - private async Task DayaLoanPayment() + private async Task ProcessDayaLoanPayment() { - // Validate user info before redirecting if (string.IsNullOrWhiteSpace(_updateUserRequest.FirstName)) { - _showPurchaseBottomSheet = false; - Snackbar.Add($"لطفا اطلاعات شخصی خود را تکمیل کنید. (نام وارد نشده)", Severity.Error); + Snackbar.Add("لطفا اطلاعات شخصی خود را تکمیل کنید. (نام وارد نشده)", Severity.Error); return; } if (string.IsNullOrWhiteSpace(_updateUserRequest.LastName)) { - _showPurchaseBottomSheet = false; - Snackbar.Add($"لطفا اطلاعات شخصی خود را تکمیل کنید. (نام خانوادگی وارد نشده)", Severity.Error); + Snackbar.Add("لطفا اطلاعات شخصی خود را تکمیل کنید. (نام خانوادگی وارد نشده)", Severity.Error); return; } if (string.IsNullOrWhiteSpace(_updateUserRequest.NationalCode)) { - _showPurchaseBottomSheet = false; - Snackbar.Add($"لطفا اطلاعات شخصی خود را تکمیل کنید. (کدملی وارد نشده)", Severity.Error); + Snackbar.Add("لطفا اطلاعات شخصی خود را تکمیل کنید. (کدملی وارد نشده)", Severity.Error); return; } if (_updateUserRequest.BirthDate == null) { - _showPurchaseBottomSheet = false; - Snackbar.Add($"لطفا اطلاعات شخصی خود را تکمیل کنید. (تاریخ تولد وارد نشده)", Severity.Error); + Snackbar.Add("لطفا اطلاعات شخصی خود را تکمیل کنید. (تاریخ تولد وارد نشده)", Severity.Error); return; } if (!_addresses.Any()) { - _showPurchaseBottomSheet = false; - Snackbar.Add($"آدرس محل سکونت شما الزامی است!", Severity.Error); + Snackbar.Add("آدرس محل سکونت شما الزامی است!", Severity.Error); return; } - - _showPurchaseBottomSheet = false; + var url = "https://dayadiamond.ir/profile/creditpurchase/?merchantcode=56146364"; await JSRuntime.InvokeVoidAsync("open", url, "_blank"); } diff --git a/src/FrontOffice.Main/Shared/PackagePurchaseDialog.razor b/src/FrontOffice.Main/Shared/PackagePurchaseDialog.razor new file mode 100644 index 0000000..adaa7d6 --- /dev/null +++ b/src/FrontOffice.Main/Shared/PackagePurchaseDialog.razor @@ -0,0 +1,234 @@ +@using FrontOffice.Main.Utilities +@inject PackageService PackageService +@inject ISnackbar Snackbar + + + + @if (_selectedPackage != null) + { + + + انتخاب روش پرداخت + + } + else + { + + + انتخاب پکیج + + } + + + @if (_isLoading) + { + + + در حال بارگذاری پکیج‌ها... + + } + else if (_selectedPackage != null) + { + @* ── Step 2: Payment method selection ── *@ + + @* Selected Package Summary *@ + + + + + + + @_selectedPackage.Title + @_selectedPackage.FormattedPrice + + + + + + + + روش پرداخت خود را انتخاب کنید: + + + @* Direct Payment — always available *@ + + + + + + پرداخت مستقیم + پرداخت آنی از طریق درگاه بانکی + + + + + + + @* Daya Loan — only for base package AND first purchase cycle *@ + @if (_selectedPackage.SupportsDayaPurchase && PurchaseCycleCount == 0) + { + + + + + + اعتبار الماسی دایا + تأمین اعتبار از طریق خرید توکن الماس + + + + + + } + else if (!_selectedPackage.SupportsDayaPurchase) + { + + اعتبار الماسی دایا فقط برای پکیج پایه قابل استفاده است. + + } + else if (PurchaseCycleCount > 0) + { + + از دور دوم به بعد، خرید پکیج فقط از طریق درگاه بانکی امکان‌پذیر است. + + } + + } + else + { + @* ── Step 1: Package tile grid ── *@ + + + پکیج مورد نظر خود را انتخاب کنید: + + + + @foreach (var pkg in _packages) + { + + + @if (pkg.IsBasePackage) + { + پایه + } + + + + + @pkg.Title + @pkg.FormattedPrice + @if (!string.IsNullOrWhiteSpace(pkg.Description)) + { + + @pkg.Description + + } + @* Key features *@ + + @if (pkg.MagicWalletMultiplier > 0) + { + + + ضریب جادویی: @pkg.MagicWalletMultiplier.ToString("F1")x + + } + @if (pkg.DiscountMultiplier > 0) + { + + + ضریب تخفیف: @pkg.DiscountMultiplier.ToString("F1")x + + } + @if (pkg.SupportsDayaPurchase) + { + + + قابل خرید با دایا + + } + + + + + } + + + } + + + بستن + + + +@code { + [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = default!; + + /// Number of previous purchase cycles — controls Daya eligibility + [Parameter] public int PurchaseCycleCount { get; set; } + + private List _packages = new(); + private PackageDto? _selectedPackage; + private bool _isLoading = true; + + protected override async Task OnInitializedAsync() + { + _isLoading = true; + try + { + _packages = await PackageService.GetAllPackagesAsync(); + } + catch (Exception ex) + { + Snackbar.Add("خطا در بارگذاری پکیج‌ها", MudBlazor.Severity.Error); + } + finally + { + _isLoading = false; + } + } + + private void SelectPackage(PackageDto package) + { + _selectedPackage = package; + } + + private void BackToList() + { + _selectedPackage = null; + } + + private void SelectPaymentMethod(PaymentMethodType method) + { + var result = new PackagePurchaseResult(_selectedPackage!, method); + MudDialog.Close(DialogResult.Ok(result)); + } + + private void Cancel() => MudDialog.Cancel(); + + private static string GetTileStyle(PackageDto pkg) + { + return pkg.IsBasePackage + ? "border:2px solid var(--mud-palette-primary); background:rgba(99,102,241,.04); cursor:pointer;" + : "border:2px solid var(--mud-palette-lines-default); background:var(--mud-palette-surface); cursor:pointer;"; + } + + private static string GetAvatarStyle(PackageDto pkg) + { + return pkg.IsBasePackage + ? "background:rgba(99,102,241,.15); color:#6366f1;" + : "background:rgba(16,185,129,.12); color:#10b981;"; + } + + public enum PaymentMethodType { DirectPayment, DayaLoan } + public record PackagePurchaseResult(PackageDto Package, PaymentMethodType Method); +} diff --git a/src/FrontOffice.Main/wwwroot/css/site.css b/src/FrontOffice.Main/wwwroot/css/site.css index d6d5ac7..79e629c 100644 --- a/src/FrontOffice.Main/wwwroot/css/site.css +++ b/src/FrontOffice.Main/wwwroot/css/site.css @@ -1611,3 +1611,30 @@ html, body { } /*#endregion*/ + +/* ── Package Purchase Dialog tiles ── */ +.pkg-tile { + transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease; + position: relative; +} +.pkg-tile:hover { + transform: translateY(-3px); + box-shadow: 0 6px 20px rgba(0,0,0,.08); + border-color: var(--mud-palette-primary) !important; +} +.pkg-tile-badge { + position: absolute; + top: 8px; + right: 8px; +} +.pkg-payment-option { + transition: transform .15s ease, box-shadow .15s ease; +} +.pkg-payment-option:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0,0,0,.06); +} +@media (max-width: 600px) { + .pkg-tile { padding: .8rem !important; } + .pkg-tile .mud-avatar-large { width: 48px !important; height: 48px !important; } +}