Yuyuqt
add: small fixes, UI designing
ec76476
Raw
History Blame Contribute Delete
25.5 kB
@page "/"
@using LibraryManagement.Shared.Models
@using BlazorWebAssembly.Services
@using Microsoft.AspNetCore.Components.Authorization
@inject LibraryApiClient ApiClient
@inject NavigationManager Navigation
@inject AuthenticationStateProvider AuthStateProvider
<AuthorizeView>
<Authorized>
<!-- Authenticated Dashboard View -->
<div class="space-y-12 animate-fade">
<section>
<h1 class="text-4xl font-black tracking-tight mb-2 text-main">
Welcome back, <span class="text-primary">@context.User.Identity?.Name</span>
</h1>
<p class="text-lg text-muted">Here is what's happening with your library account today.</p>
</section>
<AuthorizeView Roles="Librarian" Context="LibrarianContext">
<Authorized>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-12">
<div class="bg-card p-6 flex flex-col gap-3 group border border-border rounded-3xl transition-all shadow-sm">
<div class="flex items-center justify-between">
<span class="text-[10px] font-black text-muted uppercase tracking-widest">Total Books</span>
</div>
<div class="text-3xl font-black text-main">@_totalBooks</div>
</div>
<div class="bg-card p-6 flex flex-col gap-3 group border border-border rounded-3xl transition-all shadow-sm">
<div class="flex items-center justify-between">
<span class="text-[10px] font-black text-muted uppercase tracking-widest">Active Loans</span>
</div>
<div class="text-3xl font-black text-main">@_activeLoans</div>
</div>
<div class="bg-card p-6 flex flex-col gap-3 group border border-border rounded-3xl transition-all shadow-sm border-l-4 border-l-red-500">
<div class="flex items-center justify-between">
<span class="text-[10px] font-black text-red-500 uppercase tracking-widest">Overdue</span>
</div>
<div class="text-3xl font-black text-red-600">@_overdueLoans</div>
</div>
<div class="bg-card p-6 flex flex-col gap-3 group border border-border rounded-3xl transition-all shadow-sm">
<div class="flex items-center justify-between">
<span class="text-[10px] font-black text-muted uppercase tracking-widest">Active Members</span>
</div>
<div class="text-3xl font-black text-main">@_totalMembers</div>
</div>
</div>
</Authorized>
</AuthorizeView>
<AuthorizeView Roles="Member" Context="MemberContext">
<Authorized>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- Loyalty Points -->
<div class="bg-card p-8 rounded-3xl border border-border shadow-sm hover:shadow-md transition-all">
<p class="text-[10px] font-black text-muted uppercase tracking-widest mb-4">Loyalty Balance</p>
<div class="flex items-baseline gap-2 mb-4">
<h3 class="text-5xl font-black text-main">@(_loyaltyAccount?.CurrentBalance.ToString("N0") ?? "0")</h3>
<span class="text-xs font-bold text-primary">POINTS</span>
</div>
<div class="flex items-center justify-between">
<span class="text-xs font-bold text-muted uppercase">Tier: @(_loyaltyAccount?.Tier ?? "Member")</span>
</div>
</div>
<!-- Active Loans -->
<div class="bg-card p-8 rounded-3xl border border-border shadow-sm hover:shadow-md transition-all">
<p class="text-[10px] font-black text-muted uppercase tracking-widest mb-4">Current Loans</p>
<h3 class="text-5xl font-black text-main mb-4">@_activeLoans</h3>
<div class="flex gap-2">
@if (_overdueLoans > 0)
{
<span class="px-3 py-1 bg-red-500/10 text-red-600 text-[10px] font-black uppercase rounded-lg">@_overdueLoans Overdue</span>
}
else
{
<span class="px-3 py-1 bg-green-500/10 text-green-600 text-[10px] font-black uppercase rounded-lg">Healthy</span>
}
</div>
</div>
<!-- Subscription -->
<div class="bg-card p-8 rounded-3xl border border-border shadow-sm hover:shadow-md transition-all">
<p class="text-[10px] font-black text-muted uppercase tracking-widest mb-4">Membership</p>
<h3 class="text-2xl font-black text-main mb-1 uppercase">@(_currentSub?.MembershipType ?? "Free")</h3>
<p class="text-xs text-muted mb-6">@( _currentSub != null ? "Active Status" : "No Active Plan" )</p>
<a href="/membership" class="text-xs font-black text-primary uppercase tracking-widest hover:underline">Manage Plan</a>
</div>
</div>
</Authorized>
</AuthorizeView>
<section>
<h2 class="text-xl font-bold mb-6 text-main">Recent Arrivals</h2>
@if (_recentBooks.Any())
{
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6">
@foreach (var book in _recentBooks)
{
<a href="/books" class="group cursor-pointer">
<div class="bg-card aspect-[2/3] rounded-2xl border border-border overflow-hidden relative mb-2 group-hover:border-primary/50 transition-colors">
@if (!string.IsNullOrEmpty(book.CoverUrl))
{
<img src="@book.CoverUrl" alt="@book.Title" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" />
}
else
{
<div class="absolute inset-0 flex items-center justify-center text-primary/20 group-hover:text-primary/40 transition-colors">
<svg class="w-12 h-12" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" 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>
<h3 class="text-xs font-bold text-main truncate">@book.Title</h3>
<p class="text-[10px] text-muted truncate">@book.Author</p>
</a>
}
</div>
}
else
{
<div class="text-muted text-sm italic">Loading recent arrivals...</div>
}
</section>
</div>
</Authorized>
<NotAuthorized>
<!-- Guest Landing Page -->
<div class="space-y-32 animate-fade pb-24">
<!-- Hero Section -->
<section class="relative pt-20 overflow-hidden">
<div class="max-w-7xl mx-auto px-8 relative z-10">
<div class="text-center">
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-primary/10 text-primary text-[10px] font-black uppercase tracking-widest mb-8 animate-bounce">
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
</span>
New: Audiobooks Collection Now Live
</div>
<h1 class="text-8xl font-black tracking-tighter text-main mb-8 leading-[0.9]">
Where Every Page <br />
<span class="text-transparent bg-clip-text bg-gradient-to-r from-primary to-emerald-500 italic">Finds a Purpose.</span>
</h1>
<p class="text-xl text-muted max-w-2xl mx-auto leading-relaxed mb-12">
Join the next generation of readers in a digital-first library experience. Access thousands of titles, earn loyalty rewards, and track your progress seamlessly.
</p>
<div class="flex flex-col sm:flex-row items-center justify-center gap-6">
<a href="/register" class="w-full sm:w-auto bg-primary text-white px-10 py-5 rounded-2xl font-black text-lg hover:bg-primary-hover shadow-2xl shadow-primary/30 transition-all transform hover:scale-105 active:scale-95 inline-block text-center">
Start Reading Now
</a>
<a href="/membership" class="w-full sm:w-auto bg-card border border-border text-main px-10 py-5 rounded-2xl font-black text-lg hover:bg-body transition-all inline-block text-center">
View Memberships
</a>
</div>
</div>
</div>
<!-- Background Decorative Elements -->
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-full h-full -z-10 pointer-events-none opacity-20">
<div class="absolute top-20 left-1/4 w-96 h-96 bg-primary/30 rounded-full blur-[120px]"></div>
<div class="absolute bottom-0 right-1/4 w-96 h-96 bg-emerald-500/20 rounded-full blur-[120px]"></div>
</div>
</section>
<!-- Stats Grid -->
<div class="max-w-7xl mx-auto px-8">
<div class="grid grid-cols-2 md:grid-cols-4 gap-8 bg-card border border-border p-12 rounded-[3rem] shadow-xl">
<div class="text-center">
<h3 class="text-5xl font-black text-main mb-2 tracking-tighter">12k+</h3>
<p class="text-muted font-bold uppercase tracking-widest text-[10px]">Verified Books</p>
</div>
<div class="text-center border-l border-border">
<h3 class="text-5xl font-black text-main mb-2 tracking-tighter">5.2k</h3>
<p class="text-muted font-bold uppercase tracking-widest text-[10px]">Active Readers</p>
</div>
<div class="text-center border-l border-border">
<h3 class="text-5xl font-black text-main mb-2 tracking-tighter">98%</h3>
<p class="text-muted font-bold uppercase tracking-widest text-[10px]">Satisfaction</p>
</div>
<div class="text-center border-l border-border">
<h3 class="text-5xl font-black text-main mb-2 tracking-tighter">24/7</h3>
<p class="text-muted font-bold uppercase tracking-widest text-[10px]">Digital Access</p>
</div>
</div>
</div>
<!-- Features Section -->
<section class="max-w-7xl mx-auto px-8">
<div class="text-center mb-20">
<h2 class="text-xs font-black text-primary uppercase tracking-[0.3em] mb-4">Core Ecosystem</h2>
<h3 class="text-5xl font-black text-main tracking-tight">Everything you need to <br/> master your reading journey.</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
<!-- Feature 1 -->
<div class="group bg-card p-10 rounded-[2.5rem] border border-border hover:border-primary/50 transition-all hover:shadow-2xl hover:shadow-primary/5">
<div class="w-14 h-14 bg-primary/10 rounded-2xl flex items-center justify-center text-primary mb-8 group-hover:scale-110 transition-transform">
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" 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>
<h4 class="text-xl font-bold text-main mb-4 uppercase tracking-tight">Borrow Thousands</h4>
<p class="text-muted text-sm leading-relaxed">Access our vast physical and digital catalog with a single click. No more waiting in lines.</p>
</div>
<!-- Feature 2 -->
<div class="group bg-card p-10 rounded-[2.5rem] border border-border hover:border-primary/50 transition-all hover:shadow-2xl hover:shadow-primary/5">
<div class="w-14 h-14 bg-primary/10 rounded-2xl flex items-center justify-center text-primary mb-8 group-hover:scale-110 transition-transform">
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
</div>
<h4 class="text-xl font-bold text-main mb-4 uppercase tracking-tight">Track Reading</h4>
<p class="text-muted text-sm leading-relaxed">Visualize your reading habits with detailed analytics and personal dashboards.</p>
</div>
<!-- Feature 3 -->
<div class="group bg-card p-10 rounded-[2.5rem] border border-border hover:border-primary/50 transition-all hover:shadow-2xl hover:shadow-primary/5">
<div class="w-14 h-14 bg-primary/10 rounded-2xl flex items-center justify-center text-primary mb-8 group-hover:scale-110 transition-transform">
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path></svg>
</div>
<h4 class="text-xl font-bold text-main mb-4 uppercase tracking-tight">Stay Notified</h4>
<p class="text-muted text-sm leading-relaxed">Never miss a due date with Firebase-powered instant renewal notifications.</p>
</div>
<!-- Feature 4 -->
<div class="group bg-card p-10 rounded-[2.5rem] border border-border hover:border-primary/50 transition-all hover:shadow-2xl hover:shadow-primary/5">
<div class="w-14 h-14 bg-primary/10 rounded-2xl flex items-center justify-center text-primary mb-8 group-hover:scale-110 transition-transform">
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7"></path></svg>
</div>
<h4 class="text-xl font-bold text-main mb-4 uppercase tracking-tight">Earn Loyalty</h4>
<p class="text-muted text-sm leading-relaxed">Collect points for every interaction and redeem them for premium membership tiers.</p>
</div>
</div>
</section>
<!-- Testimonials -->
<section class="bg-body border-y border-border py-32 relative overflow-hidden">
<div class="max-w-7xl mx-auto px-8 relative z-10">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-24 items-center">
<div>
<h2 class="text-xs font-black text-primary uppercase tracking-[0.3em] mb-4">Wall of Praise</h2>
<h3 class="text-6xl font-black text-main tracking-tighter mb-8 leading-tight">Loved by <br/> modern readers.</h3>
<p class="text-lg text-muted leading-relaxed mb-12">
Don't just take our word for it. Join thousands of satisfied members who have transformed their reading habits.
</p>
<div class="flex gap-4">
<div class="flex -space-x-4">
@for(int i=1; i<=4; i++) {
<div class="w-12 h-12 rounded-full border-4 border-card bg-primary/20 flex items-center justify-center text-xs font-bold">U@i</div>
}
</div>
<div class="flex flex-col justify-center">
<span class="text-sm font-black text-main uppercase">5,000+ Active Members</span>
<span class="text-[10px] text-muted font-bold uppercase tracking-widest">Global Community</span>
</div>
</div>
</div>
<div class="space-y-6">
<div class="bg-card p-10 rounded-[2.5rem] border border-border shadow-xl transform hover:-rotate-1 transition-transform">
<div class="flex gap-1 text-primary mb-6">
@for(int i=0; i<5; i++) { <svg class="w-4 h-4 fill-current" viewBox="0 0 20 20"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg> }
</div>
<p class="text-main font-medium italic mb-6 leading-relaxed">"The loyalty system is a game changer. I've earned three months of free premium just by returning my books on time!"</p>
<div class="flex items-center gap-4">
<div class="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center font-black text-xs text-primary">SC</div>
<div>
<h5 class="text-sm font-bold text-main">Sarah Chen</h5>
<p class="text-[10px] text-muted font-bold uppercase">Gold Member</p>
</div>
</div>
</div>
<div class="bg-card p-10 rounded-[2.5rem] border border-border shadow-xl transform translate-x-8 hover:rotate-1 transition-transform">
<p class="text-main font-medium italic mb-6 leading-relaxed">"Finally a library system that feels like it belongs in 2026. The interface is stunning and the notifications are so helpful."</p>
<div class="flex items-center gap-4">
<div class="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center font-black text-xs text-primary">JM</div>
<div>
<h5 class="text-sm font-bold text-main">James Miller</h5>
<p class="text-[10px] text-muted font-bold uppercase">Active Reader</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Final CTA -->
<section class="max-w-7xl mx-auto px-8">
<div class="bg-primary p-16 md:p-24 rounded-[4rem] text-center text-white relative overflow-hidden shadow-2xl shadow-primary/40">
<div class="relative z-10">
<h2 class="text-5xl md:text-7xl font-black tracking-tighter mb-8 leading-tight">Ready to start your <br/> next chapter?</h2>
<p class="text-xl text-white/80 max-w-2xl mx-auto mb-12 font-medium">Join thousands of readers today and get your first month of Premium for free.</p>
<div class="flex flex-col sm:flex-row items-center justify-center gap-6">
<a href="/register" class="w-full sm:w-auto bg-white text-primary px-12 py-6 rounded-2xl font-black text-xl hover:bg-white/90 transition-all transform hover:scale-105 shadow-xl">Create Free Account</a>
<a href="/contact" class="w-full sm:w-auto border-2 border-white/30 text-white px-12 py-6 rounded-2xl font-black text-xl hover:bg-white/10 transition-all">Talk to Librarian</a>
</div>
</div>
<!-- CTA Decoration -->
<div class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 w-[600px] h-[600px] bg-white/10 rounded-full blur-[100px]"></div>
</div>
</section>
</div>
</NotAuthorized>
</AuthorizeView>
@code {
private int _totalBooks = 0;
private int _activeLoans = 0;
private int _overdueLoans = 0;
private int _totalMembers = 0;
private IEnumerable<BookDto> _recentBooks = Enumerable.Empty<BookDto>();
private LoyaltyAccountDto? _loyaltyAccount;
private SubscriptionDto? _currentSub;
private bool _isLoading = true;
protected override async Task OnInitializedAsync()
{
try
{
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity?.IsAuthenticated == true)
{
var books = await ApiClient.GetBooksAsync();
_totalBooks = books.Count();
_recentBooks = books.Take(6);
if (user.IsInRole("Member"))
{
try {
var borrowings = await ApiClient.GetMyBorrowingsAsync();
_activeLoans = borrowings.Count(b => b.Status == "Borrowed");
_overdueLoans = borrowings.Count(b => b.Status == "Overdue" || (b.Status == "Borrowed" && b.DueDate < DateTime.UtcNow));
_loyaltyAccount = await ApiClient.GetMyLoyaltyAccountAsync();
_currentSub = await ApiClient.GetMySubscriptionAsync();
} catch {}
}
if (user.IsInRole("Librarian"))
{
try {
var allBorrowings = await ApiClient.GetAllBorrowingsAsync();
_activeLoans = allBorrowings.Count(b => b.Status == "Borrowed");
_overdueLoans = allBorrowings.Count(b => b.Status == "Overdue" || (b.Status == "Borrowed" && b.DueDate < DateTime.UtcNow));
var members = await ApiClient.GetUsersAsync();
_totalMembers = members.Count();
} catch {}
}
}
}
catch { }
finally
{
_isLoading = false;
}
}
}