Update product service and detail view with gallery support

This commit is contained in:
masoodafar-web
2025-11-28 09:10:22 +03:30
parent 7d545a2849
commit 82ddafda33
4 changed files with 279 additions and 38 deletions
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Components;
using FrontOffice.Main.Utilities;
namespace FrontOffice.Main.Pages.Store;
public partial class ProductDetail : ComponentBase
public partial class ProductDetail : ComponentBase, IDisposable
{
[Inject] private ProductService ProductService { get; set; } = default!;
[Inject] private CartService Cart { get; set; } = default!;
@@ -13,19 +16,151 @@ public partial class ProductDetail : ComponentBase
private Product? _product;
private bool _loading;
private int _qty = 1;
private const int MinQty = 1;
private const int MaxQty = 20;
private IReadOnlyList<ProductGalleryImage> _galleryItems = Array.Empty<ProductGalleryImage>();
private ProductGalleryImage? _selectedGalleryImage;
private long TotalPrice => (_product?.Price ?? 0) * _qty;
private bool HasDiscount => _product is { Discount: > 0 and < 100 };
private long? OriginalPrice => HasDiscount && _product is not null
? (long)Math.Round(_product.Price / (1 - (_product.Discount / 100m)))
: null;
private CartItem? CurrentCartItem => _product is null
? null
: Cart.Items.FirstOrDefault(i => i.ProductId == _product.Id);
private bool IsInCart => CurrentCartItem is not null;
private int CurrentCartQuantity => CurrentCartItem?.Quantity ?? 0;
protected override async Task OnParametersSetAsync()
{
_loading = true;
_product = await ProductService.GetByIdAsync(id);
_loading = false;
if (_product is not null)
{
_galleryItems = BuildGalleryItems(_product);
_selectedGalleryImage = _galleryItems.FirstOrDefault();
_qty = Math.Clamp(CurrentCartItem?.Quantity ?? _qty, MinQty, MaxQty);
}
else
{
_galleryItems = Array.Empty<ProductGalleryImage>();
}
StateHasChanged();
}
protected override void OnInitialized()
{
Cart.OnChange += HandleCartChanged;
}
private async Task AddToCart()
{
if (_product is null) return;
await Cart.Add(_product, _qty);
await Cart.Add(_product, 1);
}
private async Task RemoveFromCart()
{
if (_product is null) return;
_qty--;
await Cart.UpdateQuantity(CurrentCartItem.ProductId, _qty);
}
private void IncreaseLocalQty()
{
if (_qty < MaxQty)
{
_qty++;
}
}
private void DecreaseLocalQty()
{
if (_qty > MinQty)
{
_qty--;
}
}
// private async Task IncreaseCartQuantityAsync()
// {
// if (_product is null || CurrentCartItem is null) return;
// var target = Math.Min(CurrentCartItem.Quantity + 1, MaxQty);
// if (target != CurrentCartItem.Quantity)
// {
// await Cart.UpdateQuantity(_product.Id, target);
// }
// }
//
// private async Task RemoveFromCartAsync()
// {
// if (CurrentCartItem is null) return;
// await Cart.Remove(CurrentCartItem.cartId);
// }
private void HandleCartChanged()
{
if (_product is null) return;
_qty = Math.Clamp(CurrentCartItem?.Quantity ?? MinQty, MinQty, MaxQty);
InvokeAsync(StateHasChanged);
}
public void Dispose()
{
Cart.OnChange -= HandleCartChanged;
}
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
}
private static IReadOnlyList<ProductGalleryImage> BuildGalleryItems(Product product)
{
if (product.Gallery is { Count: > 0 })
{
var result = new List<ProductGalleryImage>();
result.Add(new ProductGalleryImage(0, 0, product.Title, product.ImageUrl, product.ImageUrl));
result.AddRange(product.Gallery.Where(x => !string.IsNullOrWhiteSpace(x.ImageUrl)).ToList());
return result;
}
if (!string.IsNullOrWhiteSpace(product.ImageUrl))
{
return new[]
{
new ProductGalleryImage(0, 0, product.Title, product.ImageUrl, product.ImageUrl)
};
}
return Array.Empty<ProductGalleryImage>();
}
private string MainImageUrl => _selectedGalleryImage?.ImageUrl
?? _galleryItems.FirstOrDefault()?.ImageUrl
?? _product?.ImageUrl
?? string.Empty;
private string MainImageAlt => _selectedGalleryImage?.Title ?? _product?.Title ?? "تصویر محصول";
private string GetThumbnailUrl(ProductGalleryImage item)
=> string.IsNullOrWhiteSpace(item.ThumbnailUrl) ? item.ImageUrl : item.ThumbnailUrl;
private string GetThumbnailBorder(ProductGalleryImage item)
=> item == _selectedGalleryImage ? "2px solid rgba(30, 136, 229, 0.85)" : "1px solid rgba(0,0,0,0.12)";
private string GetThumbnailShadow(ProductGalleryImage item)
=> item == _selectedGalleryImage ? "0 0 0 2px rgba(30, 136, 229, 0.25)" : "none";
private string GetThumbnailStyle(ProductGalleryImage item)
=>
$"width:76px;height:76px;object-fit:cover;border:{GetThumbnailBorder(item)};box-shadow:{GetThumbnailShadow(item)};border-radius:0.5rem;";
private void SelectGalleryImage(ProductGalleryImage item)
{
if (_selectedGalleryImage == item) return;
_selectedGalleryImage = item;
}
}