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:
2026-06-06 07:58:59 +00:00
committed by Marek Jaisński
parent f6819d50b7
commit bcd5daa3a0
13 changed files with 959 additions and 272 deletions
@@ -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">&lt;.NET 10&gt;</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 &amp; 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 &amp; 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 &amp; 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");
}
}