Refactor CartService to use async methods for cart operations and integrate gRPC for backend synchronization
This commit is contained in:
@@ -10,6 +10,7 @@ using FrontOffice.BFF.Transaction.Protobuf.Protos.Transaction;
|
|||||||
using FrontOffice.BFF.User.Protobuf.Protos.User;
|
using FrontOffice.BFF.User.Protobuf.Protos.User;
|
||||||
using FrontOffice.BFF.UserAddress.Protobuf.Protos.UserAddress;
|
using FrontOffice.BFF.UserAddress.Protobuf.Protos.UserAddress;
|
||||||
using FrontOffice.BFF.UserOrder.Protobuf.Protos.UserOrder;
|
using FrontOffice.BFF.UserOrder.Protobuf.Protos.UserOrder;
|
||||||
|
using FrontOffice.BFF.ShopingCart.Protobuf.Protos.ShopingCart;
|
||||||
using FrontOffice.Main.Utilities;
|
using FrontOffice.Main.Utilities;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -78,6 +79,7 @@ public static class ConfigureServices
|
|||||||
// Products gRPC
|
// Products gRPC
|
||||||
services.AddScoped(CreateAuthenticatedClient<FrontOffice.BFF.Products.Protobuf.Protos.Products.ProductsContract.ProductsContractClient>);
|
services.AddScoped(CreateAuthenticatedClient<FrontOffice.BFF.Products.Protobuf.Protos.Products.ProductsContract.ProductsContractClient>);
|
||||||
services.AddScoped(CreateAuthenticatedClient<TransactionContract.TransactionContractClient>);
|
services.AddScoped(CreateAuthenticatedClient<TransactionContract.TransactionContractClient>);
|
||||||
|
services.AddScoped(CreateAuthenticatedClient<ShopingCartContract.ShopingCartContractClient>);
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<PackageReference Include="Foursat.FrontOffice.BFF.UserAddress.Protobuf" Version="0.0.114" />
|
<PackageReference Include="Foursat.FrontOffice.BFF.UserAddress.Protobuf" Version="0.0.114" />
|
||||||
<PackageReference Include="Foursat.FrontOffice.BFF.UserOrder.Protobuf" Version="0.0.112" />
|
<PackageReference Include="Foursat.FrontOffice.BFF.UserOrder.Protobuf" Version="0.0.112" />
|
||||||
<PackageReference Include="FrontOffice.BFF.Products.Protobuf" Version="0.0.11" />
|
<PackageReference Include="FrontOffice.BFF.Products.Protobuf" Version="0.0.11" />
|
||||||
|
<PackageReference Include="FrontOffice.BFF.ShopingCart.Protobuf" Version="0.0.12" />
|
||||||
<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" />
|
||||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ public partial class Cart : ComponentBase, IDisposable
|
|||||||
CartService.OnChange += StateHasChanged;
|
CartService.OnChange += StateHasChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeQty(long productId, int value)
|
private async Task ChangeQty(long productId, int value)
|
||||||
{
|
{
|
||||||
CartService.UpdateQuantity(productId, value);
|
await CartService.UpdateQuantity(productId, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Remove(long productId)
|
private async Task Remove(long productId)
|
||||||
{
|
{
|
||||||
CartService.Remove(productId);
|
await CartService.Remove(productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProceedCheckout()
|
private void ProceedCheckout()
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public partial class CheckoutSummary : ComponentBase
|
|||||||
};
|
};
|
||||||
|
|
||||||
var id = await OrderService.CreateOrderAsync(order);
|
var id = await OrderService.CreateOrderAsync(order);
|
||||||
Cart.Clear();
|
await Cart.Clear();
|
||||||
Snackbar.Add("سفارش با موفقیت ثبت شد.", Severity.Success);
|
Snackbar.Add("سفارش با موفقیت ثبت شد.", Severity.Success);
|
||||||
Navigation.NavigateTo($"{RouteConstants.Store.OrderDetail}{id}");
|
Navigation.NavigateTo($"{RouteConstants.Store.OrderDetail}{id}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ public partial class ProductDetail : ComponentBase
|
|||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddToCart()
|
private async Task AddToCart()
|
||||||
{
|
{
|
||||||
if (_product is null) return;
|
if (_product is null) return;
|
||||||
Cart.Add(_product, _qty);
|
await Cart.Add(_product, _qty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ public partial class Products : ComponentBase, IDisposable
|
|||||||
await Load();
|
await Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddToCart(Product p)
|
private async Task AddToCart(Product p)
|
||||||
{
|
{
|
||||||
Cart.Add(p, 1);
|
await Cart.Add(p, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using FrontOffice.BFF.ShopingCart.Protobuf.Protos.ShopingCart;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
|
||||||
namespace FrontOffice.Main.Utilities;
|
namespace FrontOffice.Main.Utilities;
|
||||||
|
|
||||||
public record CartItem(long ProductId, string Title, string ImageUrl, long UnitPrice, int Quantity)
|
public record CartItem(long ProductId, string Title, string ImageUrl, long UnitPrice, int Quantity)
|
||||||
@@ -7,14 +10,21 @@ public record CartItem(long ProductId, string Title, string ImageUrl, long UnitP
|
|||||||
|
|
||||||
public class CartService
|
public class CartService
|
||||||
{
|
{
|
||||||
|
private readonly ShopingCartContract.ShopingCartContractClient _client;
|
||||||
private readonly List<CartItem> _items = new();
|
private readonly List<CartItem> _items = new();
|
||||||
public event Action? OnChange;
|
public event Action? OnChange;
|
||||||
|
|
||||||
|
public CartService(ShopingCartContract.ShopingCartContractClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_ = LoadFromServerAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public IReadOnlyList<CartItem> Items => _items.AsReadOnly();
|
public IReadOnlyList<CartItem> Items => _items.AsReadOnly();
|
||||||
public long Total => _items.Sum(i => i.LineTotal);
|
public long Total => _items.Sum(i => i.LineTotal);
|
||||||
public int Count => _items.Sum(i => i.Quantity);
|
public int Count => _items.Sum(i => i.Quantity);
|
||||||
|
|
||||||
public void Add(Product product, int quantity = 1)
|
public async Task Add(Product product, int quantity = 1)
|
||||||
{
|
{
|
||||||
if (quantity <= 0) return;
|
if (quantity <= 0) return;
|
||||||
var existing = _items.FirstOrDefault(i => i.ProductId == product.Id);
|
var existing = _items.FirstOrDefault(i => i.ProductId == product.Id);
|
||||||
@@ -28,9 +38,22 @@ public class CartService
|
|||||||
_items[idx] = existing with { Quantity = existing.Quantity + quantity };
|
_items[idx] = existing with { Quantity = existing.Quantity + quantity };
|
||||||
}
|
}
|
||||||
Notify();
|
Notify();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _client.AddNewUserCartAsync(new AddNewUserCartRequest
|
||||||
|
{
|
||||||
|
ProductId = product.Id,
|
||||||
|
Count = quantity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Best-effort sync with backend; keep local state on failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateQuantity(long productId, int quantity)
|
public async Task UpdateQuantity(long productId, int quantity)
|
||||||
{
|
{
|
||||||
var existing = _items.FirstOrDefault(i => i.ProductId == productId);
|
var existing = _items.FirstOrDefault(i => i.ProductId == productId);
|
||||||
if (existing is null) return;
|
if (existing is null) return;
|
||||||
@@ -42,20 +65,87 @@ public class CartService
|
|||||||
var idx = _items.IndexOf(existing);
|
var idx = _items.IndexOf(existing);
|
||||||
_items[idx] = existing with { Quantity = quantity };
|
_items[idx] = existing with { Quantity = quantity };
|
||||||
Notify();
|
Notify();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
|
||||||
|
{
|
||||||
|
UserCartId = productId,
|
||||||
|
Count = quantity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Best-effort sync with backend; keep local state on failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(long productId)
|
public async Task Remove(long productId)
|
||||||
{
|
{
|
||||||
_items.RemoveAll(i => i.ProductId == productId);
|
_items.RemoveAll(i => i.ProductId == productId);
|
||||||
Notify();
|
Notify();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Interpret Count=0 as remove from cart
|
||||||
|
await _client.UpdateUserCartAsync(new UpdateUserCartRequest
|
||||||
|
{
|
||||||
|
UserCartId = productId,
|
||||||
|
Count = 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Best-effort sync with backend; keep local state on failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public async Task Clear()
|
||||||
{
|
{
|
||||||
|
var productIds = _items.Select(i => i.ProductId).ToList();
|
||||||
_items.Clear();
|
_items.Clear();
|
||||||
Notify();
|
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 void Notify() => OnChange?.Invoke();
|
||||||
}
|
|
||||||
|
|
||||||
|
private async Task LoadFromServerAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _client.GetAllUserCartAsync(new Empty());
|
||||||
|
_items.Clear();
|
||||||
|
foreach (var m in response.Models)
|
||||||
|
{
|
||||||
|
var item = new CartItem(
|
||||||
|
ProductId: m.Id,
|
||||||
|
Title: m.Title ?? string.Empty,
|
||||||
|
ImageUrl: string.IsNullOrWhiteSpace(m.ImagePath) ? string.Empty : UrlUtility.DownloadUrl + m.ImagePath,
|
||||||
|
UnitPrice: m.Price,
|
||||||
|
Quantity: m.SaleCount > 0 ? m.SaleCount : 1);
|
||||||
|
_items.Add(item);
|
||||||
|
}
|
||||||
|
Notify();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If backend is unreachable or user is unauthenticated, fall back to local-only cart.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user