Implement a small display fix caused by Phase 13A book subtitles.

This commit is contained in:
Nick Beckley 2026-06-15 19:42:06 +01:00
parent 2b145731e3
commit dca5b0a847
17 changed files with 1251 additions and 57 deletions

View File

@ -592,6 +592,7 @@ public sealed class WarningRepository(ISqlConnectionFactory connectionFactory) :
cw.ProjectID, cw.ProjectID,
cw.BookID, cw.BookID,
b.BookTitle, b.BookTitle,
b.Subtitle AS BookSubtitle,
cw.ChapterID, cw.ChapterID,
c.ChapterNumber, c.ChapterNumber,
c.ChapterTitle, c.ChapterTitle,
@ -1148,6 +1149,7 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
p.ProjectName, p.ProjectName,
wp.BookID, wp.BookID,
b.BookTitle, b.BookTitle,
b.Subtitle AS BookSubtitle,
wp.PlanName, wp.PlanName,
wp.GoalType, wp.GoalType,
wp.StartDate, wp.StartDate,
@ -1168,6 +1170,7 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
p.ProjectName, p.ProjectName,
wp.BookID, wp.BookID,
b.BookTitle, b.BookTitle,
b.Subtitle,
wp.PlanName, wp.PlanName,
wp.GoalType, wp.GoalType,
wp.StartDate, wp.StartDate,
@ -1274,6 +1277,7 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
p.ProjectName, p.ProjectName,
wp.BookID, wp.BookID,
b.BookTitle, b.BookTitle,
b.Subtitle AS BookSubtitle,
wp.PlanName, wp.PlanName,
wp.DeadlineDate, wp.DeadlineDate,
wp.IsActive wp.IsActive
@ -1299,6 +1303,7 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
wsi.ScheduledDate, wsi.ScheduledDate,
wsi.TaskType, wsi.TaskType,
b.BookTitle, b.BookTitle,
b.Subtitle AS BookSubtitle,
c.ChapterNumber, c.ChapterNumber,
c.ChapterTitle, c.ChapterTitle,
s.SceneNumber, s.SceneNumber,
@ -1341,6 +1346,7 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
wsi.ScheduledDate, wsi.ScheduledDate,
wsi.TaskType, wsi.TaskType,
b.BookTitle, b.BookTitle,
b.Subtitle AS BookSubtitle,
c.ChapterNumber, c.ChapterNumber,
c.ChapterTitle, c.ChapterTitle,
c.SortOrder AS ChapterSortOrder, c.SortOrder AS ChapterSortOrder,
@ -1403,6 +1409,7 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
p.ProjectName, p.ProjectName,
wp.BookID, wp.BookID,
b.BookTitle, b.BookTitle,
b.Subtitle AS BookSubtitle,
wp.PlanName, wp.PlanName,
wp.DeadlineDate, wp.DeadlineDate,
wp.IsActive wp.IsActive

View File

