Phase 8c: FO Package pages — Customer RPCs + dynamic features
- NuGet: 0.0.182 → 0.0.185 + local feed source - PackageDetail: GetPackageAsync → GetCustomerPackageDetailsAsync Features & specs now driven by API (PackageFeature) not hardcoded - Packages.razor: Un-excluded from build, dynamic feature bullets from CustomerPackageModel (DiscountMultiplier, MagicWallet, etc.) - PackageService: Enriched PackageDto with 8 new package fields GetUserPackageStatusAsync connected to real RPC (was stub)
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DateTimeConverterCL" Version="1.0.0" />
|
<PackageReference Include="DateTimeConverterCL" Version="1.0.0" />
|
||||||
<!-- Replace all FrontOffice.BFF protobuf packages with CMS protobuf -->
|
<!-- Replace all FrontOffice.BFF protobuf packages with CMS protobuf -->
|
||||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.182" />
|
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.185" />
|
||||||
<!-- <ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />-->
|
<!-- <ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />-->
|
||||||
<PackageReference Include="MudBlazor" Version="8.14.0" />
|
<PackageReference Include="MudBlazor" Version="8.14.0" />
|
||||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
||||||
@@ -56,12 +56,6 @@
|
|||||||
<Content Include="..\.dockerignore">
|
<Content Include="..\.dockerignore">
|
||||||
<Link>.dockerignore</Link>
|
<Link>.dockerignore</Link>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Remove="Pages\Package\Packages.razor" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="Pages\Package\Packages.razor.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<packageSources>
|
<packageSources>
|
||||||
<clear />
|
<clear />
|
||||||
|
<!-- Local packages (dev builds) -->
|
||||||
|
<add key="Local" value="/home/masoud/Apps/project/FourSat/nupkg" />
|
||||||
<!-- Nexus (hosts FourSat packages + proxies nuget.org) -->
|
<!-- Nexus (hosts FourSat packages + proxies nuget.org) -->
|
||||||
<add key="Nexus" value="http://194.5.195.53:32081/repository/nuget-all/index.json" allowInsecureConnections="true" />
|
<add key="Nexus" value="http://194.5.195.53:32081/repository/nuget-all/index.json" allowInsecureConnections="true" />
|
||||||
</packageSources>
|
</packageSources>
|
||||||
|
|||||||
@@ -64,20 +64,43 @@
|
|||||||
<MudText Typo="Typo.h6" Color="Color.Success">@package.FormattedPrice</MudText>
|
<MudText Typo="Typo.h6" Color="Color.Success">@package.FormattedPrice</MudText>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
<!-- Features Preview -->
|
<!-- Package Highlights -->
|
||||||
<MudStack Spacing="1" Class="mt-2">
|
<MudStack Spacing="1" Class="mt-2">
|
||||||
|
@if (package.SupportsDirectPurchase)
|
||||||
|
{
|
||||||
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
||||||
<MudText Typo="Typo.caption">عضویت در باشگاه مشتریان</MudText>
|
<MudText Typo="Typo.caption">پرداخت مستقیم</MudText>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
}
|
||||||
|
@if (package.SupportsDayaPurchase)
|
||||||
|
{
|
||||||
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
||||||
<MudText Typo="Typo.caption">دریافت پاداش هفتگی</MudText>
|
<MudText Typo="Typo.caption">پرداخت با اعتبار دایا</MudText>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
}
|
||||||
|
@if (package.DiscountMultiplier > 0)
|
||||||
|
{
|
||||||
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
||||||
<MudText Typo="Typo.caption">تیمسازی نامحدود</MudText>
|
<MudText Typo="Typo.caption">ضریب تخفیف: @package.DiscountMultiplier.ToString("F1")x</MudText>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
}
|
||||||
|
@if (package.MagicWalletMultiplier > 0)
|
||||||
|
{
|
||||||
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Check" Size="Size.Small" Color="Color.Primary" />
|
||||||
|
<MudText Typo="Typo.caption">کیف پول جادویی: @package.MagicWalletMultiplier.ToString("F1")x</MudText>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
@if (package.IsBasePackage)
|
||||||
|
{
|
||||||
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Star" Size="Size.Small" Color="Color.Warning" />
|
||||||
|
<MudText Typo="Typo.caption">پکیج پایه</MudText>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
|
|||||||
@@ -64,36 +64,38 @@ public partial class PackageDetail : IDisposable
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Load package details
|
// Load package details via Customer RPC
|
||||||
var packageResponse = await PackageClient.GetPackageAsync(request: new() { Id = Id}, cancellationToken: _loadCts.Token);
|
var response = await PackageClient.GetCustomerPackageDetailsAsync(
|
||||||
|
new GetCustomerPackageDetailsRequest { PackageId = Id },
|
||||||
|
cancellationToken: _loadCts.Token);
|
||||||
|
|
||||||
if (packageResponse != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
|
// Build features from API response
|
||||||
|
var features = response.Features?.Select(f => f.Title).ToList() ?? new List<string>();
|
||||||
|
|
||||||
|
// Build specifications from API features (highlighted ones)
|
||||||
|
var specs = response.Features?
|
||||||
|
.Where(f => f.IsHighlighted)
|
||||||
|
.Select(f => new Specification
|
||||||
|
{
|
||||||
|
Name = f.Title,
|
||||||
|
Value = f.Description,
|
||||||
|
Icon = string.IsNullOrEmpty(f.Icon) ? Icons.Material.Filled.Star : f.Icon
|
||||||
|
}).ToList() ?? new List<Specification>();
|
||||||
|
|
||||||
_package = new PackageDetailDto
|
_package = new PackageDetailDto
|
||||||
{
|
{
|
||||||
Id = packageResponse.Id,
|
Id = response.Id,
|
||||||
Title = packageResponse.Title,
|
Title = response.Title,
|
||||||
Body = packageResponse.Description,
|
Body = response.Description,
|
||||||
Image = packageResponse.ImagePath ?? string.Empty,
|
Image = response.ImagePath ?? string.Empty,
|
||||||
Specifications = new List<Specification>
|
Specifications = specs,
|
||||||
{
|
Features = features,
|
||||||
new() { Name = "ظرفیت", Value = "تا ۲۰۰ عضو", Icon = Icons.Material.Filled.Group },
|
|
||||||
new() { Name = "شجرهنامه", Value = "پیشرفته", Icon = Icons.Material.Filled.AccountTree },
|
|
||||||
new() { Name = "گزارشگیری", Value = "جامع", Icon = Icons.Material.Filled.Analytics },
|
|
||||||
new() { Name = "پشتیبانی", Value = "۲۴ ساعته", Icon = Icons.Material.Filled.Support }
|
|
||||||
},
|
|
||||||
Features = new List<string>
|
|
||||||
{
|
|
||||||
"مدیریت تیم نامحدود",
|
|
||||||
"شجرهنامه بصری",
|
|
||||||
"محاسبه کارمزد خودکار",
|
|
||||||
"گزارشهای مالی",
|
|
||||||
"پشتیبانی اولویتدار"
|
|
||||||
},
|
|
||||||
Pricing = new PricingInfo
|
Pricing = new PricingInfo
|
||||||
{
|
{
|
||||||
OriginalPrice = packageResponse.Price,
|
OriginalPrice = response.Price,
|
||||||
FinalPrice = packageResponse.Price,
|
FinalPrice = response.Price,
|
||||||
HasDiscount = false,
|
HasDiscount = false,
|
||||||
DiscountPercent = 0
|
DiscountPercent = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,15 @@ public record PackageDto(
|
|||||||
string Title,
|
string Title,
|
||||||
string Description,
|
string Description,
|
||||||
string ImageUrl,
|
string ImageUrl,
|
||||||
long Price)
|
long Price,
|
||||||
|
long ActivationFee = 0,
|
||||||
|
double DiscountMultiplier = 0,
|
||||||
|
double MagicWalletMultiplier = 0,
|
||||||
|
long MagicWalletMaxDeposit = 0,
|
||||||
|
long MagicWalletMaxCredit = 0,
|
||||||
|
bool IsBasePackage = false,
|
||||||
|
bool SupportsDayaPurchase = false,
|
||||||
|
bool SupportsDirectPurchase = false)
|
||||||
{
|
{
|
||||||
public string FormattedPrice => string.Format("{0:N0} تومان", Price);
|
public string FormattedPrice => string.Format("{0:N0} تومان", Price);
|
||||||
}
|
}
|
||||||
@@ -56,7 +64,15 @@ public class PackageService
|
|||||||
m.Title,
|
m.Title,
|
||||||
m.Description,
|
m.Description,
|
||||||
m.ImagePath,
|
m.ImagePath,
|
||||||
m.Price))
|
m.Price,
|
||||||
|
m.ActivationFee,
|
||||||
|
m.DiscountMultiplier,
|
||||||
|
m.MagicWalletMultiplier,
|
||||||
|
m.MagicWalletMaxDeposit,
|
||||||
|
m.MagicWalletMaxCredit,
|
||||||
|
m.IsBasePackage,
|
||||||
|
m.SupportsDayaPurchase,
|
||||||
|
m.SupportsDirectPurchase))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -100,16 +116,32 @@ public class PackageService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get user's package purchase status
|
/// Get user's package purchase status
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Task<UserPackageStatusDto> GetUserPackageStatusAsync(CancellationToken ct = default)
|
public async Task<UserPackageStatusDto> GetUserPackageStatusAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
// TODO: Connect to GetUserPackageStatus RPC when available in FrontOffice.BFF
|
try
|
||||||
return Task.FromResult(new UserPackageStatusDto(
|
{
|
||||||
|
var response = await _client.GetUserPackageStatusAsync(
|
||||||
|
new GetUserPackageStatusRequest(), cancellationToken: ct);
|
||||||
|
|
||||||
|
return new UserPackageStatusDto(
|
||||||
|
HasPurchasedPackage: response.HasPurchasedPackage,
|
||||||
|
PackageTitle: null,
|
||||||
|
PurchaseMethod: response.PackagePurchaseMethod,
|
||||||
|
IsClubMemberActive: response.IsClubMemberActive,
|
||||||
|
WalletBalance: response.WalletBalance,
|
||||||
|
CanActivateClubMembership: response.CanActivateClubMembership,
|
||||||
|
PurchaseDate: response.LastPurchaseDate?.ToDateTime());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new UserPackageStatusDto(
|
||||||
HasPurchasedPackage: false,
|
HasPurchasedPackage: false,
|
||||||
PackageTitle: null,
|
PackageTitle: null,
|
||||||
PurchaseMethod: null,
|
PurchaseMethod: null,
|
||||||
IsClubMemberActive: false,
|
IsClubMemberActive: false,
|
||||||
WalletBalance: 0,
|
WalletBalance: 0,
|
||||||
CanActivateClubMembership: false,
|
CanActivateClubMembership: false,
|
||||||
PurchaseDate: null));
|
PurchaseDate: null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user