This commit is contained in:
@@ -1,15 +1,25 @@
|
||||
using LiteCharms.Features.MidrandBooks.AuthorBooks;
|
||||
using MidrandBookshop.Models;
|
||||
using LiteCharms.Features.MidrandBooks;
|
||||
using LiteCharms.Features.MidrandBooks.AuthorBooks;
|
||||
using LiteCharms.Features.MidrandBooks.Authors;
|
||||
using LiteCharms.Features.MidrandBooks.Categories;
|
||||
using LiteCharms.Features.MidrandBooks.Products;
|
||||
using LiteCharms.Features.MidrandBooks.Products.Models;
|
||||
using LiteCharms.Features.Models;
|
||||
|
||||
namespace MidrandBookshop.Components.Pages;
|
||||
|
||||
public partial class Home(BooksService booksService)
|
||||
public partial class Home : ComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
public string SharedSearchQuery { get; set; } = string.Empty;
|
||||
[Inject] private ProductService ProductService { get; set; } = default!;
|
||||
[Inject] private BooksService BooksService { get; set; } = default!;
|
||||
[Inject] private AuthorService AuthorService { get; set; } = default!;
|
||||
[Inject] private CategoryService CategoryService { get; set; } = default!;
|
||||
[Inject] private NavigationManager Navigation { get; set; } = default!;
|
||||
|
||||
[CascadingParameter] public string SharedSearchQuery { get; set; } = string.Empty;
|
||||
[SupplyParameterFromQuery] public long? AuthorId { get; set; }
|
||||
|
||||
public enum ViewMode { Grid, List }
|
||||
|
||||
private ViewMode CurrentViewMode = ViewMode.Grid;
|
||||
private string ActiveCategory = "All";
|
||||
private bool ShowExpandedCategories = false;
|
||||
@@ -17,88 +27,136 @@ public partial class Home(BooksService booksService)
|
||||
private string SelectedSortOption = "default";
|
||||
private string ActivePriceFilter = "all";
|
||||
private bool OnlyShowNew = false;
|
||||
private List<string> MainCategories = ["All", "Graphic Design", "Product Design", "Architecture"];
|
||||
private List<string> DynamicExtendedCategories = [];
|
||||
|
||||
private List<string> MainCategories { get; set; } = ["All"];
|
||||
private List<string> DynamicExtendedCategories { get; set; } = [];
|
||||
|
||||
private int ItemsPerPage = 12;
|
||||
private int VisibleCount = 12;
|
||||
private List<BookItem> BooksCollection = [];
|
||||
private IEnumerable<BookItem> FilteredData
|
||||
|
||||
private List<Product> ProductsCollection { get; set; } = [];
|
||||
|
||||
protected string? ActiveAuthorFilterName { get; private set; }
|
||||
|
||||
private Dictionary<long, decimal> ProductPriceCache { get; set; } = [];
|
||||
private Dictionary<long, string> ProductPrimaryCategoryCache { get; set; } = [];
|
||||
|
||||
private IEnumerable<Product> FilteredData
|
||||
{
|
||||
get
|
||||
{
|
||||
var data = BooksCollection.AsEnumerable();
|
||||
var data = ProductsCollection.AsEnumerable();
|
||||
|
||||
// Category filtering restricts rendering solely when checking the open catalog
|
||||
if (ActiveCategory != "All" && !AuthorId.HasValue)
|
||||
data = data.Where(p => ProductPrimaryCategoryCache.ContainsKey(p.Id) &&
|
||||
ProductPrimaryCategoryCache[p.Id] == ActiveCategory);
|
||||
|
||||
// Text matching is completely restricted from evaluating author metadata properties
|
||||
if (!string.IsNullOrWhiteSpace(SharedSearchQuery))
|
||||
{
|
||||
var q = SharedSearchQuery.Trim();
|
||||
data = data.Where(b =>
|
||||
b.Title.Contains(q, StringComparison.OrdinalIgnoreCase) ||
|
||||
b.Author.Contains(q, StringComparison.OrdinalIgnoreCase) ||
|
||||
b.Isbn.Contains(q, StringComparison.OrdinalIgnoreCase)
|
||||
);
|
||||
|
||||
data = data.Where(p => (p.Name ?? "").Contains(q, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (ActiveCategory != "All")
|
||||
{
|
||||
data = data.Where(b => b.Category.Equals(ActiveCategory, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (OnlyShowNew) { data = data.Where(b => b.IsNew); }
|
||||
|
||||
data = ActivePriceFilter switch
|
||||
{
|
||||
"under-500" => data.Where(b => b.Price < 500),
|
||||
"500-1000" => data.Where(b => b.Price >= 500 && b.Price <= 1000),
|
||||
"over-1000" => data.Where(b => b.Price > 1000),
|
||||
_ => data
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
private IEnumerable<BookItem> SortedAndFilteredBooks => SelectedSortOption switch
|
||||
|
||||
private IEnumerable<Product> PaginatedBooks => FilteredData.Take(VisibleCount);
|
||||
|
||||
private bool HasMoreItems => FilteredData.Count() > VisibleCount;
|
||||
|
||||
protected override async Task OnParametersSetAsync() => await LoadCatalogDataAsync();
|
||||
|
||||
private async Task LoadCatalogDataAsync()
|
||||
{
|
||||
"price-low" => FilteredData.OrderBy(b => b.Price),
|
||||
"price-high" => FilteredData.OrderByDescending(b => b.Price),
|
||||
"title-asc" => FilteredData.OrderBy(b => b.Title),
|
||||
_ => FilteredData
|
||||
};
|
||||
private IEnumerable<BookItem> PaginatedBooks => SortedAndFilteredBooks.Take(VisibleCount);
|
||||
private int TotalFilteredCount => FilteredData.Count();
|
||||
private bool HasMoreItems => VisibleCount < TotalFilteredCount;
|
||||
ProductsCollection.Clear();
|
||||
ProductPriceCache.Clear();
|
||||
ProductPrimaryCategoryCache.Clear();
|
||||
ActiveAuthorFilterName = null;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
var extraSourceCategories = new[] { "Fine Arts", "Science", "Photography", "Typography", "Interior Design", "Industrialism", "Fashion", "Curation Studies" };
|
||||
DynamicExtendedCategories.AddRange(extraSourceCategories);
|
||||
|
||||
// Updated mock items to supply long IDs matching your screenshot items
|
||||
BooksCollection.Add(new BookItem { Id = 1L, Title = "Letters from M/M (Paris)", Author = "M/M Paris", Price = 720, Category = "Graphic Design", IsNew = true, Isbn = "9782915173" });
|
||||
BooksCollection.Add(new BookItem { Id = 2L, Title = "Daan Paans: Floating Signifiers", Author = "Daan Paans", Price = 540, Category = "Product Design", IsNew = true, Isbn = "9789492051" });
|
||||
BooksCollection.Add(new BookItem { Id = 3L, Title = "Album Architectures, Maputo", Author = "Guedes Archive", Price = 350, Category = "Architecture", IsNew = true, Isbn = "9780620751" });
|
||||
|
||||
var designPrefixes = new[] { "Minimalist", "Monolithic", "Architectural", "Japanese", "Scandinavian" };
|
||||
var designNouns = new[] { "Structures", "Typologies", "Forms & Spaces", "Systems Matrix", "Graphic Ephemera" };
|
||||
var designers = new[] { "J. Morrison", "K. Fujita", "Studio Bouroullec", "Es Devlin", "Kenya Hara" };
|
||||
|
||||
var entireCategoryPool = MainCategories.Concat(DynamicExtendedCategories).Where(c => c != "All").ToArray();
|
||||
var random = new Random(42);
|
||||
|
||||
for (int i = 4; i <= 60; i++)
|
||||
// Pipeline A: Extract scoped books directly associated with an ID token parameter
|
||||
if (AuthorId.HasValue)
|
||||
{
|
||||
BooksCollection.Add(new BookItem
|
||||
var authorResult = await AuthorService.GetAuthorAsync(AuthorId.Value);
|
||||
|
||||
if (authorResult.IsSuccess && authorResult.Value != null)
|
||||
{
|
||||
Id = (long)i,
|
||||
Title = $"{designPrefixes[random.Next(designPrefixes.Length)]} {designNouns[random.Next(designNouns.Length)]} (Vol. {random.Next(1, 4)})",
|
||||
Author = designers[random.Next(designers.Length)],
|
||||
Price = random.Next(25, 135) * 10,
|
||||
Category = entireCategoryPool[random.Next(entireCategoryPool.Length)],
|
||||
IsNew = random.NextDouble() > 0.7,
|
||||
Isbn = $"978000000{i}"
|
||||
});
|
||||
var author = authorResult.Value;
|
||||
|
||||
ActiveAuthorFilterName = author.PublisherType == PublisherTypes.Company && !string.IsNullOrWhiteSpace(author.Company)
|
||||
? author.Company
|
||||
: $"{author.Name} {author.LastName}".Trim();
|
||||
}
|
||||
|
||||
var authorBooksResult = await BooksService.GetBooksByAuthorAsync(AuthorId.Value);
|
||||
|
||||
if (authorBooksResult.IsSuccess && authorBooksResult.Value != null)
|
||||
{
|
||||
foreach (var authorBook in authorBooksResult.Value)
|
||||
{
|
||||
if (authorBook.Product != null)
|
||||
{
|
||||
var product = authorBook.Product;
|
||||
|
||||
ProductsCollection.Add(product);
|
||||
|
||||
ProductPriceCache[product.Id] = product.Price?.Amount ?? 0m;
|
||||
|
||||
var categoryResult = await ProductService.GetProductCategoriesAsync(product.Id);
|
||||
|
||||
ProductPrimaryCategoryCache[product.Id] = (categoryResult.IsSuccess && categoryResult.Value.Length > 0)
|
||||
? (categoryResult.Value[0].Name ?? "General")
|
||||
: "General";
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Pipeline B: Safe structural fallback mapping utilizing the exact backend DateRange object setup
|
||||
var selectionRange = new DateRange
|
||||
{
|
||||
From = new DateOnly(2020, 1, 1),
|
||||
To = DateOnly.FromDateTime(DateTime.UtcNow.AddDays(1)),
|
||||
MaxRecords = 100
|
||||
};
|
||||
|
||||
var allProductsResult = await ProductService.GetProductsAsync(0, selectionRange);
|
||||
|
||||
if (allProductsResult.IsSuccess && allProductsResult.Value != null)
|
||||
{
|
||||
ProductsCollection = allProductsResult.Value.ToList();
|
||||
|
||||
foreach (var product in ProductsCollection)
|
||||
{
|
||||
var priceResult = await ProductService.GetProductPriceAsync(product.Id);
|
||||
ProductPriceCache[product.Id] = priceResult.IsSuccess ? priceResult.Value.Amount : 0m;
|
||||
|
||||
var categoryResult = await ProductService.GetProductCategoriesAsync(product.Id);
|
||||
ProductPrimaryCategoryCache[product.Id] = (categoryResult.IsSuccess && categoryResult.Value.Length > 0)
|
||||
? (categoryResult.Value[0].Name ?? "General")
|
||||
: "General";
|
||||
}
|
||||
}
|
||||
|
||||
var categoriesResult = await CategoryService.GetCategoriesAsync();
|
||||
|
||||
if (categoriesResult.IsSuccess && categoriesResult.Value != null)
|
||||
{
|
||||
var cleanNames = categoriesResult.Value
|
||||
.Select(c => c.Name)
|
||||
.Where(n => !string.IsNullOrEmpty(n)).Cast<string>()
|
||||
.ToList();
|
||||
|
||||
MainCategories = ["All", .. cleanNames.Take(3)];
|
||||
DynamicExtendedCategories = [.. cleanNames.Skip(3)];
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearAuthorFilter() => Navigation.NavigateTo("/");
|
||||
private void NavigateToProduct(long id) => Navigation.NavigateTo($"/product/{id}");
|
||||
private void SetViewMode(ViewMode targetMode) => CurrentViewMode = targetMode;
|
||||
private void SelectCategory(string categoryName) { ActiveCategory = categoryName; VisibleCount = ItemsPerPage; }
|
||||
@@ -109,4 +167,4 @@ public partial class Home(BooksService booksService)
|
||||
private void ToggleNewArrivalsOnly(ChangeEventArgs e) { OnlyShowNew = e.Value is bool b && b; VisibleCount = ItemsPerPage; }
|
||||
private void ResetFilters() { SelectedSortOption = "default"; ActivePriceFilter = "all"; OnlyShowNew = false; VisibleCount = ItemsPerPage; }
|
||||
private void LoadNextPage() { if (HasMoreItems) VisibleCount += ItemsPerPage; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user