File size: 10,809 Bytes
cf599e0
b866d20
4433399
b866d20
 
cf599e0
b866d20
cf599e0
b866d20
6aad87c
 
cf599e0
 
b866d20
 
 
6aad87c
b866d20
 
 
 
 
 
 
 
6aad87c
 
 
 
 
 
b866d20
 
6aad87c
b866d20
 
 
6aad87c
b866d20
 
 
 
46cf1b2
b866d20
6aad87c
b866d20
 
6aad87c
b866d20
 
 
6aad87c
 
b866d20
 
 
 
 
6aad87c
 
b866d20
 
6aad87c
b866d20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6aad87c
b866d20
 
 
 
 
 
 
 
 
 
6aad87c
b866d20
 
 
 
 
 
6aad87c
b866d20
 
 
 
 
6aad87c
b866d20
 
 
 
 
 
 
 
 
 
 
46cf1b2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b866d20
 
 
 
 
 
46cf1b2
 
 
 
 
 
 
 
 
 
b866d20
 
 
 
 
 
 
 
 
 
 
 
46cf1b2
b866d20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4433399
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
@page "/Borrowings"
@using BlazorFrontend.Services
@using LibraryManagement.Shared.Models
@inject LibraryApiClient ApiClient
@inject IJSRuntime JS

<PageTitle>My Loans</PageTitle>

<div class="mb-8 animate-in fade-in slide-in-from-top-4 duration-500">
    <h1 class="text-3xl font-bold tracking-tight text-mystic-900">My Loans</h1>
    <p class="mt-2 text-mystic-500">Track your current borrowings and return upcoming books.</p>
</div>

