feat: Refactor dashboard screens to Modern Deep Dark style (#74)
Refactored Pulpit, Katalog, Moje, Konto screens to a unified, premium Modern Deep Dark style. Resolves #73. --------- Co-authored-by: Marek Jasiński <jasins.marek@gmail.com> Reviewed-on: #74 Co-authored-by: Antigravity <antigravity@google.com> Co-committed-by: Antigravity <antigravity@google.com>
This commit was merged in pull request #74.
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user