Simplify active story intelligence navigation
This commit is contained in:
parent
a319df2455
commit
a6ca129f4d
@ -235,7 +235,9 @@ var tests = new (string Name, Action Test)[]
|
|||||||
("Story Intelligence global progress uses whole run scene totals", StoryIntelligenceGlobalProgressUsesWholeRunSceneTotals),
|
("Story Intelligence global progress uses whole run scene totals", StoryIntelligenceGlobalProgressUsesWholeRunSceneTotals),
|
||||||
("Story Intelligence recovered provider failures stay operational", StoryIntelligenceRecoveredProviderFailuresStayOperational),
|
("Story Intelligence recovered provider failures stay operational", StoryIntelligenceRecoveredProviderFailuresStayOperational),
|
||||||
("Story Intelligence recovered JSON repair stays operational", StoryIntelligenceRecoveredJsonRepairStaysOperational),
|
("Story Intelligence recovered JSON repair stays operational", StoryIntelligenceRecoveredJsonRepairStaysOperational),
|
||||||
("Story Intelligence terminal failures remain user visible", StoryIntelligenceTerminalFailuresRemainUserVisible)
|
("Story Intelligence terminal failures remain user visible", StoryIntelligenceTerminalFailuresRemainUserVisible),
|
||||||
|
("Story Intelligence active progress only appears in global strip", StoryIntelligenceActiveProgressOnlyAppearsInGlobalStrip),
|
||||||
|
("Story Intelligence book actions do not open review centre while running", StoryIntelligenceBookActionsDoNotOpenReviewCentreWhileRunning)
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var test in tests)
|
foreach (var test in tests)
|
||||||
@ -432,6 +434,42 @@ static void StoryIntelligenceTerminalFailuresRemainUserVisible()
|
|||||||
Assert(globalViewModel.Contains("public bool HasFailures => FailedRunCount > 0 || FailedScenes > 0", StringComparison.Ordinal), "Global banner warning state should be driven by terminal persisted failures.");
|
Assert(globalViewModel.Contains("public bool HasFailures => FailedRunCount > 0 || FailedScenes > 0", StringComparison.Ordinal), "Global banner warning state should be driven by terminal persisted failures.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void StoryIntelligenceActiveProgressOnlyAppearsInGlobalStrip()
|
||||||
|
{
|
||||||
|
var root = PlotLineRoot();
|
||||||
|
var projectsView = File.ReadAllText(Path.Combine(root, "Views/Projects/Index.cshtml"));
|
||||||
|
var layout = File.ReadAllText(Path.Combine(root, "Views/Shared/_Layout.cshtml"));
|
||||||
|
var controller = File.ReadAllText(Path.Combine(root, "Controllers/OnboardingController.cs"));
|
||||||
|
|
||||||
|
Assert(!projectsView.Contains("Story Intelligence is reading", StringComparison.Ordinal), "Projects page should not duplicate the active Story Intelligence progress card.");
|
||||||
|
Assert(!projectsView.Contains("StoryIntelligenceProgress", StringComparison.Ordinal), "Projects page should not provide a second View progress target while the global strip is active.");
|
||||||
|
Assert(projectsView.Contains("HasPipelineBooks && !Model.StoryIntelligence.HasActiveImport", StringComparison.Ordinal), "Saved pipeline cards should be hidden while active progress is already shown globally.");
|
||||||
|
Assert(layout.Contains("data-story-intelligence-global-strip", StringComparison.Ordinal), "Active Story Intelligence progress should remain available in the shared page header.");
|
||||||
|
Assert(controller.Contains("return RedirectToAction(\"Details\", \"Books\", new { id = bookId });", StringComparison.Ordinal), "Starting permanent book analysis should return to the book page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StoryIntelligenceBookActionsDoNotOpenReviewCentreWhileRunning()
|
||||||
|
{
|
||||||
|
var root = PlotLineRoot();
|
||||||
|
var coreViewModels = File.ReadAllText(Path.Combine(root, "ViewModels/CoreViewModels.cs"));
|
||||||
|
var onboardingViewModels = File.ReadAllText(Path.Combine(root, "ViewModels/OnboardingViewModels.cs"));
|
||||||
|
var coreServices = File.ReadAllText(Path.Combine(root, "Services/CoreServices.cs"));
|
||||||
|
var storyService = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceService.cs"));
|
||||||
|
var bookView = File.ReadAllText(Path.Combine(root, "Views/Books/Details.cshtml"));
|
||||||
|
var projectsView = File.ReadAllText(Path.Combine(root, "Views/Projects/Index.cshtml"));
|
||||||
|
|
||||||
|
Assert(coreViewModels.Contains("public bool CanContinue", StringComparison.Ordinal), "Book action model should distinguish runnable review links from running status.");
|
||||||
|
Assert(onboardingViewModels.Contains("public bool IsInProgress", StringComparison.Ordinal), "Project dashboard book status should distinguish running analysis from saved review.");
|
||||||
|
Assert(onboardingViewModels.Contains("public bool CanContinueReview => !IsInProgress", StringComparison.Ordinal), "Project dashboard should suppress review buttons for running analysis.");
|
||||||
|
Assert(coreServices.Contains("var isInProgress = string.Equals(pipeline.Status, StoryIntelligencePipelineStatuses.InProgress", StringComparison.Ordinal), "Book action builder should inspect the pipeline status.");
|
||||||
|
Assert(coreServices.Contains("CanContinue = !isInProgress", StringComparison.Ordinal), "Book action builder should block review navigation while analysis is running.");
|
||||||
|
Assert(coreServices.Contains("ActionLabel = pipeline.IsComplete ? \"Open Review Centre\" : \"Continue review\"", StringComparison.Ordinal), "Open Review Centre should be reserved for complete Story Intelligence.");
|
||||||
|
Assert(storyService.Contains("IsInProgress = string.Equals(state.Status, StoryIntelligencePipelineStatuses.InProgress", StringComparison.Ordinal), "Project dashboard pipeline rows should map in-progress state.");
|
||||||
|
Assert(bookView.Contains("if (action.CanContinue)", StringComparison.Ordinal), "Book details should render the review link only when the pipeline can continue.");
|
||||||
|
Assert(bookView.Contains("<span class=\"status-pill\">Analysis running</span>", StringComparison.Ordinal), "Book details should show running status instead of a premature review-centre button.");
|
||||||
|
Assert(projectsView.Contains("primaryPipelineBook.CanContinueReview", StringComparison.Ordinal), "Projects page should only render review actions for continuable pipeline books.");
|
||||||
|
}
|
||||||
|
|
||||||
static void StoryIntelligenceReviewCentreRouteReplacesLinearReviewResume()
|
static void StoryIntelligenceReviewCentreRouteReplacesLinearReviewResume()
|
||||||
{
|
{
|
||||||
var root = PlotLineRoot();
|
var root = PlotLineRoot();
|
||||||
|
|||||||
@ -1372,6 +1372,7 @@ public sealed class BookService(
|
|||||||
{
|
{
|
||||||
HasCanonicalSceneSources = hasCanonicalSceneSources,
|
HasCanonicalSceneSources = hasCanonicalSceneSources,
|
||||||
CanStart = hasCanonicalSceneSources,
|
CanStart = hasCanonicalSceneSources,
|
||||||
|
CanContinue = false,
|
||||||
StatusLabel = hasCanonicalSceneSources ? "Not analysed" : "Core import required",
|
StatusLabel = hasCanonicalSceneSources ? "Not analysed" : "Core import required",
|
||||||
ActionLabel = "Analyse My Book",
|
ActionLabel = "Analyse My Book",
|
||||||
Description = hasCanonicalSceneSources
|
Description = hasCanonicalSceneSources
|
||||||
@ -1380,15 +1381,19 @@ public sealed class BookService(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isInProgress = string.Equals(pipeline.Status, StoryIntelligencePipelineStatuses.InProgress, StringComparison.OrdinalIgnoreCase);
|
||||||
return new StoryIntelligenceBookActionViewModel
|
return new StoryIntelligenceBookActionViewModel
|
||||||
{
|
{
|
||||||
HasCanonicalSceneSources = hasCanonicalSceneSources,
|
HasCanonicalSceneSources = hasCanonicalSceneSources,
|
||||||
CanStart = false,
|
CanStart = false,
|
||||||
StatusLabel = pipeline.IsComplete ? "Complete" : pipeline.Status,
|
CanContinue = !isInProgress,
|
||||||
ActionLabel = "Open Review Centre",
|
StatusLabel = pipeline.IsComplete ? "Complete" : isInProgress ? "Analysis in progress" : pipeline.Status,
|
||||||
|
ActionLabel = pipeline.IsComplete ? "Open Review Centre" : "Continue review",
|
||||||
Description = pipeline.IsComplete
|
Description = pipeline.IsComplete
|
||||||
? "Story Intelligence review is saved for this book."
|
? "Story Intelligence review is saved for this book."
|
||||||
: "Story Intelligence has saved review categories for this book."
|
: isInProgress
|
||||||
|
? "Story Intelligence is analysing this book in the background."
|
||||||
|
: "Story Intelligence has saved review categories for this book."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -257,7 +257,8 @@ public sealed class StoryIntelligenceService(
|
|||||||
BookTitle = state.BookDisplayTitle,
|
BookTitle = state.BookDisplayTitle,
|
||||||
StatusLabel = PipelineStatusLabel(state),
|
StatusLabel = PipelineStatusLabel(state),
|
||||||
Description = PipelineDescription(state),
|
Description = PipelineDescription(state),
|
||||||
IsComplete = state.IsComplete
|
IsComplete = state.IsComplete,
|
||||||
|
IsInProgress = string.Equals(state.Status, StoryIntelligencePipelineStatuses.InProgress, StringComparison.OrdinalIgnoreCase)
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string PipelineStatusLabel(StoryIntelligenceBookPipelineState state)
|
private static string PipelineStatusLabel(StoryIntelligenceBookPipelineState state)
|
||||||
@ -267,6 +268,11 @@ public sealed class StoryIntelligenceService(
|
|||||||
return "Story Intelligence Review Saved";
|
return "Story Intelligence Review Saved";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(state.Status, StoryIntelligencePipelineStatuses.InProgress, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "Story Intelligence In Progress";
|
||||||
|
}
|
||||||
|
|
||||||
return state.CurrentReviewStage switch
|
return state.CurrentReviewStage switch
|
||||||
{
|
{
|
||||||
StoryIntelligencePipelineStages.KnowledgeReview => "Review Centre",
|
StoryIntelligencePipelineStages.KnowledgeReview => "Review Centre",
|
||||||
@ -288,6 +294,8 @@ public sealed class StoryIntelligenceService(
|
|||||||
private static string PipelineDescription(StoryIntelligenceBookPipelineState state)
|
private static string PipelineDescription(StoryIntelligenceBookPipelineState state)
|
||||||
=> state.IsComplete
|
=> state.IsComplete
|
||||||
? "Story Intelligence review is saved."
|
? "Story Intelligence review is saved."
|
||||||
|
: string.Equals(state.Status, StoryIntelligencePipelineStatuses.InProgress, StringComparison.OrdinalIgnoreCase)
|
||||||
|
? "Story Intelligence is analysing this book."
|
||||||
: state.CurrentReviewStage switch
|
: state.CurrentReviewStage switch
|
||||||
{
|
{
|
||||||
StoryIntelligencePipelineStages.KnowledgeReview => "Open the Review Centre and choose which saved category to handle.",
|
StoryIntelligencePipelineStages.KnowledgeReview => "Open the Review Centre and choose which saved category to handle.",
|
||||||
|
|||||||
@ -263,6 +263,7 @@ public sealed class StoryIntelligenceBookActionViewModel
|
|||||||
{
|
{
|
||||||
public bool HasCanonicalSceneSources { get; set; }
|
public bool HasCanonicalSceneSources { get; set; }
|
||||||
public bool CanStart { get; set; }
|
public bool CanStart { get; set; }
|
||||||
|
public bool CanContinue { get; set; }
|
||||||
public string StatusLabel { get; set; } = "Not analysed";
|
public string StatusLabel { get; set; } = "Not analysed";
|
||||||
public string ActionLabel { get; set; } = "Analyse My Book";
|
public string ActionLabel { get; set; } = "Analyse My Book";
|
||||||
public string Description { get; set; } = "Analyse this book from its saved chapters and scenes.";
|
public string Description { get; set; } = "Analyse this book from its saved chapters and scenes.";
|
||||||
|
|||||||
@ -718,6 +718,8 @@ public sealed class StoryIntelligencePipelineBookStatusViewModel
|
|||||||
public string StatusLabel { get; init; } = "Story Intelligence Needs Review";
|
public string StatusLabel { get; init; } = "Story Intelligence Needs Review";
|
||||||
public string Description { get; init; } = string.Empty;
|
public string Description { get; init; } = string.Empty;
|
||||||
public bool IsComplete { get; init; }
|
public bool IsComplete { get; init; }
|
||||||
|
public bool IsInProgress { get; init; }
|
||||||
|
public bool CanContinueReview => !IsInProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class CompanionPresenceViewModel
|
public sealed class CompanionPresenceViewModel
|
||||||
|
|||||||
@ -81,10 +81,17 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a class="btn btn-primary"
|
@if (action.CanContinue)
|
||||||
asp-controller="Onboarding"
|
{
|
||||||
asp-action="ContinueStoryIntelligence"
|
<a class="btn btn-primary"
|
||||||
asp-route-bookId="@Model.Book.BookID">@action.ActionLabel</a>
|
asp-controller="Onboarding"
|
||||||
|
asp-action="ContinueStoryIntelligence"
|
||||||
|
asp-route-bookId="@Model.Book.BookID">@action.ActionLabel</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="status-pill">Analysis running</span>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -212,6 +219,11 @@
|
|||||||
return "Story Intelligence review is saved for this book.";
|
return "Story Intelligence review is saved for this book.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(pipeline.Status, StoryIntelligencePipelineStatuses.InProgress, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "Story Intelligence is analysing this book in the background.";
|
||||||
|
}
|
||||||
|
|
||||||
return pipeline.CurrentReviewStage switch
|
return pipeline.CurrentReviewStage switch
|
||||||
{
|
{
|
||||||
StoryIntelligencePipelineStages.NarrativeReview => "Open the Review Centre and choose which saved category to handle.",
|
StoryIntelligencePipelineStages.NarrativeReview => "Open the Review Centre and choose which saved category to handle.",
|
||||||
|
|||||||
@ -34,27 +34,10 @@
|
|||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (Model.StoryIntelligence.HasActiveImport)
|
@if (Model.StoryIntelligence.HasPipelineBooks && !Model.StoryIntelligence.HasActiveImport)
|
||||||
{
|
{
|
||||||
<section class="story-intelligence-dashboard-card story-intelligence-dashboard-card--compact"
|
var primaryPipelineBook = Model.StoryIntelligence.PipelineBooks.FirstOrDefault(item => item.CanContinueReview && !item.IsComplete)
|
||||||
aria-label="Active manuscript import">
|
?? Model.StoryIntelligence.PipelineBooks.FirstOrDefault(item => item.CanContinueReview)
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Manuscript import</p>
|
|
||||||
<h2>Story Intelligence is reading</h2>
|
|
||||||
<p>
|
|
||||||
<strong>@(Model.StoryIntelligence.ActiveImportBookTitle ?? "Your manuscript")</strong>
|
|
||||||
<span class="d-block">Progress: @(Model.StoryIntelligence.ActiveImportProgressLabel ?? "In progress")</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-primary"
|
|
||||||
asp-controller="Onboarding"
|
|
||||||
asp-action="StoryIntelligenceProgress"
|
|
||||||
asp-route-batchId="@Model.StoryIntelligence.ActiveBatchID">View progress</a>
|
|
||||||
</section>
|
|
||||||
}
|
|
||||||
else if (Model.StoryIntelligence.HasPipelineBooks)
|
|
||||||
{
|
|
||||||
var primaryPipelineBook = Model.StoryIntelligence.PipelineBooks.FirstOrDefault(item => !item.IsComplete)
|
|
||||||
?? Model.StoryIntelligence.PipelineBooks.First();
|
?? Model.StoryIntelligence.PipelineBooks.First();
|
||||||
<section class="story-intelligence-dashboard-card story-intelligence-dashboard-card--compact"
|
<section class="story-intelligence-dashboard-card story-intelligence-dashboard-card--compact"
|
||||||
aria-label="Story Intelligence status">
|
aria-label="Story Intelligence status">
|
||||||
@ -72,10 +55,17 @@ else if (Model.StoryIntelligence.HasPipelineBooks)
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-primary"
|
@if (primaryPipelineBook.CanContinueReview)
|
||||||
asp-controller="Onboarding"
|
{
|
||||||
asp-action="ContinueStoryIntelligence"
|
<a class="btn btn-primary"
|
||||||
asp-route-bookId="@primaryPipelineBook.BookID">@Model.StoryIntelligence.ButtonText</a>
|
asp-controller="Onboarding"
|
||||||
|
asp-action="ContinueStoryIntelligence"
|
||||||
|
asp-route-bookId="@primaryPipelineBook.BookID">@Model.StoryIntelligence.ButtonText</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="status-pill">Analysis running</span>
|
||||||
|
}
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user