diff --git a/PlotLine/Models/CoreModels.cs b/PlotLine/Models/CoreModels.cs index 4549edd..1c62908 100644 --- a/PlotLine/Models/CoreModels.cs +++ b/PlotLine/Models/CoreModels.cs @@ -1485,6 +1485,8 @@ public sealed class WriterDashboardScene public decimal SceneNumber { get; set; } public string SceneTitle { get; set; } = string.Empty; public string? Summary { get; set; } + public int RevisionStatusID { get; set; } + public string RevisionStatusName { get; set; } = string.Empty; public string DraftStatus { get; set; } = "Planned"; public int? Priority { get; set; } public bool IsBlocked { get; set; } @@ -1501,6 +1503,8 @@ public sealed class ChapterWorkflowScene public int SceneID { get; set; } public decimal SceneNumber { get; set; } public string SceneTitle { get; set; } = string.Empty; + public int RevisionStatusID { get; set; } + public string RevisionStatusName { get; set; } = string.Empty; public string DraftStatus { get; set; } = "Planned"; public int? Priority { get; set; } public bool ReadyForDraft { get; set; } diff --git a/PlotLine/Services/CoreServices.cs b/PlotLine/Services/CoreServices.cs index 7ae181b..0e6a7dc 100644 --- a/PlotLine/Services/CoreServices.cs +++ b/PlotLine/Services/CoreServices.cs @@ -2896,6 +2896,7 @@ public sealed class WriterWorkspaceService( { ProjectID = projectId, ProjectOptions = projectRows.Select(x => new SelectListItem(x.ProjectName, x.ProjectID.ToString(), x.ProjectID == projectId)).ToList(), + WriteNext = await GetDashboardScenesAsync(projectIds, "WriteNext"), ContinueWriting = await GetDashboardScenesAsync(projectIds, "ContinueWriting"), NeedsAttention = await GetDashboardScenesAsync(projectIds, "NeedsAttention"), RevisionQueue = await GetDashboardScenesAsync(projectIds, "RevisionQueue"), diff --git a/PlotLine/Sql/052_RevisionStatusWriterWorkflow.sql b/PlotLine/Sql/052_RevisionStatusWriterWorkflow.sql new file mode 100644 index 0000000..7aa3b21 --- /dev/null +++ b/PlotLine/Sql/052_RevisionStatusWriterWorkflow.sql @@ -0,0 +1,144 @@ +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO + +DELETE rs +FROM dbo.RevisionStatuses rs +WHERE rs.StatusName IN (N'Ready To Draft', N'Drafting', N'Revising', N'Polishing', N'Complete', N'On Hold') + AND NOT EXISTS (SELECT 1 FROM dbo.Scenes s WHERE s.RevisionStatusID = rs.RevisionStatusID) + AND NOT EXISTS (SELECT 1 FROM dbo.Chapters c WHERE c.RevisionStatusID = rs.RevisionStatusID); +GO + +CREATE OR ALTER PROCEDURE dbo.ChapterWorkflow_Summary + @ChapterID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT s.SceneID, s.SceneNumber, s.SceneTitle, + s.RevisionStatusID, + rs.StatusName AS RevisionStatusName, + COALESCE(sw.DraftStatus, rs.StatusName, N'Planned') AS DraftStatus, + sw.Priority, + COALESCE(sw.ReadyForDraft, 0) AS ReadyForDraft, + COALESCE(sw.ReadyForRevision, 0) AS ReadyForRevision, + COALESCE(sw.ReadyForPolish, 0) AS ReadyForPolish, + COALESCE(sw.IsBlocked, 0) AS IsBlocked, + sw.BlockedReason, + ISNULL(warnings.WarningCount, 0) AS WarningCount, + ISNULL(checklist.RemainingChecklistCount, 0) AS RemainingChecklistCount, + ISNULL(notes.UnresolvedNoteCount, 0) AS UnresolvedNoteCount + FROM dbo.Scenes s + INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID + LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID + OUTER APPLY + ( + SELECT COUNT(*) AS WarningCount + FROM dbo.ContinuityWarnings cw + WHERE cw.SceneID = s.SceneID AND cw.IsDismissed = 0 + ) warnings + OUTER APPLY + ( + SELECT COUNT(*) AS RemainingChecklistCount + FROM dbo.SceneChecklistItems sci + WHERE sci.SceneID = s.SceneID AND sci.IsCompleted = 0 + ) checklist + OUTER APPLY + ( + SELECT COUNT(*) AS UnresolvedNoteCount + FROM dbo.SceneNotes sn + WHERE sn.SceneID = s.SceneID AND sn.IsResolved = 0 + ) notes + WHERE s.ChapterID = @ChapterID AND s.IsArchived = 0 + ORDER BY s.SortOrder, s.SceneID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.WriterDashboard_Scenes + @ProjectID int = NULL, + @Queue nvarchar(50) +AS +BEGIN + SET NOCOUNT ON; + + WITH SceneBase AS + ( + SELECT p.ProjectID, p.ProjectName, b.BookID, b.BookTitle, c.ChapterID, c.ChapterNumber, c.ChapterTitle, + s.SceneID, s.SceneNumber, s.SceneTitle, s.Summary, + s.RevisionStatusID, + rs.StatusName AS RevisionStatusName, + COALESCE(sw.DraftStatus, rs.StatusName, N'Planned') AS DraftStatus, + sw.Priority, + COALESCE(sw.IsBlocked, 0) AS IsBlocked, + sw.BlockedReason, + COALESCE(sw.ReadyForDraft, 0) AS ReadyForDraft, + COALESCE(sw.ReadyForRevision, 0) AS ReadyForRevision, + COALESCE(sw.ReadyForPolish, 0) AS ReadyForPolish, + b.SortOrder AS BookSortOrder, + c.SortOrder AS ChapterSortOrder, + s.SortOrder AS SceneSortOrder + FROM dbo.Projects p + INNER JOIN dbo.Books b ON b.ProjectID = p.ProjectID AND b.IsArchived = 0 + INNER JOIN dbo.Chapters c ON c.BookID = b.BookID AND c.IsArchived = 0 + INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0 + INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID + LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID + WHERE p.IsArchived = 0 AND (@ProjectID IS NULL OR p.ProjectID = @ProjectID) + ) + SELECT TOP (40) sb.ProjectID, sb.ProjectName, sb.BookID, sb.BookTitle, sb.ChapterID, sb.ChapterNumber, sb.ChapterTitle, + sb.SceneID, sb.SceneNumber, sb.SceneTitle, sb.Summary, sb.RevisionStatusID, sb.RevisionStatusName, + sb.DraftStatus, sb.Priority, sb.IsBlocked, sb.BlockedReason, + ISNULL(warnings.WarningCount, 0) AS WarningCount, + ISNULL(checklist.RemainingChecklistCount, 0) AS RemainingChecklistCount, + ISNULL(notes.UnresolvedNoteCount, 0) AS UnresolvedNoteCount, + threads.ImportantThreads, + pinned.PinnedNotes + FROM SceneBase sb + OUTER APPLY + ( + SELECT COUNT(*) AS WarningCount + FROM dbo.ContinuityWarnings cw + WHERE cw.SceneID = sb.SceneID AND cw.IsDismissed = 0 + ) warnings + OUTER APPLY + ( + SELECT COUNT(*) AS RemainingChecklistCount + FROM dbo.SceneChecklistItems sci + WHERE sci.SceneID = sb.SceneID AND sci.IsCompleted = 0 + ) checklist + OUTER APPLY + ( + SELECT COUNT(*) AS UnresolvedNoteCount + FROM dbo.SceneNotes sn + INNER JOIN dbo.SceneNoteTypes snt ON snt.SceneNoteTypeID = sn.SceneNoteTypeID + WHERE sn.SceneID = sb.SceneID AND sn.IsResolved = 0 + AND (@Queue <> N'NeedsAttention' OR snt.TypeName IN (N'TODO', N'Continuity Note')) + ) notes + OUTER APPLY + ( + SELECT STRING_AGG(CONVERT(nvarchar(max), pt.ThreadTitle), N', ') AS ImportantThreads + FROM + ( + SELECT DISTINCT TOP (5) pt.ThreadTitle, pt.Importance + FROM dbo.ThreadEvents te + INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID + WHERE te.SceneID = sb.SceneID + ORDER BY pt.Importance DESC, pt.ThreadTitle + ) pt + ) threads + OUTER APPLY + ( + SELECT STRING_AGG(CONVERT(nvarchar(max), COALESCE(sn.NoteTitle, sn.NoteText)), N' | ') AS PinnedNotes + FROM dbo.SceneNotes sn + WHERE sn.SceneID = sb.SceneID AND sn.IsPinned = 1 AND sn.IsResolved = 0 + ) pinned + WHERE + (@Queue = N'WriteNext' AND sb.RevisionStatusName = N'Outlined') + OR (@Queue = N'ContinueWriting' AND sb.RevisionStatusName = N'Drafted') + OR (@Queue = N'NeedsAttention' AND (sb.IsBlocked = 1 OR ISNULL(warnings.WarningCount, 0) > 0 OR ISNULL(notes.UnresolvedNoteCount, 0) > 0)) + OR (@Queue = N'RevisionQueue' AND sb.RevisionStatusName = N'Needs Work') + OR (@Queue = N'PolishingQueue' AND sb.RevisionStatusName = N'Revised') + ORDER BY ISNULL(sb.Priority, 0) DESC, sb.BookSortOrder, sb.ChapterSortOrder, sb.SceneSortOrder, sb.SceneID; +END; +GO diff --git a/PlotLine/ViewModels/CoreViewModels.cs b/PlotLine/ViewModels/CoreViewModels.cs index e98d18b..f5ecf61 100644 --- a/PlotLine/ViewModels/CoreViewModels.cs +++ b/PlotLine/ViewModels/CoreViewModels.cs @@ -312,6 +312,7 @@ public sealed class WriterDashboardViewModel { public int? ProjectID { get; set; } public IReadOnlyList ProjectOptions { get; set; } = []; + public IReadOnlyList WriteNext { get; set; } = []; public IReadOnlyList ContinueWriting { get; set; } = []; public IReadOnlyList NeedsAttention { get; set; } = []; public IReadOnlyList RevisionQueue { get; set; } = []; diff --git a/PlotLine/Views/Scenes/_SceneInspector.cshtml b/PlotLine/Views/Scenes/_SceneInspector.cshtml index 1549aca..383e483 100644 --- a/PlotLine/Views/Scenes/_SceneInspector.cshtml +++ b/PlotLine/Views/Scenes/_SceneInspector.cshtml @@ -972,13 +972,16 @@
-
- - -
-
+ + + + +
- +
+ + +
@@ -997,9 +1000,6 @@
- - -
@@ -1011,6 +1011,26 @@
+
diff --git a/PlotLine/Views/Writer/Chapter.cshtml b/PlotLine/Views/Writer/Chapter.cshtml index 23faf5b..38f6639 100644 --- a/PlotLine/Views/Writer/Chapter.cshtml +++ b/PlotLine/Views/Writer/Chapter.cshtml @@ -2,9 +2,10 @@ @{ ViewData["Title"] = $"Chapter Workflow - {Model.Chapter.ChapterTitle}"; var blockedCount = Model.Scenes.Count(x => x.IsBlocked); - var readyDraftCount = Model.Scenes.Count(x => x.ReadyForDraft); - var readyRevisionCount = Model.Scenes.Count(x => x.ReadyForRevision); - var readyPolishCount = Model.Scenes.Count(x => x.ReadyForPolish); + var writeNextCount = Model.Scenes.Count(x => x.RevisionStatusName == "Outlined"); + var continueWritingCount = Model.Scenes.Count(x => x.RevisionStatusName == "Drafted"); + var revisionCount = Model.Scenes.Count(x => x.RevisionStatusName == "Needs Work"); + var polishCount = Model.Scenes.Count(x => x.RevisionStatusName == "Revised"); }
+ +
+
+
+

In progress

Continue Writing

@Model.ContinueWriting.Count scenes diff --git a/PlotLine/Views/Writer/_WriterSceneQueue.cshtml b/PlotLine/Views/Writer/_WriterSceneQueue.cshtml index b91d971..103e8e8 100644 --- a/PlotLine/Views/Writer/_WriterSceneQueue.cshtml +++ b/PlotLine/Views/Writer/_WriterSceneQueue.cshtml @@ -2,7 +2,7 @@ @if (!Model.Any()) { -

Nothing needs your attention here. When scenes are ready for this stage, they will appear in this queue.

+

Scenes appear here based on Revision Status. Use Outlined for Write Next, Drafted for Continue Writing, Needs Work for Revision, and Revised for Polishing.

} else { @@ -15,7 +15,7 @@ else @scene.BookTitle / Chapter @scene.ChapterNumber

Scene @scene.SceneNumber: @scene.SceneTitle

- @scene.DraftStatus + @(string.IsNullOrWhiteSpace(scene.RevisionStatusName) ? scene.DraftStatus : scene.RevisionStatusName)
@if (!string.IsNullOrWhiteSpace(scene.Summary)) { diff --git a/PlotLine/wwwroot/css/site.css b/PlotLine/wwwroot/css/site.css index eb3162d..9efa232 100644 --- a/PlotLine/wwwroot/css/site.css +++ b/PlotLine/wwwroot/css/site.css @@ -3146,6 +3146,17 @@ body.dragging-location [data-drag-type="location"] { font-weight: 650; } +.writer-priority-control { + display: grid; + grid-template-columns: minmax(0, 1fr) max-content; + align-items: center; + gap: 12px; +} + +.writer-priority-control .form-range { + min-width: 0; +} + .writer-note-list, .writer-queue-list { display: grid;