feat(creator): overhaul Creator flow, editor duplication, and staging setup #83
Reference in New Issue
Block a user
Delete Branch "feature/stage3-book-versioning"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This pull request completely overhauls the Creator editor flow, resolves the editor duplication race condition, aligns layout/styling themes in light and dark mode, and adds Docker staging setups.
Key Changes
window.editorCache,window.editorStates) and checking_lastInitializedEditorIdsynchronously in Blazor.ThemeService.InitializeAsync()) and anchored CSS isolation selectors to correctly sync with Light (Soft Sepia) and Deep Dark theme preferences.--nexus-onlyflag to allow iterative development without resetting PG/Neo4j database containers.- Relocate dashboard routing to /creator and editor workspace to /creator/edit/{BookId} - Implement CreateBookCommand and handler with transactional default chapter seeding - Implement PublishBookVersionCommand and GetCreatorDashboardDataQuery - Build CreatorDashboard modal and UI components with customized dark input styles - Add run-stage.sh script to automate staging environment setup, database migrations, and health checks - Update developer workflow rules in GEMINI.mdI have reviewed the changes in PR #83. Since this is my own PR, I cannot submit a formal 'Request Changes' request, but I have left inline comments pointing out key issues including a concurrency race condition in the editor's debounce timer and some violations of the project's strict Result Pattern architecture.
@@ -0,0 +38,4 @@if (book == null){throw new BookNotFoundException(request.BookId);🟡 Design/Architecture: Violation of Result Pattern
Throwing
BookNotFoundExceptioninside the handler violates the project's strict architecture rule:Result Pattern: Zero exceptions for flow control. All handlers return Result<T> via FluentResult.Suggested Fix:
Instead of throwing, return
Result.Fail(...)containing an error message or a dedicated error class:Remember to update the MapPost endpoint in
Program.csand the tests as well.@@ -0,0 +41,4 @@if (!bookExists){throw new BookNotFoundException(request.BookId);🟡 Design/Architecture: Violation of Result Pattern
Throwing
BookNotFoundExceptioninside the query handler violates the project's Result Pattern rule.Suggested Fix:
Instead of throwing, return
Result.Fail(...):@@ -86,0 +374,4 @@}// Start 5-second idle debounce timer_ = Task.Run(async () =>🔴 Blocking: Race Condition in Debounce Timer
The background task reads
_debounceCts.Tokenfrom the shared component state after the task starts executing on the thread pool. If another keystroke happens in the meantime, it will read the new token.Suggested Fix:
Capture the token synchronously on the UI thread before calling
Task.Runand use the captured token directly inside the task:@@ -86,0 +399,4 @@});}private async Task TriggerAutosaveAsync(string markdown, CancellationToken token)🟢 Minor/Suggestion: Missing component disposal check
TriggerAutosaveAsyncruns on a background thread and callsawait InvokeAsync(StateHasChanged). If the component has been disposed before or during the save operation, callingStateHasChangedmay result in runtime warnings/exceptions.Suggested Fix:
Define a
_disposedboolean flag, set it totrueinDisposeAsync, and check it before updating state:@@ -0,0 +212,4 @@// Act & Assertvar action = () => handler.Handle(command, CancellationToken.None);await action.Should().ThrowAsync<BookNotFoundException>();🟡 Design/Architecture: Update test for Result Pattern alignment
Once the handler is refactored to return
Result.Failinstead of throwingBookNotFoundException, update this test to assert failure on the returned result rather than expecting an exception.Suggested Fix:
@@ -0,0 +263,4 @@// Act & Assertvar queryMismatchedTenant = new GetBookRevisionsQuery(bookId, userId, "different-tenant");var actionTenant = () => handler.Handle(queryMismatchedTenant, CancellationToken.None);await actionTenant.Should().ThrowAsync<BookNotFoundException>();🟡 Design/Architecture: Update test for Result Pattern alignment
Update this test to assert failure on the returned result rather than expecting an exception once the query handler is refactored.
Suggested Fix:
I have resolved all 6 inline comments in the latest push:
CancellationTokensynchronously under lock on the UI thread before startingTask.Run._disposedcheck inTriggerAutosaveAsyncand before updating state after async calls, setting it inDisposeAsync.BookNotFoundExceptionthrows inPublishBookVersionCommandHandlerandGetBookRevisionsQueryHandlerwithResult.Fail.Program.csto returnResults.NotFound(errorMsg)when a Result fail contains'was not found'instead of catching the exception.PublishBookVersionTests.csandCreatorDashboardTests.csto assert result failure rather than expecting thrown exceptions. Verified all 54 tests pass successfully.