Files
midrandbooks/MidrandBookshop/Components/BookCard.razor
T
2026-07-18 12:08:14 +02:00

81 lines
3.8 KiB
Plaintext

@namespace MidrandBooks.Components
@using Microsoft.AspNetCore.Components
@using MidrandBooks.Models
<div id="book-card-@Book.Id" class="group relative flex flex-col justify-between rounded-2xl border border-slate-200/60 bg-white p-3 shadow-sm transition-all hover:shadow-md hover:-translate-y-0.5">
<!-- Cover Artwork Block -->
<div class="relative aspect-[3/4] rounded-xl overflow-hidden shadow-sm bg-slate-50 cursor-pointer" @onclick="HandleSelect">
@if (!string.IsNullOrEmpty(Book.CoverUrl))
{
<img src="@Book.CoverUrl"
alt="@Book.Title"
referrerpolicy="no-referrer"
class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" />
}
else
{
<div class="h-full w-full bg-gradient-to-br @(Book.CoverColor) p-4 flex flex-col justify-between text-white transition-transform duration-500 group-hover:scale-105">
<div class="absolute top-0 right-0 h-16 w-16 bg-white/5 rounded-full blur-sm pointer-events-none" />
<span class="text-[8px] font-mono font-bold tracking-widest block uppercase text-white/40">@Book.Category</span>
<span class="text-sm font-serif font-extrabold line-clamp-3 leading-tight text-white">@Book.Title</span>
<span class="text-[9px] font-sans text-white/70 truncate block font-semibold">By @Book.Author</span>
</div>
}
<!-- Book Spine Shadow -->
<div class="absolute top-0 bottom-0 left-0 w-2 bg-gradient-to-r from-black/25 to-transparent" />
<!-- Formats and Badges -->
<div class="absolute top-2 right-2 flex flex-col gap-1 items-end z-10">
@if (Book.Bestseller)
{
<span class="px-2 py-0.5 rounded-full text-[8px] font-mono font-extrabold uppercase bg-amber-500 text-slate-900 border border-amber-400 shadow-sm">
Bestseller
</span>
}
@if (Book.Trending)
{
<span class="px-2 py-0.5 rounded-full text-[8px] font-mono font-extrabold uppercase bg-violet-600 text-white border border-violet-500 shadow-sm">
Trending
</span>
}
</div>
</div>
<!-- Info Block -->
<div class="mt-3 flex-1 flex flex-col justify-between space-y-2">
<div>
<span class="text-[9px] font-mono font-bold text-violet-600 uppercase tracking-wider block">@Book.Category</span>
<h4 class="font-serif text-sm font-bold text-slate-800 leading-tight line-clamp-1 mt-0.5 hover:text-violet-600 cursor-pointer" @onclick="HandleSelect">
@Book.Title
</h4>
<span class="text-[10px] text-slate-500 font-medium">@Book.Author</span>
</div>
<div class="flex items-center justify-between border-t border-slate-100 pt-2">
<div>
<span class="text-[8px] font-mono font-bold text-slate-400 block uppercase">South African Price</span>
<span class="font-mono text-xs font-bold text-slate-900">R @Book.Price.ToString("F2")</span>
</div>
<div class="flex items-center gap-1.5">
<span class="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded bg-amber-50 text-amber-700 text-[10px] font-mono font-bold border border-amber-200/50">
★ @Book.Rating.ToString("F1")
</span>
<span class="text-[9px] font-mono text-slate-400">(@Book.ReviewsCount)</span>
</div>
</div>
</div>
</div>
@code {
[Parameter] public BookModel Book { get; set; } = new();
[Parameter] public EventCallback<BookModel> OnSelectBook { get; set; }
private async Task HandleSelect()
{
await OnSelectBook.InvokeAsync(Book);
}
}