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:
masoodafar-web
2025-12-05 17:27:04 +03:30
parent feaaeada47
commit 68c114e43e
14 changed files with 1011 additions and 4 deletions
@@ -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 Addresses = "/profile/addresses";
public const string Settings = "/profile/settings";
public const string ChangePassword = "/profile/change-password";
public const string Tree = "/profile/tree";
public const string Wallet = "/profile/wallet";
}
@@ -43,7 +44,10 @@ public static class RouteConstants
public static class Package
{
public const string List = "/packages";
public const string Detail = "/package/";
public const string MyPackages = "/my-packages";
public const string Purchase = "/purchase-package/";
}
public static class About
@@ -74,6 +78,7 @@ public static class RouteConstants
public const string CheckoutSummary = "/checkout-summary";
public const string Orders = "/orders";
public const string OrderDetail = "/order/"; // usage: /order/{id}
public const string OrderTracking = "/order-tracking/"; // usage: /order-tracking/{id}
public const string Categories = "/categories";
}
}