Add soft delete + trash, keyboard-first web overhaul, move chapter tags to bottom
CI / build-and-push (push) Failing after 31s
CI / deploy (push) Has been skipped

Adds SoftDelete/Trash across characters, chapters, locations, beats with a
purge schedule and Trash page. Reworks the web client for keyboard-driven
navigation (focus helpers, help overlay, keyboard.md doc). Moves the
ChapterPage tag editor to the bottom of the page to match CharacterDetailPage.
This commit is contained in:
James Wampler
2026-08-20 16:39:09 -07:00
parent 7df1fffdca
commit aca26588f9
59 changed files with 2913 additions and 170 deletions
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Options;
using Novelly.Api.Activity;
using Novelly.Api.Beats;
using Novelly.Api.Chapters;
@@ -7,6 +8,7 @@ using Novelly.Api.Locations;
using Novelly.Api.Novels;
using Novelly.Api.Questions;
using Novelly.Api.Tags;
using Novelly.Api.Trash;
using Novelly.Api.Users;
namespace Novelly.Api.Tests;
@@ -28,6 +30,8 @@ public abstract class ServiceTestFixture
protected CharacterArcService Arcs { get; private set; } = null!;
protected OpenQuestionService Questions { get; private set; } = null!;
protected GenreService Genres { get; private set; } = null!;
protected TrashService Trash { get; private set; } = null!;
protected TrashOptions TrashOptions { get; private set; } = null!;
protected CapturingLogger<NovelService> NovelLogs { get; private set; } = null!;
protected CapturingLogger<CharacterService> CharacterLogs { get; private set; } = null!;
@@ -38,6 +42,7 @@ public abstract class ServiceTestFixture
protected CapturingLogger<CharacterArcService> ArcLogs { get; private set; } = null!;
protected CapturingLogger<OpenQuestionService> QuestionLogs { get; private set; } = null!;
protected CapturingLogger<GenreService> GenreLogs { get; private set; } = null!;
protected CapturingLogger<TrashService> TrashLogs { get; private set; } = null!;
[SetUp]
public void SetUpFixture()
@@ -67,6 +72,7 @@ public abstract class ServiceTestFixture
ArcLogs = new CapturingLogger<CharacterArcService>();
QuestionLogs = new CapturingLogger<OpenQuestionService>();
GenreLogs = new CapturingLogger<GenreService>();
TrashLogs = new CapturingLogger<TrashService>();
Tags = new TagService(Db.Context, Access, ActivityLog, TagLogs, new CreateTagRequestValidator(), new UpdateTagRequestValidator());
Locations = new LocationService(Db.Context, Access, ActivityLog, LocationLogs, new CreateLocationRequestValidator(), new UpdateLocationRequestValidator());
@@ -90,6 +96,8 @@ public abstract class ServiceTestFixture
Db.Context, Access, ActivityLog, QuestionLogs,
new CreateOpenQuestionRequestValidator(), new UpdateOpenQuestionRequestValidator(), new ResolveOpenQuestionRequestValidator());
Genres = new GenreService(Db.Context, GenreLogs);
TrashOptions = new TrashOptions();
Trash = new TrashService(Db.Context, Access, ActivityLog, Options.Create(TrashOptions), TrashLogs);
OnSetUp();
}