Yuyuqt commited on
Commit
36144b0
·
1 Parent(s): 78592a1

add: pagination

Browse files
Frontend/Controllers/BooksController.cs CHANGED
@@ -13,10 +13,24 @@ namespace Frontend.Controllers
13
  _apiClient = apiClient;
14
  }
15
 
16
- public async Task<IActionResult> Index()
17
  {
18
- var books = await _apiClient.GetBooksAsync();
19
- return View(books);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
 
22
  public async Task<IActionResult> Details(int id)
 
13
  _apiClient = apiClient;
14
  }
15
 
16
+ public async Task<IActionResult> Index(int page = 1)
17
  {
18
+ const int pageSize = 8;
19
+ var allBooks = await _apiClient.GetBooksAsync() ?? Enumerable.Empty<BookDto>();
20
+ var totalCount = allBooks.Count();
21
+ var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
22
+ page = Math.Max(1, Math.Min(page, Math.Max(1, totalPages)));
23
+
24
+ var pagedResult = new Frontend.Models.PagedResult<BookDto>
25
+ {
26
+ Items = allBooks.Skip((page - 1) * pageSize).Take(pageSize),
27
+ CurrentPage = page,
28
+ TotalPages = totalPages,
29
+ TotalCount = totalCount,
30
+ PageSize = pageSize
31
+ };
32
+
33
+ return View(pagedResult);
34
  }
35
 
36
  public async Task<IActionResult> Details(int id)
Frontend/Controllers/MembersController.cs CHANGED
@@ -15,10 +15,24 @@ namespace Frontend.Controllers
15
  _apiClient = apiClient;
16
  }
17
 
18
- public async Task<IActionResult> Index()
19
  {
20
- var users = await _apiClient.GetUsersAsync();
21
- return View(users);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
 
24
  public async Task<IActionResult> Details(int id)
 
15
  _apiClient = apiClient;
16
  }
17
 
18
+ public async Task<IActionResult> Index(int page = 1)
19
  {
20
+ const int pageSize = 10;
21
+ var allUsers = await _apiClient.GetUsersAsync() ?? Enumerable.Empty<Frontend.Models.Dtos.UserDto>();
22
+ var totalCount = allUsers.Count();
23
+ var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
24
+ page = Math.Max(1, Math.Min(page, Math.Max(1, totalPages)));
25
+
26
+ var pagedResult = new Frontend.Models.PagedResult<Frontend.Models.Dtos.UserDto>
27
+ {
28
+ Items = allUsers.Skip((page - 1) * pageSize).Take(pageSize),
29
+ CurrentPage = page,
30
+ TotalPages = totalPages,
31
+ TotalCount = totalCount,
32
+ PageSize = pageSize
33
+ };
34
+
35
+ return View(pagedResult);
36
  }
37
 
38
  public async Task<IActionResult> Details(int id)
Frontend/Models/PagedResult.cs ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ namespace Frontend.Models
2
+ {
3
+ public class PagedResult<T>
4
+ {
5
+ public IEnumerable<T> Items { get; set; } = Enumerable.Empty<T>();
6
+ public int CurrentPage { get; set; }
7
+ public int TotalPages { get; set; }
8
+ public int TotalCount { get; set; }
9
+ public int PageSize { get; set; }
10
+
11
+ public bool HasPreviousPage => CurrentPage > 1;
12
+ public bool HasNextPage => CurrentPage < TotalPages;
13
+ }
14
+ }
Frontend/Views/Books/Index.cshtml CHANGED
@@ -1,4 +1,4 @@
1
- @model IEnumerable<Frontend.Models.Dtos.BookDto>
2
  @{
3
  ViewData["Title"] = "Books Catalog";
4
  }
@@ -6,7 +6,10 @@
6
  <div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8">
7
  <div>
8
  <h1 class="text-3xl font-bold tracking-tight text-slate-900">Books Catalog</h1>
9
- <p class="mt-2 text-slate-500">Explore and discover your next great read.</p>
 
 
 
10
  </div>
11
  @if (User.IsInRole("Librarian"))