@if (_isLoading)
{
    <div class="py-20 flex justify-center">
        <div class="animate-spin rounded-full h-10 w-10 border-b-2 border-mystic-900"></div>
    </div>
}
else
{
    <div class="card overflow-hidden animate-in fade-in slide-in-from-bottom-4 duration-700">
        <div class="overflow-x-auto">
            <table class="w-full text-left border-collapse">
                <thead>
                    <tr class="bg-mystic-50/50 border-b border-mystic-100">
                        <th class="px-6 py-4 text-[10px] font-bold text-mystic-400 uppercase tracking-widest">Book Information</th>
                        <th class="px-6 py-4 text-[10px] font-bold text-mystic-400 uppercase tracking-widest">Dates</th>
                        <th class="px-6 py-4 text-[10px] font-bold text-mystic-400 uppercase tracking-widest">Status</th>
                        <th class="px-6 py-4 text-[10px] font-bold text-mystic-400 uppercase tracking-widest">Fines</th>
                        <th class="px-6 py-4 text-[10px] font-bold text-mystic-400 uppercase tracking-widest text-right">Actions</th>
                    </tr>
                </thead>
                <tbody class="divide-y divide-mystic-100">
                    @if (!_borrowings.Any())
                    {
                        <tr>
                            <td colspan="5" class="px-6 py-12 text-center text-mystic-400 italic">
                                You don't have any borrowing history yet.
                            </td>
                        </tr>
                    }
                    @foreach (var loan in PaginatedBorrowings)
                    {
                        <tr class="hover:bg-mystic-50 transition-colors group">
                            <td class="px-6 py-4">
                                <div class="flex items-center gap-3">
                                    <div class="w-10 h-14 bg-mystic-100 rounded flex items-center justify-center text-mystic-300 group-hover:bg-mystic-200 transition-colors">
                                        <svg class="w-5 h-5" 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="min-w-0">
                                        <p class="text-sm font-bold text-mystic-900 truncate">@loan.BookTitle</p>
                                        <p class="text-[11px] text-mystic-500 mt-0.5">Loan ID: #@loan.Id</p>
                                    </div>
                                </div>
                            </td>
                            <td class="px-6 py-4">
                                <div class="space-y-1">
                                    <p class="text-[11px]"><span class="text-mystic-400">Borrowed:</span> <span class="font-medium text-mystic-700">@loan.BorrowDate.ToString("MMM dd, yyyy")</span></p>
                                    <p class="text-[11px]"><span class="text-mystic-400">Due:</span> <span class="font-medium @(loan.DueDate < DateTime.Now && loan.Status == "Borrowed" ? "text-rose-600" : "text-mystic-700")">@loan.DueDate.ToString("MMM dd, yyyy")</span></p>
                                    @if (loan.ReturnDate.HasValue)
                                    {
                                        <p class="text-[11px]"><span class="text-mystic-400">Returned:</span> <span class="font-medium text-emerald-600">@loan.ReturnDate.Value.ToString("MMM dd, yyyy")</span></p>
                                    }
                                </div>
                            </td>
                            <td class="px-6 py-4">
                                <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium 
                                    @(loan.Status == "Borrowed" ? "bg-amber-100 text-amber-700" : 
                                      loan.Status == "PendingReturn" ? "bg-blue-100 text-blue-700" :
                                      "bg-emerald-100 text-emerald-700")">
                                    @(loan.Status == "PendingReturn" ? "Pending Approval" : loan.Status)
                                </span>
                            </td>
                            <td class="px-6 py-4">
                                @if (loan.FineAmount > 0)
                                {
                                    <div class="flex flex-col">
                                        <span class="text-xs font-bold @(loan.IsFinePaid ? "text-mystic-500 line-through" : "text-rose-600")">
                                            MMK @loan.FineAmount.ToString("N0")
                                        </span>
                                        @if (loan.IsFinePaid)
                                        {
                                            <span class="text-[10px] text-emerald-600 font-medium">Paid</span>
                                        }
                                    </div>
                                }
                                else
                                {
                                    <span class="text-[11px] text-mystic-400">No Fines</span>
                                }
                            </td>
                            <td class="px-6 py-4 text-right">
                                @if (loan.Status == "Borrowed")
                                {
                                    <button @onclick="() => RequestReturnAsync(loan)" 
                                            class="bg-mystic-900 text-white py-1.5 px-4 rounded-xl text-xs font-bold hover:bg-mystic-800 transition-all active:scale-95 shadow-sm">
                                        Return Book
                                    </button>
                                }
                                else if (loan.Status == "PendingReturn")
                                {
                                    <span class="text-xs font-medium text-mystic-400 italic">Awaiting Approval</span>
                                }
                                else
                                {
                                    <svg class="w-5 h-5 ml-auto text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
                                }
                            </td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>

        @if (TotalPages > 1)
        {
            <div class="p-6 bg-mystic-50/30 border-t border-mystic-100 flex items-center justify-between">
                <p class="text-xs text-mystic-500">
                    Showing <span class="font-bold text-mystic-900">@((_currentPage - 1) * _pageSize + 1)</span> to 
                    <span class="font-bold text-mystic-900">@(Math.Min(_currentPage * _pageSize, _borrowings.Count()))</span> of 
                    <span class="font-bold text-mystic-900">@_borrowings.Count()</span> records
                </p>
                <div class="flex items-center gap-2">
                    <button disabled="@(_currentPage == 1)" @onclick="() => _currentPage--" 
                            class="p-2 rounded-lg border border-mystic-200 text-mystic-600 hover:bg-white disabled:opacity-30 disabled:cursor-not-allowed transition-all">
                        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
                    </button>
                    
                    <span class="text-xs font-bold text-mystic-900 px-2">Page @_currentPage of @TotalPages</span>

                    <button disabled="@(_currentPage == TotalPages)" @onclick="() => _currentPage++" 
                            class="p-2 rounded-lg border border-mystic-200 text-mystic-600 hover:bg-white disabled:opacity-30 disabled:cursor-not-allowed transition-all">
                        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
                    </button>
                </div>
            </div>
        }
    </div>
}

@code {
    private bool _isLoading = true;
    private IEnumerable<BorrowingDto> _borrowings = Enumerable.Empty<BorrowingDto>();
    
    // Pagination
    private int _currentPage = 1;
    private int _pageSize = 10;
    
    private IEnumerable<BorrowingDto> PaginatedBorrowings => _borrowings
        .Skip((_currentPage - 1) * _pageSize)
        .Take(_pageSize);
        
    private int TotalPages => (int)Math.Ceiling(_borrowings.Count() / (double)_pageSize);

    protected override async Task OnInitializedAsync()
    {
        await LoadDataAsync();
    }

    private async Task LoadDataAsync()
    {
        _isLoading = true;
        try
        {
            _borrowings = await ApiClient.GetMyBorrowingsAsync();
            _currentPage = 1;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error loading borrowings: {ex.Message}");
        }
        finally
        {
            _isLoading = false;
        }
    }

    private async Task RequestReturnAsync(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("showToast", "Return request submitted successfully!", "success");
            await LoadDataAsync();
        }
        else
        {
            await JS.InvokeVoidAsync("showToast", "Failed to submit return request.", "error");
        }
    }
}