Spaces:
Sleeping
Sleeping
File size: 7,495 Bytes
6f32ae8 d8a7fc4 6f32ae8 d3d3b46 6f32ae8 d3d3b46 6f32ae8 d3d3b46 6f32ae8 d3d3b46 6f32ae8 d8a7fc4 6f32ae8 d8a7fc4 6f32ae8 d8a7fc4 6f32ae8 d8a7fc4 6f32ae8 d8a7fc4 6f32ae8 d8a7fc4 6f32ae8 d8a7fc4 6f32ae8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | @page "/borrowings"
@using LibraryManagement.Shared.Models
@using BlazorWebAssembly.Services
@inject LibraryApiClient ApiClient
@inject NavigationManager Navigation
@inject IJSRuntime JS
<div class="max-w-6xl mx-auto py-12 px-6">
<!-- Header -->
<div class="flex justify-between items-end mb-16">
<div>
<h1 class="text-5xl font-black tracking-tight text-main mb-4">My Loans</h1>
<p class="text-lg text-muted max-w-xl">
Keep track of your reading journey and manage your active borrowings.
</p>
</div>
<a href="/books" class="hidden md:block bg-body text-main border border-border px-8 py-3 rounded-xl font-bold hover:bg-card transition-all text-center">
Browse More
</a>
</div>
@if (_isLoading)
{
<div class="space-y-6">
@for (int i = 0; i < 3; i++)
{
<div class="animate-pulse bg-card rounded-2xl border border-border h-32 w-full"></div>
}
</div>
}
else if (!_borrowings.Any())
{
<div class="text-center py-24 bg-card rounded-[2.5rem] border border-dashed border-border shadow-sm">
<h3 class="text-2xl font-bold text-main mb-3">No active loans</h3>
<p class="text-muted mb-8">You haven't borrowed any books yet. Explore our collection to get started.</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="space-y-6">
@foreach (var loan in _borrowings)
{
<div class="bg-card rounded-2xl border border-border p-6 flex flex-col md:flex-row items-center gap-8 hover:border-primary/50 transition-all group">
<!-- Book Cover / Placeholder -->
<div class="w-20 h-28 bg-body rounded-lg overflow-hidden flex-shrink-0 border border-border flex items-center justify-center">
<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>
<!-- Details -->
<div class="flex-grow text-center md:text-left">
<h3 class="text-xl font-bold text-main mb-1 group-hover:text-primary transition-colors">@loan.BookTitle</h3>
<p class="text-sm text-muted font-medium mb-4 md:mb-0">ID: @loan.BookId</p>
</div>
<!-- Dates & Fines -->
<div class="grid grid-cols-2 md:grid-cols-3 gap-8 px-8 border-x border-border hidden lg:grid flex-grow">
<div>
<p class="text-[10px] font-black text-muted uppercase tracking-[0.2em] mb-1">Borrowed</p>
<p class="text-sm font-bold text-main">@loan.BorrowDate.ToString("MMM dd, yyyy")</p>
</div>
<div>
<p class="text-[10px] font-black text-muted uppercase tracking-[0.2em] mb-1">Due Date</p>
<p class="text-sm font-bold @(loan.DueDate < DateTime.UtcNow && loan.Status == "Borrowed" ? "text-red-500" : "text-main")">
@loan.DueDate.ToString("MMM dd, yyyy")
</p>
</div>
<div class="hidden md:block">
@if (loan.ReturnDate.HasValue)
{
<p class="text-[10px] font-black text-muted uppercase tracking-[0.2em] mb-1">Returned</p>
<p class="text-sm font-bold text-green-500">@loan.ReturnDate.Value.ToString("MMM dd, yyyy")</p>
}
else if (loan.FineAmount > 0)
{
<p class="text-[10px] font-black text-muted uppercase tracking-[0.2em] mb-1">Fine</p>
<p class="text-sm font-bold @(loan.IsFinePaid ? "text-muted line-through" : "text-red-500")">
MMK @loan.FineAmount.ToString("N0")
</p>
@if (loan.IsFinePaid)
{
<span class="text-[10px] text-green-500 font-black uppercase">Paid</span>
}
}
else
{
<p class="text-[10px] font-black text-muted uppercase tracking-[0.2em] mb-1">Fine</p>
<p class="text-sm font-bold text-muted">No Fines</p>
}
</div>
</div>
<!-- Status & Action -->
<div class="flex flex-col md:items-end gap-3 min-w-[150px]">
<span class='px-4 py-1.5 rounded-full text-[10px] font-black uppercase tracking-widest text-center @GetStatusColor(loan.Status)'>
@(loan.Status == "PendingReturn" ? "Pending" : loan.Status)
</span>
@if (loan.Status == "Borrowed")
{
<button @onclick="() => ReturnRequestAsync(loan)" class="text-xs font-bold text-primary hover:underline">
Request Return
</button>
}
</div>
</div>
}
</div>
}
</div>
@code {
private bool _isLoading = true;
private List<BorrowingDto> _borrowings = new();
protected override async Task OnInitializedAsync()
{
await LoadData();
}
private async Task LoadData()
{
_isLoading = true;
try
{
_borrowings = (await ApiClient.GetMyBorrowingsAsync()).ToList();
}
catch { }
finally { _isLoading = false; }
}
private async Task ReturnRequestAsync(BorrowingDto loan)
{
bool confirmed = await JS.InvokeAsync<bool>("confirm", $"Request return for \"{loan.BookTitle}\"? It will be marked as pending until approved by a librarian.");
if (!confirmed) return;
var success = await ApiClient.RequestReturnAsync(loan.Id);
if (success)
{
await JS.InvokeVoidAsync("alert", "Return request submitted successfully!");
await LoadData();
}
else
{
await JS.InvokeVoidAsync("alert", "Failed to submit return request.");
}
}
private string GetStatusColor(string status) => status switch
{
"Borrowed" => "bg-blue-500/10 text-blue-500",
"Returned" => "bg-green-500/10 text-green-500",
"PendingReturn" => "bg-orange-500/10 text-orange-500",
_ => "bg-body text-muted"
};
}
|