Files
FrontOffice/src/FrontOffice.Main/Utilities/CartService.cs
T
masoodafar-web 6db83ccd90
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 4m55s
feat: full FrontOffice updates - discount store, blog, payment gateway, UI improvements
- Discount store pages (products, cart, orders, order detail)
- Blog pages and services
- Payment gateway callback page
- AppImage component, EmptyState, LoadingState, PageHeader
- DiscountCartService, DiscountOrderService, DiscountProductService
- BlogCategoryService, BlogPostService, SitePageService, ImageCacheService
- PhoneVerifyForm component
- Profile Hub page
- UI/UX improvements across all pages
- landing.js for homepage
- Config and routing updates
2026-02-16 00:51:39 +03:30

241 lines
7.3 KiB
C#

using DateTimeConverterCL;
using CMSMicroservice.Protobuf.Protos.UserCarts;
using Google.Protobuf.WellKnownTypes;
using Blazored.LocalStorage;
namespace FrontOffice.Main.Utilities;
public record CartItem(long cartId,long ProductId, string Title, string ImageUrl, long UnitPrice, int Quantity)
{
public long LineTotal => UnitPrice * Quantity;
public long DiscountValue => (Discount*(UnitPrice * Quantity))/100;
public int Discount { get; init; }
public string Created { get; init; } = string.Empty;
public string Description { get; init; } = string.Empty;
}
public class CartService
{
private readonly UserCartsContract.UserCartsContractClient _client;
private readonly ILocalStorageService _localStorage;
private readonly List<CartItem> _items = new();
private const string TokenStorageKey = "auth:token";
private bool _isInitialized;
public event Action? OnChange;
public CartService(CMSMicroservice.Protobuf.Protos.UserCarts.UserCartsContract.UserCartsContractClient client, ILocalStorageService localStorage)
{
_client = client;
_localStorage = localStorage;
// لود سبد خرید به صورت lazy انجام میشه - نه در constructor
}
public IReadOnlyList<CartItem> Items => _items.AsReadOnly();
public long Total => _items.Sum(i => i.LineTotal);
public long TotalDiscount => _items.Sum(i => i.DiscountValue);
public int Count => _items.Sum(i => i.Quantity);
public async Task Add(Product product, int quantity = 1)
{
if (quantity <= 0) return;
// اطمینان از لود شدن سبد خرید
await EnsureInitializedAsync();
// چک لاگین بودن کاربر
if (!await IsAuthenticatedAsync()) return;
var existing = _items.FirstOrDefault(i => i.ProductId == product.Id);
int newQuantity;
if (existing is null)
{
newQuantity = quantity;
_items.Add(new CartItem(0,product.Id, product.Title, product.ImageUrl, product.Price, newQuantity)
{
Discount = product.Discount,
Created = DateTime.Now.MiladiToJalali(),
Description = product.Description
});
}
else
{
var idx = _items.IndexOf(existing);
newQuantity = existing.Quantity + quantity;
_items[idx] = existing with { Quantity = newQuantity };
}
Notify();
try
{
if (existing is null)
{
await _client.AddNewUserCartForCustomerAsync(new AddNewUserCartForCustomerRequest
{
ProductId = product.Id,
Count = newQuantity
});
await LoadFromServerAsync();
}
else
{
await _client.UpdateUserCartForCustomerAsync(new UpdateUserCartForCustomerRequest
{
CartItemId = existing.cartId,
Count = newQuantity
});
}
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public async Task UpdateQuantity(long productId, int quantity)
{
// چک لاگین بودن کاربر
if (!await IsAuthenticatedAsync()) return;
var existing = _items.FirstOrDefault(i => i.ProductId == productId);
if (existing is null) return;
if (quantity <= 0)
{
await Remove(existing.cartId);
return;
}
var idx = _items.IndexOf(existing);
_items[idx] = existing with { Quantity = quantity };
Notify();
try
{
await _client.UpdateUserCartForCustomerAsync(new UpdateUserCartForCustomerRequest
{
CartItemId = existing.cartId,
Count = quantity
});
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public async Task Remove(long cartId)
{
// چک لاگین بودن کاربر
if (!await IsAuthenticatedAsync()) return;
_items.RemoveAll(i => i.cartId == cartId);
Notify();
try
{
// Use dedicated remove endpoint
await _client.RemoveUserCartForCustomerAsync(new RemoveUserCartForCustomerRequest
{
CartItemId = cartId
});
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public async Task Clear()
{
// چک لاگین بودن کاربر
if (!await IsAuthenticatedAsync()) return;
var productIds = _items.Select(i => i.ProductId).ToList();
_items.Clear();
Notify();
try
{
foreach (var item in productIds)
{
await _client.RemoveUserCartForCustomerAsync(new RemoveUserCartForCustomerRequest
{
CartItemId = item
});
}
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
private void Notify() => OnChange?.Invoke();
/// <summary>
/// بررسی اینکه کاربر لاگین کرده یا نه
/// </summary>
private async Task<bool> IsAuthenticatedAsync()
{
try
{
var token = await _localStorage.GetItemAsync<string>(TokenStorageKey);
return !string.IsNullOrWhiteSpace(token);
}
catch
{
return false;
}
}
/// <summary>
/// اطمینان از لود شدن سبد خرید - فقط اگر کاربر لاگین کرده باشد
/// </summary>
public async Task EnsureInitializedAsync()
{
if (_isInitialized) return;
if (await IsAuthenticatedAsync())
{
await LoadFromServerAsync();
}
_isInitialized = true;
}
private async Task LoadFromServerAsync()
{
// ابتدا چک کن که کاربر لاگین کرده
if (!await IsAuthenticatedAsync())
{
return;
}
try
{
var response = await _client.GetCustomerCartAsync(new GetUserCartForCustomerRequest());
_items.Clear();
foreach (var model in response.Models)
{
var item = new CartItem(
cartId:model.Id,
ProductId: model.ProductId,
Title: model.ProductTitle ?? string.Empty,
ImageUrl: model.ProductThumbnailPath,
UnitPrice: model.ProductPrice,
Quantity: model.Count > 0 ? model.Count : 1)
{
Discount = model.ProductDiscount,
Created = model.Created.ToDateTime().MiladiToJalali(),
Description = model.ProductShortInfomation
};
_items.Add(item);
}
Notify();
}
catch
{
// If backend is unreachable or user is unauthenticated, fall back to local-only cart.
}
}
}