@ -1,5 +1,20 @@
namespace PlotLine.Models; namespace PlotLine.Models;
public static class BookTitleFormatter
{
public static string DisplayTitle(string? title, string? subtitle)
{
if (string.IsNullOrWhiteSpace(title))
{
return string.Empty;
}
return string.IsNullOrWhiteSpace(subtitle)
? title
: $"{title}: {subtitle}";
}
}
public sealed class Project public sealed class Project
{ {
public int ProjectID { get; set; } public int ProjectID { get; set; }
@ -213,6 +228,7 @@ public sealed class Book
public int ProjectID { get; set; } public int ProjectID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? Subtitle { get; set; } public string? Subtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, Subtitle);
public string? Tagline { get; set; } public string? Tagline { get; set; }
public string? ShortDescription { get; set; } public string? ShortDescription { get; set; }
public string Status { get; set; } = BookStatuses.Planning; public string Status { get; set; } = BookStatuses.Planning;
@ -596,6 +612,8 @@ public sealed class SceneOption
{ {
public int SceneID { get; set; } public int SceneID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
public decimal SceneNumber { get; set; } public decimal SceneNumber { get; set; }
@ -684,6 +702,8 @@ public sealed class AssetEvent
public string EventTitle { get; set; } = string.Empty; public string EventTitle { get; set; } = string.Empty;
public string? EventDescription { get; set; } public string? EventDescription { get; set; }
public string? BookTitle { get; set; } public string? BookTitle { get; set; }
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public decimal SceneNumber { get; set; } public decimal SceneNumber { get; set; }
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
@ -749,6 +769,8 @@ public sealed class AssetCustodyEvent
public string? Description { get; set; } public string? Description { get; set; }
public string? CustodianSummary { get; set; } public string? CustodianSummary { get; set; }
public string? BookTitle { get; set; } public string? BookTitle { get; set; }
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public decimal SceneNumber { get; set; } public decimal SceneNumber { get; set; }
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
@ -857,6 +879,8 @@ public sealed class SceneCharacter
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public DateTime CreatedDate { get; set; } public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; } public DateTime UpdatedDate { get; set; }
} }
@ -884,6 +908,8 @@ public sealed class CharacterAttributeEvent
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public string AttributeValue { get; set; } = string.Empty; public string AttributeValue { get; set; } = string.Empty;
public string? Description { get; set; } public string? Description { get; set; }
public DateTime CreatedDate { get; set; } public DateTime CreatedDate { get; set; }
@ -912,6 +938,8 @@ public sealed class CharacterKnowledgeItem
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int KnowledgeStateID { get; set; } public int KnowledgeStateID { get; set; }
public string KnowledgeStateName { get; set; } = string.Empty; public string KnowledgeStateName { get; set; } = string.Empty;
public int? SourceEventID { get; set; } public int? SourceEventID { get; set; }
@ -970,6 +998,8 @@ public sealed class CharacterRelationship
public bool IsInitialRelationship { get; set; } public bool IsInitialRelationship { get; set; }
public int? InitialBookID { get; set; } public int? InitialBookID { get; set; }
public string? InitialBookTitle { get; set; } public string? InitialBookTitle { get; set; }
public string? InitialBookSubtitle { get; set; }
public string InitialBookDisplayTitle => BookTitleFormatter.DisplayTitle(InitialBookTitle, InitialBookSubtitle);
public bool ReaderInitiallyKnows { get; set; } public bool ReaderInitiallyKnows { get; set; }
public int? InitialRelationshipStateID { get; set; } public int? InitialRelationshipStateID { get; set; }
public string? InitialRelationshipStateName { get; set; } public string? InitialRelationshipStateName { get; set; }
@ -1002,6 +1032,8 @@ public sealed class RelationshipEvent
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int RelationshipStateID { get; set; } public int RelationshipStateID { get; set; }
public string RelationshipStateName { get; set; } = string.Empty; public string RelationshipStateName { get; set; } = string.Empty;
public int? Intensity { get; set; } public int? Intensity { get; set; }
@ -1135,6 +1167,8 @@ public sealed class ContinuityWarning
public int ProjectID { get; set; } public int ProjectID { get; set; }
public int? BookID { get; set; } public int? BookID { get; set; }
public string? BookTitle { get; set; } public string? BookTitle { get; set; }
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int? ChapterID { get; set; } public int? ChapterID { get; set; }
public decimal? ChapterNumber { get; set; } public decimal? ChapterNumber { get; set; }
public string? ChapterTitle { get; set; } public string? ChapterTitle { get; set; }
@ -1210,12 +1244,16 @@ public sealed class SceneDependency
public string? DependencyDirection { get; set; } public string? DependencyDirection { get; set; }
public int SourceBookID { get; set; } public int SourceBookID { get; set; }
public string SourceBookTitle { get; set; } = string.Empty; public string SourceBookTitle { get; set; } = string.Empty;
public string? SourceBookSubtitle { get; set; }
public string SourceBookDisplayTitle => BookTitleFormatter.DisplayTitle(SourceBookTitle, SourceBookSubtitle);
public int SourceChapterID { get; set; } public int SourceChapterID { get; set; }
public decimal SourceChapterNumber { get; set; } public decimal SourceChapterNumber { get; set; }
public decimal SourceSceneNumber { get; set; } public decimal SourceSceneNumber { get; set; }
public string SourceSceneTitle { get; set; } = string.Empty; public string SourceSceneTitle { get; set; } = string.Empty;
public int TargetBookID { get; set; } public int TargetBookID { get; set; }
public string TargetBookTitle { get; set; } = string.Empty; public string TargetBookTitle { get; set; } = string.Empty;
public string? TargetBookSubtitle { get; set; }
public string TargetBookDisplayTitle => BookTitleFormatter.DisplayTitle(TargetBookTitle, TargetBookSubtitle);
public int TargetChapterID { get; set; } public int TargetChapterID { get; set; }
public decimal TargetChapterNumber { get; set; } public decimal TargetChapterNumber { get; set; }
public decimal TargetSceneNumber { get; set; } public decimal TargetSceneNumber { get; set; }
@ -1241,6 +1279,8 @@ public sealed class ChapterOption
public int ChapterID { get; set; } public int ChapterID { get; set; }
public int BookID { get; set; } public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int BookNumber { get; set; } public int BookNumber { get; set; }
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
@ -1256,11 +1296,15 @@ public sealed class SceneMovePreview
public string SourceChapterTitle { get; set; } = string.Empty; public string SourceChapterTitle { get; set; } = string.Empty;
public int SourceBookID { get; set; } public int SourceBookID { get; set; }
public string SourceBookTitle { get; set; } = string.Empty; public string SourceBookTitle { get; set; } = string.Empty;
public string? SourceBookSubtitle { get; set; }
public string SourceBookDisplayTitle => BookTitleFormatter.DisplayTitle(SourceBookTitle, SourceBookSubtitle);
public int TargetChapterID { get; set; } public int TargetChapterID { get; set; }
public decimal TargetChapterNumber { get; set; } public decimal TargetChapterNumber { get; set; }
public string TargetChapterTitle { get; set; } = string.Empty; public string TargetChapterTitle { get; set; } = string.Empty;
public int TargetBookID { get; set; } public int TargetBookID { get; set; }
public string TargetBookTitle { get; set; } = string.Empty; public string TargetBookTitle { get; set; } = string.Empty;
public string? TargetBookSubtitle { get; set; }
public string TargetBookDisplayTitle => BookTitleFormatter.DisplayTitle(TargetBookTitle, TargetBookSubtitle);
public string Position { get; set; } = "End"; public string Position { get; set; } = "End";
public int DependencyCount { get; set; } public int DependencyCount { get; set; }
public int ActiveWarningCount { get; set; } public int ActiveWarningCount { get; set; }
@ -1271,6 +1315,8 @@ public sealed class AnalyticsMetricPoint
public int SceneID { get; set; } public int SceneID { get; set; }
public int BookID { get; set; } public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int ChapterID { get; set; } public int ChapterID { get; set; }
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
@ -1299,6 +1345,8 @@ public sealed class AnalyticsRevisionScene
public int SceneID { get; set; } public int SceneID { get; set; }
public int BookID { get; set; } public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int ChapterID { get; set; } public int ChapterID { get; set; }
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public decimal SceneNumber { get; set; } public decimal SceneNumber { get; set; }
@ -1319,6 +1367,8 @@ public sealed class AnalyticsSceneGap
public int SceneID { get; set; } public int SceneID { get; set; }
public int BookID { get; set; } public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int ChapterID { get; set; } public int ChapterID { get; set; }
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public decimal SceneNumber { get; set; } public decimal SceneNumber { get; set; }
@ -1384,6 +1434,8 @@ public sealed class AnalyticsWarningHotspot
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int WarningCount { get; set; } public int WarningCount { get; set; }
public int HighestSeveritySort { get; set; } public int HighestSeveritySort { get; set; }
} }
@ -1510,6 +1562,8 @@ public sealed class StoryBibleTimelineSummary
{ {
public int BookID { get; set; } public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int ChapterID { get; set; } public int ChapterID { get; set; }
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
@ -1645,6 +1699,8 @@ public sealed class WriterDashboardScene
public string ProjectName { get; set; } = string.Empty; public string ProjectName { get; set; } = string.Empty;
public int BookID { get; set; } public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int ChapterID { get; set; } public int ChapterID { get; set; }
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
@ -1720,6 +1776,8 @@ public sealed class WritingPlanSummary
public string ProjectName { get; set; } = string.Empty; public string ProjectName { get; set; } = string.Empty;
public int? BookID { get; set; } public int? BookID { get; set; }
public string? BookTitle { get; set; } public string? BookTitle { get; set; }
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public string PlanName { get; set; } = string.Empty; public string PlanName { get; set; } = string.Empty;
public DateTime DeadlineDate { get; set; } public DateTime DeadlineDate { get; set; }
public bool IsActive { get; set; } public bool IsActive { get; set; }
@ -1733,6 +1791,8 @@ public sealed class WritingPlanManagementItem
public string ProjectName { get; set; } = string.Empty; public string ProjectName { get; set; } = string.Empty;
public int? BookID { get; set; } public int? BookID { get; set; }
public string? BookTitle { get; set; } public string? BookTitle { get; set; }
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public string PlanName { get; set; } = string.Empty; public string PlanName { get; set; } = string.Empty;
public string GoalType { get; set; } = string.Empty; public string GoalType { get; set; } = string.Empty;
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
@ -1753,6 +1813,8 @@ public sealed class WritingSchedulePreviewItem
public DateTime ScheduledDate { get; set; } public DateTime ScheduledDate { get; set; }
public string TaskType { get; set; } = string.Empty; public string TaskType { get; set; } = string.Empty;
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
public decimal SceneNumber { get; set; } public decimal SceneNumber { get; set; }
@ -1774,6 +1836,8 @@ public sealed class WritingScheduleDashboardItem
public DateTime ScheduledDate { get; set; } public DateTime ScheduledDate { get; set; }
public string TaskType { get; set; } = string.Empty; public string TaskType { get; set; } = string.Empty;
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
public int ChapterSortOrder { get; set; } public int ChapterSortOrder { get; set; }
@ -1818,6 +1882,8 @@ public sealed class ExportSceneBriefHeader
public string ProjectName { get; set; } = string.Empty; public string ProjectName { get; set; } = string.Empty;
public int BookID { get; set; } public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int ChapterID { get; set; } public int ChapterID { get; set; }
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
@ -1852,6 +1918,8 @@ public sealed class ExportChapterBriefHeader
public string ProjectName { get; set; } = string.Empty; public string ProjectName { get; set; } = string.Empty;
public int BookID { get; set; } public int BookID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int ChapterID { get; set; } public int ChapterID { get; set; }
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
@ -1867,6 +1935,8 @@ public sealed class ExportChapterBriefHeader
public sealed class ExportResearchItem public sealed class ExportResearchItem
{ {
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public string ChapterTitle { get; set; } = string.Empty; public string ChapterTitle { get; set; } = string.Empty;
public int SceneID { get; set; } public int SceneID { get; set; }
@ -1905,6 +1975,8 @@ public sealed class CharacterArcPoint
{ {
public int SceneID { get; set; } public int SceneID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public decimal SceneNumber { get; set; } public decimal SceneNumber { get; set; }
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
@ -1930,6 +2002,8 @@ public sealed class RelationshipProgressionPoint
public int CharacterRelationshipID { get; set; } public int CharacterRelationshipID { get; set; }
public int SceneID { get; set; } public int SceneID { get; set; }
public string BookTitle { get; set; } = string.Empty; public string BookTitle { get; set; } = string.Empty;
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public decimal ChapterNumber { get; set; } public decimal ChapterNumber { get; set; }
public decimal SceneNumber { get; set; } public decimal SceneNumber { get; set; }
public string SceneTitle { get; set; } = string.Empty; public string SceneTitle { get; set; } = string.Empty;
@ -1976,6 +2050,8 @@ public sealed class Scenario
public DateTime UpdatedDate { get; set; } public DateTime UpdatedDate { get; set; }
public string ProjectName { get; set; } = string.Empty; public string ProjectName { get; set; } = string.Empty;
public string? BookTitle { get; set; } public string? BookTitle { get; set; }
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public int SceneCount { get; set; } public int SceneCount { get; set; }
public int WarningCount { get; set; } public int WarningCount { get; set; }
} }
@ -2038,6 +2114,8 @@ public sealed class ArchivedItem
public string? ProjectName { get; set; } public string? ProjectName { get; set; }
public int? BookID { get; set; } public int? BookID { get; set; }
public string? BookTitle { get; set; } public string? BookTitle { get; set; }
public string? BookSubtitle { get; set; }
public string BookDisplayTitle => BookTitleFormatter.DisplayTitle(BookTitle, BookSubtitle);
public DateTime? ArchivedDate { get; set; } public DateTime? ArchivedDate { get; set; }
public string? ArchivedReason { get; set; } public string? ArchivedReason { get; set; }
public DateTime UpdatedDate { get; set; } public DateTime UpdatedDate { get; set; }

View File

@ -646,7 +646,7 @@ public sealed class ProjectService(
: $"Chapter {warning.ChapterNumber.Value:g}: {warning.ChapterTitle}"; : $"Chapter {warning.ChapterNumber.Value:g}: {warning.ChapterTitle}";
} }
return warning.BookTitle ?? warning.EntityType; return string.IsNullOrWhiteSpace(warning.BookDisplayTitle) ? warning.EntityType : warning.BookDisplayTitle;
} }
private static string RelativeTimeLabel(DateTime createdDateUtc) private static string RelativeTimeLabel(DateTime createdDateUtc)
@ -1577,8 +1577,8 @@ public sealed class SceneService(
model.RelationshipTypeOptions = ToRelationshipTypeSelectList(characterLookups.RelationshipTypes); model.RelationshipTypeOptions = ToRelationshipTypeSelectList(characterLookups.RelationshipTypes);
model.RelationshipStateOptions = ChapterService.ToSelectList(characterLookups.RelationshipStates, x => x.RelationshipStateID, x => x.StateName); model.RelationshipStateOptions = ChapterService.ToSelectList(characterLookups.RelationshipStates, x => x.RelationshipStateID, x => x.StateName);
model.LocationOptions = ToOptionalSelectList(projectLocations, x => x.LocationID, x => new string(' ', x.Depth * 2) + x.LocationName); model.LocationOptions = ToOptionalSelectList(projectLocations, x => x.LocationID, x => new string(' ', x.Depth * 2) + x.LocationName);
model.SceneOptions = ToOptionalSelectList(allSceneOptions.Where(x => x.SceneID != model.SceneID), x => x.SceneID, x => $"{x.BookTitle} / Ch {x.ChapterNumber} / Scene {x.SceneNumber}: {x.SceneTitle}"); model.SceneOptions = ToOptionalSelectList(allSceneOptions.Where(x => x.SceneID != model.SceneID), x => x.SceneID, x => $"{x.BookDisplayTitle} / Ch {x.ChapterNumber} / Scene {x.SceneNumber}: {x.SceneTitle}");
model.ChapterOptions = ChapterService.ToSelectList(chapterOptions, x => x.ChapterID, x => $"{x.BookTitle} / Chapter {x.ChapterNumber}: {x.ChapterTitle}"); model.ChapterOptions = ChapterService.ToSelectList(chapterOptions, x => x.ChapterID, x => $"{x.BookDisplayTitle} / Chapter {x.ChapterNumber}: {x.ChapterTitle}");
model.SceneDependencyTypeOptions = ChapterService.ToSelectList(dependencyLookups.DependencyTypes, x => x.SceneDependencyTypeID, x => x.TypeName); model.SceneDependencyTypeOptions = ChapterService.ToSelectList(dependencyLookups.DependencyTypes, x => x.SceneDependencyTypeID, x => x.TypeName);
model.NewThreadEvent = new ThreadEventCreateViewModel model.NewThreadEvent = new ThreadEventCreateViewModel
{ {
@ -1971,7 +1971,7 @@ public sealed class TimelineService(
SelectedSceneID = selectedScene?.SceneID, SelectedSceneID = selectedScene?.SceneID,
SelectedScene = selectedScene, SelectedScene = selectedScene,
Filter = filter, Filter = filter,
BookOptions = allBooks.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString(), x.BookID == bookId)).ToList(), BookOptions = allBooks.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookDisplayTitle}", x.BookID.ToString(), x.BookID == bookId)).ToList(),
ChapterOptions = timeline.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString())).ToList(), ChapterOptions = timeline.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString())).ToList(),
ChapterFromOptions = timeline.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == filter.ChapterFromID)).ToList(), ChapterFromOptions = timeline.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == filter.ChapterFromID)).ToList(),
ChapterToOptions = timeline.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == filter.ChapterToID)).ToList(), ChapterToOptions = timeline.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == filter.ChapterToID)).ToList(),
@ -2631,7 +2631,7 @@ public sealed class StoryStateService(
Filter = filter, Filter = filter,
ShowBookContext = ShouldShowBookContext(filter, scopedScenes, timeline.Chapters), ShowBookContext = ShouldShowBookContext(filter, scopedScenes, timeline.Chapters),
ShowChapterContext = ShouldShowChapterContext(filter, scopedScenes, timeline.Chapters), ShowChapterContext = ShouldShowChapterContext(filter, scopedScenes, timeline.Chapters),
BookOptions = timeline.Books.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString(), x.BookID == filter.BookID)).ToList(), BookOptions = timeline.Books.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookDisplayTitle}", x.BookID.ToString(), x.BookID == filter.BookID)).ToList(),
ChapterOptions = timeline.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == filter.ChapterID)).ToList(), ChapterOptions = timeline.Chapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == filter.ChapterID)).ToList(),
SceneOptions = orderedScenes.Select(x => new SelectListItem($"Scene {x.SceneNumber:g}: {x.SceneTitle}", x.SceneID.ToString(), x.SceneID == filter.SelectedSceneID)).ToList(), SceneOptions = orderedScenes.Select(x => new SelectListItem($"Scene {x.SceneNumber:g}: {x.SceneTitle}", x.SceneID.ToString(), x.SceneID == filter.SelectedSceneID)).ToList(),
CharacterOptions = allCharacters.Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString(), filter.CharacterIDs.Contains(x.CharacterID))).ToList(), CharacterOptions = allCharacters.Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString(), filter.CharacterIDs.Contains(x.CharacterID))).ToList(),
@ -3732,7 +3732,7 @@ public sealed class StoryStateService(
return new SceneContext( return new SceneContext(
book?.BookID ?? 0, book?.BookID ?? 0,
book is null ? string.Empty : $"Book {book.BookNumber}: {book.BookTitle}", book is null ? string.Empty : $"Book {book.BookNumber}: {book.BookDisplayTitle}",
chapter?.ChapterID ?? 0, chapter?.ChapterID ?? 0,
chapter is null ? string.Empty : $"Ch {chapter.ChapterNumber:g}: {chapter.ChapterTitle}", chapter is null ? string.Empty : $"Ch {chapter.ChapterNumber:g}: {chapter.ChapterTitle}",
scene.SceneID, scene.SceneID,
@ -4289,7 +4289,7 @@ public sealed class PlotService(IProjectRepository projects, IBookRepository boo
{ {
model.PlotImportance = ChapterService.ToSelectList(lookupData.PlotImportance, x => x.PlotImportanceID, x => x.ImportanceName); model.PlotImportance = ChapterService.ToSelectList(lookupData.PlotImportance, x => x.PlotImportanceID, x => x.ImportanceName);
model.BookOptions = (await books.ListByProjectAsync(model.ProjectID)) model.BookOptions = (await books.ListByProjectAsync(model.ProjectID))
.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString())) .Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookDisplayTitle}", x.BookID.ToString()))
.ToList(); .ToList();
model.ParentPlotLineOptions = parentCandidates model.ParentPlotLineOptions = parentCandidates
.Select(x => new SelectListItem(x.PlotLineName, x.PlotLineID.ToString())) .Select(x => new SelectListItem(x.PlotLineName, x.PlotLineID.ToString()))
@ -4304,7 +4304,7 @@ public sealed class PlotService(IProjectRepository projects, IBookRepository boo
model.ThreadTypeOptions = ChapterService.ToSelectList(lookupData.ThreadTypes, x => x.ThreadTypeID, x => x.TypeName); model.ThreadTypeOptions = ChapterService.ToSelectList(lookupData.ThreadTypes, x => x.ThreadTypeID, x => x.TypeName);
model.ThreadStatusOptions = ChapterService.ToSelectList(lookupData.ThreadStatuses, x => x.ThreadStatusID, x => x.StatusName); model.ThreadStatusOptions = ChapterService.ToSelectList(lookupData.ThreadStatuses, x => x.ThreadStatusID, x => x.StatusName);
model.SceneOptions = (await plots.ListSceneOptionsAsync(model.ProjectID)) model.SceneOptions = (await plots.ListSceneOptionsAsync(model.ProjectID))
.Select(x => new SelectListItem($"{x.BookTitle} / Ch {x.ChapterNumber} / Scene {x.SceneNumber}: {x.SceneTitle}", x.SceneID.ToString())) .Select(x => new SelectListItem($"{x.BookDisplayTitle} / Ch {x.ChapterNumber} / Scene {x.SceneNumber}: {x.SceneTitle}", x.SceneID.ToString()))
.ToList(); .ToList();
return model; return model;
} }
@ -4393,7 +4393,7 @@ public sealed class ContinuityValidationService(
Warnings = rows, Warnings = rows,
ProjectWarnings = dashboardWarnings, ProjectWarnings = dashboardWarnings,
WarningGroups = BuildWarningSeverityGroups(dashboardWarnings), WarningGroups = BuildWarningSeverityGroups(dashboardWarnings),
BookOptions = projectBooks.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString(), x.BookID == bookId)).ToList(), BookOptions = projectBooks.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookDisplayTitle}", x.BookID.ToString(), x.BookID == bookId)).ToList(),
ChapterOptions = projectChapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber:g}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == chapterId)).ToList(), ChapterOptions = projectChapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber:g}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == chapterId)).ToList(),
WarningTypeOptions = lookupData.WarningTypes.Select(x => new SelectListItem(x.TypeName, x.WarningTypeID.ToString(), x.WarningTypeID == warningTypeId)).ToList(), WarningTypeOptions = lookupData.WarningTypes.Select(x => new SelectListItem(x.TypeName, x.WarningTypeID.ToString(), x.WarningTypeID == warningTypeId)).ToList(),
SeverityOptions = lookupData.Severities.Select(x => new SelectListItem(x.SeverityName, x.WarningSeverityID.ToString(), x.WarningSeverityID == warningSeverityId)).ToList(), SeverityOptions = lookupData.Severities.Select(x => new SelectListItem(x.SeverityName, x.WarningSeverityID.ToString(), x.WarningSeverityID == warningSeverityId)).ToList(),
@ -4441,8 +4441,8 @@ public sealed class ContinuityValidationService(
IReadOnlyDictionary<int, Chapter> chaptersById) IReadOnlyDictionary<int, Chapter> chaptersById)
{ {
var bookDisplayName = warning.BookID.HasValue && booksById.TryGetValue(warning.BookID.Value, out var book) var bookDisplayName = warning.BookID.HasValue && booksById.TryGetValue(warning.BookID.Value, out var book)
? $"Book {book.BookNumber:g}: {book.BookTitle}" ? $"Book {book.BookNumber:g}: {book.BookDisplayTitle}"
: warning.BookTitle ?? string.Empty; : warning.BookDisplayTitle;
var chapterDisplayName = warning.ChapterID.HasValue && chaptersById.TryGetValue(warning.ChapterID.Value, out var chapter) var chapterDisplayName = warning.ChapterID.HasValue && chaptersById.TryGetValue(warning.ChapterID.Value, out var chapter)
? $"Chapter {chapter.ChapterNumber:g}: {chapter.ChapterTitle}" ? $"Chapter {chapter.ChapterNumber:g}: {chapter.ChapterTitle}"
: warning.ChapterNumber.HasValue : warning.ChapterNumber.HasValue
@ -4587,7 +4587,7 @@ public sealed class ContinuityValidationService(
if (string.IsNullOrWhiteSpace(bookDisplayName) && bookId.HasValue && booksById.TryGetValue(bookId.Value, out var book)) if (string.IsNullOrWhiteSpace(bookDisplayName) && bookId.HasValue && booksById.TryGetValue(bookId.Value, out var book))
{ {
bookDisplayName = $"Book {book.BookNumber:g}: {book.BookTitle}"; bookDisplayName = $"Book {book.BookNumber:g}: {book.BookDisplayTitle}";
} }
if (string.IsNullOrWhiteSpace(chapterDisplayName) && chapterId.HasValue && chaptersById.TryGetValue(chapterId.Value, out var chapter)) if (string.IsNullOrWhiteSpace(chapterDisplayName) && chapterId.HasValue && chaptersById.TryGetValue(chapterId.Value, out var chapter))
@ -4918,7 +4918,7 @@ public sealed class AnalyticsService(IProjectRepository projects, IBookRepositor
EndChapterNumber = endChapterNumber, EndChapterNumber = endChapterNumber,
StartSceneNumber = startSceneNumber, StartSceneNumber = startSceneNumber,
EndSceneNumber = endSceneNumber, EndSceneNumber = endSceneNumber,
BookOptions = (await books.ListByProjectAsync(projectId)).Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString(), x.BookID == bookId)).ToList(), BookOptions = (await books.ListByProjectAsync(projectId)).Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookDisplayTitle}", x.BookID.ToString(), x.BookID == bookId)).ToList(),
MetricGraphs = data.MetricPoints MetricGraphs = data.MetricPoints
.GroupBy(x => x.MetricName) .GroupBy(x => x.MetricName)
.Select(x => new AnalyticsMetricGraphViewModel { MetricName = x.Key, Points = x.OrderBy(p => p.GlobalSceneIndex).ToList() }) .Select(x => new AnalyticsMetricGraphViewModel { MetricName = x.Key, Points = x.OrderBy(p => p.GlobalSceneIndex).ToList() })
@ -5091,7 +5091,7 @@ public sealed class RelationshipMapService(IProjectRepository projects, ICharact
FocusCharacterID = focusCharacterId, FocusCharacterID = focusCharacterId,
ShowFullNetwork = showFullNetwork, ShowFullNetwork = showFullNetwork,
StoryPointLabel = point.Label, StoryPointLabel = point.Label,
BookOptions = data.Books.OrderBy(x => x.SortOrder).ThenBy(x => x.BookNumber).Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString(), x.BookID == point.BookID)).ToList(), BookOptions = data.Books.OrderBy(x => x.SortOrder).ThenBy(x => x.BookNumber).Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookDisplayTitle}", x.BookID.ToString(), x.BookID == point.BookID)).ToList(),
ChapterOptions = data.Chapters.OrderBy(x => BookSort(data, x.BookID)).ThenBy(x => x.SortOrder).Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == point.ChapterID)).ToList(), ChapterOptions = data.Chapters.OrderBy(x => BookSort(data, x.BookID)).ThenBy(x => x.SortOrder).Select(x => new SelectListItem($"Ch {x.ChapterNumber}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == point.ChapterID)).ToList(),
SceneOptions = data.Scenes.OrderBy(x => SceneIndex(sceneOrder, x.SceneID)).Select(x => new SelectListItem($"Ch {ChapterNumber(data, x.ChapterID)} / Scene {x.SceneNumber}: {x.SceneTitle}", x.SceneID.ToString(), x.SceneID == point.SceneID)).ToList(), SceneOptions = data.Scenes.OrderBy(x => SceneIndex(sceneOrder, x.SceneID)).Select(x => new SelectListItem($"Ch {ChapterNumber(data, x.ChapterID)} / Scene {x.SceneNumber}: {x.SceneTitle}", x.SceneID.ToString(), x.SceneID == point.SceneID)).ToList(),
CharacterOptions = data.Characters.OrderBy(x => x.CharacterName).Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString(), x.CharacterID == focusCharacterId)).ToList(), CharacterOptions = data.Characters.OrderBy(x => x.CharacterName).Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString(), x.CharacterID == focusCharacterId)).ToList(),
@ -5148,7 +5148,7 @@ public sealed class RelationshipMapService(IProjectRepository projects, ICharact
}; };
history.AddRange(visibleEvents.Select(x => new RelationshipMapHistoryItemViewModel history.AddRange(visibleEvents.Select(x => new RelationshipMapHistoryItemViewModel
{ {
Label = $"{x.BookTitle} / Ch {x.ChapterNumber} / Scene {x.SceneNumber}: {x.SceneTitle}", Label = $"{x.BookDisplayTitle} / Ch {x.ChapterNumber} / Scene {x.SceneNumber}: {x.SceneTitle}",
StateName = x.RelationshipStateName, StateName = x.RelationshipStateName,
Intensity = x.Intensity, Intensity = x.Intensity,
Notes = x.Description, Notes = x.Description,
@ -5195,7 +5195,7 @@ public sealed class RelationshipMapService(IProjectRepository projects, ICharact
var scene = data.Scenes.First(x => x.SceneID == sceneId.Value); var scene = data.Scenes.First(x => x.SceneID == sceneId.Value);
var chapter = data.Chapters.FirstOrDefault(x => x.ChapterID == scene.ChapterID); var chapter = data.Chapters.FirstOrDefault(x => x.ChapterID == scene.ChapterID);
var book = chapter is null ? null : data.Books.FirstOrDefault(x => x.BookID == chapter.BookID); var book = chapter is null ? null : data.Books.FirstOrDefault(x => x.BookID == chapter.BookID);
return (book?.BookID, chapter?.ChapterID, scene.SceneID, sceneOrder[scene.SceneID], $"{book?.BookTitle ?? "Book"} / Ch {chapter?.ChapterNumber} / Scene {scene.SceneNumber}"); return (book?.BookID, chapter?.ChapterID, scene.SceneID, sceneOrder[scene.SceneID], $"{book?.BookDisplayTitle ?? "Book"} / Ch {chapter?.ChapterNumber} / Scene {scene.SceneNumber}");
} }
if (chapterId.HasValue) if (chapterId.HasValue)
@ -5205,7 +5205,7 @@ public sealed class RelationshipMapService(IProjectRepository projects, ICharact
{ {
var chapter = data.Chapters.FirstOrDefault(x => x.ChapterID == chapterId.Value); var chapter = data.Chapters.FirstOrDefault(x => x.ChapterID == chapterId.Value);
var book = chapter is null ? null : data.Books.FirstOrDefault(x => x.BookID == chapter.BookID); var book = chapter is null ? null : data.Books.FirstOrDefault(x => x.BookID == chapter.BookID);
return (book?.BookID, chapter?.ChapterID, null, chapterScenes.Max(x => sceneOrder[x.SceneID]), $"End of {book?.BookTitle ?? "Book"} / Ch {chapter?.ChapterNumber}"); return (book?.BookID, chapter?.ChapterID, null, chapterScenes.Max(x => sceneOrder[x.SceneID]), $"End of {book?.BookDisplayTitle ?? "Book"} / Ch {chapter?.ChapterNumber}");
} }
} }
@ -5216,7 +5216,7 @@ public sealed class RelationshipMapService(IProjectRepository projects, ICharact
if (bookScenes.Count > 0) if (bookScenes.Count > 0)
{ {
var book = data.Books.FirstOrDefault(x => x.BookID == bookId.Value); var book = data.Books.FirstOrDefault(x => x.BookID == bookId.Value);
return (book?.BookID, null, null, bookScenes.Max(x => sceneOrder[x.SceneID]), $"End of {book?.BookTitle ?? "Book"}"); return (book?.BookID, null, null, bookScenes.Max(x => sceneOrder[x.SceneID]), $"End of {book?.BookDisplayTitle ?? "Book"}");
} }
} }
@ -5580,7 +5580,7 @@ public sealed class WritingScheduleService(
WritingPlanID = plan.WritingPlanID, WritingPlanID = plan.WritingPlanID,
PlanName = plan.PlanName, PlanName = plan.PlanName,
ProjectName = project?.ProjectName ?? string.Empty, ProjectName = project?.ProjectName ?? string.Empty,
BookName = book?.BookTitle ?? "All Books", BookName = book?.BookDisplayTitle ?? "All Books",
GoalType = plan.GoalType, GoalType = plan.GoalType,
StartDate = plan.StartDate.Date, StartDate = plan.StartDate.Date,
DeadlineDate = plan.DeadlineDate.Date, DeadlineDate = plan.DeadlineDate.Date,
@ -5683,7 +5683,7 @@ public sealed class WritingScheduleService(
.Select(x => new SelectListItem(x.ProjectName, x.ProjectID.ToString(), x.ProjectID == model.ProjectID)) .Select(x => new SelectListItem(x.ProjectName, x.ProjectID.ToString(), x.ProjectID == model.ProjectID))
.ToList(); .ToList();
model.BookOptions = bookRows model.BookOptions = bookRows
.Select(x => new SelectListItem(x.BookTitle, x.BookID.ToString(), x.BookID == model.BookID)) .Select(x => new SelectListItem(x.BookDisplayTitle, x.BookID.ToString(), x.BookID == model.BookID))
.ToList(); .ToList();
model.GoalTypeOptions = ScheduleOptionList(["FirstDraft", "Revision", "BetaReady", "Custom"], model.GoalType); model.GoalTypeOptions = ScheduleOptionList(["FirstDraft", "Revision", "BetaReady", "Custom"], model.GoalType);
model.RebalanceModeOptions = ScheduleOptionList(["Ask", "Automatic", "Never"], model.RebalanceMode); model.RebalanceModeOptions = ScheduleOptionList(["Ask", "Automatic", "Never"], model.RebalanceMode);
@ -6484,7 +6484,7 @@ public sealed class ExportService(IExportRepository exports) : IExportService
private static void AppendSceneHeader(StringBuilder builder, ExportSceneBriefHeader scene, bool markdown) private static void AppendSceneHeader(StringBuilder builder, ExportSceneBriefHeader scene, bool markdown)
{ {
Heading(builder, $"{scene.BookTitle} / Chapter {scene.ChapterNumber} / Scene {scene.SceneNumber}: {scene.SceneTitle}", markdown); Heading(builder, $"{scene.BookDisplayTitle} / Chapter {scene.ChapterNumber} / Scene {scene.SceneNumber}: {scene.SceneTitle}", markdown);
builder.AppendLine($"- POV: {scene.PovCharacterName ?? "Not set"}"); builder.AppendLine($"- POV: {scene.PovCharacterName ?? "Not set"}");
builder.AppendLine($"- Location: {scene.LocationPath ?? "Not set"}"); builder.AppendLine($"- Location: {scene.LocationPath ?? "Not set"}");
builder.AppendLine($"- Time: {scene.TimeLabel}"); builder.AppendLine($"- Time: {scene.TimeLabel}");
@ -6499,7 +6499,7 @@ public sealed class ExportService(IExportRepository exports) : IExportService
private static void AppendChapterHeader(StringBuilder builder, ExportChapterBriefHeader chapter, bool markdown) private static void AppendChapterHeader(StringBuilder builder, ExportChapterBriefHeader chapter, bool markdown)
{ {
Heading(builder, $"{chapter.BookTitle} / Chapter {chapter.ChapterNumber}: {chapter.ChapterTitle}", markdown); Heading(builder, $"{chapter.BookDisplayTitle} / Chapter {chapter.ChapterNumber}: {chapter.ChapterTitle}", markdown);
builder.AppendLine($"- Revision: {chapter.RevisionStatusName}"); builder.AppendLine($"- Revision: {chapter.RevisionStatusName}");
AppendBlock(builder, "Summary", chapter.Summary, markdown); AppendBlock(builder, "Summary", chapter.Summary, markdown);
AppendBlock(builder, "Goal", chapter.GoalSummary, markdown); AppendBlock(builder, "Goal", chapter.GoalSummary, markdown);
@ -6519,9 +6519,9 @@ public sealed class ExportService(IExportRepository exports) : IExportService
{ {
if (!rows.Any()) return; if (!rows.Any()) return;
Heading(builder, "Timeline Summary", markdown); Heading(builder, "Timeline Summary", markdown);
foreach (var book in rows.GroupBy(x => new { x.BookID, x.BookTitle })) foreach (var book in rows.GroupBy(x => new { x.BookID, x.BookDisplayTitle }))
{ {
builder.AppendLine(markdown ? $"### {book.Key.BookTitle}" : book.Key.BookTitle); builder.AppendLine(markdown ? $"### {book.Key.BookDisplayTitle}" : book.Key.BookDisplayTitle);
foreach (var scene in book) foreach (var scene in book)
{ {
builder.AppendLine($"- Chapter {scene.ChapterNumber}, Scene {scene.SceneNumber}: {scene.SceneTitle}"); builder.AppendLine($"- Chapter {scene.ChapterNumber}, Scene {scene.SceneNumber}: {scene.SceneTitle}");
@ -6590,7 +6590,7 @@ public sealed class ExportService(IExportRepository exports) : IExportService
builder.AppendLine($"- Height: {c.Height}"); builder.AppendLine($"- Height: {c.Height}");
builder.AppendLine($"- Eye colour: {c.EyeColour}"); builder.AppendLine($"- Eye colour: {c.EyeColour}");
if (!string.IsNullOrWhiteSpace(c.DefaultDescription)) builder.AppendLine().AppendLine(c.DefaultDescription).AppendLine(); if (!string.IsNullOrWhiteSpace(c.DefaultDescription)) builder.AppendLine().AppendLine(c.DefaultDescription).AppendLine();
foreach (var app in packet.CharacterAppearances) builder.AppendLine($"- Appearance: {app.BookTitle} / Ch {app.ChapterNumber} / Scene {app.SceneNumber}: {app.SceneTitle} ({app.RoleInSceneTypeName})"); foreach (var app in packet.CharacterAppearances) builder.AppendLine($"- Appearance: {app.BookDisplayTitle} / Ch {app.ChapterNumber} / Scene {app.SceneNumber}: {app.SceneTitle} ({app.RoleInSceneTypeName})");
foreach (var rel in packet.CharacterRelationships) builder.AppendLine($"- Relationship: {rel.CharacterAName} / {rel.CharacterBName}: {rel.RelationshipTypeName}"); foreach (var rel in packet.CharacterRelationships) builder.AppendLine($"- Relationship: {rel.CharacterAName} / {rel.CharacterBName}: {rel.RelationshipTypeName}");
foreach (var item in packet.CharacterKnowledge) builder.AppendLine($"- Knowledge: {item.KnowledgeStateName} - {item.AssetName ?? item.ThreadTitle}: {item.Description}"); foreach (var item in packet.CharacterKnowledge) builder.AppendLine($"- Knowledge: {item.KnowledgeStateName} - {item.AssetName ?? item.ThreadTitle}: {item.Description}");
builder.AppendLine(); builder.AppendLine();
@ -6614,7 +6614,7 @@ public sealed class ExportService(IExportRepository exports) : IExportService
Heading(builder, "Research Pack", markdown); Heading(builder, "Research Pack", markdown);
foreach (var item in rows) foreach (var item in rows)
{ {
builder.AppendLine($"- {item.BookTitle} / Ch {item.ChapterNumber} / Scene {item.SceneNumber}: {item.Topic} - {item.Title}"); builder.AppendLine($"- {item.BookDisplayTitle} / Ch {item.ChapterNumber} / Scene {item.SceneNumber}: {item.Topic} - {item.Title}");
if (!string.IsNullOrWhiteSpace(item.Detail)) builder.AppendLine($" {item.Detail}"); if (!string.IsNullOrWhiteSpace(item.Detail)) builder.AppendLine($" {item.Detail}");
if (!string.IsNullOrWhiteSpace(item.Reference)) builder.AppendLine($" {item.Reference}"); if (!string.IsNullOrWhiteSpace(item.Reference)) builder.AppendLine($" {item.Reference}");
} }
@ -6753,7 +6753,7 @@ public sealed class ScenarioService(IProjectRepository projects, IBookRepository
ProjectID = projectId, ProjectID = projectId,
BookID = bookId, BookID = bookId,
BookOptions = (await books.ListByProjectAsync(projectId)) BookOptions = (await books.ListByProjectAsync(projectId))
.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString(), x.BookID == bookId)) .Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookDisplayTitle}", x.BookID.ToString(), x.BookID == bookId))
.ToList() .ToList()
}; };
} }
@ -7515,7 +7515,7 @@ public sealed class CharacterService(
CharacterOptions = projectCharacters.Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString())).ToList(), CharacterOptions = projectCharacters.Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString())).ToList(),
RelationshipTypeOptions = ToRelationshipTypeSelectList(relationshipLookups.RelationshipTypes), RelationshipTypeOptions = ToRelationshipTypeSelectList(relationshipLookups.RelationshipTypes),
RelationshipStateOptions = relationshipLookups.RelationshipStates.Select(x => new SelectListItem(x.StateName, x.RelationshipStateID.ToString())).ToList(), RelationshipStateOptions = relationshipLookups.RelationshipStates.Select(x => new SelectListItem(x.StateName, x.RelationshipStateID.ToString())).ToList(),
BookOptions = projectBooks.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString())).ToList() BookOptions = projectBooks.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookDisplayTitle}", x.BookID.ToString())).ToList()
}; };
} }

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@
chapterLabel = $"Chapter {point.ChapterNumber:g}", chapterLabel = $"Chapter {point.ChapterNumber:g}",
sceneNumber = point.SceneNumber.ToString("g"), sceneNumber = point.SceneNumber.ToString("g"),
sceneTitle = point.SceneTitle, sceneTitle = point.SceneTitle,
bookTitle = point.BookTitle, bookTitle = point.BookDisplayTitle,
timelineUrl = Url.Action("Index", "Timeline", new { ProjectID = Model.Project.ProjectID, BookID = point.BookID, SelectedSceneID = point.SceneID, FocusType = "scene", FocusID = point.SceneID }) timelineUrl = Url.Action("Index", "Timeline", new { ProjectID = Model.Project.ProjectID, BookID = point.BookID, SelectedSceneID = point.SceneID, FocusType = "scene", FocusID = point.SceneID })
}) })
.ToList(); .ToList();