12
  {
@@ -33,8 +36,8 @@
33
  </div>
34
 
35
  <!-- Book Grid -->
36
- <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-8">
37
- @foreach (var book in Model)
38
  {
39
  <div class="group card hover:shadow-lg transition-all duration-300">
40
  <div class="relative aspect-[2/3] w-full bg-slate-100 flex items-center justify-center text-slate-300 overflow-hidden text-center p-4">
@@ -78,3 +81,77 @@
78
  </div>
79
  }
80
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @model Frontend.Models.PagedResult<Frontend.Models.Dtos.BookDto>
2
  @{
3
  ViewData["Title"] = "Books Catalog";
4
  }
 
6
  <div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8">
7
  <div>
8
  <h1 class="text-3xl font-bold tracking-tight text-slate-900">Books Catalog</h1>
9
+ <p class="mt-2 text-slate-500">
10
+ Explore and discover your next great read.
11
+ <span class="ml-2 text-xs text-slate-400">(@Model.TotalCount total)</span>
12
+ </p>
13
  </div>
14
  @if (User.IsInRole("Librarian"))
15
  {
 
36
  </div>
37
 
38
  <!-- Book Grid -->
39
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4 gap-8">
40
+ @foreach (var book in Model.Items)
41
  {
42
  <div class="group card hover:shadow-lg transition-all duration-300">
43
  <div class="relative aspect-[2/3] w-full bg-slate-100 flex items-center justify-center text-slate-300 overflow-hidden text-center p-4">
 
81
  </div>
82
  }
83
  </div>
84
+
85
+ @* Pagination *@
86
+ @if (Model.TotalPages > 1)
87
+ {
88
+ <div class="mt-10 flex items-center justify-between">
89
+ <p class="text-sm text-slate-500">
90
+ Showing <span class="font-medium text-slate-700">@((Model.CurrentPage - 1) * Model.PageSize + 1)</span>
91
+
92
+ <span class="font-medium text-slate-700">@(Math.Min(Model.CurrentPage * Model.PageSize, Model.TotalCount))</span>
93
+ of <span class="font-medium text-slate-700">@Model.TotalCount</span> books
94
+ </p>
95
+ <nav class="flex items-center gap-1" aria-label="Pagination">
96
+ @* Previous *@
97
+ @if (Model.HasPreviousPage)
98
+ {
99
+ <a asp-action="Index" asp-route-page="@(Model.CurrentPage - 1)"
100
+ class="inline-flex items-center px-3 py-1.5 rounded-md border border-slate-200 text-sm font-medium text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">
101
+ <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
102
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
103
+ </svg>
104
+ Prev
105
+ </a>
106
+ }
107
+ else
108
+ {
109
+ <span class="inline-flex items-center px-3 py-1.5 rounded-md border border-slate-100 text-sm font-medium text-slate-300 cursor-not-allowed select-none">
110
+ <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
111
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
112
+ </svg>
113
+ Prev
114
+ </span>
115
+ }
116
+
117
+ @* Page numbers *@
118
+ @for (int p = 1; p <= Model.TotalPages; p++)
119
+ {
120
+ if (p == Model.CurrentPage)
121
+ {
122
+ <span class="inline-flex items-center justify-center w-8 h-8 rounded-md bg-slate-900 text-white text-sm font-semibold">@p</span>
123
+ }
124
+ else if (p == 1 || p == Model.TotalPages || (p >= Model.CurrentPage - 2 && p <= Model.CurrentPage + 2))
125
+ {
126
+ <a asp-action="Index" asp-route-page="@p"
127
+ class="inline-flex items-center justify-center w-8 h-8 rounded-md border border-slate-200 text-sm font-medium text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">@p</a>
128
+ }
129
+ else if (p == Model.CurrentPage - 3 || p == Model.CurrentPage + 3)
130
+ {
131
+ <span class="inline-flex items-center justify-center w-8 h-8 text-sm text-slate-400">…</span>
132
+ }
133
+ }
134
+
135
+ @* Next *@
136
+ @if (Model.HasNextPage)
137
+ {
138
+ <a asp-action="Index" asp-route-page="@(Model.CurrentPage + 1)"
139
+ class="inline-flex items-center px-3 py-1.5 rounded-md border border-slate-200 text-sm font-medium text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">
140
+ Next
141
+ <svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
142
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
143
+ </svg>
144
+ </a>
145
+ }
146
+ else
147
+ {
148
+ <span class="inline-flex items-center px-3 py-1.5 rounded-md border border-slate-100 text-sm font-medium text-slate-300 cursor-not-allowed select-none">
149
+ Next
150
+ <svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
151
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
152
+ </svg>
153
+ </span>
154
+ }
155
+ </nav>
156
+ </div>
157
+ }
Frontend/Views/Members/Index.cshtml CHANGED
@@ -1,4 +1,4 @@
1
- @model IEnumerable<Frontend.Models.Dtos.UserDto>
2
  @{
3
  ViewData["Title"] = "Members Directory";
4
  }
@@ -6,7 +6,10 @@
6
  <div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8">
7
  <div>
8
  <h1 class="text-3xl font-bold tracking-tight text-slate-900">Members Directory</h1>
9
- <p class="mt-2 text-slate-500">Manage library members and administrator accounts.</p>
 
 
 
10
  </div>
11
  <div class="flex items-center gap-3">
12
  <a asp-action="Create" class="btn-primary">
@@ -29,7 +32,7 @@
29
  </tr>
30
  </thead>
31
  <tbody class="bg-white divide-y divide-slate-200">
32
- @foreach (var user in Model)
33
  {
34
  <tr class="hover:bg-slate-50 transition-colors">
35
  <td class="px-6 py-4 whitespace-nowrap">
@@ -88,4 +91,78 @@
88
  </tbody>
89
  </table>
90
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  </div>
 
1
+ @model Frontend.Models.PagedResult<Frontend.Models.Dtos.UserDto>
2
  @{
3
  ViewData["Title"] = "Members Directory";
4
  }
 
6
  <div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8">
7
  <div>
8
  <h1 class="text-3xl font-bold tracking-tight text-slate-900">Members Directory</h1>
9
+ <p class="mt-2 text-slate-500">
10
+ Manage library members and administrator accounts.
11
+ <span class="ml-2 text-xs text-slate-400">(@Model.TotalCount total)</span>
12
+ </p>
13
  </div>
14
  <div class="flex items-center gap-3">
15
  <a asp-action="Create" class="btn-primary">
 
32
  </tr>
33
  </thead>
34
  <tbody class="bg-white divide-y divide-slate-200">
35
+ @foreach (var user in Model.Items)
36
  {
37
  <tr class="hover:bg-slate-50 transition-colors">
38
  <td class="px-6 py-4 whitespace-nowrap">
 
91
  </tbody>
92
  </table>
93
  </div>
94
+
95
+ @* Pagination *@
96
+ @if (Model.TotalPages > 1)
97
+ {
98
+ <div class="px-6 py-4 border-t border-slate-200 flex items-center justify-between bg-white">
99
+ <p class="text-sm text-slate-500">
100
+ Showing <span class="font-medium text-slate-700">@((Model.CurrentPage - 1) * Model.PageSize + 1)</span>
101
+
102
+ <span class="font-medium text-slate-700">@(Math.Min(Model.CurrentPage * Model.PageSize, Model.TotalCount))</span>
103
+ of <span class="font-medium text-slate-700">@Model.TotalCount</span> members
104
+ </p>
105
+ <nav class="flex items-center gap-1" aria-label="Pagination">
106
+ @* Previous *@
107
+ @if (Model.HasPreviousPage)
108
+ {
109
+ <a asp-action="Index" asp-route-page="@(Model.CurrentPage - 1)"
110
+ class="inline-flex items-center px-3 py-1.5 rounded-md border border-slate-200 text-sm font-medium text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">
111
+ <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
112
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
113
+ </svg>
114
+ Prev
115
+ </a>
116
+ }
117
+ else
118
+ {
119
+ <span class="inline-flex items-center px-3 py-1.5 rounded-md border border-slate-100 text-sm font-medium text-slate-300 cursor-not-allowed select-none">
120
+ <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
121
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
122
+ </svg>
123
+ Prev
124
+ </span>
125
+ }
126
+
127
+ @* Page numbers *@
128
+ @for (int p = 1; p <= Model.TotalPages; p++)
129
+ {
130
+ if (p == Model.CurrentPage)
131
+ {
132
+ <span class="inline-flex items-center justify-center w-8 h-8 rounded-md bg-slate-900 text-white text-sm font-semibold">@p</span>
133
+ }
134
+ else if (p == 1 || p == Model.TotalPages || (p >= Model.CurrentPage - 2 && p <= Model.CurrentPage + 2))
135
+ {
136
+ <a asp-action="Index" asp-route-page="@p"
137
+ class="inline-flex items-center justify-center w-8 h-8 rounded-md border border-slate-200 text-sm font-medium text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">@p</a>
138
+ }
139
+ else if (p == Model.CurrentPage - 3 || p == Model.CurrentPage + 3)
140
+ {
141
+ <span class="inline-flex items-center justify-center w-8 h-8 text-sm text-slate-400">…</span>
142
+ }
143
+ }
144
+
145
+ @* Next *@
146
+ @if (Model.HasNextPage)
147
+ {
148
+ <a asp-action="Index" asp-route-page="@(Model.CurrentPage + 1)"
149
+ class="inline-flex items-center px-3 py-1.5 rounded-md border border-slate-200 text-sm font-medium text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">
150
+ Next
151
+ <svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
152
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
153
+ </svg>
154
+ </a>
155
+ }
156
+ else
157
+ {
158
+ <span class="inline-flex items-center px-3 py-1.5 rounded-md border border-slate-100 text-sm font-medium text-slate-300 cursor-not-allowed select-none">
159
+ Next
160
+ <svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
161
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
162
+ </svg>
163
+ </span>
164
+ }
165
+ </nav>
166
+ </div>
167
+ }
168
  </div>