Spaces:
Sleeping
Sleeping
Yuyuqt
add: books, borrowings, home, login, membership, register, reward, userinsights, wishlist, themes
6f32ae8 | @model Frontend.Models.PagedResult<LibraryManagement.Shared.Models.UserDto> | |
| @{ | |
| ViewData["Title"] = "Members Directory"; | |
| } | |
| <div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8"> | |
| <div> | |
| <h1 class="text-3xl font-bold tracking-tight text-slate-900">Members Directory</h1> | |
| <p class="mt-2 text-slate-500"> | |
| Manage library members and administrator accounts. | |
| <span class="ml-2 text-xs text-slate-400">(@Model.TotalCount total)</span> | |
| </p> | |
| </div> | |
| <div class="flex items-center gap-3"> | |
| <a asp-action="Create" class="btn-primary"> | |
| <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path></svg> | |
| Add New Member | |
| </a> | |
| </div> | |
| </div> | |
| <div class="card overflow-hidden"> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-slate-200"> | |
| <thead class="bg-slate-50"> | |
| <tr> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Member</th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Contact</th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Role</th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Status</th> | |
| <th scope="col" class="relative px-6 py-3"><span class="sr-only">Actions</span></th> | |
| </tr> | |
| </thead> | |
| <tbody class="bg-white divide-y divide-slate-200"> | |
| @foreach (var user in Model.Items) | |
| { | |
| <tr class="hover:bg-slate-50 transition-colors"> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <div class="flex items-center"> | |
| <div class="h-10 w-10 flex-shrink-0 rounded-full bg-slate-900 flex items-center justify-center text-white font-bold text-sm"> | |
| @(user.FullName?.Substring(0, 2).ToUpper() ?? "U") | |
| </div> | |
| <div class="ml-4"> | |
| <div class="text-sm font-medium text-slate-900"> | |
| <a asp-action="Details" asp-route-id="@user.Id" class="hover:underline">@user.FullName</a> | |
| </div> | |
| @if (!string.IsNullOrEmpty(user.StudentId)) | |
| { | |
| <div class="text-xs text-slate-500 text-slate-500">ID: @user.StudentId</div> | |
| } | |
| </div> | |
| </div> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <div class="text-sm text-slate-900">@user.Email</div> | |
| <div class="text-xs text-slate-500">@(string.IsNullOrEmpty(user.PhoneNumber) ? "No phone" : user.PhoneNumber)</div> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <span class="inline-flex rounded-full px-2 text-xs font-semibold leading-5 @(user.Role == "Admin" ? "bg-amber-100 text-amber-800" : "bg-blue-100 text-blue-800")"> | |
| @user.Role | |
| </span> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| @if (user.BanStatus == true) | |
| { | |
| <span class="inline-flex rounded-full bg-rose-100 px-2 text-xs font-semibold leading-5 text-rose-800"> | |
| Suspended | |
| </span> | |
| } | |
| else if (user.IsActive) | |
| { | |
| <span class="inline-flex rounded-full bg-emerald-100 px-2 text-xs font-semibold leading-5 text-emerald-800"> | |
| Active | |
| </span> | |
| } | |
| else | |
| { | |
| <span class="inline-flex rounded-full bg-slate-100 px-2 text-xs font-semibold leading-5 text-slate-800"> | |
| Inactive | |
| </span> | |
| } | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
| <div class="flex items-center justify-end gap-3"> | |
| <a asp-action="Details" asp-route-id="@user.Id" class="text-slate-600 hover:text-slate-900 transition-colors">Details</a> | |
| <a asp-action="Edit" asp-route-id="@user.Id" class="text-indigo-600 hover:text-indigo-900 transition-colors">Edit</a> | |
| </div> | |
| </td> | |
| </tr> | |
| } | |
| </tbody> | |
| </table> | |
| </div> | |
| @* Pagination *@ | |
| @if (Model.TotalPages > 1) | |
| { | |
| <div class="px-6 py-4 border-t border-slate-200 flex items-center justify-between bg-white"> | |
| <p class="text-sm text-slate-500"> | |
| Showing <span class="font-medium text-slate-700">@((Model.CurrentPage - 1) * Model.PageSize + 1)</span> | |
| – | |
| <span class="font-medium text-slate-700">@(Math.Min(Model.CurrentPage * Model.PageSize, Model.TotalCount))</span> | |
| of <span class="font-medium text-slate-700">@Model.TotalCount</span> members | |
| </p> | |
| <nav class="flex items-center gap-1" aria-label="Pagination"> | |
| @* Previous *@ | |
| @if (Model.HasPreviousPage) | |
| { | |
| <a asp-action="Index" asp-route-page="@(Model.CurrentPage - 1)" | |
| 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"> | |
| <svg class="w-4 h-4 mr-1" 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" /> | |
| </svg> | |
| Prev | |
| </a> | |
| } | |
| else | |
| { | |
| <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"> | |
| <svg class="w-4 h-4 mr-1" 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" /> | |
| </svg> | |
| Prev | |
| </span> | |
| } | |
| @* Page numbers *@ | |
| @for (int p = 1; p <= Model.TotalPages; p++) | |
| { | |
| if (p == Model.CurrentPage) | |
| { | |
| <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> | |
| } | |
| else if (p == 1 || p == Model.TotalPages || (p >= Model.CurrentPage - 2 && p <= Model.CurrentPage + 2)) | |
| { | |
| <a asp-action="Index" asp-route-page="@p" | |
| 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> | |
| } | |
| else if (p == Model.CurrentPage - 3 || p == Model.CurrentPage + 3) | |
| { | |
| <span class="inline-flex items-center justify-center w-8 h-8 text-sm text-slate-400">…</span> | |
| } | |
| } | |
| @* Next *@ | |
| @if (Model.HasNextPage) | |
| { | |
| <a asp-action="Index" asp-route-page="@(Model.CurrentPage + 1)" | |
| 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"> | |
| Next | |
| <svg class="w-4 h-4 ml-1" 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" /> | |
| </svg> | |
| </a> | |
| } | |
| else | |
| { | |
| <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"> | |
| Next | |
| <svg class="w-4 h-4 ml-1" 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" /> | |
| </svg> | |
| </span> | |
| } | |
| </nav> | |
| </div> | |
| } | |
| </div> | |