View File

@ -97,9 +97,9 @@
{ {
<span>@relationship.InitialRelationshipStateName@if (relationship.InitialIntensity.HasValue) { <text> / Intensity @relationship.InitialIntensity</text> }</span> <span>@relationship.InitialRelationshipStateName@if (relationship.InitialIntensity.HasValue) { <text> / Intensity @relationship.InitialIntensity</text> }</span>
} }
@if (!string.IsNullOrWhiteSpace(relationship.InitialBookTitle)) @if (!string.IsNullOrWhiteSpace(relationship.InitialBookDisplayTitle))
{ {
<span>Initial book: @relationship.InitialBookTitle</span> <span>Initial book: @relationship.InitialBookDisplayTitle</span>
} }
@if (!string.IsNullOrWhiteSpace(relationship.Notes)) @if (!string.IsNullOrWhiteSpace(relationship.Notes))
{ {
@ -289,7 +289,7 @@
@foreach (var item in current.Events) @foreach (var item in current.Events)
{ {
<div class="asset-mini-item"> <div class="asset-mini-item">
<strong>@item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</strong> <strong>@item.BookDisplayTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</strong>
<span>@item.RelationshipStateName <span class="status-pill">@item.RelationshipCategoryName</span> / Intensity @(item.Intensity?.ToString() ?? "Unknown")</span> <span>@item.RelationshipStateName <span class="status-pill">@item.RelationshipCategoryName</span> / Intensity @(item.Intensity?.ToString() ?? "Unknown")</span>
<span>Awareness: @(item.IsKnownToOtherCharacter ? "known to other character" : "not known to other character")</span> <span>Awareness: @(item.IsKnownToOtherCharacter ? "known to other character" : "not known to other character")</span>
@if (!string.IsNullOrWhiteSpace(item.Description)) @if (!string.IsNullOrWhiteSpace(item.Description))
@ -324,7 +324,7 @@
{ {
<div class="asset-mini-item"> <div class="asset-mini-item">
<strong>@item.CharacterAName -> @item.CharacterBName: @item.RelationshipStateName</strong> <strong>@item.CharacterAName -> @item.CharacterBName: @item.RelationshipStateName</strong>
<span>@item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</span> <span>@item.BookDisplayTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</span>
<span>@item.RelationshipTypeName <span class="status-pill">@item.RelationshipCategoryName</span> / Awareness: @(item.IsKnownToOtherCharacter ? "known to other character" : "not known to other character")</span> <span>@item.RelationshipTypeName <span class="status-pill">@item.RelationshipCategoryName</span> / Awareness: @(item.IsKnownToOtherCharacter ? "known to other character" : "not known to other character")</span>
<span>Reciprocal: @(item.IsReciprocal ? "Yes" : "No") / Confidence / intensity: @(item.Intensity?.ToString() ?? "Unknown")</span> <span>Reciprocal: @(item.IsReciprocal ? "Yes" : "No") / Confidence / intensity: @(item.Intensity?.ToString() ?? "Unknown")</span>
@if (!string.IsNullOrWhiteSpace(item.Description)) @if (!string.IsNullOrWhiteSpace(item.Description))
@ -351,7 +351,7 @@
@foreach (var appearance in Model.Appearances) @foreach (var appearance in Model.Appearances)
{ {
<div class="asset-mini-item"> <div class="asset-mini-item">
<strong>@appearance.BookTitle / Ch @appearance.ChapterNumber / Scene @appearance.SceneNumber: @appearance.SceneTitle</strong> <strong>@appearance.BookDisplayTitle / Ch @appearance.ChapterNumber / Scene @appearance.SceneNumber: @appearance.SceneTitle</strong>
<span>@appearance.RoleInSceneTypeName / @appearance.PresenceTypeName</span> <span>@appearance.RoleInSceneTypeName / @appearance.PresenceTypeName</span>
<span>@appearance.OutfitDescription @appearance.PhysicalCondition @appearance.EmotionalState</span> <span>@appearance.OutfitDescription @appearance.PhysicalCondition @appearance.EmotionalState</span>
<a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@appearance.SceneID" asp-route-FocusType="character" asp-route-FocusID="@Model.Character.CharacterID">Open in timeline</a> <a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@appearance.SceneID" asp-route-FocusType="character" asp-route-FocusID="@Model.Character.CharacterID">Open in timeline</a>
@ -374,7 +374,7 @@
{ {
<div class="asset-mini-item"> <div class="asset-mini-item">
<strong>@item.AttributeName: @item.AttributeValue</strong> <strong>@item.AttributeName: @item.AttributeValue</strong>
<span>@item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</span> <span>@item.BookDisplayTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</span>
<span>@item.Description</span> <span>@item.Description</span>
<a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@item.SceneID" asp-route-FocusType="character" asp-route-FocusID="@Model.Character.CharacterID">Open in timeline</a> <a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@item.SceneID" asp-route-FocusType="character" asp-route-FocusID="@Model.Character.CharacterID">Open in timeline</a>
</div> </div>
@ -396,7 +396,7 @@
{ {
<div class="asset-mini-item"> <div class="asset-mini-item">
<strong>@item.KnowledgeStateName: @(item.AssetName ?? item.ThreadTitle)</strong> <strong>@item.KnowledgeStateName: @(item.AssetName ?? item.ThreadTitle)</strong>
<span>@item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</span> <span>@item.BookDisplayTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</span>
<span>@item.Description</span> <span>@item.Description</span>
<a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@item.SceneID" asp-route-FocusType="character" asp-route-FocusID="@Model.Character.CharacterID">Open in timeline</a> <a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@item.SceneID" asp-route-FocusType="character" asp-route-FocusID="@Model.Character.CharacterID">Open in timeline</a>
</div> </div>

View File

@ -10,7 +10,7 @@
chapterLabel = $"Chapter {point.ChapterNumber:g}", chapterLabel = $"Chapter {point.ChapterNumber:g}",
sceneNumber = point.SceneNumber.ToString("g"), sceneNumber = point.SceneNumber.ToString("g"),
sceneTitle = point.SceneTitle, sceneTitle = point.SceneTitle,
bookTitle = point.BookTitle, bookTitle = point.BookDisplayTitle,
characterAppears = true, characterAppears = true,
characterPresence = point.IsPov ? "POV scene" : point.PresenceTypeName ?? "Appears", characterPresence = point.IsPov ? "POV scene" : point.PresenceTypeName ?? "Appears",
timelineUrl = Url.Action("Edit", "Scenes", new { id = point.SceneID }) timelineUrl = Url.Action("Edit", "Scenes", new { id = point.SceneID })
@ -112,7 +112,7 @@
{ {
<article class="writer-queue-card"> <article class="writer-queue-card">
<div class="writer-queue-title"> <div class="writer-queue-title">
<h3>@point.BookTitle / Ch @point.ChapterNumber / Scene @point.SceneNumber: @point.SceneTitle</h3> <h3>@point.BookDisplayTitle / Ch @point.ChapterNumber / Scene @point.SceneNumber: @point.SceneTitle</h3>
@if (point.IsPov) { <span class="status-pill">POV</span> } @if (point.IsPov) { <span class="status-pill">POV</span> }
</div> </div>
<div class="story-bible-meta"> <div class="story-bible-meta">

View File

@ -20,7 +20,7 @@
{ {
<article class="writer-queue-card"> <article class="writer-queue-card">
<div class="writer-queue-title"> <div class="writer-queue-title">
<h3>@item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</h3> <h3>@item.BookDisplayTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</h3>
<span class="status-pill">@item.KnowledgeStateName</span> <span class="status-pill">@item.KnowledgeStateName</span>
</div> </div>
<p><strong>@(item.AssetName ?? item.ThreadTitle ?? "Knowledge")</strong> @item.Description</p> <p><strong>@(item.AssetName ?? item.ThreadTitle ?? "Knowledge")</strong> @item.Description</p>

View File

@ -34,7 +34,7 @@
{ {
<article class="writer-queue-card"> <article class="writer-queue-card">
<div class="writer-queue-title"> <div class="writer-queue-title">
<h3>@item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</h3> <h3>@item.BookDisplayTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle</h3>
<span class="status-pill">@item.RelationshipStateName</span> <span class="status-pill">@item.RelationshipStateName</span>
</div> </div>
<div class="story-bible-meta"> <div class="story-bible-meta">

View File

@ -15,13 +15,13 @@
<div class="row g-3"> <div class="row g-3">
<div class="col-md-6"> <div class="col-md-6">
<p class="eyebrow">Current position</p> <p class="eyebrow">Current position</p>
<p><strong>@Model.Preview.SourceBookTitle</strong></p> <p><strong>@Model.Preview.SourceBookDisplayTitle</strong></p>
<p>Chapter @Model.Preview.SourceChapterNumber: @Model.Preview.SourceChapterTitle</p> <p>Chapter @Model.Preview.SourceChapterNumber: @Model.Preview.SourceChapterTitle</p>
<p>Scene @Model.Preview.SceneNumber: @Model.Preview.SceneTitle</p> <p>Scene @Model.Preview.SceneNumber: @Model.Preview.SceneTitle</p>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<p class="eyebrow">Proposed position</p> <p class="eyebrow">Proposed position</p>
<p><strong>@Model.Preview.TargetBookTitle</strong></p> <p><strong>@Model.Preview.TargetBookDisplayTitle</strong></p>
<p>Chapter @Model.Preview.TargetChapterNumber: @Model.Preview.TargetChapterTitle</p> <p>Chapter @Model.Preview.TargetChapterNumber: @Model.Preview.TargetChapterTitle</p>
<p>Position: @Model.Preview.Position.ToLowerInvariant()</p> <p>Position: @Model.Preview.Position.ToLowerInvariant()</p>
</div> </div>

View File

@ -119,7 +119,7 @@
<span class="asset-event-marker">@assetEvent.MarkerText</span> <span class="asset-event-marker">@assetEvent.MarkerText</span>
<div class="story-asset-event-details"> <div class="story-asset-event-details">
<strong>@assetEvent.EventTitle</strong> <strong>@assetEvent.EventTitle</strong>
<span>@assetEvent.BookTitle / Ch @assetEvent.ChapterNumber / Scene @assetEvent.SceneNumber: @assetEvent.SceneTitle</span> <span>@assetEvent.BookDisplayTitle / Ch @assetEvent.ChapterNumber / Scene @assetEvent.SceneNumber: @assetEvent.SceneTitle</span>
<span>@assetEvent.FromStateName -> @assetEvent.ToStateName</span> <span>@assetEvent.FromStateName -> @assetEvent.ToStateName</span>
<a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@assetEvent.SceneID" asp-route-FocusType="asset" asp-route-FocusID="@Model.Asset.StoryAssetID">Open in timeline</a> <a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@assetEvent.SceneID" asp-route-FocusType="asset" asp-route-FocusID="@Model.Asset.StoryAssetID">Open in timeline</a>
</div> </div>
@ -143,7 +143,7 @@
<div class="asset-mini-item"> <div class="asset-mini-item">
<strong>@custody.CustodyEventTypeName</strong> <strong>@custody.CustodyEventTypeName</strong>
<span>@custody.CustodianSummary</span> <span>@custody.CustodianSummary</span>
<span>@custody.BookTitle / Ch @custody.ChapterNumber / Scene @custody.SceneNumber: @custody.SceneTitle</span> <span>@custody.BookDisplayTitle / Ch @custody.ChapterNumber / Scene @custody.SceneNumber: @custody.SceneTitle</span>
<a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@custody.SceneID" asp-route-FocusType="asset" asp-route-FocusID="@Model.Asset.StoryAssetID">Open in timeline</a> <a asp-controller="Timeline" asp-action="Index" asp-route-ProjectID="@Model.Project.ProjectID" asp-route-SelectedSceneID="@custody.SceneID" asp-route-FocusType="asset" asp-route-FocusID="@Model.Asset.StoryAssetID">Open in timeline</a>
</div> </div>
} }

View File

@ -2,7 +2,7 @@
@{ @{
ViewData["Title"] = $"Story Bible - {Model.Project.ProjectName}"; ViewData["Title"] = $"Story Bible - {Model.Project.ProjectName}";
ViewData["ProjectSection"] = "Story Bible"; ViewData["ProjectSection"] = "Story Bible";
var timelineByBook = Model.Timeline.GroupBy(x => new { x.BookID, x.BookTitle }); var timelineByBook = Model.Timeline.GroupBy(x => new { x.BookID, x.BookDisplayTitle });
var relationshipsByCharacter = Model.Relationships.GroupBy(x => new { x.CharacterAID, x.CharacterAName }); var relationshipsByCharacter = Model.Relationships.GroupBy(x => new { x.CharacterAID, x.CharacterAName });
var revisionByGroup = Model.RevisionItems.GroupBy(x => x.GroupName); var revisionByGroup = Model.RevisionItems.GroupBy(x => x.GroupName);
} }
@ -295,7 +295,7 @@
@foreach (var book in timelineByBook) @foreach (var book in timelineByBook)
{ {
<article class="story-bible-card story-bible-wide-card"> <article class="story-bible-card story-bible-wide-card">
<h3>@book.Key.BookTitle</h3> <h3>@book.Key.BookDisplayTitle</h3>
@foreach (var chapter in book.GroupBy(x => new { x.ChapterID, x.ChapterNumber, x.ChapterTitle })) @foreach (var chapter in book.GroupBy(x => new { x.ChapterID, x.ChapterNumber, x.ChapterTitle }))
{ {
<div class="story-bible-chapter-summary"> <div class="story-bible-chapter-summary">

View File

@ -34,10 +34,10 @@
sceneId = column.Scene?.SceneID, sceneId = column.Scene?.SceneID,
isPlaceholder = column.Scene is null, isPlaceholder = column.Scene is null,
axisLabel = column.Scene is null ? string.Empty : $"Ch {column.Chapter?.Chapter.ChapterNumber:g}.{column.Scene.SceneNumber:g}", axisLabel = column.Scene is null ? string.Empty : $"Ch {column.Chapter?.Chapter.ChapterNumber:g}.{column.Scene.SceneNumber:g}",
chapterLabel = column.Chapter is null ? column.Book.Book.BookTitle : $"Chapter {column.Chapter.Chapter.ChapterNumber:g}", chapterLabel = column.Chapter is null ? column.Book.Book.BookDisplayTitle : $"Chapter {column.Chapter.Chapter.ChapterNumber:g}",
sceneNumber = column.Scene?.SceneNumber.ToString("g"), sceneNumber = column.Scene?.SceneNumber.ToString("g"),
sceneTitle = column.Scene?.SceneTitle ?? column.PlaceholderLabel, sceneTitle = column.Scene?.SceneTitle ?? column.PlaceholderLabel,
bookTitle = column.Book.Book.BookTitle, bookTitle = column.Book.Book.BookDisplayTitle,
timelineUrl = column.Scene is null ? null : Url.Action("Index", "Timeline", new { projectId = Model.Project.ProjectID, bookId = column.Book.Book.BookID, selectedSceneId = column.Scene.SceneID }) timelineUrl = column.Scene is null ? null : Url.Action("Index", "Timeline", new { projectId = Model.Project.ProjectID, bookId = column.Book.Book.BookID, selectedSceneId = column.Scene.SceneID })
}).ToList(); }).ToList();
var timelineMetricShapePayload = new var timelineMetricShapePayload = new
@ -117,7 +117,7 @@
<a asp-controller="Projects" asp-action="Details" asp-route-id="@Model.Project.ProjectID">@Model.Project.ProjectName</a> <a asp-controller="Projects" asp-action="Details" asp-route-id="@Model.Project.ProjectID">@Model.Project.ProjectName</a>
@if (selectedBook is not null) @if (selectedBook is not null)
{ {
<span>@selectedBook.BookTitle</span> <span>@selectedBook.BookDisplayTitle</span>
} }
<span>Timeline</span> <span>Timeline</span>
</nav> </nav>
@ -611,7 +611,7 @@ else
<div class="timeline-book" style="--book-scene-count:@BookSceneColumns(book)"> <div class="timeline-book" style="--book-scene-count:@BookSceneColumns(book)">
<div class="timeline-book-title"> <div class="timeline-book-title">
<span>Book @book.Book.BookNumber</span> <span>Book @book.Book.BookNumber</span>
<strong>@book.Book.BookTitle</strong> <strong>@book.Book.BookDisplayTitle</strong>
</div> </div>
<div class="timeline-chapters"> <div class="timeline-chapters">
@if (!book.Chapters.Any()) @if (!book.Chapters.Any())

View File

@ -85,7 +85,7 @@
{ {
<tr> <tr>
<td>@item.TaskType</td> <td>@item.TaskType</td>
<td>@item.BookTitle</td> <td>@item.BookDisplayTitle</td>
<td>Chapter @item.ChapterNumber: @item.ChapterTitle</td> <td>Chapter @item.ChapterNumber: @item.ChapterTitle</td>
<td> <td>
<a asp-controller="Scenes" asp-action="Edit" asp-route-id="@item.SceneID"> <a asp-controller="Scenes" asp-action="Edit" asp-route-id="@item.SceneID">
@ -154,7 +154,7 @@
<tr> <tr>
<td>@item.ScheduledDate.ToString("dd MMM yyyy")</td> <td>@item.ScheduledDate.ToString("dd MMM yyyy")</td>
<td>@item.TaskType</td> <td>@item.TaskType</td>
<td>@item.BookTitle</td> <td>@item.BookDisplayTitle</td>
<td>Chapter @item.ChapterNumber: @item.ChapterTitle</td> <td>Chapter @item.ChapterNumber: @item.ChapterTitle</td>
<td> <td>
<a asp-controller="Scenes" asp-action="Edit" asp-route-id="@item.SceneID"> <a asp-controller="Scenes" asp-action="Edit" asp-route-id="@item.SceneID">

View File

@ -58,7 +58,7 @@ else
@foreach (var plan in Model.Plans) @foreach (var plan in Model.Plans)
{ {
<option value="@plan.WritingPlanID" selected="@(plan.WritingPlanID == Model.SelectedWritingPlanID)"> <option value="@plan.WritingPlanID" selected="@(plan.WritingPlanID == Model.SelectedWritingPlanID)">
@plan.PlanName - @plan.ProjectName / @(plan.BookTitle ?? "All Books") - @plan.DeadlineDate.ToString("dd MMM yyyy") - @(plan.IsActive ? "Active" : "Inactive") @plan.PlanName - @plan.ProjectName / @(string.IsNullOrWhiteSpace(plan.BookDisplayTitle) ? "All Books" : plan.BookDisplayTitle) - @plan.DeadlineDate.ToString("dd MMM yyyy") - @(plan.IsActive ? "Active" : "Inactive")
</option> </option>
} }
</select> </select>
@ -83,7 +83,7 @@ else
<tr> <tr>
<td>@plan.PlanName</td> <td>@plan.PlanName</td>
<td>@plan.ProjectName</td> <td>@plan.ProjectName</td>
<td>@(plan.BookTitle ?? "All Books")</td> <td>@(string.IsNullOrWhiteSpace(plan.BookDisplayTitle) ? "All Books" : plan.BookDisplayTitle)</td>
<td>@plan.DeadlineDate.ToString("dd MMM yyyy")</td> <td>@plan.DeadlineDate.ToString("dd MMM yyyy")</td>
<td>@(plan.IsActive ? "Yes" : "No")</td> <td>@(plan.IsActive ? "Yes" : "No")</td>
<td> <td>
@ -165,7 +165,7 @@ else
<tr> <tr>
<td>@item.ScheduledDate.ToString("dd MMM yyyy")</td> <td>@item.ScheduledDate.ToString("dd MMM yyyy")</td>
<td>@item.TaskType</td> <td>@item.TaskType</td>
<td>@item.BookTitle</td> <td>@item.BookDisplayTitle</td>
<td>Chapter @item.ChapterNumber: @item.ChapterTitle</td> <td>Chapter @item.ChapterNumber: @item.ChapterTitle</td>
<td>Scene @item.SceneNumber: @item.SceneTitle</td> <td>Scene @item.SceneNumber: @item.SceneTitle</td>
<td>@(item.Priority?.ToString() ?? "-")</td> <td>@(item.Priority?.ToString() ?? "-")</td>

View File

@ -70,7 +70,7 @@
<tr> <tr>
<td>@plan.PlanName</td> <td>@plan.PlanName</td>
<td>@plan.ProjectName</td> <td>@plan.ProjectName</td>
<td>@(plan.BookTitle ?? "All Books")</td> <td>@(string.IsNullOrWhiteSpace(plan.BookDisplayTitle) ? "All Books" : plan.BookDisplayTitle)</td>
<td>@plan.GoalType</td> <td>@plan.GoalType</td>
<td>@plan.StartDate.ToString("dd MMM yyyy")</td> <td>@plan.StartDate.ToString("dd MMM yyyy")</td>
<td>@plan.DeadlineDate.ToString("dd MMM yyyy")</td> <td>@plan.DeadlineDate.ToString("dd MMM yyyy")</td>

View File

@ -12,7 +12,7 @@ else
<article class="writer-queue-card"> <article class="writer-queue-card">
<div class="writer-queue-title"> <div class="writer-queue-title">
<div> <div>
<span class="eyebrow">@scene.BookTitle / Chapter @scene.ChapterNumber</span> <span class="eyebrow">@scene.BookDisplayTitle / Chapter @scene.ChapterNumber</span>
<h3> <h3>
<a asp-controller="Scenes" asp-action="Edit" asp-route-id="@scene.SceneID"> <a asp-controller="Scenes" asp-action="Edit" asp-route-id="@scene.SceneID">
Scene @scene.SceneNumber: @scene.SceneTitle Scene @scene.SceneNumber: @scene.SceneTitle