2 Commits

Author SHA1 Message Date
mjasin 39d9423d67 fix(ui): implement ServerIdentityService to resolve SSR profile fetch issues
- Added ServerIdentityService to handle profile requests directly from DB on server
- Updated Program.cs to use ServerIdentityService in the Web project
- Cleaned up diagnostic logs across the solution
- Finalized Dashboard glass panel animations
2026-05-10 09:49:44 +02:00
mjasin ab605dff42 chore: add diagnostic console logging to identity service and UI components 2026-05-10 09:39:26 +02:00
4 changed files with 88 additions and 2 deletions
+4 -1
View File
@@ -43,4 +43,7 @@ description: Design System & Component rules for Blazor
- **Interactive Flow:** - **Interactive Flow:**
- AI Assistant interactions must be non-blocking and smoothly transition using CSS animations. - AI Assistant interactions must be non-blocking and smoothly transition using CSS animations.
- Interactive elements must have clear `:hover`, `:active`, and `:focus` states. - Interactive elements must have clear `:hover`, `:active`, and `:focus` states.
- **Glass Panel Standard:** All primary data panels (`.glass-panel`) must implement the following interaction signature:
- `transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)`
- `:hover` state must include: `transform: translateY(-4px)`, increased background opacity, and a subtle `--nexus-neon` border highlight (e.g., `rgba(0, 255, 153, 0.2)`).
@@ -128,6 +128,14 @@
border: 1px solid rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 20px; border-radius: 20px;
padding: 1.5rem; padding: 1.5rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.glass-panel:hover {
background: rgba(255, 255, 255, 0.05);
border-color: rgba(0, 255, 153, 0.2);
transform: translateY(-4px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
} }
/* Reading Card */ /* Reading Card */
+2 -1
View File
@@ -52,7 +52,8 @@ builder.Services.AddHttpClient("NexusAPI", client =>
}); });
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("NexusAPI")); builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("NexusAPI"));
builder.Services.AddScoped<IIdentityService, NexusReader.UI.Shared.Services.IdentityService>(); builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<IIdentityService, NexusReader.Web.New.Services.ServerIdentityService>();
builder.Services.AddCascadingAuthenticationState(); builder.Services.AddCascadingAuthenticationState();
builder.Services.AddApplication(); builder.Services.AddApplication();
@@ -0,0 +1,74 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using NexusReader.Application.DTOs.User;
using NexusReader.Data.Persistence;
using NexusReader.Domain.Entities;
using NexusReader.UI.Shared.Services;
namespace NexusReader.Web.New.Services;
public class ServerIdentityService : IIdentityService
{
private readonly UserManager<NexusUser> _userManager;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
public ServerIdentityService(
UserManager<NexusUser> userManager,
IHttpContextAccessor httpContextAccessor,
IDbContextFactory<AppDbContext> dbContextFactory)
{
_userManager = userManager;
_httpContextAccessor = httpContextAccessor;
_dbContextFactory = dbContextFactory;
}
public Task<bool> LoginAsync(string email, string password, bool rememberMe = false)
=> throw new NotSupportedException("Use standard Identity endpoints for login on server.");
public Task LogoutAsync()
=> throw new NotSupportedException("Use standard Identity endpoints for logout on server.");
public Task<bool> RegisterAsync(string email, string password)
=> throw new NotSupportedException("Use standard Identity endpoints for registration on server.");
public Task<bool> RefreshTokenAsync() => Task.FromResult(true);
public async Task<UserProfile?> GetProfileAsync()
{
var user = _httpContextAccessor.HttpContext?.User;
if (user == null || !user.Identity?.IsAuthenticated == true) return null;
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId == null) return null;
using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var profile = await dbContext.Users
.Where(u => u.Id == userId)
.Select(u => new UserProfile(
u.Email ?? string.Empty,
u.AITokensUsed,
u.TenantId != null && u.TenantId.Length == 36 ? new Guid(u.TenantId) : Guid.Empty,
u.SubscriptionPlan != null ? new SubscriptionPlanDto
{
Id = u.SubscriptionPlan.Id,
Name = u.SubscriptionPlan.PlanName,
AITokenLimit = u.SubscriptionPlan.AITokenLimit,
MonthlyPrice = u.SubscriptionPlan.MonthlyPrice
} : new SubscriptionPlanDto(),
u.QuizResults.Any(q => q.TotalQuestions > 0)
? (int)u.QuizResults.Where(q => q.TotalQuestions > 0).Average(q => (double)q.Score / q.TotalQuestions * 100)
: 0,
u.Ebooks.OrderByDescending(e => e.LastReadDate).Select(e => new LastReadBookDto
{
Id = e.Id,
Title = e.Title
}).FirstOrDefault()
))
.FirstOrDefaultAsync();
return profile;
}
}