using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Rendering; using PlotLine.Models; namespace PlotLine.ViewModels; public sealed class ProjectEditViewModel { public int ProjectID { get; set; } [Required, StringLength(200)] [Display(Name = "Project / Series name")] public string ProjectName { get; set; } = string.Empty; public string? Description { get; set; } [Display(Name = "Genre / Metric Preset")] public string GenreMetricPresetKey { get; set; } = "Custom"; public IReadOnlyList GenreMetricPresetOptions { get; set; } = []; } public sealed class ProjectIndexViewModel { public IReadOnlyList ActiveProjects { get; init; } = []; public IReadOnlyList ArchivedProjects { get; init; } = []; public TrialVisibilityViewModel? Trial { get; init; } } public sealed class ProjectIndexCardViewModel { public int ProjectID { get; set; } public string ProjectName { get; set; } = string.Empty; public string? Description { get; set; } public DateTime CreatedDate { get; set; } public DateTime UpdatedDate { get; set; } public bool IsArchived { get; set; } public DateTime? ArchivedDate { get; set; } public string AccessRole { get; set; } = "Owner"; public bool IsOwner => string.Equals(AccessRole, "Owner", StringComparison.OrdinalIgnoreCase); public int BookCount { get; set; } public int TotalWords { get; set; } public DateTime? LastBookUpdatedDate { get; set; } public IReadOnlyList CoverThumbnailPaths { get; set; } = []; } public sealed class ProjectDetailViewModel { public Project Project { get; set; } = new(); public ProjectDashboardSummaryViewModel Summary { get; set; } = new(); public ProjectDashboardSeriesProgressViewModel SeriesProgress { get; set; } = new(); public IReadOnlyList RecentActivity { get; set; } = []; public ProjectDashboardAttentionViewModel NeedsAttention { get; set; } = new(); public IReadOnlyList Books { get; set; } = []; } public sealed class ProjectDashboardSummaryViewModel { public int TotalBooks { get; set; } public int PublishedBooks { get; set; } public int DraftingBooks { get; set; } public int PlanningBooks { get; set; } public int TotalWords { get; set; } public int CharacterCount { get; set; } public int LocationCount { get; set; } public int ActivePlotThreadCount { get; set; } public int ContinuityWarningCount { get; set; } } public sealed class ProjectDashboardBookCardViewModel { public int BookID { get; set; } public int BookNumber { get; set; } public string BookTitle { get; set; } = string.Empty; public string? Subtitle { get; set; } public string Status { get; set; } = BookStatuses.Planning; public string StatusCssClass { get; set; } = "dashboard-status-planning"; public int CurrentWordCount { get; set; } public int? TargetWordCount { get; set; } public decimal? ProgressPercentage { get; set; } public int ChapterCount { get; set; } public int SceneCount { get; set; } public DateTime UpdatedDate { get; set; } public string CoverThumbnailPath { get; set; } = "/images/placeholders/book-cover.svg"; public string WordCountLabel => TargetWordCount is > 0 ? $"{CurrentWordCount:N0} / {TargetWordCount.Value:N0} words" : $"{CurrentWordCount:N0} words"; } public sealed class ProjectDashboardSeriesProgressViewModel { public decimal Percentage { get; set; } public string SupportingLine { get; set; } = string.Empty; public string CalculationLabel { get; set; } = string.Empty; } public sealed class ProjectDashboardActivityItemViewModel { public string ActivityType { get; set; } = string.Empty; public string EntityType { get; set; } = string.Empty; public string? EntityName { get; set; } public string? Description { get; set; } public string UserLabel { get; set; } = string.Empty; public string TimeLabel { get; set; } = string.Empty; public string IconLabel { get; set; } = string.Empty; } public sealed class ProjectDashboardAttentionViewModel { public IReadOnlyList Warnings { get; set; } = []; public IReadOnlyList Threads { get; set; } = []; public IReadOnlyList OverdueBooks { get; set; } = []; public IReadOnlyList BookItems { get; set; } = []; public bool ShowMissingSchedulePrompt { get; set; } public bool HasItems => Warnings.Any() || Threads.Any() || OverdueBooks.Any() || BookItems.Any() || ShowMissingSchedulePrompt; } public sealed class ProjectDashboardWarningItemViewModel { public string SeverityName { get; set; } = string.Empty; public string WarningTypeName { get; set; } = string.Empty; public string Message { get; set; } = string.Empty; public string? LocationLabel { get; set; } } public sealed class ProjectDashboardThreadItemViewModel { public int PlotThreadID { get; set; } public string ThreadTitle { get; set; } = string.Empty; public string PlotLineName { get; set; } = string.Empty; public string ThreadStatusName { get; set; } = string.Empty; public int Importance { get; set; } public string AttentionLabel { get; set; } = string.Empty; } public sealed class ProjectDashboardOverdueBookViewModel { public int BookID { get; set; } public string BookTitle { get; set; } = string.Empty; public DateTime TargetCompletionDate { get; set; } } public sealed class ProjectDashboardBookAttentionItemViewModel { public int BookID { get; set; } public string BookTitle { get; set; } = string.Empty; public string Message { get; set; } = string.Empty; public string Label { get; set; } = "Worth a look"; } public sealed class ProjectCollaboratorsViewModel { public Project Project { get; set; } = new(); public IReadOnlyList Collaborators { get; set; } = []; public IReadOnlyList PendingInvitations { get; set; } = []; public ProjectCollaboratorInviteViewModel Invite { get; set; } = new(); } public sealed class ProjectCollaboratorInviteViewModel { public int ProjectID { get; set; } [Required, EmailAddress, StringLength(256)] [Display(Name = "Collaborator email")] public string EmailAddress { get; set; } = string.Empty; } public sealed class ProjectActivityViewModel { public Project Project { get; set; } = new(); public IReadOnlyList Activities { get; set; } = []; } public sealed class BookEditViewModel { public int BookID { get; set; } public int ProjectID { get; set; } [Required, StringLength(200)] [Display(Name = "Book title")] public string BookTitle { get; set; } = string.Empty; [StringLength(200)] public string? Subtitle { get; set; } [StringLength(300)] public string? Tagline { get; set; } [Display(Name = "Short description")] public string? ShortDescription { get; set; } [Required, StringLength(50)] public string Status { get; set; } = BookStatuses.Planning; [Display(Name = "Book number")] public int BookNumber { get; set; } = 1; [Display(Name = "Target word count")] [Range(1, int.MaxValue, ErrorMessage = "Target word count must be greater than zero.")] public int? TargetWordCount { get; set; } [DataType(DataType.Date)] [Display(Name = "Writing start date")] public DateTime? WritingStartDate { get; set; } [DataType(DataType.Date)] [Display(Name = "Target completion date")] public DateTime? TargetCompletionDate { get; set; } [DataType(DataType.Date)] [Display(Name = "Publication date")] public DateTime? PublicationDate { get; set; } [Display(Name = "Uses explicit scenes")] public bool UsesExplicitScenes { get; set; } [Display(Name = "Cover image")] public IFormFile? CoverUpload { get; set; } public string CoverThumbnailPath { get; set; } = "/images/placeholders/book-cover.svg"; public string? CoverStandardPath { get; set; } public string? Description { get; set; } public Project? Project { get; set; } public IReadOnlyList StatusOptions { get; set; } = []; } public sealed class BookDetailViewModel { public Project Project { get; set; } = new(); public Book Book { get; set; } = new(); public ManuscriptDocumentModel? ManuscriptDocument { get; set; } public IReadOnlyList Chapters { get; set; } = []; } public sealed class ChapterEditViewModel { public int ChapterID { get; set; } public int BookID { get; set; } [Display(Name = "Chapter number")] public decimal ChapterNumber { get; set; } = 1; [Required, StringLength(200)] [Display(Name = "Chapter title")] public string ChapterTitle { get; set; } = string.Empty; public string? Summary { get; set; } [Display(Name = "Revision status")] public int RevisionStatusID { get; set; } public bool IsLinkedToManuscript { get; set; } public Book? Book { get; set; } public Project? Project { get; set; } public IReadOnlyList RevisionStatuses { get; set; } = []; } public sealed class ChapterDetailViewModel { public Project Project { get; set; } = new(); public Book Book { get; set; } = new(); public Chapter Chapter { get; set; } = new(); public IReadOnlyList Scenes { get; set; } = []; public int PendingCharacterSuggestionCount { get; set; } public int PendingAssetSuggestionCount { get; set; } public int PendingLocationSuggestionCount { get; set; } } public sealed class SceneEditViewModel { public int SceneID { get; set; } public int ChapterID { get; set; } [Display(Name = "Scene number")] public decimal SceneNumber { get; set; } = 1; [Required, StringLength(200)] [Display(Name = "Scene title")] public string SceneTitle { get; set; } = string.Empty; public string? Summary { get; set; } [Display(Name = "POV character")] public int? POVCharacterID { get; set; } [Display(Name = "Time mode")] public int TimeModeID { get; set; } public string TimeModeName { get; set; } = string.Empty; [Display(Name = "Start date/time")] public DateTime? StartDateTime { get; set; } [Display(Name = "End date/time")] public DateTime? EndDateTime { get; set; } [Display(Name = "Duration")] public decimal? DurationAmount { get; set; } [Display(Name = "Duration unit")] public int? DurationUnitID { get; set; } [Display(Name = "Relative time")] public string? RelativeTimeText { get; set; } [Display(Name = "Time confidence")] public int TimeConfidenceID { get; set; } [Display(Name = "Purpose notes")] public string? ScenePurposeNotes { get; set; } [Display(Name = "Outcome notes")] public string? SceneOutcomeNotes { get; set; } [Display(Name = "Revision status")] public int RevisionStatusID { get; set; } public bool IsLinkedToManuscript { get; set; } [Display(Name = "Primary location")] public int? PrimaryLocationID { get; set; } [Display(Name = "Floor plan")] public int? FloorPlanID { get; set; } [Display(Name = "Initial floor")] public int? InitialFloorPlanFloorID { get; set; } [Display(Name = "Scene purposes")] public List SelectedPurposeTypeIds { get; set; } = []; public List Metrics { get; set; } = []; public IReadOnlyList ThreadEvents { get; set; } = []; public ThreadEventCreateViewModel NewThreadEvent { get; set; } = new(); public IReadOnlyList AssetEvents { get; set; } = []; public IReadOnlyList AssetCustodyEvents { get; set; } = []; public IReadOnlyList SceneAssetLocations { get; set; } = []; public AssetEventCreateViewModel NewAssetEvent { get; set; } = new(); public AssetCustodyCreateViewModel NewAssetCustodyEvent { get; set; } = new(); public SceneAssetLocationCreateViewModel NewSceneAssetLocation { get; set; } = new(); public IReadOnlyList SceneCharacters { get; set; } = []; public IReadOnlyList CharacterSuggestions { get; set; } = []; public IReadOnlyList AssetSuggestions { get; set; } = []; public IReadOnlyList LocationSuggestions { get; set; } = []; public IReadOnlyList CharacterAttributeEvents { get; set; } = []; public IReadOnlyList CharacterKnowledge { get; set; } = []; public IReadOnlyList RelationshipEvents { get; set; } = []; public SceneCharacterCreateViewModel NewSceneCharacter { get; set; } = new(); public CharacterAttributeEventCreateViewModel NewCharacterAttributeEvent { get; set; } = new(); public CharacterKnowledgeCreateViewModel NewCharacterKnowledge { get; set; } = new(); public RelationshipEventCreateViewModel NewRelationshipEvent { get; set; } = new(); public IReadOnlyList PlotLineOptions { get; set; } = []; public IReadOnlyList PlotThreadOptions { get; set; } = []; public IReadOnlyList PlotLineEventOptions { get; set; } = []; public IReadOnlyList PlotThreadEventOptions { get; set; } = []; public IReadOnlyList ThreadTypeOptions { get; set; } = []; public IReadOnlyList ThreadEventTypeOptions { get; set; } = []; public IReadOnlyList PlotEventTypeOptions { get; set; } = []; public IReadOnlyList AssetOptions { get; set; } = []; public IReadOnlyList AssetKindOptions { get; set; } = []; public IReadOnlyList AssetStateOptions { get; set; } = []; public IReadOnlyList AssetEventTypeOptions { get; set; } = []; public IReadOnlyList AssetCustodyEventTypeOptions { get; set; } = []; public IReadOnlyList CustodyRoleOptions { get; set; } = []; public IReadOnlyList CharacterOptions { get; set; } = []; public IReadOnlyList CharacterRoleOptions { get; set; } = []; public IReadOnlyList PresenceTypeOptions { get; set; } = []; public IReadOnlyList CharacterAttributeTypeOptions { get; set; } = []; public IReadOnlyList KnowledgeStateOptions { get; set; } = []; public IReadOnlyList RelationshipOptions { get; set; } = []; public IReadOnlyList RelationshipTypeOptions { get; set; } = []; public IReadOnlyList RelationshipStateOptions { get; set; } = []; public IReadOnlyList LocationOptions { get; set; } = []; public IReadOnlyList SceneOptions { get; set; } = []; public IReadOnlyList ChapterOptions { get; set; } = []; public IReadOnlyList SceneDependencyTypeOptions { get; set; } = []; public IReadOnlyList AssetDependencyMessages { get; set; } = []; public IReadOnlyList LocationConsistencyMessages { get; set; } = []; public IReadOnlyList Warnings { get; set; } = []; public IReadOnlyList AgeContinuityWarnings { get; set; } = []; public IReadOnlyList DependenciesThisSceneNeeds { get; set; } = []; public IReadOnlyList DependenciesNeedingThisScene { get; set; } = []; public SceneDependencyCreateViewModel NewDependency { get; set; } = new(); public SceneMoveRequestViewModel MoveRequest { get; set; } = new(); public SceneWorkflow Workflow { get; set; } = new(); public IReadOnlyList Notes { get; set; } = []; public IReadOnlyList ChecklistItems { get; set; } = []; public IReadOnlyList Attachments { get; set; } = []; public SceneNoteEditViewModel NewNote { get; set; } = new(); public SceneChecklistItemEditViewModel NewChecklistItem { get; set; } = new(); public SceneAttachmentEditViewModel NewAttachment { get; set; } = new(); public IReadOnlyList SceneNoteTypeOptions { get; set; } = []; public IReadOnlyList SceneAttachmentTypeOptions { get; set; } = []; public StorageUsage? StorageUsage { get; set; } public StoryStateViewModel StoryState { get; set; } = new(); public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } public bool ReturnToTimeline { get; set; } public Chapter? Chapter { get; set; } public Book? Book { get; set; } public Project? Project { get; set; } public IReadOnlyList RevisionStatuses { get; set; } = []; public IReadOnlyList TimeModes { get; set; } = []; public IReadOnlyList DurationUnits { get; set; } = []; public IReadOnlyList TimeConfidences { get; set; } = []; public IReadOnlyList ScenePurposeTypes { get; set; } = []; } public sealed class SceneDetailsSaveViewModel { public int SceneID { get; set; } [Display(Name = "Scene number")] public decimal SceneNumber { get; set; } [Required, StringLength(200)] [Display(Name = "Scene title")] public string SceneTitle { get; set; } = string.Empty; [Display(Name = "Revision status")] public int RevisionStatusID { get; set; } [Display(Name = "POV character")] public int? POVCharacterID { get; set; } [Display(Name = "Primary location")] public int? PrimaryLocationID { get; set; } [Display(Name = "Synopsis")] public string? Summary { get; set; } [Display(Name = "Time mode")] public int TimeModeID { get; set; } [Display(Name = "Time confidence")] public int TimeConfidenceID { get; set; } [Display(Name = "Start date/time")] public DateTime? StartDateTime { get; set; } [Display(Name = "End date/time")] public DateTime? EndDateTime { get; set; } [Display(Name = "Duration")] public decimal? DurationAmount { get; set; } [Display(Name = "Duration unit")] public int? DurationUnitID { get; set; } [Display(Name = "Relative time")] public string? RelativeTimeText { get; set; } } public sealed class ScenePurposeSaveViewModel { public int SceneID { get; set; } public List SelectedPurposeTypeIds { get; set; } = []; [Display(Name = "Purpose notes")] public string? ScenePurposeNotes { get; set; } [Display(Name = "Outcome notes")] public string? SceneOutcomeNotes { get; set; } } public sealed class SceneMetricsSaveViewModel { public int SceneID { get; set; } public List Metrics { get; set; } = []; } public sealed class SceneCharactersSaveViewModel { public int SceneID { get; set; } public int? POVCharacterID { get; set; } public List Characters { get; set; } = []; public SceneCharacterCreateViewModel NewCharacter { get; set; } = new(); } public sealed class SceneLocationsSaveViewModel { public int SceneID { get; set; } public int? PrimaryLocationID { get; set; } } public sealed class SceneAssetsSaveViewModel { public int SceneID { get; set; } public List AssetEvents { get; set; } = []; public AssetEventCreateViewModel NewAssetEvent { get; set; } = new(); } public sealed class SceneAssetEventInputViewModel { public int AssetEventID { get; set; } public int StoryAssetID { get; set; } public bool Remove { get; set; } } public sealed class SceneNotesSaveViewModel { public int SceneID { get; set; } public List Notes { get; set; } = []; public SceneNoteEditViewModel NewNote { get; set; } = new(); } public sealed class SceneNoteInputViewModel { public int SceneNoteID { get; set; } public int SceneID { get; set; } public int SceneNoteTypeID { get; set; } public string? NoteTitle { get; set; } public string NoteText { get; set; } = string.Empty; public int? SortOrder { get; set; } public bool IsPinned { get; set; } public bool IsResolved { get; set; } public bool Remove { get; set; } } public sealed class SceneChecklistSaveViewModel { public int SceneID { get; set; } public List ChecklistItems { get; set; } = []; public SceneChecklistItemEditViewModel NewChecklistItem { get; set; } = new(); } public sealed class SceneChecklistItemInputViewModel { public int SceneChecklistItemID { get; set; } public int SceneID { get; set; } public string ItemText { get; set; } = string.Empty; public bool IsCompleted { get; set; } public int? SortOrder { get; set; } public bool Remove { get; set; } } public sealed class SceneCharacterParticipationInputViewModel { public int SceneCharacterID { get; set; } public int SceneID { get; set; } public int CharacterID { get; set; } public bool Remove { get; set; } public int? RoleInSceneTypeID { get; set; } public int? PresenceTypeID { get; set; } public int? LocationID { get; set; } public int? EntryLocationID { get; set; } public int? ExitLocationID { get; set; } public string? AppearanceNotes { get; set; } public string? OutfitDescription { get; set; } public string? PhysicalCondition { get; set; } public string? EmotionalState { get; set; } } public sealed class StoryStateViewModel { public List Characters { get; set; } = []; public List Assets { get; set; } = []; public List Locations { get; set; } = []; } public sealed class CharacterStoryStateViewModel { public int CharacterID { get; set; } public string CharacterName { get; set; } = string.Empty; public int? LocationID { get; set; } public string CurrentLocation { get; set; } = "Unknown"; public string State { get; set; } = "Unknown"; public int? LastConfirmedSceneID { get; set; } public string LastConfirmed { get; set; } = "Never"; } public sealed class AssetStoryStateViewModel { public int StoryAssetID { get; set; } public string AssetName { get; set; } = string.Empty; public int? LocationID { get; set; } public string CurrentHolderOrLocation { get; set; } = "Unknown"; public string State { get; set; } = "Unknown"; public int? LastConfirmedSceneID { get; set; } public string LastConfirmed { get; set; } = "Never"; } public sealed class LocationStoryStateViewModel { public int LocationID { get; set; } public string LocationName { get; set; } = string.Empty; public string LocationPath { get; set; } = string.Empty; public List CharactersPresent { get; set; } = []; public List AssetsPresent { get; set; } = []; } public sealed class ContinuityFilter { public int ProjectID { get; set; } public List EntityTypes { get; set; } = ["Characters", "Assets", "Locations"]; public List CharacterIDs { get; set; } = []; public List AssetIDs { get; set; } = []; public List LocationIDs { get; set; } = []; public string SceneRangeMode { get; set; } = "EntireProject"; public int? BookID { get; set; } public int? ChapterID { get; set; } public int? StartSceneID { get; set; } public int? EndSceneID { get; set; } public string DisplayMode { get; set; } = "StoryState"; public int? SelectedSceneID { get; set; } public bool ShowAcknowledgedWarnings { get; set; } public bool IncludeCharacters => EntityTypes.Contains("Characters", StringComparer.OrdinalIgnoreCase); public bool IncludeAssets => EntityTypes.Contains("Assets", StringComparer.OrdinalIgnoreCase); public bool IncludeLocations => EntityTypes.Contains("Locations", StringComparer.OrdinalIgnoreCase); } public sealed class ContinuityExplorerViewModel { public Project Project { get; set; } = new(); public ContinuityFilter Filter { get; set; } = new(); public StoryStateViewModel StoryState { get; set; } = new(); public IReadOnlyList CharacterJourney { get; set; } = []; public IReadOnlyList AssetJourney { get; set; } = []; public IReadOnlyList LocationActivity { get; set; } = []; public IReadOnlyList ContinuityWarnings { get; set; } = []; public IReadOnlyList BookOptions { get; set; } = []; public IReadOnlyList ChapterOptions { get; set; } = []; public IReadOnlyList SceneOptions { get; set; } = []; public IReadOnlyList CharacterOptions { get; set; } = []; public IReadOnlyList AssetOptions { get; set; } = []; public IReadOnlyList LocationOptions { get; set; } = []; public bool ShowBookContext { get; set; } public bool ShowChapterContext { get; set; } } public sealed class ContinuityWarningViewModel { public string WarningKey { get; set; } = string.Empty; public string WarningType { get; set; } = string.Empty; public string WarningTypeDisplayName { get; set; } = string.Empty; public string Severity { get; set; } = "Information"; public string Message { get; set; } = string.Empty; public int? CharacterID { get; set; } public string CharacterName { get; set; } = string.Empty; public int? AssetID { get; set; } public int? BookID { get; set; } public int? ChapterID { get; set; } public int? SceneID { get; set; } public string SceneDisplayName { get; set; } = string.Empty; public string ContextSceneDisplayName { get; set; } = string.Empty; public string BookDisplayName { get; set; } = string.Empty; public string ChapterDisplayName { get; set; } = string.Empty; public bool IsAcknowledged { get; set; } public string? AcknowledgementNotes { get; set; } public int? KnowledgeId { get; set; } public string KnowledgeTitle { get; set; } = string.Empty; public string KnowledgeDescription { get; set; } = string.Empty; public string PreviousKnowledgeState { get; set; } = string.Empty; public string CurrentKnowledgeState { get; set; } = string.Empty; public int? PreviousBookId { get; set; } public int? PreviousChapterId { get; set; } public int? PreviousSceneId { get; set; } public string PreviousSceneDisplayName { get; set; } = string.Empty; public string PreviousContextSceneDisplayName { get; set; } = string.Empty; public int? CurrentBookId { get; set; } public int? CurrentChapterId { get; set; } public int? CurrentSceneId { get; set; } public string CurrentSceneDisplayName { get; set; } = string.Empty; public string CurrentContextSceneDisplayName { get; set; } = string.Empty; public int? EstablishedSceneId { get; set; } public string EstablishedSceneDisplayName { get; set; } = string.Empty; public string EstablishedContextSceneDisplayName { get; set; } = string.Empty; public int? ReferencedSceneId { get; set; } public string ReferencedSceneDisplayName { get; set; } = string.Empty; public string ReferencedContextSceneDisplayName { get; set; } = string.Empty; public string InvestigationDisplayMode { get; set; } = string.Empty; public int? InvestigationSceneId { get; set; } public bool CanInvestigate { get; set; } } public sealed class CharacterJourneyViewModel { public int CharacterID { get; set; } public string CharacterName { get; set; } = string.Empty; public int BookID { get; set; } public string BookDisplayName { get; set; } = string.Empty; public int ChapterID { get; set; } public string ChapterDisplayName { get; set; } = string.Empty; public int SceneID { get; set; } public string SceneTitle { get; set; } = string.Empty; public string SceneLabel { get; set; } = string.Empty; public string SceneDisplayName { get; set; } = string.Empty; public string Location { get; set; } = string.Empty; } public sealed class AssetJourneyViewModel { public int StoryAssetID { get; set; } public string AssetName { get; set; } = string.Empty; public int BookID { get; set; } public string BookDisplayName { get; set; } = string.Empty; public int ChapterID { get; set; } public string ChapterDisplayName { get; set; } = string.Empty; public int SceneID { get; set; } public string SceneTitle { get; set; } = string.Empty; public string SceneLabel { get; set; } = string.Empty; public string SceneDisplayName { get; set; } = string.Empty; public string HolderOrLocation { get; set; } = string.Empty; } public sealed class LocationActivityViewModel { public int LocationID { get; set; } public string LocationName { get; set; } = string.Empty; public int BookID { get; set; } public string BookDisplayName { get; set; } = string.Empty; public int ChapterID { get; set; } public string ChapterDisplayName { get; set; } = string.Empty; public int SceneID { get; set; } public string SceneTitle { get; set; } = string.Empty; public string SceneLabel { get; set; } = string.Empty; public string SceneDisplayName { get; set; } = string.Empty; public string CharactersPresent { get; set; } = string.Empty; public string AssetsPresent { get; set; } = string.Empty; public List Characters { get; set; } = []; public List Assets { get; set; } = []; } public sealed class LocationActivityEntityViewModel { public int EntityID { get; set; } public string EntityName { get; set; } = string.Empty; public string EntityType { get; set; } = string.Empty; public string StateType { get; set; } = string.Empty; public int? LastConfirmedSceneID { get; set; } public string LastConfirmedSceneDisplayName { get; set; } = string.Empty; public string? Note { get; set; } } public sealed class SceneWorkflowEditViewModel { public int SceneID { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } public string DraftStatus { get; set; } = "Planned"; public int? EstimatedWordCount { get; set; } public int? ActualWordCount { get; set; } public DateTime? DraftedDate { get; set; } public DateTime? LastWorkedOn { get; set; } public bool ReadyForDraft { get; set; } public bool ReadyForRevision { get; set; } public bool ReadyForPolish { get; set; } public bool IsBlocked { get; set; } public string? BlockedReason { get; set; } public int? Priority { get; set; } } public sealed class SceneNoteEditViewModel { public int SceneNoteID { get; set; } public int SceneID { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } public int SceneNoteTypeID { get; set; } public string? NoteTitle { get; set; } public string NoteText { get; set; } = string.Empty; public int? SortOrder { get; set; } public bool IsPinned { get; set; } public bool IsResolved { get; set; } } public sealed class SceneChecklistItemEditViewModel { public int SceneChecklistItemID { get; set; } public int SceneID { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } public string ItemText { get; set; } = string.Empty; public bool IsCompleted { get; set; } public int? SortOrder { get; set; } } public sealed class SceneAttachmentEditViewModel { public int SceneAttachmentID { get; set; } public int SceneID { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } public int SceneAttachmentTypeID { get; set; } public string Title { get; set; } = string.Empty; public string? FilePath { get; set; } public string? ExternalUrl { get; set; } public string? Notes { get; set; } public IFormFile? UploadedFile { get; set; } } public sealed class ChapterGoalEditViewModel { public int ChapterGoalID { get; set; } public int ChapterID { get; set; } public string? GoalSummary { get; set; } public string? EmotionalGoal { get; set; } public string? ReaderTakeaway { get; set; } public string? MustInclude { get; set; } public string? RisksOrConcerns { get; set; } } public sealed class ChapterWorkflowViewModel { public Project Project { get; set; } = new(); public Book Book { get; set; } = new(); public Chapter Chapter { get; set; } = new(); public ChapterGoal Goal { get; set; } = new(); public IReadOnlyList Scenes { get; set; } = []; } public sealed class WriterDashboardViewModel { public int? ProjectID { get; set; } public IReadOnlyList ProjectOptions { get; set; } = []; public WritingScheduleDashboardViewModel WritingSchedule { get; set; } = new(); public IReadOnlyList WriteNext { get; set; } = []; public IReadOnlyList ContinueWriting { get; set; } = []; public IReadOnlyList RevisionQueue { get; set; } = []; public IReadOnlyList PolishingQueue { get; set; } = []; public IReadOnlyList StoryHealthReminders { get; set; } = []; } public sealed class WritingScheduleDashboardViewModel { public int ActivePlanCount { get; set; } public int RebalancePlanCount { get; set; } public IReadOnlyList TodayTasks { get; set; } = []; public IReadOnlyList UpcomingTasks { get; set; } = []; public int TotalPlannedTasks { get; set; } public int CompletedTasks { get; set; } public int SkippedTasks { get; set; } public int RemainingTasks { get; set; } public DateTime? ProjectedFinishDate { get; set; } public bool HasActivePlans => ActivePlanCount > 0; public bool HasActiveSchedule => TotalPlannedTasks > 0; } public sealed class WritingPlanViewModel { public int WritingPlanID { get; set; } public int UserID { get; set; } public int ProjectID { get; set; } public int? BookID { get; set; } public string PlanName { get; set; } = string.Empty; public string GoalType { get; set; } = string.Empty; public DateTime StartDate { get; set; } public DateTime DeadlineDate { get; set; } public string AvailableDaysJson { get; set; } = string.Empty; public int SessionLengthMinutes { get; set; } public bool IncludeBlockedScenes { get; set; } public string RebalanceMode { get; set; } = "Ask"; public bool IsActive { get; set; } = true; public DateTime CreatedDateUtc { get; set; } public DateTime ModifiedDateUtc { get; set; } } public sealed class WritingPlansManagementViewModel { public IReadOnlyList Plans { get; set; } = []; } public sealed class WritingDayAvailabilityViewModel { public string Day { get; set; } = string.Empty; public bool IsAvailable { get; set; } public int Minutes { get; set; } = 60; } public sealed class WritingPlanSettingsEditViewModel { public int WritingPlanID { get; set; } [Required, StringLength(200)] public string PlanName { get; set; } = string.Empty; public string ProjectName { get; set; } = string.Empty; public string BookName { get; set; } = "All Books"; public string GoalType { get; set; } = string.Empty; [DataType(DataType.Date)] public DateTime StartDate { get; set; } [DataType(DataType.Date)] public DateTime DeadlineDate { get; set; } [Range(1, int.MaxValue, ErrorMessage = "Session length minutes must be greater than zero.")] public int SessionLengthMinutes { get; set; } public List SelectedWritingDays { get; set; } = []; public List WritingDayAvailability { get; set; } = []; public bool IncludeBlockedScenes { get; set; } public string RebalanceMode { get; set; } = "Ask"; public bool IsActive { get; set; } public IReadOnlyList RebalanceModeOptions { get; set; } = []; public IReadOnlyList SessionLengthOptions { get; set; } = []; public IReadOnlyList WritingDayOptions { get; set; } = [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ]; } public sealed class WritingScheduleItemViewModel { public int WritingScheduleItemID { get; set; } public int WritingPlanID { get; set; } public int SceneID { get; set; } public DateTime ScheduledDate { get; set; } public string TaskType { get; set; } = string.Empty; public int? EstimatedMinutes { get; set; } public int? TargetWords { get; set; } public string ScheduleStatus { get; set; } = string.Empty; public DateTime? CompletedDateUtc { get; set; } public string? Notes { get; set; } public DateTime CreatedDateUtc { get; set; } public DateTime ModifiedDateUtc { get; set; } } public sealed class WritingSchedulePreviewViewModel { public int? SelectedWritingPlanID { get; set; } public IReadOnlyList Plans { get; set; } = []; public IReadOnlyList PlansRequiringRebalance { get; set; } = []; public WritingPlanSummary? SelectedPlan { get; set; } public IReadOnlyList Items { get; set; } = []; public WritingScheduleRebalancePreviewViewModel? RebalancePreview { get; set; } public bool SelectedPlanRequiresRebalance => SelectedWritingPlanID.HasValue && PlansRequiringRebalance.Any(x => x.WritingPlanID == SelectedWritingPlanID.Value); public int TotalTasks => Items.Count; public int DraftTasks => Items.Count(x => x.TaskType == "Draft"); public int RevisionTasks => Items.Count(x => x.TaskType == "Revise"); public int PolishTasks => Items.Count(x => x.TaskType == "Polish"); public int TotalEstimatedMinutes => Items.Sum(x => x.EstimatedMinutes ?? 0); public DateTime? ProjectedFinishDate => Items.Count == 0 ? null : Items.Max(x => x.ScheduledDate); } public sealed class WritingScheduleRebalancePreviewViewModel { public int WritingPlanID { get; set; } public DateTime? OriginalFinishDate { get; set; } public DateTime? ProposedFinishDate { get; set; } public IReadOnlyList Changes { get; set; } = []; public int TasksAffected => Changes.Count; } public sealed class WritingScheduleRebalanceChangeViewModel { public int WritingScheduleItemID { get; set; } public string TaskType { get; set; } = string.Empty; public string SceneName { get; set; } = string.Empty; public DateTime CurrentDate { get; set; } public DateTime ProposedDate { get; set; } } public sealed class WritingScheduleSetupViewModel { [Required, StringLength(200)] public string PlanName { get; set; } = "Writing Schedule"; [Required] public int? ProjectID { get; set; } public int? BookID { get; set; } [Required] public string GoalType { get; set; } = "FirstDraft"; [DataType(DataType.Date)] public DateTime StartDate { get; set; } = DateTime.Today; [DataType(DataType.Date)] public DateTime DeadlineDate { get; set; } = DateTime.Today.AddMonths(1); public List SelectedWritingDays { get; set; } = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]; [Range(1, int.MaxValue, ErrorMessage = "Session length minutes must be greater than zero.")] public int SessionLengthMinutes { get; set; } = 60; public List WritingDayAvailability { get; set; } = []; public bool IncludeBlockedScenes { get; set; } [Required] public string RebalanceMode { get; set; } = "Ask"; public IReadOnlyList ProjectOptions { get; set; } = []; public IReadOnlyList BookOptions { get; set; } = []; public IReadOnlyList GoalTypeOptions { get; set; } = []; public IReadOnlyList RebalanceModeOptions { get; set; } = []; public IReadOnlyList SessionLengthOptions { get; set; } = []; public IReadOnlyList WritingDayOptions { get; set; } = [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ]; } public sealed class WritingScheduleSetupResult { public int WritingPlanID { get; init; } public int GeneratedItemCount { get; init; } public string? ValidationMessage { get; init; } } public sealed class CharacterArcViewModel { public Project Project { get; set; } = new(); public Character Character { get; set; } = new(); public IReadOnlyList ArcPoints { get; set; } = []; public IReadOnlyList RelationshipEvents { get; set; } = []; public IReadOnlyList KnowledgeEvents { get; set; } = []; public IReadOnlyList Issues { get; set; } = []; } public sealed class RelationshipEvolutionViewModel { public Project Project { get; set; } = new(); public CharacterRelationship Relationship { get; set; } = new(); public IReadOnlyList Events { get; set; } = []; public IReadOnlyList Issues { get; set; } = []; } public sealed class KnowledgeTimelineViewModel { public Project Project { get; set; } = new(); public Character Character { get; set; } = new(); public IReadOnlyList KnowledgeEvents { get; set; } = []; public IReadOnlyList Issues { get; set; } = []; } public sealed class StoryDynamicsViewModel { public Project Project { get; set; } = new(); public IReadOnlyList Pairings { get; set; } = []; public IReadOnlyList Issues { get; set; } = []; } public sealed class ScenarioListViewModel { public Project Project { get; set; } = new(); public IReadOnlyList Scenarios { get; set; } = []; } public sealed class ScenarioCreateViewModel { public int ProjectID { get; set; } public int? BookID { get; set; } public string ScenarioName { get; set; } = string.Empty; public string? Description { get; set; } public IReadOnlyList BookOptions { get; set; } = []; } public sealed class ScenarioTimelineViewModel { public Project Project { get; set; } = new(); public Scenario Scenario { get; set; } = new(); public IReadOnlyList Books { get; set; } = []; public IReadOnlyList Chapters { get; set; } = []; public IReadOnlyList Scenes { get; set; } = []; public IReadOnlyList Warnings { get; set; } = []; public IReadOnlyList ChapterOptions { get; set; } = []; } public sealed class ScenarioCompareViewModel { public Project Project { get; set; } = new(); public Scenario Scenario { get; set; } = new(); public IReadOnlyList MovedScenes { get; set; } = []; public IReadOnlyList ScenarioWarnings { get; set; } = []; } public sealed class ArchivedItemsViewModel { public int? ProjectID { get; set; } public string? EntityType { get; set; } public IReadOnlyList ProjectOptions { get; set; } = []; public IReadOnlyList EntityTypeOptions { get; set; } = []; public IReadOnlyList Items { get; set; } = []; } public sealed class Phase1AcceptanceViewModel { public int? ProjectID { get; set; } public string? ProjectName { get; set; } public IReadOnlyList ProjectOptions { get; set; } = []; public IReadOnlyList Items { get; set; } = []; public int PassCount => Items.Count(x => x.Status == "Pass"); public int ReviewCount => Items.Count(x => x.Status == "Review"); public int BlockedCount => Items.Count(x => x.Status == "Blocked"); } public sealed class TimelineViewModel { public Project Project { get; set; } = new(); public ProjectTimelineSettings Settings { get; set; } = new(); public int? SelectedBookID { get; set; } public int? SelectedSceneID { get; set; } public SceneEditViewModel? SelectedScene { get; set; } public TimelineFilterViewModel Filter { get; set; } = new(); public IReadOnlyList Books { get; set; } = []; public IReadOnlyList BookOptions { get; set; } = []; public IReadOnlyList ChapterOptions { get; set; } = []; public IReadOnlyList ChapterFromOptions { get; set; } = []; public IReadOnlyList ChapterToOptions { get; set; } = []; public IReadOnlyList SceneOptions { get; set; } = []; public IReadOnlyList SceneFromOptions { get; set; } = []; public IReadOnlyList SceneToOptions { get; set; } = []; public IReadOnlyList PlotLineOptions { get; set; } = []; public IReadOnlyList PlotThreadOptions { get; set; } = []; public IReadOnlyList AssetOptions { get; set; } = []; public IReadOnlyList QuickAddAssets { get; set; } = []; public IReadOnlyList AssetEventTypeOptions { get; set; } = []; public IReadOnlyList AssetStateOptions { get; set; } = []; public IReadOnlyList AssetDependencyTypeOptions { get; set; } = []; public IReadOnlyList CharacterOptions { get; set; } = []; public IReadOnlyList QuickAddCharacters { get; set; } = []; public IReadOnlyList CharacterRoleOptions { get; set; } = []; public IReadOnlyList PresenceTypeOptions { get; set; } = []; public IReadOnlyList PovCharacterOptions { get; set; } = []; public IReadOnlyList LocationOptions { get; set; } = []; public IReadOnlyList QuickAddLocations { get; set; } = []; public IReadOnlyList LocationRelationshipTypeOptions { get; set; } = []; public IReadOnlyList WarningSeverityOptions { get; set; } = []; public IReadOnlyList WarningTypeOptions { get; set; } = []; public IReadOnlyList RevisionStatusOptions { get; set; } = []; public IReadOnlyList ScenePurposeOptions { get; set; } = []; public IReadOnlyList ViewPresets { get; set; } = []; public IReadOnlyList OrderedScenes { get; set; } = []; public IReadOnlyList MetricGraphs { get; set; } = []; public IReadOnlyList PlotLanes { get; set; } = []; public IReadOnlyList AssetLanes { get; set; } = []; public IReadOnlyList CharacterLanes { get; set; } = []; public IReadOnlyDictionary SceneOverviews { get; set; } = new Dictionary(); public IReadOnlyDictionary> WarningsByScene { get; set; } = new Dictionary>(); public IReadOnlySet MatchingSceneIDs { get; set; } = new HashSet(); public IReadOnlySet FocusSceneIDs { get; set; } = new HashSet(); public IReadOnlyDictionary ScenePovLabels { get; set; } = new Dictionary(); public string? FocusLabel { get; set; } public ContinuityValidationSummary? LastValidationSummary { get; set; } } public sealed class TimelineSettingsViewModel { public int ProjectID { get; set; } public string ProjectName { get; set; } = string.Empty; public bool ShowSceneCards { get; set; } = true; public bool ShowMetricShape { get; set; } = true; public bool ShowPlotLines { get; set; } = true; public bool ShowStoryAssets { get; set; } public bool ShowCharacterAppearances { get; set; } public bool ShowWarnings { get; set; } = true; public List SelectedMetricTypeIDs { get; set; } = []; public IReadOnlyList MetricOptions { get; set; } = []; } public sealed class TimelineMetricSettingOptionViewModel { public int MetricTypeID { get; set; } public string MetricName { get; set; } = string.Empty; public string? Description { get; set; } [Display(Name = "Level")] [Range(-10, 50)] public int SortOrder { get; set; } public bool IsSelected { get; set; } } public sealed class TimelineSceneOverviewViewModel { public IReadOnlyList Characters { get; set; } = []; public IReadOnlyList Assets { get; set; } = []; public IReadOnlyList Locations { get; set; } = []; } public sealed class TimelineSceneCharacterOverviewViewModel { public int CharacterID { get; set; } public string CharacterName { get; set; } = string.Empty; public bool IsPov { get; set; } } public sealed class TimelineSceneAssetOverviewViewModel { public int StoryAssetID { get; set; } public string AssetName { get; set; } = string.Empty; public int Importance { get; set; } public string? EventTypeName { get; set; } } public sealed class ProjectSceneMetricsViewModel { public Project Project { get; set; } = new(); public IReadOnlyList Metrics { get; set; } = []; public SceneMetricTypeEditViewModel NewMetric { get; set; } = new(); public bool HasEnabledMetrics => Metrics.Any(x => x.IsEnabledForProject && x.IsActive); } public sealed class ProjectRestorePointListViewModel { public Project Project { get; init; } = new(); public IReadOnlyList RestorePoints { get; init; } = []; } public sealed class SceneMetricTypeEditViewModel { public int MetricTypeID { get; set; } public int ProjectID { get; set; } [Required, StringLength(100)] [Display(Name = "Metric name")] public string MetricName { get; set; } = string.Empty; public string? Description { get; set; } [Display(Name = "Minimum value")] public int MinValue { get; set; } = 1; [Display(Name = "Maximum value")] public int MaxValue { get; set; } = 10; [Display(Name = "Default value")] public int DefaultValue { get; set; } = 5; [Display(Name = "Sort order")] public int SortOrder { get; set; } = 100; [Display(Name = "Active")] public bool IsActive { get; set; } = true; } public sealed class StoryBibleViewModel { public Project Project { get; set; } = new(); public string? Query { get; set; } public IReadOnlyList Characters { get; set; } = []; public IReadOnlyList PlotThreads { get; set; } = []; public IReadOnlyList Assets { get; set; } = []; public IReadOnlyList Relationships { get; set; } = []; public IReadOnlyList Locations { get; set; } = []; public IReadOnlyList Timeline { get; set; } = []; public IReadOnlyList OpenQuestions { get; set; } = []; public IReadOnlyList RevisionItems { get; set; } = []; public IReadOnlyList SearchResults { get; set; } = []; } public sealed class TimelineFilterViewModel { public int ProjectID { get; set; } public int? BookID { get; set; } public int? SelectedSceneID { get; set; } public int? ChapterFromID { get; set; } public int? ChapterToID { get; set; } public int? SceneFromID { get; set; } public int? SceneToID { get; set; } public int? PlotLineID { get; set; } public int? PlotThreadID { get; set; } public int? StoryAssetID { get; set; } public int? CharacterID { get; set; } public int? LocationID { get; set; } public int? WarningSeverityID { get; set; } public int? WarningTypeID { get; set; } public int? RevisionStatusID { get; set; } public int? ScenePurposeTypeID { get; set; } public int? POVCharacterID { get; set; } public string? FocusType { get; set; } public int? FocusID { get; set; } public bool ShowSceneCards { get; set; } = true; public bool ShowPlotLines { get; set; } = true; public bool ShowAssets { get; set; } = true; public bool ShowCharacters { get; set; } = true; public bool ShowLocations { get; set; } = true; public bool ShowWarnings { get; set; } = true; public bool ShowMetrics { get; set; } = true; public bool ShowWarningsOnly { get; set; } public bool IncludeDismissedWarnings { get; set; } public bool IncludeIntentionalWarnings { get; set; } } public sealed class TimelinePresetSaveViewModel { public int ProjectID { get; set; } public int? BookID { get; set; } public string PresetName { get; set; } = string.Empty; public TimelineFilterViewModel Filter { get; set; } = new(); public string VisibilityJson { get; set; } = "{}"; } public sealed class TimelineSceneDropMoveViewModel { public int ProjectID { get; set; } public int? BookID { get; set; } public int SceneID { get; set; } public int AnchorSceneID { get; set; } public int? TargetChapterID { get; set; } public string Position { get; set; } = "After"; public int? ReturnSelectedSceneID { get; set; } } public sealed class TimelineSceneCharacterDropViewModel { public int SceneID { get; set; } public int? CharacterID { get; set; } public int? RoleInSceneTypeID { get; set; } public int? PresenceTypeID { get; set; } public int? LocationID { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class TimelineAssetEventDropViewModel { public int SceneID { get; set; } public int? StoryAssetID { get; set; } public int AssetEventTypeID { get; set; } public int? FromStateID { get; set; } public int? ToStateID { get; set; } public string EventTitle { get; set; } = string.Empty; public string? EventDescription { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } public int? LocationID { get; set; } public string? CustodyNote { get; set; } } public sealed class TimelineAssetDependencyDropViewModel { public int ProjectID { get; set; } public int SourceAssetID { get; set; } public int TargetAssetID { get; set; } public int AssetDependencyTypeID { get; set; } public int? SourceRequiredStateID { get; set; } public int? TargetBlockedStateID { get; set; } public string? Description { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class TimelineLocationRelationshipDropViewModel { public int ProjectID { get; set; } public int FromLocationID { get; set; } public int ToLocationID { get; set; } public int LocationRelationshipTypeID { get; set; } public bool IsBidirectional { get; set; } public int? Strength { get; set; } public bool CanHearNormalSpeech { get; set; } public bool CanHearLoudNoise { get; set; } public bool CanSeeMovement { get; set; } public bool CanSeeClearly { get; set; } public bool CanTravelDirectly { get; set; } public string? Notes { get; set; } public int? ReturnBookID { get; set; } } public sealed class BookTimelineViewModel { public Book Book { get; set; } = new(); public IReadOnlyList Chapters { get; set; } = []; } public sealed class ChapterTimelineViewModel { public Chapter Chapter { get; set; } = new(); public IReadOnlyList Scenes { get; set; } = []; } public sealed class SceneMetricInputViewModel { public int MetricTypeID { get; set; } public string MetricName { get; set; } = string.Empty; public string? Description { get; set; } public int MinValue { get; set; } public int MaxValue { get; set; } public int Value { get; set; } public string? Notes { get; set; } } public sealed class MetricGraphViewModel { public int MetricTypeID { get; set; } public string MetricName { get; set; } = string.Empty; public int MinValue { get; set; } public int MaxValue { get; set; } public IReadOnlyList Points { get; set; } = []; } public sealed class SceneMetricValueUpdateViewModel { public int SceneID { get; set; } public int MetricTypeID { get; set; } public int MetricID { get; set; } public int Value { get; set; } } public sealed class MetricGraphPointViewModel { public int SceneID { get; set; } public decimal SceneNumber { get; set; } public string SceneTitle { get; set; } = string.Empty; public int Value { get; set; } public int Percent { get; set; } } public sealed class PlotLineEditViewModel : IValidatableObject { public int PlotLineID { get; set; } public int ProjectID { get; set; } [Required, StringLength(200)] [Display(Name = "Plot line name")] public string PlotLineName { get; set; } = string.Empty; [Display(Name = "Type")] public int PlotLineTypeID { get; set; } [Range(1, int.MaxValue, ErrorMessage = "Choose a plot importance.")] [Display(Name = "Importance")] public int PlotImportanceID { get; set; } = 2; public int? BookID { get; set; } public string? Description { get; set; } [Display(Name = "Parent plot line")] public int? ParentPlotLineID { get; set; } [Display(Name = "Emerges from plot line")] public int? EmergesFromPlotLineID { get; set; } [Display(Name = "Sort order")] public int SortOrder { get; set; } [Display(Name = "Colour")] public string Colour { get; set; } = "#2f6f63"; [Display(Name = "Visible on timeline")] public bool IsVisibleOnTimeline { get; set; } = true; public Project? Project { get; set; } public IReadOnlyList PlotImportance { get; set; } = []; public IReadOnlyList BookOptions { get; set; } = []; public IReadOnlyList ParentPlotLineOptions { get; set; } = []; public IEnumerable Validate(ValidationContext validationContext) { if (PlotLineID > 0 && EmergesFromPlotLineID == PlotLineID) { yield return new ValidationResult( "A plot line cannot emerge from itself.", [nameof(EmergesFromPlotLineID)]); } } } public sealed class PlotLineListViewModel { public Project Project { get; set; } = new(); public IReadOnlyList PlotLines { get; set; } = []; } public sealed class PlotThreadEditViewModel { public int PlotThreadID { get; set; } public int ProjectID { get; set; } [Display(Name = "Plot line")] public int PlotLineID { get; set; } [Required, StringLength(200)] [Display(Name = "Thread title")] public string ThreadTitle { get; set; } = string.Empty; [Display(Name = "Type")] public int ThreadTypeID { get; set; } [Display(Name = "Status")] public int ThreadStatusID { get; set; } [Range(1, 10)] public int Importance { get; set; } = 5; public string? Summary { get; set; } [Display(Name = "Introduced scene")] public int? IntroducedSceneID { get; set; } [Display(Name = "Planned resolution scene")] public int? PlannedResolutionSceneID { get; set; } [Display(Name = "Actual resolution scene")] public int? ActualResolutionSceneID { get; set; } public Project? Project { get; set; } public IReadOnlyList PlotLineOptions { get; set; } = []; public IReadOnlyList ThreadTypeOptions { get; set; } = []; public IReadOnlyList ThreadStatusOptions { get; set; } = []; public IReadOnlyList SceneOptions { get; set; } = []; } public sealed class PlotThreadListViewModel { public Project Project { get; set; } = new(); public IReadOnlyList PlotThreads { get; set; } = []; } public sealed class ThreadEventCreateViewModel { public int SceneID { get; set; } public int? PlotLineID { get; set; } public int? PlotThreadID { get; set; } public string? NewThreadTitle { get; set; } public int? NewThreadTypeID { get; set; } public int EventTypeID { get; set; } [Range(1, int.MaxValue, ErrorMessage = "Choose an event type.")] public int PlotEventTypeID { get; set; } = 2; public int? TargetPlotLineID { get; set; } public List SelectedTargetPlotLineIDs { get; set; } = []; public string EventTitle { get; set; } = string.Empty; public string? EventDescription { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class ThreadEventEditViewModel { public int ThreadEventID { get; set; } public int SceneID { get; set; } public int PlotThreadID { get; set; } public int EventTypeID { get; set; } [Range(1, int.MaxValue, ErrorMessage = "Choose an event type.")] public int PlotEventTypeID { get; set; } = 2; public int? TargetPlotLineID { get; set; } public List SelectedTargetPlotLineIDs { get; set; } = []; public string EventTitle { get; set; } = string.Empty; public string? EventDescription { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class PlotLineLaneViewModel { public PlotLineItem PlotLine { get; set; } = new(); public IReadOnlyList SceneSlots { get; set; } = []; } public sealed class PlotLaneSceneViewModel { public Scene Scene { get; set; } = new(); public IReadOnlyList Events { get; set; } = []; } public sealed class StoryAssetListViewModel { public Project Project { get; set; } = new(); public IReadOnlyList Assets { get; set; } = []; public IReadOnlyList AssetDependencyTypeOptions { get; set; } = []; public IReadOnlyList AssetStateOptions { get; set; } = []; } public sealed class StoryAssetEditViewModel { public int StoryAssetID { get; set; } public int ProjectID { get; set; } [Required, StringLength(200)] [Display(Name = "Asset name")] public string AssetName { get; set; } = string.Empty; public List Aliases { get; set; } = []; [Display(Name = "Kind")] public int AssetKindID { get; set; } public string? Description { get; set; } [Range(1, 10)] public int Importance { get; set; } = 5; [Display(Name = "Current state")] public int? CurrentStateID { get; set; } [Display(Name = "Current location")] public int? CurrentLocationID { get; set; } [Display(Name = "Resolved")] public bool IsResolved { get; set; } [Display(Name = "Show in Quick Add Bar")] public bool ShowInQuickAddBar { get; set; } public string? ImagePath { get; set; } public string? ThumbnailPath { get; set; } [Display(Name = "Upload image")] public IFormFile? ImageUpload { get; set; } [Display(Name = "Remove image")] public bool RemoveImage { get; set; } public Project? Project { get; set; } public IReadOnlyList AssetKindOptions { get; set; } = []; public IReadOnlyList AssetStateOptions { get; set; } = []; public IReadOnlyList LocationOptions { get; set; } = []; } public sealed class StoryAssetDetailViewModel { public Project Project { get; set; } = new(); public StoryAsset Asset { get; set; } = new(); public IReadOnlyList ProjectAssets { get; set; } = []; public IReadOnlyList Events { get; set; } = []; public IReadOnlyList Dependencies { get; set; } = []; public IReadOnlyList CustodyEvents { get; set; } = []; public AssetDependencyEditViewModel NewDependency { get; set; } = new(); public IReadOnlyList AssetOptions { get; set; } = []; public IReadOnlyList AssetDependencyTypeOptions { get; set; } = []; public IReadOnlyList AssetStateOptions { get; set; } = []; } public sealed class AssetEventCreateViewModel { public int SceneID { get; set; } public int? StoryAssetID { get; set; } public string? NewAssetName { get; set; } public int? NewAssetKindID { get; set; } public int AssetEventTypeID { get; set; } public int? FromStateID { get; set; } public int? ToStateID { get; set; } public string EventTitle { get; set; } = string.Empty; public string? EventDescription { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class AssetCustodyCreateViewModel { public int SceneID { get; set; } public int StoryAssetID { get; set; } public int AssetCustodyEventTypeID { get; set; } public int CustodyRoleID { get; set; } public List CustodianCharacterIds { get; set; } = []; public string CustodianNames { get; set; } = string.Empty; public string? Description { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class SceneAssetLocationCreateViewModel { public int SceneAssetLocationID { get; set; } public int SceneID { get; set; } public int StoryAssetID { get; set; } public int LocationID { get; set; } public string? Description { get; set; } public bool UpdateCurrentLocation { get; set; } = true; public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class AssetDependencyEditViewModel { public int AssetDependencyID { get; set; } public int ProjectID { get; set; } public int SourceAssetID { get; set; } public int TargetAssetID { get; set; } public int AssetDependencyTypeID { get; set; } public int? SourceRequiredStateID { get; set; } public int? TargetBlockedStateID { get; set; } public string? Description { get; set; } } public sealed class AssetLaneViewModel { public StoryAsset Asset { get; set; } = new(); public IReadOnlyList SceneSlots { get; set; } = []; } public sealed class AssetLaneSceneViewModel { public Scene Scene { get; set; } = new(); public IReadOnlyList Events { get; set; } = []; } public sealed class CharacterListViewModel { public Project Project { get; set; } = new(); public IReadOnlyList Characters { get; set; } = []; } public sealed class CharacterEditViewModel { public const int CustomSexValue = -1; public int CharacterID { get; set; } public int ProjectID { get; set; } [Required, StringLength(200)] [Display(Name = "Display name")] public string CharacterName { get; set; } = string.Empty; public List Aliases { get; set; } = []; [Display(Name = "Sex")] public int? SexValueID { get; set; } public string? Sex { get; set; } [Display(Name = "Add custom sex")] [StringLength(100)] public string? CustomSex { get; set; } [Display(Name = "Birth date")] public DateTime? BirthDate { get; set; } [Display(Name = "Age at series start")] public int? AgeAtSeriesStart { get; set; } public string? Height { get; set; } [Display(Name = "Eye colour")] public string? EyeColour { get; set; } [Display(Name = "Story importance")] public int? CharacterImportance { get; set; } [Display(Name = "Show in Quick Add Bar")] public bool ShowInQuickAddBar { get; set; } [Display(Name = "Default description")] public string? DefaultDescription { get; set; } public string? ImagePath { get; set; } public string? ThumbnailPath { get; set; } public int? AvatarSourceCharacterImageID { get; set; } public string? AvatarImagePath { get; set; } public string? AvatarThumbnailPath { get; set; } [Display(Name = "Upload image")] public IFormFile? ImageUpload { get; set; } [Display(Name = "Remove image")] public bool RemoveImage { get; set; } public IReadOnlyList ImportanceOptions { get; set; } = [ new("Use timeline fallback", string.Empty), new("Major", "10"), new("Secondary / Supporting", "6"), new("Minor", "2") ]; public IReadOnlyList SexOptions { get; set; } = []; public Project? Project { get; set; } } public sealed class CharacterDetailViewModel { public Project Project { get; set; } = new(); public Character Character { get; set; } = new(); public IReadOnlyList Aliases { get; set; } = []; public IReadOnlyList ProjectCharacters { get; set; } = []; public IReadOnlyList Images { get; set; } = []; public string DisplayAge { get; set; } = "Unknown"; public IReadOnlyList Appearances { get; set; } = []; public IReadOnlyList AttributeEvents { get; set; } = []; public IReadOnlyList KnowledgeItems { get; set; } = []; public IReadOnlyList InitialRelationships { get; set; } = []; public IReadOnlyList CurrentRelationships { get; set; } = []; public IReadOnlyList RelationshipEvents { get; set; } = []; public InitialRelationshipEditViewModel NewInitialRelationship { get; set; } = new(); public IReadOnlyList CharacterOptions { get; set; } = []; public IReadOnlyList RelationshipTypeOptions { get; set; } = []; public IReadOnlyList RelationshipStateOptions { get; set; } = []; public IReadOnlyList BookOptions { get; set; } = []; } public sealed class CharacterImageUploadViewModel { public int CharacterID { get; set; } [Display(Name = "Image")] public IFormFile? ImageUpload { get; set; } [StringLength(500)] public string? Caption { get; set; } } public sealed class CharacterAvatarCropViewModel { public int CharacterID { get; set; } public int CharacterImageID { get; set; } public string CharacterName { get; set; } = string.Empty; public string ImagePath { get; set; } = string.Empty; public string? Caption { get; set; } public int CropX { get; set; } public int CropY { get; set; } public int CropSize { get; set; } } public sealed class CurrentRelationshipViewModel { public CharacterRelationship Relationship { get; set; } = new(); public RelationshipEvent? LatestEvent { get; set; } public IReadOnlyList Events { get; set; } = []; public string CurrentStateName => LatestEvent?.RelationshipStateName ?? Relationship.InitialRelationshipStateName ?? Relationship.RelationshipTypeName; public int? CurrentIntensity => LatestEvent?.Intensity ?? Relationship.InitialIntensity; public string? CurrentNotes => LatestEvent?.Description ?? Relationship.Notes; } public sealed class RelationshipMapViewModel { public Project Project { get; set; } = new(); public int? BookID { get; set; } public int? ChapterID { get; set; } public int? SceneID { get; set; } public int? FocusCharacterID { get; set; } public bool ShowFullNetwork { get; set; } public string StoryPointLabel { get; set; } = "Project Start"; public IReadOnlyList BookOptions { get; set; } = []; public IReadOnlyList ChapterOptions { get; set; } = []; public IReadOnlyList SceneOptions { get; set; } = []; public IReadOnlyList CharacterOptions { get; set; } = []; public IReadOnlyList Categories { get; set; } = []; public IReadOnlyList Nodes { get; set; } = []; public IReadOnlyList Links { get; set; } = []; public IReadOnlyList Relationships { get; set; } = []; } public sealed class RelationshipMapCategoryViewModel { public int RelationshipCategoryID { get; set; } public string CategoryName { get; set; } = string.Empty; public string CssClass { get; set; } = "relationship-category-other"; } public sealed class RelationshipMapNodeViewModel { public int CharacterID { get; set; } public string CharacterName { get; set; } = string.Empty; public int? CharacterImportance { get; set; } public string? AvatarImagePath { get; set; } public string? AvatarThumbnailPath { get; set; } public string? ImagePath { get; set; } public string? ThumbnailPath { get; set; } public bool IsFocus { get; set; } } public sealed class RelationshipMapLinkViewModel { public int CharacterRelationshipID { get; set; } public int SourceCharacterID { get; set; } public int TargetCharacterID { get; set; } public string RelationshipLabel { get; set; } = string.Empty; public string CategoryName { get; set; } = "Other"; public string CssClass { get; set; } = "relationship-category-other"; public int? Intensity { get; set; } } public sealed class RelationshipMapRelationshipViewModel { public int CharacterRelationshipID { get; set; } public int CharacterAID { get; set; } public string CharacterAName { get; set; } = string.Empty; public int CharacterBID { get; set; } public string CharacterBName { get; set; } = string.Empty; public string RelationshipTypeName { get; set; } = string.Empty; public string RelationshipCategoryName { get; set; } = "Other"; public string CssClass { get; set; } = "relationship-category-other"; public string InitialStateName { get; set; } = string.Empty; public string CurrentStateName { get; set; } = string.Empty; public int? InitialIntensity { get; set; } public int? CurrentIntensity { get; set; } public bool IsReciprocal { get; set; } public string? Notes { get; set; } public IReadOnlyList History { get; set; } = []; } public sealed class RelationshipMapHistoryItemViewModel { public string Label { get; set; } = string.Empty; public string StateName { get; set; } = string.Empty; public int? Intensity { get; set; } public string? Notes { get; set; } public int? SceneID { get; set; } } public sealed class InitialRelationshipEditViewModel { public int CharacterRelationshipID { get; set; } public int ProjectID { get; set; } public int CharacterID { get; set; } public int? ReturnCharacterID { get; set; } public int RelatedCharacterID { get; set; } public int RelationshipTypeID { get; set; } public int? InitialBookID { get; set; } public int? InitialRelationshipStateID { get; set; } public int? InitialIntensity { get; set; } public bool IsReciprocal { get; set; } public bool ReaderInitiallyKnows { get; set; } public string? Notes { get; set; } } public sealed class SceneCharacterCreateViewModel { public int SceneID { get; set; } public int? CharacterID { get; set; } public string? NewCharacterName { get; set; } public int? RoleInSceneTypeID { get; set; } public int? PresenceTypeID { get; set; } public int? LocationID { get; set; } public int? EntryLocationID { get; set; } public int? ExitLocationID { get; set; } public string? AppearanceNotes { get; set; } public string? OutfitDescription { get; set; } public string? PhysicalCondition { get; set; } public string? EmotionalState { get; set; } public string? KnowledgeNotes { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class SceneCharacterEditViewModel { public int SceneCharacterID { get; set; } public int SceneID { get; set; } public int CharacterID { get; set; } public int? RoleInSceneTypeID { get; set; } public int? PresenceTypeID { get; set; } public int? LocationID { get; set; } public int? EntryLocationID { get; set; } public int? ExitLocationID { get; set; } public string? AppearanceNotes { get; set; } public string? OutfitDescription { get; set; } public string? PhysicalCondition { get; set; } public string? EmotionalState { get; set; } public string? KnowledgeNotes { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class CharacterAttributeEventCreateViewModel { public int CharacterAttributeEventID { get; set; } public int SceneID { get; set; } public int CharacterID { get; set; } public int CharacterAttributeTypeID { get; set; } public string AttributeValue { get; set; } = string.Empty; public string? Description { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class CharacterKnowledgeCreateViewModel { public int CharacterKnowledgeID { get; set; } public int SceneID { get; set; } public int CharacterID { get; set; } public int? StoryAssetID { get; set; } public int? PlotThreadID { get; set; } public int KnowledgeStateID { get; set; } public int? SourceEventID { get; set; } public string? Description { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class RelationshipEventCreateViewModel { public int RelationshipEventID { get; set; } public int SceneID { get; set; } public int? CharacterRelationshipID { get; set; } public int? CharacterAID { get; set; } public int? CharacterBID { get; set; } public int? RelationshipTypeID { get; set; } public int RelationshipStateID { get; set; } public int? Intensity { get; set; } public bool IsKnownToOtherCharacter { get; set; } public string? Description { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class CharacterLaneViewModel { public Character Character { get; set; } = new(); public IReadOnlyList SceneSlots { get; set; } = []; } public sealed class CharacterLaneSceneViewModel { public Scene Scene { get; set; } = new(); public IReadOnlyList Appearances { get; set; } = []; } public sealed class LocationListViewModel { public Project Project { get; set; } = new(); public IReadOnlyList Locations { get; set; } = []; public IReadOnlyList RelationshipTypeOptions { get; set; } = []; } public sealed class LocationEditViewModel { public int LocationID { get; set; } public int ProjectID { get; set; } public int? ParentLocationID { get; set; } [Required, StringLength(200)] [Display(Name = "Location name")] public string LocationName { get; set; } = string.Empty; public List Aliases { get; set; } = []; [Display(Name = "Location type")] public int? LocationTypeID { get; set; } public string? Description { get; set; } [Display(Name = "Show in Quick Add Bar")] public bool ShowInQuickAddBar { get; set; } [Display(Name = "Exclude from Word Companion detection")] public bool ExcludeFromCompanionDetection { get; set; } [Display(Name = "Detection priority")] [Range(0, 1000, ErrorMessage = "Detection priority must be between 0 and 1000.")] public int DetectionPriority { get; set; } = 50; public Project? Project { get; set; } public IReadOnlyList ParentLocationOptions { get; set; } = []; public IReadOnlyList LocationTypeOptions { get; set; } = []; } public sealed class LocationDetailViewModel { public Project Project { get; set; } = new(); public LocationItem Location { get; set; } = new(); public IReadOnlyList ChildLocations { get; set; } = []; public IReadOnlyList Relationships { get; set; } = []; public IReadOnlyList Scenes { get; set; } = []; public IReadOnlyList Characters { get; set; } = []; public IReadOnlyList Assets { get; set; } = []; public LocationRelationshipEditViewModel NewRelationship { get; set; } = new(); public IReadOnlyList LocationOptions { get; set; } = []; public IReadOnlyList RelationshipTypeOptions { get; set; } = []; } public sealed class LocationRelationshipEditViewModel { public int LocationRelationshipID { get; set; } public int ProjectID { get; set; } public int FromLocationID { get; set; } public int ToLocationID { get; set; } public int LocationRelationshipTypeID { get; set; } public bool IsBidirectional { get; set; } public int? Strength { get; set; } public bool CanHearNormalSpeech { get; set; } public bool CanHearLoudNoise { get; set; } public bool CanSeeMovement { get; set; } public bool CanSeeClearly { get; set; } public bool CanTravelDirectly { get; set; } public string? Notes { get; set; } } public sealed class FloorPlanListViewModel { public Project Project { get; set; } = new(); public IReadOnlyList FloorPlans { get; set; } = []; public FloorPlanEditViewModel NewFloorPlan { get; set; } = new(); public IReadOnlyList LocationOptions { get; set; } = []; } public sealed class FloorPlanEditViewModel { public int FloorPlanID { get; set; } public int ProjectID { get; set; } public int? LocationID { get; set; } public string? LocationName { get; set; } public string? LocationPath { get; set; } [Required, StringLength(200)] public string Name { get; set; } = string.Empty; public string? Description { get; set; } } public sealed class FloorPlanEditorViewModel { public Project Project { get; set; } = new(); public FloorPlanEditViewModel FloorPlan { get; set; } = new(); public List Floors { get; set; } = []; public List Blocks { get; set; } = []; public List Transitions { get; set; } = []; public FloorPlanFloorEditViewModel NewFloor { get; set; } = new(); public FloorPlanBlockEditViewModel NewBlock { get; set; } = new(); public FloorPlanTransitionEditViewModel NewTransition { get; set; } = new(); public IReadOnlyList LocationOptions { get; set; } = []; public IReadOnlyList Locations { get; set; } = []; public IReadOnlyList BlockTypeOptions { get; set; } = []; public IReadOnlyList TransitionTypeOptions { get; set; } = []; public IReadOnlyList OccupancyScenes { get; set; } = []; public IReadOnlyList OccupancyTokens { get; set; } = []; public IReadOnlyList OccupancyWarnings { get; set; } = []; } public sealed class FloorPlanOccupancySceneViewModel { public int SceneID { get; set; } public int? InitialFloorPlanFloorID { get; set; } public string Label { get; set; } = string.Empty; } public sealed class FloorPlanOccupancyTokenViewModel { public int SceneID { get; set; } public int FloorPlanFloorID { get; set; } public int LocationID { get; set; } public string LocationName { get; set; } = string.Empty; public string EntityType { get; set; } = string.Empty; public int EntityID { get; set; } public string DisplayName { get; set; } = string.Empty; public string? ImagePath { get; set; } public string? ThumbnailPath { get; set; } } public sealed class FloorPlanOccupancyWarningViewModel { public int SceneID { get; set; } public int? FloorPlanFloorID { get; set; } public string Severity { get; set; } = "warning"; public string Message { get; set; } = string.Empty; } public sealed class FloorPlanFloorEditViewModel { public int FloorPlanFloorID { get; set; } public int FloorPlanID { get; set; } public int? LocationID { get; set; } public string? LocationName { get; set; } public string? LocationPath { get; set; } [Required, StringLength(100)] public string Name { get; set; } = string.Empty; public string? Description { get; set; } public int SortOrder { get; set; } [Range(8, 80)] public int GridWidth { get; set; } = 24; [Range(8, 80)] public int GridHeight { get; set; } = 16; public string? BackgroundImagePath { get; set; } public string? BackgroundImageOriginalFileName { get; set; } [Range(0.05, 1.0)] public decimal BackgroundOpacity { get; set; } = 0.35m; [Range(0.1, 5.0)] public decimal BackgroundScale { get; set; } = 1m; [Range(-10000, 10000)] public decimal BackgroundOffsetX { get; set; } [Range(-10000, 10000)] public decimal BackgroundOffsetY { get; set; } public bool BackgroundLocked { get; set; } = true; } public sealed class FloorPlanFloorBackgroundSettingsViewModel { public int FloorPlanID { get; set; } public int FloorPlanFloorID { get; set; } [Range(0.05, 1.0)] public decimal BackgroundOpacity { get; set; } = 0.35m; [Range(0.1, 5.0)] public decimal BackgroundScale { get; set; } = 1m; [Range(-10000, 10000)] public decimal BackgroundOffsetX { get; set; } [Range(-10000, 10000)] public decimal BackgroundOffsetY { get; set; } public bool BackgroundLocked { get; set; } = true; } public sealed class FloorPlanBlockEditViewModel { public int FloorPlanBlockID { get; set; } public int FloorPlanFloorID { get; set; } public int? LocationID { get; set; } public string AddBlockLocationMode { get; set; } = "Existing"; public string? NewLocationName { get; set; } public string LocationName { get; set; } = string.Empty; public string LocationPath { get; set; } = string.Empty; [Range(0, 79)] public int X { get; set; } [Range(0, 79)] public int Y { get; set; } [Range(1, 80)] public int WidthCells { get; set; } = 2; [Range(1, 80)] public int HeightCells { get; set; } = 1; [Required, StringLength(40)] public string BlockType { get; set; } = FloorPlanBlockTypes.Room; // Future L-shape support should add ShapeType, FootWidthCells, FootHeightCells, and CornerPosition // only when the database, save/load flow, rendering, and collision checks can move together. [StringLength(200)] public string? LabelOverride { get; set; } public string? Notes { get; set; } public string DisplayLabel => string.IsNullOrWhiteSpace(LabelOverride) ? LocationName : LabelOverride; } public sealed class FloorPlanTransitionEditViewModel { public int FloorPlanTransitionID { get; set; } public int FloorPlanFloorID { get; set; } [Required] public int? FromLocationID { get; set; } public string FromLocationName { get; set; } = string.Empty; public string FromLocationPath { get; set; } = string.Empty; [Required] public int? ToLocationID { get; set; } public string ToLocationName { get; set; } = string.Empty; public string ToLocationPath { get; set; } = string.Empty; [Required, StringLength(50)] public string TransitionType { get; set; } = FloorPlanTransitionTypes.Door; public string? Notes { get; set; } public bool IsLocked { get; set; } [StringLength(255)] public string? DisplayLabel { get; set; } [StringLength(20)] public string PositionWithinLocation { get; set; } = FloorPlanTransitionPositions.Centre; public string Label => string.IsNullOrWhiteSpace(DisplayLabel) ? TransitionType : DisplayLabel; } public sealed class AvatarViewModel { public string DisplayName { get; set; } = string.Empty; public string? ImagePath { get; set; } public string? ThumbnailPath { get; set; } public string Size { get; set; } = "md"; public string? CssClass { get; set; } public bool Lazy { get; set; } = true; } public sealed class WarningDashboardViewModel { public Project Project { get; set; } = new(); public int? BookID { get; set; } public int? ChapterID { get; set; } public int? SceneID { get; set; } public int? WarningTypeID { get; set; } public int? WarningSeverityID { get; set; } public string? EntityType { get; set; } public string? Category { get; set; } public bool IncludeDismissed { get; set; } public bool IncludeIntentional { get; set; } public bool ShowAcknowledgedWarnings { get; set; } public IReadOnlyList Warnings { get; set; } = []; public IReadOnlyList ProjectWarnings { get; set; } = []; public IReadOnlyList WarningGroups { get; set; } = []; public IReadOnlyList BookOptions { get; set; } = []; public IReadOnlyList ChapterOptions { get; set; } = []; public IReadOnlyList WarningTypeOptions { get; set; } = []; public IReadOnlyList SeverityOptions { get; set; } = []; public IReadOnlyList EntityTypeOptions { get; set; } = []; public IReadOnlyList CategoryOptions { get; set; } = []; public IReadOnlyDictionary CountsBySeverity { get; set; } = new Dictionary(); public IReadOnlyDictionary CountsByType { get; set; } = new Dictionary(); public ContinuityValidationSummary? LastValidationSummary { get; set; } public bool HasAnyWarnings { get; set; } } public sealed class SceneContinuityEditorViewModel { public SceneEditViewModel Scene { get; set; } = new(); public WarningDashboardViewModel Dashboard { get; set; } = new(); } public sealed class WarningSeverityGroupViewModel { public string Severity { get; set; } = string.Empty; public bool IsExpanded { get; set; } public IReadOnlyList Warnings { get; set; } = []; } public sealed class ProjectWarningViewModel { public string Source { get; set; } = "Persisted"; public int? ContinuityWarningID { get; set; } public string WarningType { get; set; } = string.Empty; public string WarningTypeDisplayName { get; set; } = string.Empty; public string Category { get; set; } = "General"; public string Severity { get; set; } = "Information"; public string Message { get; set; } = string.Empty; public string? Details { get; set; } public int? BookID { get; set; } public string BookDisplayName { get; set; } = string.Empty; public int? ChapterID { get; set; } public string ChapterDisplayName { get; set; } = string.Empty; public int? SceneID { get; set; } public string SceneDisplayName { get; set; } = string.Empty; public string ContextSceneDisplayName { get; set; } = string.Empty; public int? CharacterID { get; set; } public string CharacterName { get; set; } = string.Empty; public int? AssetID { get; set; } public string EntityType { get; set; } = string.Empty; public bool IsDismissed { get; set; } public bool IsIntentional { get; set; } public bool IsAcknowledged { get; set; } public string? AcknowledgementNotes { get; set; } public string WarningKey { get; set; } = string.Empty; public int? KnowledgeId { get; set; } public string KnowledgeTitle { get; set; } = string.Empty; public string KnowledgeDescription { get; set; } = string.Empty; public string PreviousKnowledgeState { get; set; } = string.Empty; public string CurrentKnowledgeState { get; set; } = string.Empty; public int? PreviousBookId { get; set; } public int? PreviousChapterId { get; set; } public int? PreviousSceneId { get; set; } public string PreviousSceneDisplayName { get; set; } = string.Empty; public string PreviousContextSceneDisplayName { get; set; } = string.Empty; public int? CurrentBookId { get; set; } public int? CurrentChapterId { get; set; } public int? CurrentSceneId { get; set; } public string CurrentSceneDisplayName { get; set; } = string.Empty; public string CurrentContextSceneDisplayName { get; set; } = string.Empty; public int? EstablishedSceneId { get; set; } public string EstablishedSceneDisplayName { get; set; } = string.Empty; public string EstablishedContextSceneDisplayName { get; set; } = string.Empty; public int? ReferencedSceneId { get; set; } public string ReferencedSceneDisplayName { get; set; } = string.Empty; public string ReferencedContextSceneDisplayName { get; set; } = string.Empty; public string InvestigationDisplayMode { get; set; } = string.Empty; public int? InvestigationSceneId { get; set; } public bool CanInvestigate { get; set; } public DateTime LastDetectedDate { get; set; } } public sealed class WarningDashboardReturnFilter { public int ProjectID { get; set; } public int? BookID { get; set; } public int? ChapterID { get; set; } public int? SceneID { get; set; } public int? WarningTypeID { get; set; } public int? WarningSeverityID { get; set; } public string? EntityType { get; set; } public string? Category { get; set; } public bool IncludeDismissed { get; set; } public bool IncludeIntentional { get; set; } public bool ShowAcknowledgedWarnings { get; set; } } public sealed class SceneDependencyCreateViewModel { public int SceneDependencyID { get; set; } public int ProjectID { get; set; } public int CurrentSceneID { get; set; } public int RelatedSceneID { get; set; } public string Direction { get; set; } = "CurrentDependsOnRelated"; public int SceneDependencyTypeID { get; set; } public bool IsHardDependency { get; set; } public string? Description { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } } public sealed class SceneMoveRequestViewModel { public int SceneID { get; set; } public int TargetChapterID { get; set; } public string Position { get; set; } = "End"; public bool RenumberScenes { get; set; } public int? ReturnProjectID { get; set; } public int? ReturnBookID { get; set; } public string? ReturnUrl { get; set; } } public sealed class SceneMovePreviewViewModel { public SceneMovePreview Preview { get; set; } = new(); public SceneMoveRequestViewModel Request { get; set; } = new(); public IReadOnlyList CurrentWarnings { get; set; } = []; } public sealed class AnalyticsViewModel { public Project Project { get; set; } = new(); public int? BookID { get; set; } public decimal? StartChapterNumber { get; set; } public decimal? EndChapterNumber { get; set; } public decimal? StartSceneNumber { get; set; } public decimal? EndSceneNumber { get; set; } public IReadOnlyList BookOptions { get; set; } = []; public IReadOnlyList MetricGraphs { get; set; } = []; public IReadOnlyList RevisionSummary { get; set; } = []; public IReadOnlyList RevisionAttentionScenes { get; set; } = []; public IReadOnlyList PurposeSummary { get; set; } = []; public IReadOnlyList SceneGaps { get; set; } = []; public IReadOnlyList ThreadHealth { get; set; } = []; public IReadOnlyList AssetHealth { get; set; } = []; public IReadOnlyList WarningSeverityCounts { get; set; } = []; public IReadOnlyList WarningTypeCounts { get; set; } = []; public IReadOnlyList WarningHotspots { get; set; } = []; public IReadOnlyList PovSummary { get; set; } = []; public IReadOnlyList MultiPovChapters { get; set; } = []; public IReadOnlyList CharacterAppearances { get; set; } = []; public IReadOnlyList BusyScenes { get; set; } = []; public IReadOnlyList LocationUsage { get; set; } = []; public IReadOnlyList PacingReviewNotes { get; set; } = []; } public sealed class AnalyticsMetricGraphViewModel { public string MetricName { get; set; } = string.Empty; public IReadOnlyList Points { get; set; } = []; } public sealed class AnalyticsReviewNote { public string MetricName { get; set; } = string.Empty; public string SceneRange { get; set; } = string.Empty; public string ValuePattern { get; set; } = string.Empty; public string ReviewNote { get; set; } = string.Empty; public int? SceneID { get; set; } }