6db83ccd90
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 4m55s
- 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
241 lines
7.3 KiB
C#
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.
|
|
}
|
|
}
|
|
}
|