Spaces:
Sleeping
Sleeping
| @page "/wishlist" | |
| @using LibraryManagement.Shared.Models | |
| @using BlazorWebAssembly.Services | |
| @inject LibraryApiClient ApiClient | |
| @inject WishlistService WishlistService | |
| @inject NavigationManager Navigation | |
| @inject IJSRuntime JS | |
| <div class="max-w-6xl mx-auto py-12 px-6"> | |
| <!-- Header --> | |
| <div class="text-center mb-16"> | |
| <h1 class="text-5xl font-black tracking-tight text-main mb-4">My Wishlist</h1> | |
| <p class="text-lg text-muted max-w-xl mx-auto"> | |
| A curated collection of books you're waiting for. We'll notify you as soon as they're back on the shelf. | |
| </p> | |
| </div> | |
| @if (_isLoading) | |
| { | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> | |
| @for (int i = 0; i < 3; i++) | |
| { | |
| <div class="animate-pulse bg-card rounded-2xl border border-border h-48"></div> | |
| } | |
| </div> | |
| } | |
| else if (!_wishlistItems.Any()) | |
| { | |
| <div class="text-center py-24 bg-card rounded-[2.5rem] border border-dashed border-border"> | |
| <h3 class="text-2xl font-bold text-main mb-4">Your wishlist is empty</h3> | |
| <p class="text-muted mb-8">Found a book that's currently borrowed? Add it to your wishlist!</p> | |
| <a href="/books" class="bg-primary text-white px-10 py-4 rounded-xl font-black hover:bg-primary-hover transition-all shadow-lg shadow-primary/20 inline-block text-center"> | |
| Go to Catalog | |
| </a> | |
| </div> | |
| } | |
| else | |
| { | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> | |
| @foreach (var book in _wishlistItems) | |
| { | |
| <div class="bg-card rounded-2xl border border-border p-6 flex items-center gap-6 group hover:border-primary transition-all"> | |
| <div class="w-20 h-28 bg-body rounded-lg overflow-hidden flex-shrink-0 border border-border flex items-center justify-center"> | |
| @if (!string.IsNullOrEmpty(book.CoverUrl)) | |
| { | |
| <img src="@book.CoverUrl" alt="@book.Title" class="w-full h-full object-cover" /> | |
| } | |
| else | |
| { | |
| <svg class="w-8 h-8 text-muted opacity-20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18 18.246 18.477 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg> | |
| } | |
| </div> | |
| <div class="flex-grow"> | |
| <h3 class="font-bold text-main line-clamp-1 group-hover:text-primary transition-colors">@book.Title</h3> | |
| <p class="text-xs text-muted mb-4 font-medium">@book.Author</p> | |
| <div class="flex items-center justify-between mt-auto"> | |
| <span class='px-2 py-0.5 rounded text-[9px] font-black uppercase tracking-widest @(book.AvailableCopies > 0 ? "bg-green-500/10 text-green-600" : "bg-orange-500/10 text-orange-600")'> | |
| @(book.AvailableCopies > 0 ? "Available" : "Waiting") | |
| </span> | |
| <button @onclick="() => RemoveFromWishlistAsync(book.Id)" class="text-xs font-bold text-red-400 hover:text-red-500 transition-colors">Remove</button> | |
| </div> | |
| </div> | |
| </div> | |
| } | |
| </div> | |
| } | |
| </div> | |
| @code { | |
| private bool _isLoading = true; | |
| private List<BookDto> _wishlistItems = new(); | |
| protected override async Task OnInitializedAsync() | |
| { | |
| await LoadData(); | |
| } | |
| private async Task LoadData() | |
| { | |
| _isLoading = true; | |
| try | |
| { | |
| _wishlistItems = await WishlistService.GetWishlistAsync(); | |
| } | |
| catch { } | |
| finally | |
| { | |
| _isLoading = false; | |
| } | |
| } | |
| private async Task RemoveFromWishlistAsync(int bookId) | |
| { | |
| await WishlistService.RemoveFromWishlistAsync(bookId); | |
| await LoadData(); | |
| await JS.InvokeVoidAsync("alert", "Removed from wishlist"); | |
| } | |
| } | |