Files
FrontOffice/src/FrontOffice.Main/Utilities/CartService.cs

185 lines
5.5 KiB
C#

using DateTimeConverterCL;
using FrontOffice.BFF.ShopingCart.Protobuf.Protos.ShopingCart;
using Google.Protobuf.WellKnownTypes;
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 ShopingCartContract.ShopingCartContractClient _client;
private readonly List<CartItem> _items = new();
public event Action? OnChange;
public CartService(ShopingCartContract.ShopingCartContractClient client)
{
_client = client;
_ = LoadFromServerAsync();
}
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;
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.AddNewUserCartAsync(new AddNewUserCartRequest
{
ProductId = product.Id,
Count = newQuantity
});
await LoadFromServerAsync();
}
else
{
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
{
UserCartId = existing.cartId,
Count = newQuantity
});
}
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public async Task UpdateQuantity(long productId, int quantity)
{
var existing = _items.FirstOrDefault(i => i.ProductId == productId);
if (existing is null) return;
if (quantity <= 0)
{
Remove(existing.cartId);
return;
}
var idx = _items.IndexOf(existing);
_items[idx] = existing with { Quantity = quantity };
Notify();
try
{
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
{
UserCartId = existing.cartId,
Count = quantity
});
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public async Task Remove(long cartId)
{
_items.RemoveAll(i => i.cartId == cartId);
Notify();
try
{
// Interpret Count=0 as remove from cart
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
{
UserCartId = cartId,
Count = 0
});
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
public async Task Clear()
{
var productIds = _items.Select(i => i.ProductId).ToList();
_items.Clear();
Notify();
try
{
foreach (var id in productIds)
{
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
{
UserCartId = id,
Count = 0
});
}
}
catch
{
// Best-effort sync with backend; keep local state on failure
}
}
private void Notify() => OnChange?.Invoke();
private async Task LoadFromServerAsync()
{
try
{
var response = await _client.GetAllUserCartAsync(new Empty());
_items.Clear();
foreach (var model in response.Models)
{
var item = new CartItem(
cartId:model.Id,
ProductId: model.ProductId,
Title: model.ProductTitle ?? string.Empty,
ImageUrl: string.IsNullOrWhiteSpace(model.ProductThumbnailPath) ? string.Empty : UrlUtility.DownloadUrl + 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.
}
}
}