feat(intelligence): implement Global AI Q&A screen and paywall blocker

- Implemented standard empty and active chat conversation states for the `/intelligence` page
- Created interactive `AiResponseRenderer` with AOT-compliant sentence splitting and payment gateway simulation
- Added scoped `LibraryStateService` to synchronize book ownership and updates across the application
- Obfuscated paywalled content in DOM to prevent inspection bypass
- Fixed local port connection mismatch by updating API configurations to use port 5104
This commit is contained in:
2026-06-06 10:41:48 +02:00
parent bcd5daa3a0
commit faf6ec826e
15 changed files with 932 additions and 318 deletions
+44
View File
@@ -57,6 +57,7 @@ builder.Services.AddScoped<IReaderNavigationService, ReaderNavigationService>();
builder.Services.AddScoped<IKnowledgeGraphService, KnowledgeGraphService>();
builder.Services.AddScoped<IReaderInteractionService, ReaderInteractionService>();
builder.Services.AddScoped<IReaderStateService, ReaderStateService>();
builder.Services.AddScoped<ILibraryStateService, LibraryStateService>();
builder.Services.AddScoped<KnowledgeCoordinator>();
builder.Services.AddScoped<ISyncService, SyncService>();
@@ -454,6 +455,48 @@ app.MapGet("/api/library/books", async (ClaimsPrincipal user, IMediator mediator
return Results.BadRequest(errorMsg);
}).RequireAuthorization();
app.MapPost("/api/library/purchase", async (
ClaimsPrincipal user,
[FromBody] PurchaseBookRequest request,
IDbContextFactory<AppDbContext> dbContextFactory) =>
{
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrEmpty(userId)) return Results.Unauthorized();
using var dbContext = await dbContextFactory.CreateDbContextAsync();
// Find or create author
var authorName = "Nexus Architect";
var author = await dbContext.Authors.FirstOrDefaultAsync(a => a.Name == authorName);
if (author == null)
{
author = new Author { Name = authorName };
dbContext.Authors.Add(author);
await dbContext.SaveChangesAsync();
}
// Check if the book already exists for the user
var bookExists = await dbContext.Ebooks.AnyAsync(e => e.UserId == userId && e.Title == request.Title);
if (!bookExists)
{
var newBook = new Ebook
{
Title = request.Title,
AuthorId = author.Id,
UserId = userId,
FilePath = "wwwroot/assets/book.epub",
AddedDate = DateTime.UtcNow,
Progress = 0,
Description = "Zaawansowany kurs budowania skalowalnych SaaS z Native AOT, CQRS, MediatR, FluentResults i izolowanym systemem stylów Blazor CSS.",
IsReadyForReading = true
};
dbContext.Ebooks.Add(newBook);
await dbContext.SaveChangesAsync();
}
return Results.Ok();
}).RequireAuthorization();
app.MapGet("/api/book/{bookId:guid}/concepts-map", async (
Guid bookId,
ClaimsPrincipal user,
@@ -729,3 +772,4 @@ public record KnowledgeRequest(string Text, Guid? EbookId = null);
public record GroundednessRequest(string Answer, string Context);
public record SemanticSearchRequest(string QueryText, int Limit = 5);
public record AskQuestionRequest(string Question, Guid? EbookId = null, int Limit = 5);
public record PurchaseBookRequest(string Title);