feat: داشبورد — لیست کاشی پکیج‌ها + مدال انتخاب روش پرداخت

- حذف متن hardcoded 'پکیج پایه ۵۶ میلیون تومان' و دیالوگ inline
- ایجاد PackagePurchaseDialog — کامپوننت مستقل 2 مرحله‌ای:
  مرحله ۱: نمایش کاشی‌های پکیج (ریسپانسیو ۲-۳ تایی)
  مرحله ۲: انتخاب روش پرداخت (درگاه بانکی / دایا الماس)
- دایا الماس فقط برای پکیج پایه + دور اول نمایش داده میشه
- پکیج‌ها داینامیک از API بارگذاری میشن
- تغییر متن CTA از 'شروع فرآیند تامین اعتبار' به 'تهیه پکیج'
- تغییر متن راهنما از 'پکیج پایه را تهیه کنید' به 'یکی از پکیج‌ها را تهیه کنید'
- CSS: hover effects + badge + responsive tiles
This commit is contained in:
masoodafar-web
2026-02-27 20:14:40 +03:30
parent ecc4f447ba
commit a3681a8bcd
4 changed files with 311 additions and 130 deletions
+2 -86
View File
@@ -119,7 +119,7 @@
لینک دعوت شما هنوز فعال نشده است
</MudText>
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-1" Style="max-width:560px; margin:0 auto;">
برای فعال‌سازی لینک دعوت، ابتدا <strong>پکیج پایه</strong> را تهیه کنید و سپس در <strong>باشگاه مشتریان</strong> عضو شوید.
برای فعال‌سازی لینک دعوت، ابتدا یکی از <strong>پکیج‌ها</strong> را تهیه کنید و سپس در <strong>باشگاه مشتریان</strong> عضو شوید.
</MudText>
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-4" Style="max-width:560px; margin:0 auto;">
از مزایای ویژه عضویت بهره‌مند شده و با فعالیت در زمینه توسعه فروشگاه‌ها پاداش دریافت کنید.
@@ -130,7 +130,7 @@
Class="rounded-pill"
StartIcon="@Icons.Material.Filled.ShoppingCart"
OnClick="OpenPurchaseOptions">
شروع فرآیند تامین اعتبار
تهیه پکیج
</MudButton>
</MudPaper>
}
@@ -195,88 +195,4 @@
</MudGrid>
</MudContainer>
@* ── Purchase Options Dialog ── *@
<MudDialog @bind-Visible="_showPurchaseBottomSheet">
<DialogContent>
<MudStack Spacing="3" Class="pa-2">
<MudText Typo="Typo.h6" Align="Align.Center">
خرید پکیج پایه ۵۶ میلیون تومان
</MudText>
<MudDivider />
<MudText Typo="Typo.body2" Class="mud-text-secondary">
برای خرید پکیج پایه، یکی از روش‌های زیر را انتخاب کنید:
</MudText>
<MudPaper Elevation="0" Class="pa-4 rounded-lg" Style="border: 2px solid var(--mud-palette-primary);">
<MudStack Spacing="2">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudIcon Icon="@Icons.Material.Filled.CreditCard" Color="Color.Primary" Size="Size.Medium" />
<MudText Typo="Typo.subtitle1" Color="Color.Primary"><b>پرداخت مستقیم</b></MudText>
</MudStack>
<MudText Typo="Typo.body2" Class="mud-text-secondary">پرداخت آنی از طریق درگاه بانکی</MudText>
@if (_purchaseCycleCount > 0)
{
<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
Disabled="@_isProcessingPayment"
StartIcon="@Icons.Material.Filled.Payment"
OnClick="DirectPayment">
@if (_isProcessingPayment)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="me-2" />
<span>در حال انتقال به درگاه...</span>
}
else
{
<span>پرداخت با کارت بانکی</span>
}
</MudButton>
}
else
{
<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
Disabled="@_isProcessingPayment"
StartIcon="@Icons.Material.Filled.Payment"
OnClick="DirectPayment">
@if (_isProcessingPayment)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="me-2" />
<span>در حال انتقال به درگاه...</span>
}
else
{
<span>پرداخت با کارت بانکی</span>
}
</MudButton>
}
</MudStack>
</MudPaper>
@if (_purchaseCycleCount == 0)
{
<MudPaper Elevation="0" Class="pa-4 rounded-lg" Style="border: 2px solid var(--mud-palette-tertiary);">
<MudStack Spacing="2">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudIcon Icon="@Icons.Material.Filled.Diamond" Color="Color.Tertiary" Size="Size.Medium" />
<MudText Typo="Typo.subtitle1" Color="Color.Tertiary"><b>اعتبار الماسی دایا</b></MudText>
</MudStack>
<MudText Typo="Typo.body2" Class="mud-text-secondary">تأمین اعتبار از طریق خرید توکن الماس و دریافت وام</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Tertiary" FullWidth="true"
StartIcon="@Icons.Material.Filled.AccountBalance" OnClick="DayaLoanPayment">
تأمین اعتبار الماسی
</MudButton>
</MudStack>
</MudPaper>
}
else
{
<MudAlert Severity="Severity.Info" Dense="true" Class="mt-1">
از دور دوم به بعد، خرید پکیج فقط از طریق درگاه بانکی امکان‌پذیر است.
</MudAlert>
}
<MudButton Variant="Variant.Text" Color="Color.Default" FullWidth="true" OnClick="ClosePurchaseOptions">
بستن
</MudButton>
</MudStack>
</DialogContent>
</MudDialog>
@@ -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<Shared.PackagePurchaseDialog>
{
{ x => x.PurchaseCycleCount, _purchaseCycleCount }
};
var dialog = await DialogService.ShowAsync<Shared.PackagePurchaseDialog>(
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");
}