@page "/books" @using LibraryManagement.Shared.Models @using BlazorWebAssembly.Services @using Microsoft.AspNetCore.Authorization @inject LibraryApiClient ApiClient @inject WishlistService WishlistService @inject IJSRuntime JS @inject NavigationManager Navigation
@if (_viewMode == ViewMode.List) {

Book Collection

Discover your next favorite story among our curated selection. (@FilteredBooks.Count() total)

@if (_isCategoryDropdownOpen) {
@foreach (var cat in _categories) { }
}
@if (_isLoading) {
@for (int i = 0; i < 8; i++) {
}
} else if (!FilteredBooks.Any()) {

No results found

Try adjusting your search or filters.

} else {
@foreach (var book in PaginatedBooks) {
@if (!string.IsNullOrEmpty(book.CoverUrl)) { @book.Title } else {
No Cover
} @if (book.AvailableCopies <= 0) {
Out of Stock
}

@book.Title

@book.Author

@foreach (var cat in book.Categories.Take(2)) { @cat.Name }
@book.Status @book.AvailableCopies / @book.TotalCopies
@if (book.AvailableCopies == 0) { }
}
@if (TotalPages > 1) {
@for (int i = 1; i <= TotalPages; i++) { var pageNumber = i; }
} } } else if (_viewMode == ViewMode.Details && _selectedBook != null) {
@if (!string.IsNullOrWhiteSpace(_selectedBook.CoverUrl)) { @_selectedBook.Title } else { }
@_selectedBook.Status

@_selectedBook.Title

by @_selectedBook.Author

Description

@(string.IsNullOrEmpty(_selectedBook.Description) ? "No description available for this book." : _selectedBook.Description)

ISBN

@_selectedBook.Isbn

Availability

@(_selectedBook.AvailableCopies > 0 ? $"{_selectedBook.AvailableCopies} / {_selectedBook.TotalCopies} units in stock" : "Currently occupied")

@if (_selectedBook.AvailableCopies > 0) { } else {
}
Sign In to Borrow
} else if (_viewMode == ViewMode.Create || _viewMode == ViewMode.Edit) {

@(_viewMode == ViewMode.Create ? "Add New Book" : "Edit Book")

Fill in the details below to @(_viewMode == ViewMode.Create ? "add a new book to" : "update the") library catalog.

@foreach (var cat in _categories) { }
}
@code { private enum ViewMode { List, Details, Create, Edit } private ViewMode _viewMode = ViewMode.List; private bool _isLoading = true; private bool _isSubmitting = false; private string _searchQuery = ""; private int? _selectedCategoryId; private string _activeQuickFilter = "All"; // Pagination private int _currentPage = 1; private int _pageSize = 12; private IEnumerable _books = Enumerable.Empty(); private IEnumerable _categories = Enumerable.Empty(); private IEnumerable _myBorrowings = Enumerable.Empty(); private IEnumerable _allBorrowings = Enumerable.Empty(); private BookDto? _selectedBook; // Form Model private BookFormModel _formModel = new(); private bool _isCategoryDropdownOpen = false; private string SelectedCategoryName => _categories.FirstOrDefault(c => c.Id == _selectedCategoryId)?.Name ?? ""; protected override async Task OnInitializedAsync() { await LoadDataAsync(); } private void ToggleCategoryDropdown() => _isCategoryDropdownOpen = !_isCategoryDropdownOpen; private async Task LoadDataAsync() { _isLoading = true; try { _books = await ApiClient.GetBooksAsync(); _categories = await ApiClient.GetCategoriesAsync(); // Fetch borrowings for Trending and Recommended _myBorrowings = await ApiClient.GetMyBorrowingsAsync(); try { _allBorrowings = await ApiClient.GetAllBorrowingsAsync(); } catch { } _currentPage = 1; } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } finally { _isLoading = false; } } private IEnumerable FilteredBooks { get { var books = _books; // Apply Search if (!string.IsNullOrWhiteSpace(_searchQuery)) { books = books.Where(b => b.Title.Contains(_searchQuery, StringComparison.OrdinalIgnoreCase) || b.Author.Contains(_searchQuery, StringComparison.OrdinalIgnoreCase) || b.Isbn.Contains(_searchQuery, StringComparison.OrdinalIgnoreCase)); } // Apply Category Filter if (_selectedCategoryId != null) { books = books.Where(b => b.Categories.Any(c => c.Id == _selectedCategoryId)); } // Apply Quick Filter switch (_activeQuickFilter) { case "New Arrivals": books = books.Where(b => b.CreatedAt >= DateTime.UtcNow.AddDays(-7)); break; case "Trending": var trendingIds = _allBorrowings .Where(br => br.BorrowDate >= DateTime.UtcNow.AddDays(-7)) .GroupBy(br => br.BookId) .OrderByDescending(g => g.Count()) .Select(g => g.Key) .Take(12) .ToList(); if (trendingIds.Any()) books = books.Where(b => trendingIds.Contains(b.Id)); else books = Enumerable.Empty(); // Or show nothing if no data break; case "Recommended": var myCategoryIds = _myBorrowings.SelectMany(br => _books.FirstOrDefault(b => b.Id == br.BookId)?.Categories.Select(c => c.Id) ?? Enumerable.Empty()) .Concat(_favourites.SelectMany(b => b.Categories.Select(c => c.Id))) .Concat(_wishlist.SelectMany(b => b.Categories.Select(c => c.Id))) .Distinct() .ToList(); if (myCategoryIds.Any()) books = books.Where(b => b.Categories.Any(c => myCategoryIds.Contains(c.Id))).Take(8); break; } return books; } } private IEnumerable PaginatedBooks => FilteredBooks .Skip((_currentPage - 1) * _pageSize) .Take(_pageSize); private int TotalPages => (int)Math.Ceiling(FilteredBooks.Count() / (double)_pageSize); private void SetQuickFilter(string filter) { _activeQuickFilter = filter; _selectedCategoryId = null; // Clear category when switching quick filters _currentPage = 1; } private void FilterByCategory(int? id) { _selectedCategoryId = id; _currentPage = 1; } private void ShowList() { _viewMode = ViewMode.List; _selectedBook = null; } private async Task ShowDetails(int id) { _isLoading = true; _selectedBook = await ApiClient.GetBookAsync(id); _viewMode = ViewMode.Details; _isLoading = false; } private void ShowCreate() { _formModel = new BookFormModel(); _viewMode = ViewMode.Create; } private async Task ShowEdit(int id) { _isLoading = true; var book = await ApiClient.GetBookAsync(id); if (book != null) { _selectedBook = book; _formModel = new BookFormModel { Id = book.Id, Title = book.Title, Author = book.Author, Isbn = book.Isbn, Description = book.Description, TotalCopies = book.TotalCopies, CoverUrl = book.CoverUrl, CategoryIds = book.Categories.Select(c => c.Id).ToList() }; _viewMode = ViewMode.Edit; } _isLoading = false; } private void ToggleCategory(int id, bool isChecked) { if (isChecked && !_formModel.CategoryIds.Contains(id)) _formModel.CategoryIds.Add(id); else if (!isChecked) _formModel.CategoryIds.Remove(id); } private List _wishlist = new(); private List _favourites = new(); protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await LoadCollectionsAsync(); StateHasChanged(); } } private async Task LoadCollectionsAsync() { _wishlist = await WishlistService.GetWishlistAsync(); _favourites = await WishlistService.GetFavouritesAsync(); } private bool IsFavourite(int id) => _favourites.Any(b => b.Id == id); private bool IsInWishlist(int id) => _wishlist.Any(b => b.Id == id); private async Task ToggleFavourite(BookDto book) { if (IsFavourite(book.Id)) { await WishlistService.RemoveFromFavouriteAsync(book.Id); await JS.InvokeVoidAsync("alert", $"Removed \"{book.Title}\" from favourites"); } else { await WishlistService.AddToFavouritesAsync(book); await JS.InvokeVoidAsync("alert", $"Added \"{book.Title}\" to favourites!"); } await LoadCollectionsAsync(); } private async Task ToggleWishlist(BookDto book) { if (IsInWishlist(book.Id)) { await WishlistService.RemoveFromWishlistAsync(book.Id); await JS.InvokeVoidAsync("alert", $"Removed \"{book.Title}\" from wishlist"); } else { await WishlistService.AddToWishlistAsync(book); await JS.InvokeVoidAsync("alert", $"Added \"{book.Title}\" to wishlist!"); } await LoadCollectionsAsync(); } private async Task HandleSubmit() { _isSubmitting = true; try { if (_viewMode == ViewMode.Create) { var request = new BookCreateRequest { Title = _formModel.Title, Author = _formModel.Author, Isbn = _formModel.Isbn, Description = _formModel.Description, TotalCopies = _formModel.TotalCopies, CoverUrl = _formModel.CoverUrl, CategoryIds = _formModel.CategoryIds }; var result = await ApiClient.CreateBookAsync(request); if (result != null) { await JS.InvokeVoidAsync("alert", "Book added successfully!"); await LoadDataAsync(); ShowList(); } } else { var request = new BookUpdateRequest { Title = _formModel.Title, Author = _formModel.Author, Description = _formModel.Description, TotalCopies = _formModel.TotalCopies, CoverUrl = _formModel.CoverUrl, CategoryIds = _formModel.CategoryIds }; var result = await ApiClient.UpdateBookAsync(_formModel.Id, request); if (result != null) { await JS.InvokeVoidAsync("alert", "Book updated successfully!"); await LoadDataAsync(); ShowList(); } } } catch (Exception ex) { await JS.InvokeVoidAsync("alert", $"Error: {ex.Message}"); } finally { _isSubmitting = false; } } private async Task DeleteAsync(int id) { bool confirmed = await JS.InvokeAsync("confirm", "Are you sure you want to remove this book from the catalog?"); if (!confirmed) return; var success = await ApiClient.DeleteBookAsync(id); if (success) { await JS.InvokeVoidAsync("alert", "Book deleted successfully"); await LoadDataAsync(); ShowList(); } } private async Task BorrowAsync(int id) { var result = await ApiClient.BorrowBookAsync(new BorrowRequest { BookId = id }); if (result != null) { await JS.InvokeVoidAsync("alert", "Book borrowed successfully! Check 'My Loans' for details."); await LoadDataAsync(); await ShowDetails(id); } else { await JS.InvokeVoidAsync("alert", "Failed to borrow book. Check your loan limit."); } } private class BookFormModel { public int Id { get; set; } public string Title { get; set; } = ""; public string Author { get; set; } = ""; public string Isbn { get; set; } = ""; public string? Description { get; set; } public int TotalCopies { get; set; } = 1; public string? CoverUrl { get; set; } public List CategoryIds { get; set; } = new(); } }