@using System.Globalization @using System.Linq @using Prefab.Web.Client.Models.Shared @if (Product.IsOnSale) { Sale } @foreach (var badge in Product.Badges.Where(static badge => !string.IsNullOrWhiteSpace(badge))) { @badge } @BadgesContent?.Invoke(Product) @if (!string.IsNullOrWhiteSpace(Product.CategoryName)) { if (!string.IsNullOrWhiteSpace(Product.CategoryUrl)) { @Product.CategoryName } else { @Product.CategoryName } } @Product.Title @if (Product.ReviewCount > 0) { @GetRatingTitle() } @foreach (var star in Enumerable.Range(1, TotalStars)) { var isActive = star <= Math.Clamp(Product.Rating, 0, TotalStars); } @DescriptionContent?.Invoke(Product) @code { private static readonly CultureInfo PriceCulture = CultureInfo.CurrentCulture; private const string PlaceholderImageDataUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA0MDAgNDAwJz48cmVjdCB3aWR0aD0nNDAwJyBoZWlnaHQ9JzQwMCcgZmlsbD0nI2YzZjNmMycvPjx0ZXh0IHg9JzUwJScgeT0nNTAlJyBkb21pbmFudC1iYXNlbGluZT0nbWlkZGxlJyB0ZXh0LWFuY2hvcj0nbWlkZGxlJyBmb250LWZhbWlseT0nT3BlbiBTYW5zLCBBcmlhbCwgc2Fucy1zZXJpZicgZm9udC1zaXplPScyOCcgZmlsbD0nIzliOWI5Yic+SW1hZ2UgY29taW5nIHNvb248L3RleHQ+PC9zdmc+"; [Parameter, EditorRequired] public ProductCardModel Product { get; set; } = default!; [Parameter] public string Layout { get; set; } = "grid"; [Parameter] public string CssClass { get; set; } = string.Empty; [Parameter] public string ImagePlaceholderText { get; set; } = "Image coming soon"; [Parameter] public RenderFragment? BadgesContent { get; set; } [Parameter] public RenderFragment? DescriptionContent { get; set; } [Parameter] public bool ShowPrice { get; set; } private bool HasBadges => Product.IsOnSale || Product.Badges.Any(static badge => !string.IsNullOrWhiteSpace(badge)); private bool ShouldRenderRating => Product.Rating > 0 || Product.ReviewCount > 0; private bool ShowBadgesSection => HasBadges || BadgesContent is not null; private bool ShowMetaSection => !string.IsNullOrWhiteSpace(Product.CategoryName); private bool ShowDescriptionSection => DescriptionContent is not null; private RenderFragment? PricesTemplate => !ShowPrice || !Product.HasPrice ? null : @ @FormatMoney(Product.FromPrice!) @if (Product.OldPrice.HasValue) { @FormatDecimal(Product.OldPrice.Value) } ; private string RatingAriaLabel => Product.Rating > 0 ? $"{Math.Clamp(Product.Rating, 0, TotalStars)} out of {TotalStars}" : "No rating yet"; private IReadOnlyDictionary? RootAttributes => string.IsNullOrWhiteSpace(Product.Sku) ? null : new Dictionary { ["data-sku"] = Product.Sku }; private string FormatMoney(MoneyModel money) => money.Amount.ToString("C", PriceCulture); private string FormatDecimal(decimal value) => value.ToString("C", PriceCulture); private string GetRatingTitle() => $"Reviews ({Math.Max(Product.ReviewCount, 0)})"; private string ImageSource => string.IsNullOrWhiteSpace(Product.PrimaryImageUrl) ? PlaceholderImageDataUrl : Product.PrimaryImageUrl!; private string ImageAltText => string.IsNullOrWhiteSpace(Product.PrimaryImageUrl) ? ImagePlaceholderText : Product.Title; private const int TotalStars = 5; }