From 7be7ce95cebaf4e7f79d775474bb5e9ebba5323c Mon Sep 17 00:00:00 2001 From: Nick Beckley Date: Sun, 30 Aug 2026 21:07:25 +0000 Subject: [PATCH] Stabilise story intelligence review rendering --- PlotLine.Tests/Program.cs | 30 +++---- .../Onboarding/StoryIntelligenceAssets.cshtml | 13 ++-- .../StoryIntelligenceCharacters.cshtml | 15 +++- .../StoryIntelligenceKnowledge.cshtml | 13 ++-- .../StoryIntelligenceLocations.cshtml | 78 +++---------------- .../StoryIntelligenceNarrative.cshtml | 13 ++-- .../StoryIntelligenceRelationships.cshtml | 13 ++-- 7 files changed, 61 insertions(+), 114 deletions(-) diff --git a/PlotLine.Tests/Program.cs b/PlotLine.Tests/Program.cs index c6b4dd9..3fade72 100644 --- a/PlotLine.Tests/Program.cs +++ b/PlotLine.Tests/Program.cs @@ -1014,6 +1014,7 @@ static void StoryIntelligenceHeavyReviewModulesOfferBoundedRendering() var root = PlotLineRoot(); var heavyViews = new[] { + "StoryIntelligenceCharacters.cshtml", "StoryIntelligenceLocations.cshtml", "StoryIntelligenceAssets.cshtml", "StoryIntelligenceRelationships.cshtml", @@ -1024,18 +1025,19 @@ static void StoryIntelligenceHeavyReviewModulesOfferBoundedRendering() foreach (var viewName in heavyViews) { var view = File.ReadAllText(Path.Combine(root, "Views/Onboarding", viewName)); - if (viewName == "StoryIntelligenceLocations.cshtml") - { - Assert(view.Contains("const pageSize = 24;", StringComparison.Ordinal), "StoryIntelligenceLocations.cshtml should cap visible candidates with DOM-preserving pagination."); - Assert(view.Contains("data-location-page-prev", StringComparison.Ordinal), "StoryIntelligenceLocations.cshtml should provide previous-page navigation."); - Assert(view.Contains("data-location-page-next", StringComparison.Ordinal), "StoryIntelligenceLocations.cshtml should provide next-page navigation."); - continue; - } - - Assert(view.Contains("const int previewLimit = 50", StringComparison.Ordinal), $"{viewName} should cap default rendering."); - Assert(view.Contains("asp-route-view=\"all\"", StringComparison.Ordinal), $"{viewName} should provide a View All route."); + Assert(view.Contains("const int previewLimit = 25", StringComparison.Ordinal) || view.Contains("const pageSize = 24;", StringComparison.Ordinal), $"{viewName} should cap visible candidates to a small page."); + Assert(!view.Contains("asp-route-view=\"all\"", StringComparison.Ordinal), $"{viewName} should not expose an unbounded View All path."); Assert(view.Contains("hiddenCandidateCount", StringComparison.Ordinal), $"{viewName} should disclose hidden remaining decisions."); } + + var assets = File.ReadAllText(Path.Combine(root, "Views/Onboarding/StoryIntelligenceAssets.cshtml")); + var relationships = File.ReadAllText(Path.Combine(root, "Views/Onboarding/StoryIntelligenceRelationships.cshtml")); + var knowledge = File.ReadAllText(Path.Combine(root, "Views/Onboarding/StoryIntelligenceKnowledge.cshtml")); + var narrative = File.ReadAllText(Path.Combine(root, "Views/Onboarding/StoryIntelligenceNarrative.cshtml")); + Assert(!assets.Contains("Model.AssetReview.Candidates.Where(target => !string.Equals(target.Key, candidate.Key", StringComparison.Ordinal), "Asset merge options should not enumerate the full candidate set per visible card."); + Assert(!relationships.Contains("Model.RelationshipReview.Candidates.Where(target => !string.Equals(target.Key, candidate.Key", StringComparison.Ordinal), "Relationship merge options should not enumerate the full candidate set per visible card."); + Assert(!knowledge.Contains("Model.KnowledgeReview.Candidates.Where(target => !string.Equals(target.Key, candidate.Key", StringComparison.Ordinal), "Knowledge merge options should not enumerate the full candidate set per visible card."); + Assert(!narrative.Contains("Model.NarrativeReview.Candidates.Where(item => item.NarrativeCandidateID != candidate.NarrativeCandidateID", StringComparison.Ordinal), "Narrative merge options should not enumerate the full candidate set per visible card."); } static void StoryIntelligenceReviewCentreAddsNoAiCalls() @@ -2429,10 +2431,10 @@ static void LocationReviewUxSupportsLargeCandidateSets() var viewModels = File.ReadAllText(Path.Combine(root, "ViewModels/OnboardingViewModels.cs")); var service = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceLocationImportService.cs")); - Assert(view.Contains("data-location-search", StringComparison.Ordinal), "Location review should provide candidate search."); - Assert(view.Contains("data-location-status-filter", StringComparison.Ordinal), "Location review should provide status filtering."); - Assert(view.Contains("const pageSize = 24;", StringComparison.Ordinal), "Location review should show roughly 20-25 candidates at once."); - Assert(view.Contains("data-location-page-status", StringComparison.Ordinal), "Location review should show paging/review counts."); + Assert(view.Contains("const int previewLimit = 25", StringComparison.Ordinal), "Location review should render a bounded candidate batch."); + Assert(view.Contains("hiddenCandidateCount", StringComparison.Ordinal), "Location review should disclose remaining decisions outside the current batch."); + Assert(view.Contains("story-review-page-status", StringComparison.Ordinal), "Location review should show bounded review counts."); + Assert(!view.Contains("data-location-page-next", StringComparison.Ordinal), "Location review should not hide a full candidate DOM behind client-side paging."); Assert(view.Contains("EvidenceSummaries", StringComparison.Ordinal), "Location review should expose compact aggregated evidence."); Assert(!view.Contains("data-location-bulk=\"create-all\"", StringComparison.Ordinal), "Location review should not offer bulk create-all."); Assert(!view.Contains("data-location-bulk=\"ignore-all\"", StringComparison.Ordinal), "Location review should not offer bulk ignore-all."); diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceAssets.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceAssets.cshtml index 856a797..9671015 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceAssets.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceAssets.cshtml @@ -1,11 +1,8 @@ @model StoryIntelligenceProgressViewModel @{ ViewData["Title"] = "Review assets"; - const int previewLimit = 50; - var showAll = string.Equals(Context.Request.Query["view"], "all", StringComparison.OrdinalIgnoreCase); - var assetCandidates = showAll - ? Model.AssetReview.Candidates.ToList() - : Model.AssetReview.Candidates.Take(previewLimit).ToList(); + const int previewLimit = 25; + var assetCandidates = Model.AssetReview.Candidates.Take(previewLimit).ToList(); var hiddenCandidateCount = Math.Max(0, Model.AssetReview.Candidates.Count - assetCandidates.Count); } @@ -46,7 +43,7 @@
@if (hiddenCandidateCount > 0) { - View all @Model.AssetReview.Candidates.Count.ToString("N0") + Showing @assetCandidates.Count.ToString("N0") of @Model.AssetReview.Candidates.Count.ToString("N0") } @@ -132,7 +129,7 @@
- @for (var i = 0; i < Model.CharacterReview.Candidates.Count; i++) + @for (var i = 0; i < characterCandidates.Count; i++) { - var candidate = Model.CharacterReview.Candidates[i]; + var candidate = characterCandidates[i];
@@ -145,6 +152,10 @@
+ @if (hiddenCandidateCount > 0) + { + Save this batch to continue with @hiddenCandidateCount.ToString("N0") more character decision@(hiddenCandidateCount == 1 ? string.Empty : "s"). + } Back to Review Centre
diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceKnowledge.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceKnowledge.cshtml index c77aeb5..986799d 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceKnowledge.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceKnowledge.cshtml @@ -1,11 +1,8 @@ @model StoryIntelligenceProgressViewModel @{ ViewData["Title"] = "Review knowledge"; - const int previewLimit = 50; - var showAll = string.Equals(Context.Request.Query["view"], "all", StringComparison.OrdinalIgnoreCase); - var knowledgeCandidates = showAll - ? Model.KnowledgeReview.Candidates.ToList() - : Model.KnowledgeReview.Candidates.Take(previewLimit).ToList(); + const int previewLimit = 25; + var knowledgeCandidates = Model.KnowledgeReview.Candidates.Take(previewLimit).ToList(); var hiddenCandidateCount = Math.Max(0, Model.KnowledgeReview.Candidates.Count - knowledgeCandidates.Count); } @@ -46,7 +43,7 @@
@if (hiddenCandidateCount > 0) { - View all @Model.KnowledgeReview.Candidates.Count.ToString("N0") + Showing @knowledgeCandidates.Count.ToString("N0") of @Model.KnowledgeReview.Candidates.Count.ToString("N0") } @@ -138,7 +135,7 @@ -
-
- - -
-
- - -
-
- Showing @Math.Min(locationCandidates.Count, 24).ToString("N0") of @locationCandidates.Count.ToString("N0") -
-
- - -
-
@for (var i = 0; i < locationCandidates.Count; i++) { @@ -146,7 +130,7 @@ - @foreach (var option in Model.NarrativeReview.Candidates.Where(item => item.NarrativeCandidateID != candidate.NarrativeCandidateID && string.Equals(item.CandidateType, candidate.CandidateType, StringComparison.OrdinalIgnoreCase)).Select(item => new SelectListItem(item.ProposedTitle, item.NarrativeCandidateID.ToString()))) + @foreach (var option in narrativeCandidates.Where(item => item.NarrativeCandidateID != candidate.NarrativeCandidateID && string.Equals(item.CandidateType, candidate.CandidateType, StringComparison.OrdinalIgnoreCase)).Select(item => new SelectListItem(item.ProposedTitle, item.NarrativeCandidateID.ToString()))) { } @@ -222,7 +219,7 @@
@if (hiddenCandidateCount > 0) { - @hiddenCandidateCount.ToString("N0") more narrative decision@(hiddenCandidateCount == 1 ? string.Empty : "s") available after this batch. + Save this batch to continue with @hiddenCandidateCount.ToString("N0") more narrative decision@(hiddenCandidateCount == 1 ? string.Empty : "s"). } Back to Review Centre diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceRelationships.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceRelationships.cshtml index d470b47..dd42d9b 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceRelationships.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceRelationships.cshtml @@ -1,11 +1,8 @@ @model StoryIntelligenceProgressViewModel @{ ViewData["Title"] = "Review relationships"; - const int previewLimit = 50; - var showAll = string.Equals(Context.Request.Query["view"], "all", StringComparison.OrdinalIgnoreCase); - var relationshipCandidates = showAll - ? Model.RelationshipReview.Candidates.ToList() - : Model.RelationshipReview.Candidates.Take(previewLimit).ToList(); + const int previewLimit = 25; + var relationshipCandidates = Model.RelationshipReview.Candidates.Take(previewLimit).ToList(); var hiddenCandidateCount = Math.Max(0, Model.RelationshipReview.Candidates.Count - relationshipCandidates.Count); } @@ -46,7 +43,7 @@
@if (hiddenCandidateCount > 0) { - View all @Model.RelationshipReview.Candidates.Count.ToString("N0") + Showing @relationshipCandidates.Count.ToString("N0") of @Model.RelationshipReview.Candidates.Count.ToString("N0") } @@ -148,7 +145,7 @@