From 6263cf6b00c8d90f4b7273b84b96b7a450e6461c Mon Sep 17 00:00:00 2001 From: Nick Beckley Date: Wed, 2 Sep 2026 19:53:50 +0000 Subject: [PATCH] Add review candidate panel headers --- PlotLine.Tests/Program.cs | 11 +++++ .../Onboarding/StoryIntelligenceAssets.cshtml | 2 +- .../StoryIntelligenceCharacters.cshtml | 2 +- .../StoryIntelligenceKnowledge.cshtml | 6 +-- .../StoryIntelligenceLocations.cshtml | 2 +- .../StoryIntelligenceNarrative.cshtml | 2 +- .../StoryIntelligenceRelationships.cshtml | 6 +-- PlotLine/wwwroot/css/onboarding.css | 45 ++++++++++++++----- 8 files changed, 56 insertions(+), 20 deletions(-) diff --git a/PlotLine.Tests/Program.cs b/PlotLine.Tests/Program.cs index 8e5fb3c..bf5179f 100644 --- a/PlotLine.Tests/Program.cs +++ b/PlotLine.Tests/Program.cs @@ -941,12 +941,23 @@ static void StoryIntelligenceReviewModulePagesOmitWizardChrome() Assert(view.Contains("

Story Intelligence

", StringComparison.Ordinal), $"{viewName} should use a simple module header eyebrow."); Assert(view.Contains("StoryIntelligenceReviewCentre", StringComparison.Ordinal), $"{viewName} should return to Review Centre."); Assert(view.Contains($">{title}", StringComparison.Ordinal), $"{viewName} should keep its focused module title."); + Assert(view.Contains("summary class=\"story-review-card-header\"", StringComparison.Ordinal), $"{viewName} should give each candidate panel a clear shared header."); Assert(!view.Contains("StoryIntelligenceComplete", StringComparison.Ordinal), $"{viewName} should not route to the old final wizard step."); foreach (var marker in forbidden) { Assert(!view.Contains(marker, StringComparison.OrdinalIgnoreCase), $"{viewName} should not render obsolete wizard chrome: {marker}."); } } + + var stylesheet = File.ReadAllText(Path.Combine(root, "wwwroot/css/onboarding.css")); + Assert(stylesheet.Contains(".story-review-card-header", StringComparison.Ordinal), "Shared review candidate header CSS should be present."); + Assert(stylesheet.Contains("justify-content: space-between;", StringComparison.Ordinal), "Review candidate headers should align title and status consistently."); + Assert(stylesheet.Contains("border-bottom: 1px solid rgba(31, 42, 68, .1);", StringComparison.Ordinal), "Review candidate headers should be visually separated from card bodies."); + + var knowledgeView = File.ReadAllText(Path.Combine(root, "Views/Onboarding/StoryIntelligenceKnowledge.cshtml")); + var relationshipsView = File.ReadAllText(Path.Combine(root, "Views/Onboarding/StoryIntelligenceRelationships.cshtml")); + Assert(knowledgeView.Contains("@candidate.KnowledgeStatement", StringComparison.Ordinal), "Knowledge review headers should foreground the knowledge item."); + Assert(relationshipsView.Contains("@candidate.CharacterAName / @candidate.CharacterBName", StringComparison.Ordinal), "Relationship review headers should foreground both participants."); } static void StoryIntelligenceDownstreamReviewUsesCompletedSceneAnalysis() diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceAssets.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceAssets.cshtml index 5a4fbb3..9562b64 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceAssets.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceAssets.cshtml @@ -55,7 +55,7 @@ { var candidate = assetCandidates[i];
- + @candidate.AssetName @candidate.Category · @candidate.AppearsInScenes.ToString("N0") scene@(candidate.AppearsInScenes == 1 ? string.Empty : "s") diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceCharacters.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceCharacters.cshtml index 9a0c67d..976e740 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceCharacters.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceCharacters.cshtml @@ -77,7 +77,7 @@ { var candidate = characterCandidates[i];
- + @candidate.CharacterName @candidate.Classification · @candidate.AppearsInScenes.ToString("N0") scene@(candidate.AppearsInScenes == 1 ? string.Empty : "s") diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceKnowledge.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceKnowledge.cshtml index a0bb915..19baa7a 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceKnowledge.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceKnowledge.cshtml @@ -59,10 +59,10 @@ { var candidate = knowledgeCandidates[i];
- + - @candidate.CharacterName - @candidate.KnowledgeStateName - @candidate.KnowledgeStatement + @candidate.KnowledgeStatement + @candidate.CharacterName - @candidate.KnowledgeStateName @candidate.Confidence diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceLocations.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceLocations.cshtml index 33e1eab..f059c9d 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceLocations.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceLocations.cshtml @@ -60,7 +60,7 @@ : string.Empty; var searchText = string.Join(" ", new[] { candidate.LocationName, candidate.ExistingLocationName, candidate.Category, candidate.ParentLocationHint, candidate.ProposedParentLocationName });
- + @candidate.LocationName @candidate.Category · @candidate.AppearsInScenes.ToString("N0") scene@(candidate.AppearsInScenes == 1 ? string.Empty : "s") diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceNarrative.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceNarrative.cshtml index 09164e6..5a46a6c 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceNarrative.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceNarrative.cshtml @@ -55,7 +55,7 @@ { var candidate = narrativeCandidates[i];
- + @candidate.ProposedTitle @candidate.CandidateType @(!string.IsNullOrWhiteSpace(candidate.Category) ? $"- {candidate.Category}" : string.Empty) diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceRelationships.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceRelationships.cshtml index 5610d1a..395ea4e 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceRelationships.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceRelationships.cshtml @@ -59,10 +59,10 @@ { var candidate = relationshipCandidates[i];
- + - @candidate.CharacterAName - @candidate.RelationshipType / @candidate.CharacterBName - @candidate.AppearsInScenes.ToString("N0") scene@(candidate.AppearsInScenes == 1 ? string.Empty : "s") + @candidate.CharacterAName / @candidate.CharacterBName + @candidate.RelationshipType - @candidate.AppearsInScenes.ToString("N0") scene@(candidate.AppearsInScenes == 1 ? string.Empty : "s") @candidate.Confidence diff --git a/PlotLine/wwwroot/css/onboarding.css b/PlotLine/wwwroot/css/onboarding.css index 440f80c..3474d5e 100644 --- a/PlotLine/wwwroot/css/onboarding.css +++ b/PlotLine/wwwroot/css/onboarding.css @@ -1469,35 +1469,52 @@ summary.story-review-chapter-heading { border: 1px solid rgba(31, 42, 68, .1); border-radius: 8px; background: rgba(255, 255, 255, .64); - padding: .8rem; + overflow: hidden; } -.story-character-card summary { - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - gap: .65rem; +.story-review-card-header { align-items: center; + background: rgba(47, 111, 99, .07); + border-bottom: 1px solid rgba(31, 42, 68, .1); cursor: pointer; + display: flex; + flex-wrap: wrap; + gap: .55rem 1rem; + justify-content: space-between; + padding: .75rem .85rem; } -.story-character-card summary span { +.story-review-card-header span { display: grid; - gap: .1rem; + gap: .12rem; min-width: 0; } -.story-character-card summary small, -.story-character-card summary em { +.story-review-card-header strong { + color: var(--bs-body-color); + font-size: 1rem; + line-height: 1.25; + overflow-wrap: anywhere; +} + +.story-review-card-header small, +.story-review-card-header em { color: var(--bs-secondary-color); font-size: .82rem; font-style: normal; font-weight: 700; } +.story-review-card-header em { + color: var(--plotdirector-green, #2f6f63); + flex: 0 0 auto; + text-align: right; +} + .story-character-card__body { display: grid; gap: .75rem; - padding-top: .85rem; + padding: .85rem; } .story-character-card__body dl { @@ -2324,6 +2341,14 @@ summary.story-review-chapter-heading { grid-template-columns: 1fr; } + .story-review-card-header { + align-items: flex-start; + } + + .story-review-card-header em { + text-align: left; + } + .story-review-centre-grid { grid-template-columns: 1fr; }