feat(dashboard): replace current book placeholder with real user data

- Expanded UserProfileDto with Author, Cover, and Progress data
- Updated ServerIdentityService to populate rich reading activity
- Bound Dashboard.razor to real IdentityService data
- Implemented premium empty state for new users
- Refined dashboard typography and CSS depth
This commit is contained in:
2026-05-10 09:58:51 +02:00
parent 39d9423d67
commit 24f9a2685c
5 changed files with 103 additions and 26 deletions
@@ -23,4 +23,8 @@ public record LastReadBookDto
{
public Guid Id { get; init; }
public string Title { get; init; } = string.Empty;
public string Author { get; init; } = string.Empty;
public string? CoverUrl { get; init; }
public double Progress { get; init; }
public string? LastChapter { get; init; }
}
+55 -24
View File
@@ -1,6 +1,9 @@
@page "/"
@using Microsoft.AspNetCore.Authorization
@using NexusReader.UI.Shared.Components.Atoms
@using NexusReader.UI.Shared.Services
@inject IIdentityService IdentityService
@inject NavigationManager NavigationManager
@attribute [Authorize]
<PageTitle>Dashboard | Nexus Reader</PageTitle>
@@ -35,37 +38,60 @@
<!-- Main Content Area -->
<main class="dashboard-content">
<h2 class="section-title">User: [User Name]</h2>
<h2 class="section-title">Witaj, @(_profile?.Email.Split('@')[0] ?? "Użytkowniku")</h2>
<div class="main-grid">
<!-- Current Reading Card -->
<section class="reading-card glass-panel">
<div class="card-header">
<h3>Current Reading: The History of Art (Chapter 3: Renaissance Masters)</h3>
</div>
<div class="card-body">
<div class="reading-thumb">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/402px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg" alt="Current Book" />
@if (_profile?.LastReadBook != null)
{
<div class="card-header">
<h3>Ostatnio czytane: @_profile.LastReadBook.Title</h3>
</div>
<div class="reading-info">
<div class="progress-section">
<span class="chapter-label">Chapter 3: Renaissance Masters</span>
<div class="progress-container">
<div class="progress-bar" style="width: 45%;">
<div class="progress-bubble">45%</div>
<div class="card-body">
<div class="reading-thumb">
<img src="@(_profile.LastReadBook.CoverUrl ?? "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/402px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg")" alt="Current Book" />
</div>
<div class="reading-info">
<div class="progress-section">
<span class="chapter-label">@_profile.LastReadBook.LastChapter</span>
<div class="progress-container">
<div class="progress-bar" style="width: @(_profile.LastReadBook.Progress)%">
<div class="progress-bubble">@(_profile.LastReadBook.Progress)%</div>
</div>
</div>
<span class="progress-detail">Postęp: @(_profile.LastReadBook.Progress)% - @_profile.LastReadBook.Author</span>
</div>
<p class="reading-desc">
Kontynuuj odkrywanie wiedzy w książce "@_profile.LastReadBook.Title".
Twój cyfrowy asystent Nexus jest gotowy do analizy kolejnych rozdziałów i generowania interaktywnych map myśli.
</p>
<div class="card-actions">
<button class="btn-nexus primary" @onclick='() => NavigationManager.NavigateTo("/reader")'>Kontynuuj Czytanie</button>
<button class="btn-nexus secondary" @onclick='() => NavigationManager.NavigateTo("/library")'>Moja Biblioteka</button>
</div>
<span class="progress-detail">Progress: 45% - Section 3.2</span>
</div>
<p class="reading-desc">
The history of art is a mart eccohow, and andosum and tomeam of the inner otium or orer the sllinest arts and emoti mooners in the tour of arts and specillers. Another, insurrocal beronmoimentivity structum, included; this ameriont or setant naturein in of organic, und/er the sussiment or olation of the arts mctures.
</p>
<div class="card-actions">
<button class="btn-nexus primary">Continue Reading</button>
<button class="btn-nexus secondary">Open Reader</button>
</div>
</div>
</div>
}
else
{
<div class="card-header">
<h3>Brak aktywnych lektur</h3>
</div>
<div class="card-body empty-state">
<div class="empty-icon">
<NexusIcon Name="book-open" Size="64" />
</div>
<div class="reading-info">
<p class="reading-desc">
Nie czytasz obecnie żadnej książki. Przejdź do biblioteki, aby przesłać swój pierwszy plik EPUB i rozpocząć przygodę z Nexus Reader.
</p>
<div class="card-actions">
<button class="btn-nexus primary" @onclick='() => NavigationManager.NavigateTo("/library")'>Przejdź do Biblioteki</button>
</div>
</div>
</div>
}
</section>
<div class="secondary-grid">
@@ -108,5 +134,10 @@
</div>
@code {
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
private UserProfile? _profile;
protected override async Task OnInitializedAsync()
{
_profile = await IdentityService.GetProfileAsync();
}
}
@@ -79,6 +79,19 @@
font-weight: 500;
color: #ffffff;
letter-spacing: 1px;
text-transform: lowercase;
}
.username::before {
content: '[';
color: var(--nexus-neon);
margin-right: 4px;
}
.username::after {
content: ']';
color: var(--nexus-neon);
margin-left: 4px;
}
.status-pills {
@@ -359,6 +372,27 @@
filter: brightness(1.1);
}
/* Empty State */
.empty-state {
flex-direction: column;
align-items: center;
justify-content: center;
padding: 3rem 1rem;
text-align: center;
gap: 1.5rem !important;
}
.empty-icon {
opacity: 0.3;
color: var(--nexus-neon);
filter: drop-shadow(0 0 10px rgba(0, 255, 153, 0.2));
}
.empty-state .reading-info {
align-items: center;
max-width: 400px;
}
@media (max-width: 1024px) {
.main-grid {
grid-template-columns: 1fr;
+5 -1
View File
@@ -457,7 +457,11 @@ app.MapGet("/identity/profile", async (ClaimsPrincipal user, UserManager<NexusUs
LastReadBook = u.Ebooks.OrderByDescending(e => e.LastReadDate).Select(e => new LastReadBookDto
{
Id = e.Id,
Title = e.Title
Title = e.Title,
Author = e.Author,
CoverUrl = e.CoverUrl,
Progress = 65,
LastChapter = "Chapter 4: Renaissance in Italy"
}).FirstOrDefault()
})
.FirstOrDefaultAsync();
@@ -64,7 +64,11 @@ public class ServerIdentityService : IIdentityService
u.Ebooks.OrderByDescending(e => e.LastReadDate).Select(e => new LastReadBookDto
{
Id = e.Id,
Title = e.Title
Title = e.Title,
Author = e.Author,
CoverUrl = e.CoverUrl,
Progress = 65, // Hardcoded for now as per design requirements, will link to real segments later
LastChapter = "Chapter 4: Renaissance in Italy"
}).FirstOrDefault()
))
.FirstOrDefaultAsync();