Spaces:
Sleeping
Sleeping
File size: 15,876 Bytes
b0a0ed0 c8d434a b0a0ed0 c8d434a b0a0ed0 25f925e 7fe61c9 ad67295 7fe61c9 c312181 b0a0ed0 c312181 ad67295 25f925e b0a0ed0 c8d434a b0a0ed0 c8d434a b0a0ed0 c8d434a b0a0ed0 c8d434a ad67295 | 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 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | <!DOCTYPE html>
<html lang="en" class="h-full bg-slate-50">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Library Management</title>
<!-- Google Fonts: Inter -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Tailwind CSS Play CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
colors: {
primary: {
50: '#f8fafc',
100: '#f1f5f9',
200: '#e2e8f0',
300: '#cbd5e1',
400: '#94a3b8',
500: '#64748b',
600: '#475569',
700: '#334155',
800: '#1e293b',
900: '#0f172a',
950: '#020617',
},
},
}
}
}
</script>
<style type="text/tailwindcss">
@@layer base {
body { @@apply text-slate-900 antialiased h-full; }
}
@@layer components {
.nav-link { @@apply flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-md transition-colors; }
.nav-link-active { @@apply bg-slate-900 text-white; }
.nav-link-inactive { @@apply text-slate-600 hover:text-slate-900 hover:bg-slate-100; }
.card { @@apply bg-white border border-slate-200 rounded-xl shadow-sm overflow-hidden; }
.btn-primary { @@apply inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white shadow transition-colors hover:bg-slate-900/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:pointer-events-none disabled:opacity-50; }
.btn-secondary { @@apply inline-flex items-center justify-center rounded-md border border-slate-200 bg-white px-4 py-2 text-sm font-medium shadow-sm transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:pointer-events-none disabled:opacity-50; }
}
</style>
</head>
<body class="h-full">
<div class="flex h-full overflow-hidden">
<!-- Sidebar -->
<aside class="hidden md:flex md:w-64 md:flex-col md:fixed md:inset-y-0 z-50">
<div class="flex flex-col flex-grow border-r border-slate-200 bg-white pt-5 pb-4 overflow-y-auto">
<div class="flex items-center flex-shrink-0 px-4 gap-2">
<div class="bg-slate-900 rounded-lg p-1.5 text-white">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 6 4 14"/><path d="M12 6v14"/><path d="M8 8v12"/><path d="M4 4v16"/><path d="M4 12V4l4 4"/><path d="m12 6 4 4"/></svg>
</div>
<span class="text-lg font-bold tracking-tight">Library OS</span>
</div>
<div class="mt-8 flex-grow flex flex-col">
<nav class="flex-1 px-2 space-y-1">
<a asp-controller="Home" asp-action="Index" class="nav-link @(ViewContext.RouteData.Values["Controller"]?.ToString() == "Home" ? "nav-link-active" : "nav-link-inactive")">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mr-3"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="15" rx="1"/></svg>
Dashboard
</a>
<a href="/Books" class="nav-link nav-link-inactive">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mr-3"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"/></svg>
Books Catalog
</a>
@if (User.Identity?.IsAuthenticated != true || !User.IsInRole("Librarian"))
{
<a href="/Borrowings" class="nav-link nav-link-inactive">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mr-3"><path d="m21 21-6-6m6 6v-4.8m0 4.8h-4.8"/><path d="M3 16.2V21m0 0h4.8M3 21l6-6"/><path d="M21 7.8V3m0 0h-4.8M21 3l-6 6"/><path d="M3 7.8V3m0 0h4.8M3 3l6 6"/></svg>
Active Loans
</a>
}
@if (User.Identity?.IsAuthenticated == true && !User.IsInRole("Librarian"))
{
<a asp-controller="Membership" asp-action="Index" class="nav-link @(ViewContext.RouteData.Values["Controller"]?.ToString() == "Membership" ? "nav-link-active" : "nav-link-inactive")">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mr-3"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/></svg>
My Membership
</a>
<a asp-controller="Rewards" asp-action="Index" class="nav-link @(ViewContext.RouteData.Values["Controller"]?.ToString() == "Rewards" ? "nav-link-active" : "nav-link-inactive")">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mr-3"><path d="M20 7h-9"/><path d="M14 17H5"/><circle cx="17" cy="17" r="3"/><circle cx="7" cy="7" r="3"/></svg>
Rewards
</a>
}
@if (User.Identity?.IsAuthenticated == true && User.IsInRole("Librarian"))
{
<a href="/Members" class="nav-link @(ViewContext.RouteData.Values["Controller"]?.ToString() == "Members" ? "nav-link-active" : "nav-link-inactive")">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mr-3"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
Members
</a>
<a asp-controller="Borrowings" asp-action="Manage" class="nav-link @(ViewContext.RouteData.Values["Controller"]?.ToString() == "Borrowings" && ViewContext.RouteData.Values["Action"]?.ToString() == "Manage" ? "nav-link-active" : "nav-link-inactive")">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mr-3"><path d="M11 15h2a2 2 0 1 0 0-4h-2a2 2 0 1 1 0-4h2"/><path d="M12 5v14"/><path d="M18 19V5l-4 4"/><circle cx="12" cy="12" r="10"/></svg>
Manage Borrowings
</a>
<a asp-controller="Rewards" asp-action="Manage" class="nav-link @(ViewContext.RouteData.Values["Controller"]?.ToString() == "Rewards" ? "nav-link-active" : "nav-link-inactive")">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flex-shrink-0 mr-3"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
Rewards
</a>
}
</nav>
</div>
<div class="px-4 py-4 border-t border-slate-100">
<div class="flex items-center gap-3">
<div class="h-8 w-8 rounded-full bg-slate-900 flex items-center justify-center text-white font-bold text-xs uppercase">
@(User.Identity?.IsAuthenticated == true ? User.Identity.Name?.Substring(0, 2) : "GU")
</div>
<div class="flex flex-col">
@if (User.Identity?.IsAuthenticated == true)
{
<span class="text-sm font-medium">@User.Identity.Name</span>
<span class="text-xs text-slate-500">@User.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Role)?.Value</span>
}
else
{
<span class="text-sm font-medium">Guest User</span>
<span class="text-xs text-slate-500">Sign in to borrow</span>
}
</div>
</div>
</div>
</div>
</aside>
<!-- Main Content Wrapper -->
<div class="md:pl-64 flex flex-col flex-1 w-0">
<!-- Topbar -->
<header class="flex-shrink-0 flex h-16 bg-white border-bottom border-slate-200">
<div class="flex-1 px-4 flex justify-between">
<div class="flex-1 flex items-center">
<div class="w-full max-w-sm">
<label for="search" class="sr-only">Search</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="h-4 w-4 text-slate-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
</svg>
</div>
<input id="search" name="search" class="block w-full pl-10 pr-3 py-2 border border-slate-200 rounded-md leading-5 bg-slate-50 placeholder-slate-400 focus:outline-none focus:ring-1 focus:ring-slate-900 focus:border-slate-900 sm:text-sm transition-all" placeholder="Search books, authors, ISBN..." type="search">
</div>
</div>
</div>
<div class="ml-4 flex items-center md:ml-6 gap-4">
<button type="button" class="bg-white p-1 rounded-full text-slate-400 hover:text-slate-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-900">
<span class="sr-only">View notifications</span>
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 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" />
</svg>
</button>
@if (User.Identity?.IsAuthenticated == true)
{
<a href="/Auth/Logout" class="btn-secondary py-1.5">Logout</a>
}
else
{
<a href="/Auth/Login" class="btn-primary py-1.5">Sign In</a>
}
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-1 relative overflow-y-auto focus:outline-none">
<div class="py-8 px-4 sm:px-6 md:px-8 max-w-7xl mx-auto">
@RenderBody()
</div>
</main>
</div>
</div>
<!-- Toast Notification Container -->
<div id="toast-container" class="fixed bottom-5 right-5 z-[100] flex flex-col gap-3 pointer-events-none"></div>
<script>
function showToast(message, type = 'success') {
const container = document.getElementById('toast-container');
const toast = document.createElement('div');
const bgColor = type === 'success' ? 'bg-slate-900' : 'bg-red-600';
const icon = type === 'success'
? '<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>'
: '<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>';
toast.className = `${bgColor} text-white px-4 py-3 rounded-lg shadow-lg flex items-center gap-3 transition-all duration-300 transform translate-y-10 opacity-0 pointer-events-auto min-w-[250px]`;
toast.innerHTML = `
<div class="shrink-0">${icon}</div>
<div class="text-sm font-medium tracking-tight">${message}</div>
`;
container.appendChild(toast);
// Animate in
setTimeout(() => {
toast.classList.remove('translate-y-10', 'opacity-0');
}, 10);
// Remove after 3 seconds
setTimeout(() => {
toast.classList.add('translate-y-[-10px]', 'opacity-0');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
// Auto-show notifications from TempData if available
document.addEventListener('DOMContentLoaded', () => {
const success = '@TempData["Success"]';
const error = '@TempData["Error"]';
if (success) showToast(success, 'success');
if (error) showToast(error, 'error');
});
</script>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
<!-- test --> |