feat(ui): refactor dashboard screens to Modern Deep Dark style
This commit is contained in:
@@ -57,10 +57,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="empty-text">
|
<div class="empty-text">
|
||||||
<h3>Brak aktywnych lektur</h3>
|
<h3>Brak aktywnych lektur</h3>
|
||||||
<p>Przejdź do biblioteki, aby rozpocząć przygodę z Nexus Reader.</p>
|
<p>Przejdź do katalogu lub swoich książek, aby rozpocząć przygodę z Nexus Reader.</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-nexus primary" @onclick='() => NavigationManager.NavigateTo("/library")'>
|
<button class="btn-nexus primary" @onclick='() => NavigationManager.NavigateTo("/my-books")'>
|
||||||
Przejdź do Biblioteki
|
Przejdź do Moich Książek
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,17 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background: #1a1a1e;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-reading-card:hover {
|
||||||
|
background: #1e1e24;
|
||||||
|
border-color: rgba(16, 185, 129, 0.2);
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-layout {
|
.card-layout {
|
||||||
@@ -55,7 +66,7 @@
|
|||||||
|
|
||||||
.author-name {
|
.author-name {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: #A0A0A0;
|
color: #a1a1aa;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +91,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.percentage {
|
.percentage {
|
||||||
color: var(--nexus-neon);
|
color: #10b981;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar-container {
|
.progress-bar-container {
|
||||||
@@ -92,8 +103,8 @@
|
|||||||
|
|
||||||
.progress-bar-fill {
|
.progress-bar-fill {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--nexus-neon);
|
background: #10b981;
|
||||||
box-shadow: 0 0 10px rgba(0, 255, 153, 0.4);
|
box-shadow: 0 0 10px rgba(16, 185, 129, 0.4);
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
transition: width 1s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: width 1s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
@@ -101,7 +112,7 @@
|
|||||||
.book-excerpt {
|
.book-excerpt {
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #B0B0B0;
|
color: #a1a1aa;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 3;
|
-webkit-line-clamp: 3;
|
||||||
@@ -134,26 +145,26 @@
|
|||||||
|
|
||||||
.btn-nexus.outline {
|
.btn-nexus.outline {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--nexus-neon);
|
color: #10b981;
|
||||||
border: 1px solid rgba(0, 255, 153, 0.3);
|
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-nexus.outline:hover {
|
.btn-nexus.outline:hover {
|
||||||
background: rgba(0, 255, 153, 0.05);
|
background: rgba(16, 185, 129, 0.05);
|
||||||
border-color: var(--nexus-neon);
|
border-color: #10b981;
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 5px 15px rgba(0, 255, 153, 0.1);
|
box-shadow: 0 5px 15px rgba(16, 185, 129, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-nexus.primary {
|
.btn-nexus.primary {
|
||||||
background: var(--nexus-neon);
|
background: #10b981;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-nexus.primary:hover {
|
.btn-nexus.primary:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
box-shadow: 0 5px 15px rgba(0, 255, 153, 0.2);
|
box-shadow: 0 5px 15px rgba(16, 185, 129, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Empty State */
|
/* Empty State */
|
||||||
@@ -168,9 +179,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.empty-icon {
|
.empty-icon {
|
||||||
color: var(--nexus-neon);
|
color: #10b981;
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
filter: drop-shadow(0 0 10px rgba(0, 255, 153, 0.2));
|
filter: drop-shadow(0 0 10px rgba(16, 185, 129, 0.2));
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-text h3 {
|
.empty-text h3 {
|
||||||
|
|||||||
@@ -46,49 +46,54 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<NavLink class="nav-item" href="/" Match="NavLinkMatch.All" @onclick="CloseMobileMenu">
|
<NavLink class="nav-item" href="/dashboard" Match="NavLinkMatch.All" @onclick="CloseMobileMenu" title="Pulpit">
|
||||||
<div class="nav-icon">
|
<div class="nav-icon">
|
||||||
<NexusIcon Name="home" Size="18" />
|
<NexusIcon Name="home" Size="20" />
|
||||||
</div>
|
</div>
|
||||||
<span class="nav-text">Dashboard</span>
|
<span class="nav-text">Pulpit</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink class="nav-item" href="/library" @onclick="CloseMobileMenu">
|
<NavLink class="nav-item" href="/catalog" @onclick="CloseMobileMenu" title="Katalog">
|
||||||
<div class="nav-icon">
|
<div class="nav-icon">
|
||||||
<NexusIcon Name="book-open" Size="18" />
|
<NexusIcon Name="layout" Size="20" />
|
||||||
</div>
|
</div>
|
||||||
<span class="nav-text">Library</span>
|
<span class="nav-text">Katalog</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink class="nav-item" href="/concepts-map" @onclick="CloseMobileMenu">
|
<NavLink class="nav-item" href="/my-books" @onclick="CloseMobileMenu" title="Moje">
|
||||||
<div class="nav-icon">
|
<div class="nav-icon">
|
||||||
<NexusIcon Name="map" Size="18" />
|
<NexusIcon Name="book-open" Size="20" />
|
||||||
</div>
|
</div>
|
||||||
<span class="nav-text">Concepts Map</span>
|
<span class="nav-text">Moje</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink class="nav-item" href="/intelligence" @onclick="CloseMobileMenu">
|
<NavLink class="nav-item" href="/profile" @onclick="CloseMobileMenu" title="Konto">
|
||||||
<div class="nav-icon">
|
<div class="nav-icon">
|
||||||
<NexusIcon Name="cpu" Size="18" />
|
<NexusIcon Name="user" Size="20" />
|
||||||
</div>
|
</div>
|
||||||
<span class="nav-text">Global AI Q&A</span>
|
<span class="nav-text">Konto</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink class="nav-item" href="/profile" @onclick="CloseMobileMenu">
|
<NavLink class="nav-item" href="/concepts-map" @onclick="CloseMobileMenu" title="Mapa Pojęć">
|
||||||
<div class="nav-icon">
|
<div class="nav-icon">
|
||||||
<NexusIcon Name="message-square" Size="18" />
|
<NexusIcon Name="map" Size="20" />
|
||||||
</div>
|
</div>
|
||||||
<span class="nav-text">Profile</span>
|
<span class="nav-text">Mapa Pojęć</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink class="nav-item" href="/settings" @onclick="CloseMobileMenu">
|
<NavLink class="nav-item" href="/intelligence" @onclick="CloseMobileMenu" title="Globalne AI">
|
||||||
<div class="nav-icon">
|
<div class="nav-icon">
|
||||||
<NexusIcon Name="settings" Size="18" />
|
<NexusIcon Name="robot" Size="20" />
|
||||||
</div>
|
</div>
|
||||||
<span class="nav-text">Settings</span>
|
<span class="nav-text">Globalne AI</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink class="nav-item" href="/concenters" @onclick="CloseMobileMenu">
|
<NavLink class="nav-item" href="/settings" @onclick="CloseMobileMenu" title="Ustawienia">
|
||||||
<div class="nav-icon">
|
<div class="nav-icon">
|
||||||
<NexusIcon Name="target" Size="18" />
|
<NexusIcon Name="settings" Size="20" />
|
||||||
</div>
|
</div>
|
||||||
<span class="nav-text">Concenters</span>
|
<span class="nav-text">Ustawienia</span>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink class="nav-item" href="/concenters" @onclick="CloseMobileMenu" title="Koncentry">
|
||||||
|
<div class="nav-icon">
|
||||||
|
<NexusIcon Name="target" Size="20" />
|
||||||
|
</div>
|
||||||
|
<span class="nav-text">Koncentry</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
@@ -100,8 +105,8 @@
|
|||||||
<span class="user-name">@context.User.Identity?.Name</span>
|
<span class="user-name">@context.User.Identity?.Name</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="logout-btn" @onclick="HandleLogout" title="Logout">
|
<button class="logout-btn" @onclick="HandleLogout" title="Wyloguj">
|
||||||
<NexusIcon Name="log-out" Size="18" />
|
<NexusIcon Name="log-out" Size="20" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -2,163 +2,157 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: #121212;
|
background: #121214;
|
||||||
color: #e0e0e0;
|
color: #e4e4e7;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .hub-sidebar {
|
::deep .hub-sidebar {
|
||||||
width: 260px;
|
width: 80px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #161616;
|
background: #0d0d0d;
|
||||||
border-right: 1px solid rgba(255, 255, 255, 0.05);
|
border-right: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
transition: width 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .sidebar-header {
|
::deep .sidebar-header {
|
||||||
padding: 2.5rem 1.5rem;
|
padding: 2rem 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .logo {
|
::deep .logo {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.75rem;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .logo-icon {
|
::deep .logo-icon {
|
||||||
color: var(--nexus-neon);
|
color: #10b981;
|
||||||
filter: drop-shadow(0 0 10px rgba(0, 255, 153, 0.4));
|
filter: drop-shadow(0 0 8px rgba(16, 185, 129, 0.3));
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .logo-text {
|
::deep .logo-text {
|
||||||
font-family: var(--nexus-font-serif);
|
display: none;
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #ffffff;
|
|
||||||
letter-spacing: -0.01em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .sidebar-nav {
|
::deep .sidebar-nav {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0;
|
padding: 0.5rem 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .nav-item {
|
::deep .nav-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1rem;
|
justify-content: center;
|
||||||
padding: 1rem 1.5rem;
|
width: 100%;
|
||||||
color: #A0A0A0;
|
height: 54px;
|
||||||
|
color: #8b8273;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: all 0.2s ease;
|
transition: color 0.2s ease, background-color 0.2s ease;
|
||||||
border-left: 3px solid transparent;
|
position: relative;
|
||||||
font-family: var(--nexus-font-sans);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .nav-item:hover {
|
::deep .nav-item:hover {
|
||||||
background: rgba(255, 255, 255, 0.02);
|
color: #10b981;
|
||||||
color: #ffffff;
|
background: rgba(255, 255, 255, 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .nav-item.active {
|
::deep .nav-item.active {
|
||||||
color: #ffffff;
|
color: #10b981;
|
||||||
background: rgba(0, 255, 153, 0.03);
|
background: rgba(16, 185, 129, 0.04);
|
||||||
border-left: 3px solid var(--nexus-neon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .nav-item.active .nav-icon {
|
::deep .nav-item.active::before {
|
||||||
color: var(--nexus-neon);
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 15%;
|
||||||
|
height: 70%;
|
||||||
|
width: 3px;
|
||||||
|
background: #10b981;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
box-shadow: 0 0 10px rgba(16, 185, 129, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .nav-icon {
|
::deep .nav-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 20px;
|
width: 24px;
|
||||||
opacity: 0.7;
|
height: 24px;
|
||||||
transition: opacity 0.2s;
|
transition: color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .nav-item:hover .nav-icon,
|
::deep .nav-text {
|
||||||
::deep .nav-item.active .nav-icon {
|
display: none;
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .sidebar-footer {
|
::deep .sidebar-footer {
|
||||||
padding: 1.25rem 1.5rem;
|
padding: 1.5rem 0;
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
gap: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .user-brief {
|
::deep .user-brief {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
justify-content: center;
|
||||||
gap: 0.75rem;
|
width: 100%;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .user-avatar {
|
::deep .user-avatar {
|
||||||
width: 32px;
|
width: 36px;
|
||||||
height: 32px;
|
height: 36px;
|
||||||
background: #222;
|
background: #1a1a1e;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 0.8rem;
|
font-size: 0.9rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #A0A0A0;
|
color: #e4e4e7;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .user-details {
|
::deep .user-details {
|
||||||
display: flex;
|
display: none;
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
::deep .user-name {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #A0A0A0;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .logout-btn {
|
::deep .logout-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: #666;
|
color: #8b8273;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0.4rem;
|
padding: 0.5rem;
|
||||||
border-radius: 6px;
|
border-radius: 8px;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
::deep .logout-btn:hover {
|
::deep .logout-btn:hover {
|
||||||
background: rgba(255, 255, 255, 0.05);
|
background: rgba(239, 68, 68, 0.08);
|
||||||
color: #ffffff;
|
color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hub-main {
|
.hub-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: radial-gradient(circle at center, #1a1a1a 0%, #121212 100%);
|
background: #121214;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hub-content {
|
.hub-content {
|
||||||
@@ -179,11 +173,11 @@
|
|||||||
::deep .nexus-loader {
|
::deep .nexus-loader {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border: 2px solid rgba(0, 255, 153, 0.1);
|
border: 2px solid rgba(16, 185, 129, 0.1);
|
||||||
border-top-color: var(--nexus-neon);
|
border-top-color: #10b981;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 0.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
animation: spin 0.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
||||||
filter: drop-shadow(0 0 5px var(--nexus-neon));
|
filter: drop-shadow(0 0 5px #10b981);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
<!-- Intelligence Card -->
|
<!-- Intelligence Card -->
|
||||||
<div class="metric-card glass-panel">
|
<div class="metric-card glass-panel">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<NexusIcon Name="robot" Size="24" Color="var(--nexus-neon)" />
|
<NexusIcon Name="robot" Size="24" Color="#10b981" />
|
||||||
<h3>Interfejs AI</h3>
|
<h3>Interfejs AI</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
<!-- Sync Card -->
|
<!-- Sync Card -->
|
||||||
<div class="metric-card glass-panel">
|
<div class="metric-card glass-panel">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<NexusIcon Name="activity" Size="24" Color="var(--nexus-neon)" />
|
<NexusIcon Name="activity" Size="24" Color="#10b981" />
|
||||||
<h3>Wydajność Nauki</h3>
|
<h3>Wydajność Nauki</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
<!-- Account Status Card -->
|
<!-- Account Status Card -->
|
||||||
<div class="metric-card glass-panel full-width">
|
<div class="metric-card glass-panel full-width">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<NexusIcon Name="shield" Size="24" Color="var(--nexus-neon)" />
|
<NexusIcon Name="shield" Size="24" Color="#10b981" />
|
||||||
<h3>Status Autoryzacji</h3>
|
<h3>Status Autoryzacji</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body status-layout">
|
<div class="card-body status-layout">
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #0a0c10;
|
background-color: #121214;
|
||||||
color: #e0e6ed;
|
color: #e4e4e7;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: 800px;
|
width: 800px;
|
||||||
height: 800px;
|
height: 800px;
|
||||||
background: radial-gradient(circle, rgba(0, 255, 153, 0.05) 0%, transparent 70%);
|
background: radial-gradient(circle, rgba(16, 185, 129, 0.03) 0%, transparent 70%);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
@@ -63,17 +63,17 @@
|
|||||||
.avatar-inner {
|
.avatar-inner {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
background: #151921;
|
background: #1a1a1e;
|
||||||
border: 2px solid var(--nexus-neon);
|
border: 2px solid #10b981;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 3.5rem;
|
font-size: 3.5rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: var(--nexus-neon);
|
color: #10b981;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
box-shadow: 0 0 30px rgba(0, 255, 153, 0.2), inset 0 0 20px rgba(0, 255, 153, 0.1);
|
box-shadow: 0 0 30px rgba(16, 185, 129, 0.2), inset 0 0 20px rgba(16, 185, 129, 0.1);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 140px;
|
width: 140px;
|
||||||
height: 140px;
|
height: 140px;
|
||||||
border: 1px solid rgba(0, 255, 153, 0.3);
|
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: pulse-ring 3s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
animation: pulse-ring 3s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
.system-rank {
|
.system-rank {
|
||||||
font-family: 'Inter', 'Courier New', Courier, monospace;
|
font-family: 'Inter', 'Courier New', Courier, monospace;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: var(--nexus-neon);
|
color: #10b981;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
@@ -120,11 +120,17 @@
|
|||||||
|
|
||||||
.glass-panel {
|
.glass-panel {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
|
background: #1a1a1e;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glass-panel:hover {
|
.glass-panel:hover {
|
||||||
border-color: rgba(0, 255, 153, 0.2);
|
border-color: rgba(16, 185, 129, 0.2);
|
||||||
transform: translateY(-4px);
|
transform: translateY(-4px);
|
||||||
|
background: #1e1e24;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-card {
|
.metric-card {
|
||||||
@@ -185,8 +191,8 @@
|
|||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--nexus-neon);
|
background: #10b981;
|
||||||
box-shadow: 0 0 15px rgba(0, 255, 153, 0.5);
|
box-shadow: 0 0 15px rgba(16, 185, 129, 0.5);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +212,7 @@
|
|||||||
.score-value {
|
.score-value {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
color: var(--nexus-neon);
|
color: #10b981;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +229,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
background: rgba(0, 255, 153, 0.05);
|
background: rgba(16, 185, 129, 0.05);
|
||||||
|
border: 1px solid rgba(16, 185, 129, 0.1);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
color: #cbd5e0;
|
color: #cbd5e0;
|
||||||
@@ -259,10 +266,16 @@
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-badge.pro {
|
.plan-badge.pro, .plan-badge.enterprise {
|
||||||
background: rgba(0, 255, 153, 0.1);
|
background: rgba(16, 185, 129, 0.15);
|
||||||
color: var(--nexus-neon);
|
color: #10b981;
|
||||||
border: 1px solid rgba(0, 255, 153, 0.2);
|
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-badge.free {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: #a1a1aa;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tenant-tag {
|
.tenant-tag {
|
||||||
@@ -318,11 +331,11 @@
|
|||||||
.nexus-loader {
|
.nexus-loader {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
border: 4px solid rgba(0, 255, 153, 0.1);
|
border: 4px solid rgba(16, 185, 129, 0.1);
|
||||||
border-top-color: var(--nexus-neon);
|
border-top-color: #10b981;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 1.5s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
animation: spin 1.5s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||||
filter: drop-shadow(0 0 10px var(--nexus-neon));
|
filter: drop-shadow(0 0 10px #10b981);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin { to { transform: rotate(360deg); } }
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
|
|||||||
@@ -0,0 +1,234 @@
|
|||||||
|
@page "/catalog"
|
||||||
|
@attribute [Authorize]
|
||||||
|
@using NexusReader.UI.Shared.Components.Organisms
|
||||||
|
@using NexusReader.Application.DTOs.User
|
||||||
|
@using NexusReader.UI.Shared.Services
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@inject HttpClient Http
|
||||||
|
@inject IReaderNavigationService ReaderNavigation
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
|
<div class="catalog-page">
|
||||||
|
<header class="catalog-header">
|
||||||
|
<div class="header-title-section">
|
||||||
|
<h1>Katalog Kursów</h1>
|
||||||
|
<p class="subtitle">Rozwijaj swoje kompetencje techniczne z interaktywnymi kursami zintegrowanymi z asystentem Nexus AI</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="catalog-content">
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<div class="catalog-loading-container">
|
||||||
|
<div class="loader-card">
|
||||||
|
<div class="spinner-glow small"></div>
|
||||||
|
<span class="loader-text">Wczytywanie katalogu...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="loading-grid">
|
||||||
|
@for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
<div class="skeleton-card">
|
||||||
|
<div class="skeleton-cover"></div>
|
||||||
|
<div class="skeleton-details">
|
||||||
|
<div class="skeleton-line title"></div>
|
||||||
|
<div class="skeleton-line author"></div>
|
||||||
|
<div class="skeleton-line button"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="catalog-grid">
|
||||||
|
@* Render real books first *@
|
||||||
|
@if (_books != null && _books.Any())
|
||||||
|
{
|
||||||
|
@foreach (var book in _books)
|
||||||
|
{
|
||||||
|
<div class="course-card" @onclick="() => OpenBook(book.Id)">
|
||||||
|
<div class="card-cover-container">
|
||||||
|
<img src="@(book.CoverUrl ?? "https://api.dicebear.com/7.x/identicon/svg?seed=" + book.Title)" alt="@book.Title" class="card-cover" />
|
||||||
|
<div class="cover-overlay">
|
||||||
|
<span class="start-action">Uruchom kurs</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-details">
|
||||||
|
<span class="category-badge">E-Book</span>
|
||||||
|
<h3 class="course-title" title="@book.Title">@book.Title</h3>
|
||||||
|
<p class="course-author">Autor: @book.Author.Name</p>
|
||||||
|
<p class="course-desc">
|
||||||
|
@(string.IsNullOrEmpty(book.Description) ? "Rozpocznij naukę i buduj strukturę pojęć w oparciu o autorskie algorytmy ekstrakcji wiedzy Nexus AI." : book.Description)
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card-footer-info">
|
||||||
|
<span class="meta-item">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
|
||||||
|
Rozdziały: Wiele
|
||||||
|
</span>
|
||||||
|
<span class="meta-item text-success">Dostępny</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
<button class="btn-nexus start-course-btn" @onclick="() => OpenBook(book.Id)" @onclick:stopPropagation="true">
|
||||||
|
ZACZNIJ KURS
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@* Curated Showcase Mock Courses to look extremely premium *@
|
||||||
|
<div class="course-card">
|
||||||
|
<div class="card-cover-container">
|
||||||
|
<div class="mock-cover dotnet-gradient">
|
||||||
|
<span class="cover-code-text"><.NET 10></span>
|
||||||
|
</div>
|
||||||
|
<div class="cover-overlay">
|
||||||
|
<span class="start-action">Zarejestruj się</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-details">
|
||||||
|
<span class="category-badge architecture">Architektura</span>
|
||||||
|
<h3 class="course-title">.NET 10 & Blazor SaaS Architecture</h3>
|
||||||
|
<p class="course-author">Autor: Nexus Architect</p>
|
||||||
|
<p class="course-desc">
|
||||||
|
Zaawansowany kurs budowania skalowalnych SaaS z Native AOT, CQRS, MediatR, FluentResults i izolowanym systemem stylów Blazor CSS.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card-footer-info">
|
||||||
|
<span class="meta-item">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
|
||||||
|
12 Modułów
|
||||||
|
</span>
|
||||||
|
<span class="meta-item text-premium">Premium</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
<button class="btn-nexus start-course-btn mock-btn" @onclick="ShowPremiumAlert">
|
||||||
|
ZACZNIJ KURS
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="course-card">
|
||||||
|
<div class="card-cover-container">
|
||||||
|
<div class="mock-cover blazor-gradient">
|
||||||
|
<span class="cover-code-text">BLAZOR</span>
|
||||||
|
</div>
|
||||||
|
<div class="cover-overlay">
|
||||||
|
<span class="start-action">Zarejestruj się</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-details">
|
||||||
|
<span class="category-badge design">Performance</span>
|
||||||
|
<h3 class="course-title">Blazor State & Rendering Masterclass</h3>
|
||||||
|
<p class="course-author">Autor: Nexus Architect</p>
|
||||||
|
<p class="course-desc">
|
||||||
|
Techniki optymalizacji renderowania, zarządzanie stanem w aplikacjach rozproszonych oraz głęboka integracja JavaScript Interop.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card-footer-info">
|
||||||
|
<span class="meta-item">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
|
||||||
|
8 Modułów
|
||||||
|
</span>
|
||||||
|
<span class="meta-item text-premium">Premium</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
<button class="btn-nexus start-course-btn mock-btn" @onclick="ShowPremiumAlert">
|
||||||
|
ZACZNIJ KURS
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="course-card">
|
||||||
|
<div class="card-cover-container">
|
||||||
|
<div class="mock-cover graph-gradient">
|
||||||
|
<span class="cover-code-text">D3.JS GRAPH</span>
|
||||||
|
</div>
|
||||||
|
<div class="cover-overlay">
|
||||||
|
<span class="start-action">Zarejestruj się</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-details">
|
||||||
|
<span class="category-badge analytics">Wizualizacja</span>
|
||||||
|
<h3 class="course-title">D3.js & interactive Knowledge Graphs</h3>
|
||||||
|
<p class="course-author">Autor: Nexus Architect</p>
|
||||||
|
<p class="course-desc">
|
||||||
|
Projektowanie interaktywnych grafów pojęć i dynamicznych map myśli 2D/3D zsynchronizowanych z modelem językowym AI.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card-footer-info">
|
||||||
|
<span class="meta-item">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>
|
||||||
|
10 Modułów
|
||||||
|
</span>
|
||||||
|
<span class="meta-item text-premium">Premium</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
<button class="btn-nexus start-course-btn mock-btn" @onclick="ShowPremiumAlert">
|
||||||
|
ZACZNIJ KURS
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool _isLoading = true;
|
||||||
|
private List<LastReadBookDto>? _books;
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
await LoadBooksAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadBooksAsync()
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_books = await Http.GetFromJsonAsync<List<LastReadBookDto>>("api/library/books");
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[Catalog] Failed to load books: {ex.Message}");
|
||||||
|
if (OperatingSystem.IsBrowser())
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenBook(Guid bookId)
|
||||||
|
{
|
||||||
|
ReaderNavigation.NavigateToBook(bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowPremiumAlert()
|
||||||
|
{
|
||||||
|
// Showcase callback
|
||||||
|
NavigationManager.NavigateTo("/profile");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,358 @@
|
|||||||
|
.catalog-page {
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
animation: fadeIn 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-header {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-header h1 {
|
||||||
|
font-family: var(--nexus-font-serif);
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
color: #ffffff;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-header .subtitle {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #a1a1aa;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Catalog Grid */
|
||||||
|
.catalog-grid, .loading-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||||
|
gap: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-card {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #1a1a1e;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3);
|
||||||
|
border-color: rgba(16, 185, 129, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-cover-container {
|
||||||
|
position: relative;
|
||||||
|
height: 200px;
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-cover {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-card:hover .card-cover {
|
||||||
|
transform: scale(1.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gradients for Mock Course Covers */
|
||||||
|
.mock-cover {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-code-text {
|
||||||
|
font-family: var(--nexus-font-sans, "Outfit", sans-serif);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #ffffff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dotnet-gradient {
|
||||||
|
background: linear-gradient(135deg, #512bd4 0%, #10b981 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blazor-gradient {
|
||||||
|
background: linear-gradient(135deg, #f05a28 0%, #10b981 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph-gradient {
|
||||||
|
background: linear-gradient(135deg, #0d9488 0%, #10b981 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(13, 13, 15, 0.6);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-card:hover .cover-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-action {
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 0.6rem 1.25rem;
|
||||||
|
border: 2px solid #ffffff;
|
||||||
|
border-radius: 30px;
|
||||||
|
transform: translateY(10px);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-card:hover .start-action {
|
||||||
|
transform: translateY(0);
|
||||||
|
background: #ffffff;
|
||||||
|
color: #121214;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-details {
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-badge {
|
||||||
|
align-self: flex-start;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #a1a1aa;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-badge.architecture {
|
||||||
|
color: #f43f5e;
|
||||||
|
background: rgba(244, 63, 94, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-badge.design {
|
||||||
|
color: #0ea5e9;
|
||||||
|
background: rgba(14, 165, 233, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-badge.analytics {
|
||||||
|
color: #a855f7;
|
||||||
|
background: rgba(168, 85, 247, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 0.4rem 0;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-family: var(--nexus-font-sans, "Outfit", sans-serif);
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-author {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #a1a1aa;
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-desc {
|
||||||
|
font-size: 0.88rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #a1a1aa;
|
||||||
|
margin: 0 0 1.5rem 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer-info {
|
||||||
|
margin-top: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #a1a1aa;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-success {
|
||||||
|
color: #10b981;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-premium {
|
||||||
|
color: #10b981;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-actions {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-course-btn {
|
||||||
|
width: 100%;
|
||||||
|
background: #10b981;
|
||||||
|
color: #0d0d0d;
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-course-btn:hover {
|
||||||
|
background: #059669;
|
||||||
|
color: #ffffff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(16, 185, 129, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skeleton Loading */
|
||||||
|
.skeleton-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 440px;
|
||||||
|
background: #1a1a1e;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-cover {
|
||||||
|
height: 200px;
|
||||||
|
background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: loading 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-details {
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-line {
|
||||||
|
background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: loading 1.5s infinite;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-line.title {
|
||||||
|
height: 20px;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-line.author {
|
||||||
|
height: 14px;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-line.button {
|
||||||
|
height: 36px;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-loading-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-loading-container .loader-card {
|
||||||
|
position: absolute;
|
||||||
|
top: 100px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.25rem;
|
||||||
|
padding: 1.25rem 2.25rem;
|
||||||
|
border-radius: 40px;
|
||||||
|
background: rgba(13, 13, 15, 0.85);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
animation: scaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-glow {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: 2px solid rgba(16, 185, 129, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: #10b981;
|
||||||
|
animation: spin 1s cubic-bezier(0.55, 0.055, 0.675, 0.19) infinite;
|
||||||
|
box-shadow: 0 0 15px rgba(16, 185, 129, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader-text {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(15px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
0% { background-position: 200% 0; }
|
||||||
|
100% { background-position: -200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scaleIn {
|
||||||
|
from { transform: translate(-50%, -50%) scale(0.9); opacity: 0; }
|
||||||
|
to { transform: translate(-50%, -50%) scale(1); opacity: 1; }
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
<NexusIcon Name="book-open" Size="64" Class="dim-icon" />
|
<NexusIcon Name="book-open" Size="64" Class="dim-icon" />
|
||||||
<h2>Brak Aktywnych Książek</h2>
|
<h2>Brak Aktywnych Książek</h2>
|
||||||
<p>Nie wybrano żadnej książki lub ta książka nie ma jeszcze wygenerowanej mapy pojęć przez Nexus AI.</p>
|
<p>Nie wybrano żadnej książki lub ta książka nie ma jeszcze wygenerowanej mapy pojęć przez Nexus AI.</p>
|
||||||
<a href="/library" class="btn-nexus btn-nexus-primary">Przejdź do Biblioteki</a>
|
<a href="/my-books" class="btn-nexus btn-nexus-primary">Przejdź do Moich Książek</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<div class="header-back">
|
<div class="header-back">
|
||||||
<button class="btn-nexus btn-nexus-secondary btn-back" @onclick="GoBackToLibrary">
|
<button class="btn-nexus btn-nexus-secondary btn-back" @onclick="GoBackToLibrary">
|
||||||
<NexusIcon Name="arrow-left" Size="16" />
|
<NexusIcon Name="arrow-left" Size="16" />
|
||||||
<span>Biblioteka</span>
|
<span>Moje Książki</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
@@ -257,7 +257,7 @@
|
|||||||
|
|
||||||
private void GoBackToLibrary()
|
private void GoBackToLibrary()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo("/library");
|
NavigationManager.NavigateTo("/my-books");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GoToReader()
|
private void GoToReader()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
|
@page "/dashboard"
|
||||||
@using Microsoft.AspNetCore.Authorization
|
@using Microsoft.AspNetCore.Authorization
|
||||||
@using NexusReader.UI.Shared.Components.Atoms
|
@using NexusReader.UI.Shared.Components.Atoms
|
||||||
@using NexusReader.UI.Shared.Components.Organisms
|
@using NexusReader.UI.Shared.Components.Organisms
|
||||||
@@ -138,6 +139,26 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Detailed Content Block Showcase -->
|
||||||
|
<section class="architecture-guide-panel glass-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<h4>Architektura Systemu Nexus</h4>
|
||||||
|
<NexusIcon Name="book" Size="16" />
|
||||||
|
</div>
|
||||||
|
<div class="architecture-content">
|
||||||
|
<h3>.NET 10 & Blazor Hybrid Architecture</h3>
|
||||||
|
<p>
|
||||||
|
Nasza platforma została zaprojektowana w oparciu o najnowszy stos technologiczny <strong>.NET 10</strong> oraz model komponentowy <strong>Blazor</strong>, zapewniając pełną kompatybilność z kompilacją <strong>Native AOT</strong> (Ahead-Of-Time). Dzięki temu aplikacja charakteryzuje się błyskawicznym czasu uruchamiania i minimalnym zużyciem pamięci, co jest kluczowe w scenariuszach mobilnych i hybrydowych.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Wykorzystanie wzorca <strong>CQRS</strong> (Command Query Responsibility Segregation) wraz z biblioteką <strong>MediatR</strong> oddziela operacje odczytu od zapisu, gwarantując skalowalność i przejrzystość kodu. Wszystkie operacje biznesowe są reprezentowane przez niezależne procedury obsługi (handlers) zwracające unifikowany typ wyniku <code>Result<T></code>, eliminując rzucanie wyjątków dla przepływów sterowania.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Warstwa prezentacji opiera się na izolowanych komponentach Razor z dedykowanymi arkuszami stylów CSS, co ułatwia zarządzanie modularnym i rozszerzalnym interfejsem użytkownika w duchu Modern Deep Dark.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #0D0D0D;
|
background: #0d0d0d;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-grid-bg {
|
.header-grid-bg {
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: -5px;
|
inset: -5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--nexus-neon);
|
background: #10b981;
|
||||||
filter: blur(20px);
|
filter: blur(20px);
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@@ -102,13 +103,13 @@
|
|||||||
|
|
||||||
.status-pill {
|
.status-pill {
|
||||||
padding: 0.6rem 1.25rem;
|
padding: 0.6rem 1.25rem;
|
||||||
background: rgba(0, 255, 153, 0.05);
|
background: rgba(16, 185, 129, 0.05);
|
||||||
border: 1px solid rgba(0, 255, 153, 0.3);
|
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
box-shadow: 0 0 15px rgba(0, 255, 153, 0.1);
|
box-shadow: 0 0 15px rgba(16, 185, 129, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pill-label { color: #A0A0A0; }
|
.pill-label { color: #A0A0A0; }
|
||||||
@@ -136,25 +137,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.glass-panel {
|
.glass-panel {
|
||||||
background: rgba(20, 20, 20, 0.8); /* Fallback for browsers without backdrop-filter */
|
background: #1a1a1e;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
border-radius: 20px;
|
border-radius: 12px;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@supports (backdrop-filter: blur(10px)) {
|
|
||||||
.glass-panel {
|
|
||||||
background: rgba(255, 255, 255, 0.03);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.glass-panel:hover {
|
.glass-panel:hover {
|
||||||
background: rgba(255, 255, 255, 0.05);
|
background: #1e1e24;
|
||||||
border-color: rgba(0, 255, 153, 0.2);
|
border-color: rgba(16, 185, 129, 0.2);
|
||||||
transform: translateY(-4px);
|
transform: translateY(-4px);
|
||||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reading Card */
|
/* Reading Card */
|
||||||
@@ -373,7 +367,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-nexus.primary {
|
.btn-nexus.primary {
|
||||||
background: var(--nexus-neon);
|
background: #10b981;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,3 +600,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Architecture Guide Block --- */
|
||||||
|
.architecture-guide-panel {
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
background: #1a1a1e;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-content {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-family: var(--nexus-font-sans, "Outfit", sans-serif);
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-content h3 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ffffff;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-content p {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #e4e4e7;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.architecture-content code {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: #10b981;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+22
-26
@@ -1,4 +1,4 @@
|
|||||||
@page "/library"
|
@page "/my-books"
|
||||||
@attribute [Authorize]
|
@attribute [Authorize]
|
||||||
@using NexusReader.UI.Shared.Components.Organisms
|
@using NexusReader.UI.Shared.Components.Organisms
|
||||||
@using NexusReader.Application.DTOs.User
|
@using NexusReader.Application.DTOs.User
|
||||||
@@ -7,26 +7,26 @@
|
|||||||
@inject HttpClient Http
|
@inject HttpClient Http
|
||||||
@inject IReaderNavigationService ReaderNavigation
|
@inject IReaderNavigationService ReaderNavigation
|
||||||
|
|
||||||
<div class="library-page">
|
<div class="my-books-page">
|
||||||
<header class="library-header">
|
<header class="my-books-header">
|
||||||
<div class="header-title-section">
|
<div class="header-title-section">
|
||||||
<h1>Moja Biblioteka</h1>
|
<h1>Moje Książki</h1>
|
||||||
<p class="subtitle">Zarządzaj swoją kolekcją e-booków i rozwijaj strukturę wiedzy z Nexus AI</p>
|
<p class="subtitle">Twoje aktywne lektury i postępy w nauce z Nexus AI</p>
|
||||||
</div>
|
</div>
|
||||||
<AuthorizeView Roles="Admin, ContentManager">
|
<AuthorizeView Roles="Admin, ContentManager">
|
||||||
<NexusButton Class="add-book-trigger" OnClick="() => _isModalOpen = true">
|
<button class="btn-nexus add-book-trigger" @onclick="() => _isModalOpen = true">
|
||||||
<span class="btn-icon">+</span> Dodaj E-book
|
<span class="btn-icon">+</span> Dodaj E-book
|
||||||
</NexusButton>
|
</button>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<BookIngestionModal @bind-IsOpen="_isModalOpen" @bind-IsOpen:after="RefreshLibrary" />
|
<BookIngestionModal @bind-IsOpen="_isModalOpen" @bind-IsOpen:after="RefreshLibrary" />
|
||||||
|
|
||||||
<div class="library-content">
|
<div class="my-books-content">
|
||||||
@if (_isLoading)
|
@if (_isLoading)
|
||||||
{
|
{
|
||||||
<div class="library-loading-container">
|
<div class="my-books-loading-container">
|
||||||
<div class="loader-card glass-panel">
|
<div class="loader-card">
|
||||||
<div class="spinner-glow small"></div>
|
<div class="spinner-glow small"></div>
|
||||||
<span class="loader-text">Wczytywanie biblioteki...</span>
|
<span class="loader-text">Wczytywanie biblioteki...</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<div class="loading-grid">
|
<div class="loading-grid">
|
||||||
@for (int i = 0; i < 3; i++)
|
@for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
<div class="skeleton-card glass-panel">
|
<div class="skeleton-card">
|
||||||
<div class="skeleton-cover"></div>
|
<div class="skeleton-cover"></div>
|
||||||
<div class="skeleton-details">
|
<div class="skeleton-details">
|
||||||
<div class="skeleton-line title"></div>
|
<div class="skeleton-line title"></div>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
}
|
}
|
||||||
else if (_books == null || !_books.Any())
|
else if (_books == null || !_books.Any())
|
||||||
{
|
{
|
||||||
<div class="empty-state-container glass-panel">
|
<div class="empty-state-container">
|
||||||
<div class="empty-icon-pulse">
|
<div class="empty-icon-pulse">
|
||||||
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
|
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
|
||||||
@@ -56,17 +56,8 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3>Pusta biblioteka</h3>
|
<h3>Pusta biblioteka</h3>
|
||||||
<p>Nie masz jeszcze żadnych książek w swojej kolekcji.</p>
|
<p>Nie masz jeszcze żadnych książek na swojej półce. Przejdź do katalogu, aby rozpocząć kurs.</p>
|
||||||
<AuthorizeView Roles="Admin, ContentManager">
|
<a href="/catalog" class="btn-nexus catalog-btn">Przeglądaj Katalog</a>
|
||||||
<Authorized>
|
|
||||||
<button class="btn-nexus btn-nexus-primary" @onclick="() => _isModalOpen = true">
|
|
||||||
Prześlij pierwszą książkę
|
|
||||||
</button>
|
|
||||||
</Authorized>
|
|
||||||
<NotAuthorized>
|
|
||||||
<p class="restricted-info">Skontaktuj się z administratorem, aby dodać książki do swojego konta.</p>
|
|
||||||
</NotAuthorized>
|
|
||||||
</AuthorizeView>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -74,7 +65,7 @@
|
|||||||
<div class="books-grid">
|
<div class="books-grid">
|
||||||
@foreach (var book in _books)
|
@foreach (var book in _books)
|
||||||
{
|
{
|
||||||
<div class="book-card glass-panel" @onclick="() => OpenBook(book.Id)">
|
<div class="book-card" @onclick="() => OpenBook(book.Id)">
|
||||||
<div class="book-cover-container">
|
<div class="book-cover-container">
|
||||||
<img src="@(book.CoverUrl ?? "https://api.dicebear.com/7.x/identicon/svg?seed=" + book.Title)" alt="@book.Title" class="book-cover" />
|
<img src="@(book.CoverUrl ?? "https://api.dicebear.com/7.x/identicon/svg?seed=" + book.Title)" alt="@book.Title" class="book-cover" />
|
||||||
<div class="cover-overlay">
|
<div class="cover-overlay">
|
||||||
@@ -98,6 +89,12 @@
|
|||||||
{
|
{
|
||||||
<span class="new-badge">Nowa</span>
|
<span class="new-badge">Nowa</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
<button class="btn-nexus primary-accent-btn">
|
||||||
|
KONTYNUUJ KURS
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -131,7 +128,7 @@
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[Library] Failed to load books: {ex.Message}");
|
Console.WriteLine($"[MyBooks] Failed to load books: {ex.Message}");
|
||||||
if (OperatingSystem.IsBrowser())
|
if (OperatingSystem.IsBrowser())
|
||||||
{
|
{
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
@@ -145,7 +142,6 @@
|
|||||||
|
|
||||||
private async Task RefreshLibrary()
|
private async Task RefreshLibrary()
|
||||||
{
|
{
|
||||||
// Refresh when modal closes or when a book is successfully ingested
|
|
||||||
await LoadBooksAsync();
|
await LoadBooksAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
+107
-90
@@ -1,11 +1,11 @@
|
|||||||
.library-page {
|
.my-books-page {
|
||||||
padding: 3rem 2rem;
|
padding: 3rem 2rem;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
animation: fadeIn 0.6s ease-out;
|
animation: fadeIn 0.6s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.library-header {
|
.my-books-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -16,35 +16,36 @@
|
|||||||
|
|
||||||
.header-title-section h1 {
|
.header-title-section h1 {
|
||||||
font-family: var(--nexus-font-serif);
|
font-family: var(--nexus-font-serif);
|
||||||
font-size: 2.8rem;
|
font-size: 2.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 0 0 0.5rem 0;
|
margin: 0 0 0.5rem 0;
|
||||||
background: linear-gradient(135deg, var(--nexus-text) 0%, rgba(255, 255, 255, 0.7) 100%);
|
color: #ffffff;
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
letter-spacing: -0.5px;
|
letter-spacing: -0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-title-section .subtitle {
|
.header-title-section .subtitle {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: #a1a1aa;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-book-trigger {
|
.add-book-trigger {
|
||||||
background: var(--nexus-neon) !important;
|
background: transparent;
|
||||||
color: #000000 !important;
|
color: #10b981;
|
||||||
border: none !important;
|
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||||
box-shadow: 0 4px 15px var(--nexus-primary-glow) !important;
|
padding: 0.75rem 1.5rem;
|
||||||
font-weight: 600 !important;
|
font-size: 0.9rem;
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
font-weight: 600;
|
||||||
border-radius: var(--radius-md) !important;
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-book-trigger:hover {
|
.add-book-trigger:hover {
|
||||||
transform: translateY(-2px) !important;
|
background: rgba(16, 185, 129, 0.05);
|
||||||
box-shadow: 0 8px 20px rgba(0, 255, 153, 0.5) !important;
|
border-color: #10b981;
|
||||||
filter: brightness(1.1);
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(16, 185, 129, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-icon {
|
.btn-icon {
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
.books-grid, .loading-grid {
|
.books-grid, .loading-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
gap: 2rem;
|
gap: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.book-card {
|
.book-card {
|
||||||
@@ -65,34 +66,22 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: var(--radius-lg);
|
border-radius: 12px;
|
||||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
background: #1a1a1e;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.book-card::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background: radial-gradient(800px circle at var(--x, 0) var(--y, 0), rgba(255, 255, 255, 0.06), transparent 40%);
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.5s;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.book-card:hover {
|
.book-card:hover {
|
||||||
transform: translateY(-8px) scale(1.02);
|
transform: translateY(-4px);
|
||||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3), 0 0 2px rgba(255, 255, 255, 0.1) inset;
|
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3);
|
||||||
border-color: rgba(0, 255, 153, 0.2);
|
border-color: rgba(16, 185, 129, 0.2);
|
||||||
}
|
|
||||||
|
|
||||||
.book-card:hover::before {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.book-cover-container {
|
.book-cover-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 380px;
|
height: 360px;
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -105,17 +94,17 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.book-card:hover .book-cover {
|
.book-card:hover .book-cover {
|
||||||
transform: scale(1.08);
|
transform: scale(1.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cover-overlay {
|
.cover-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(15, 23, 42, 0.6);
|
background: rgba(13, 13, 15, 0.6);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -131,8 +120,8 @@
|
|||||||
.read-action {
|
.read-action {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 1.1rem;
|
font-size: 1rem;
|
||||||
padding: 0.75rem 1.5rem;
|
padding: 0.6rem 1.25rem;
|
||||||
border: 2px solid #ffffff;
|
border: 2px solid #ffffff;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
transform: translateY(10px);
|
transform: translateY(10px);
|
||||||
@@ -142,7 +131,7 @@
|
|||||||
.book-card:hover .read-action {
|
.book-card:hover .read-action {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
color: #0f172a;
|
color: #121214;
|
||||||
}
|
}
|
||||||
|
|
||||||
.book-details {
|
.book-details {
|
||||||
@@ -150,35 +139,34 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
background: rgba(15, 23, 42, 0.3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.book-title {
|
.book-title {
|
||||||
font-size: 1.25rem;
|
font-size: 1.2rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: 0 0 0.5rem 0;
|
margin: 0 0 0.4rem 0;
|
||||||
color: var(--nexus-text);
|
color: #ffffff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-family: var(--nexus-font-sans);
|
font-family: var(--nexus-font-sans, "Outfit", sans-serif);
|
||||||
}
|
}
|
||||||
|
|
||||||
.book-author {
|
.book-author {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: rgba(255, 255, 255, 0.5);
|
color: #a1a1aa;
|
||||||
margin: 0 0 1rem 0;
|
margin: 0 0 1.25rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.new-badge {
|
.new-badge {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--nexus-primary);
|
color: #10b981;
|
||||||
background: rgba(0, 255, 153, 0.15);
|
background: rgba(16, 185, 129, 0.1);
|
||||||
padding: 0.25rem 0.75rem;
|
padding: 0.25rem 0.75rem;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
border: 1px solid rgba(0, 255, 153, 0.3);
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Book Progress Bar */
|
/* Book Progress Bar */
|
||||||
@@ -191,25 +179,51 @@
|
|||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.05);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-fill {
|
.progress-fill {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(90deg, var(--nexus-neon) 0%, #00ccff 100%);
|
background: #10b981;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-text {
|
.progress-text {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: rgba(255, 255, 255, 0.4);
|
color: #a1a1aa;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-actions {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-accent-btn {
|
||||||
|
width: 100%;
|
||||||
|
background: #10b981;
|
||||||
|
color: #0d0d0d;
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-accent-btn:hover {
|
||||||
|
background: #059669;
|
||||||
|
color: #ffffff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(16, 185, 129, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
/* Empty State */
|
/* Empty State */
|
||||||
.empty-state-container {
|
.empty-state-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -218,12 +232,14 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 5rem 2rem;
|
padding: 5rem 2rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: var(--radius-lg);
|
background: #1a1a1e;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-icon-pulse {
|
.empty-icon-pulse {
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
color: rgba(255, 255, 255, 0.2);
|
color: #a1a1aa;
|
||||||
animation: pulse 3s infinite alternate;
|
animation: pulse 3s infinite alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,35 +247,45 @@
|
|||||||
font-family: var(--nexus-font-serif);
|
font-family: var(--nexus-font-serif);
|
||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
margin: 0 0 0.5rem 0;
|
margin: 0 0 0.5rem 0;
|
||||||
color: var(--nexus-text);
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state-container p {
|
.empty-state-container p {
|
||||||
color: rgba(255, 255, 255, 0.5);
|
color: #a1a1aa;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
margin: 0 0 2rem 0;
|
margin: 0 0 2rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.restricted-info {
|
.catalog-btn {
|
||||||
font-size: 0.85rem;
|
background: #10b981;
|
||||||
font-style: italic;
|
color: #0d0d0d;
|
||||||
color: rgba(255, 255, 255, 0.35) !important;
|
padding: 0.75rem 2rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-btn:hover {
|
||||||
|
background: #059669;
|
||||||
|
color: #ffffff;
|
||||||
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Skeleton Loading */
|
/* Skeleton Loading */
|
||||||
.skeleton-card {
|
.skeleton-card {
|
||||||
border-radius: var(--radius-lg);
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 480px;
|
height: 480px;
|
||||||
background: rgba(255, 255, 255, 0.02) !important;
|
background: #1a1a1e;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.05) !important;
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15) !important;
|
opacity: 0.6;
|
||||||
opacity: 0.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton-cover {
|
.skeleton-cover {
|
||||||
height: 380px;
|
height: 360px;
|
||||||
background: linear-gradient(90deg, rgba(255,255,255,0.04) 25%, rgba(255,255,255,0.12) 50%, rgba(255,255,255,0.04) 75%) !important;
|
background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%);
|
||||||
background-size: 200% 100%;
|
background-size: 200% 100%;
|
||||||
animation: loading 1.5s infinite;
|
animation: loading 1.5s infinite;
|
||||||
}
|
}
|
||||||
@@ -272,7 +298,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.skeleton-line {
|
.skeleton-line {
|
||||||
background: linear-gradient(90deg, rgba(255,255,255,0.04) 25%, rgba(255,255,255,0.12) 50%, rgba(255,255,255,0.04) 75%) !important;
|
background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%);
|
||||||
background-size: 200% 100%;
|
background-size: 200% 100%;
|
||||||
animation: loading 1.5s infinite;
|
animation: loading 1.5s infinite;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -294,12 +320,12 @@
|
|||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.library-loading-container {
|
.my-books-loading-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.library-loading-container .loader-card {
|
.my-books-loading-container .loader-card {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 180px;
|
top: 180px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@@ -310,35 +336,26 @@
|
|||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
padding: 1.25rem 2.25rem;
|
padding: 1.25rem 2.25rem;
|
||||||
border-radius: 40px;
|
border-radius: 40px;
|
||||||
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4), 0 0 1px rgba(255, 255, 255, 0.15) inset;
|
background: rgba(13, 13, 15, 0.85);
|
||||||
background: rgba(15, 23, 42, 0.75);
|
|
||||||
backdrop-filter: blur(16px);
|
backdrop-filter: blur(16px);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
animation: scaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
animation: scaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner-glow {
|
.spinner-glow {
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
border: 3px solid rgba(0, 255, 153, 0.1);
|
|
||||||
border-radius: 50%;
|
|
||||||
border-top-color: var(--nexus-neon);
|
|
||||||
animation: spin 1s cubic-bezier(0.55, 0.055, 0.675, 0.19) infinite;
|
|
||||||
box-shadow: 0 0 15px rgba(0, 255, 153, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner-glow.small {
|
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
border-width: 2px;
|
border: 2px solid rgba(16, 185, 129, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: #10b981;
|
||||||
|
animation: spin 1s cubic-bezier(0.55, 0.055, 0.675, 0.19) infinite;
|
||||||
|
box-shadow: 0 0 15px rgba(16, 185, 129, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader-text {
|
.loader-text {
|
||||||
font-family: var(--nexus-font-sans);
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
letter-spacing: 0.2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* Animations */
|
||||||
Reference in New Issue
Block a user