diff --git a/PlotLine/Services/CoreServices.cs b/PlotLine/Services/CoreServices.cs index fd3db9a..0a4b9b7 100644 --- a/PlotLine/Services/CoreServices.cs +++ b/PlotLine/Services/CoreServices.cs @@ -4052,6 +4052,7 @@ public sealed class ContinuityValidationService( ShowAcknowledgedWarnings = showAcknowledgedWarnings, Warnings = rows, ProjectWarnings = dashboardWarnings, + WarningGroups = BuildWarningSeverityGroups(dashboardWarnings), BookOptions = projectBooks.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString(), x.BookID == bookId)).ToList(), ChapterOptions = projectChapters.Select(x => new SelectListItem($"Ch {x.ChapterNumber:g}: {x.ChapterTitle}", x.ChapterID.ToString(), x.ChapterID == chapterId)).ToList(), WarningTypeOptions = lookupData.WarningTypes.Select(x => new SelectListItem(x.TypeName, x.WarningTypeID.ToString(), x.WarningTypeID == warningTypeId)).ToList(), @@ -4297,6 +4298,33 @@ public sealed class ContinuityValidationService( warning.InvestigationSceneId = warning.CurrentSceneId ?? warning.ReferencedSceneId ?? warning.SceneID; } + private static IReadOnlyList BuildWarningSeverityGroups(IReadOnlyList warnings) + { + var groups = warnings + .GroupBy(x => WarningSeverityGroupLabel(x.Severity)) + .ToDictionary(x => x.Key, x => x.ToList(), StringComparer.OrdinalIgnoreCase); + + return new[] { "Critical", "Warning", "Information" } + .Where(groups.ContainsKey) + .Select(severity => new WarningSeverityGroupViewModel + { + Severity = severity, + IsExpanded = severity is "Critical" or "Warning", + Warnings = groups[severity] + }) + .ToList(); + } + + private static string WarningSeverityGroupLabel(string severity) + { + return severity switch + { + "Critical" or "Error" => "Critical", + "Warning" => "Warning", + _ => "Information" + }; + } + private static string WarningCategoryForPersisted(ContinuityWarning warning) { return warning.EntityType switch diff --git a/PlotLine/ViewModels/CoreViewModels.cs b/PlotLine/ViewModels/CoreViewModels.cs index d44c10e..ec24dd0 100644 --- a/PlotLine/ViewModels/CoreViewModels.cs +++ b/PlotLine/ViewModels/CoreViewModels.cs @@ -1671,6 +1671,7 @@ public sealed class WarningDashboardViewModel 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; } = []; @@ -1683,6 +1684,13 @@ public sealed class WarningDashboardViewModel public bool HasAnyWarnings { get; set; } } +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"; diff --git a/PlotLine/Views/Warnings/Index.cshtml b/PlotLine/Views/Warnings/Index.cshtml index ebdbe80..a1f890d 100644 --- a/PlotLine/Views/Warnings/Index.cshtml +++ b/PlotLine/Views/Warnings/Index.cshtml @@ -7,7 +7,7 @@ @functions { private static string SeverityBadgeClass(string severity) => severity switch { - "Critical" => "bg-danger", + "Critical" or "Error" => "bg-danger", "Warning" => "bg-warning text-dark", _ => "bg-info text-dark" }; @@ -143,194 +143,189 @@ } else { -
- - - - - - - - - - - - - - - - @foreach (var warning in Model.ProjectWarnings) - { - - - - - - - - - - - - } - -
SeverityCategoryWarning TypeMessageBookChapterSceneStatusActions
@warning.Severity@warning.Category@warning.WarningTypeDisplayName -
@warning.Message
- @if (!string.IsNullOrWhiteSpace(warning.Details)) - { - @warning.Details - } - @if (!string.IsNullOrWhiteSpace(warning.KnowledgeTitle)) - { - Knowledge: @warning.KnowledgeTitle - } - @if (!string.IsNullOrWhiteSpace(warning.PreviousKnowledgeState) && !string.IsNullOrWhiteSpace(warning.CurrentKnowledgeState)) - { - State: @warning.PreviousKnowledgeState → @warning.CurrentKnowledgeState - } - @if (warning.PreviousSceneId.HasValue) - { - - Previous: - @warning.PreviousContextSceneDisplayName - - } - @if (warning.CurrentSceneId.HasValue) - { - - Current: - @warning.CurrentContextSceneDisplayName - - } - @if (warning.EstablishedSceneId.HasValue) - { - - Established: - @(string.IsNullOrWhiteSpace(warning.EstablishedContextSceneDisplayName) ? warning.EstablishedSceneDisplayName : warning.EstablishedContextSceneDisplayName) - - } - @if (warning.ReferencedSceneId.HasValue) - { - - Earlier reference: - @(string.IsNullOrWhiteSpace(warning.ReferencedContextSceneDisplayName) ? warning.ReferencedSceneDisplayName : warning.ReferencedContextSceneDisplayName) - - } - @if (warning.IsAcknowledged && !string.IsNullOrWhiteSpace(warning.AcknowledgementNotes)) - { - Note: @warning.AcknowledgementNotes - } -
@(string.IsNullOrWhiteSpace(warning.BookDisplayName) ? "Project" : warning.BookDisplayName)@(string.IsNullOrWhiteSpace(warning.ChapterDisplayName) ? "-" : warning.ChapterDisplayName) - @if (warning.SceneID.HasValue) - { - @warning.ContextSceneDisplayName - } - else - { - Project-wide - } - - @if (warning.IsDismissed) - { - Dismissed - } - else if (warning.IsIntentional || warning.IsAcknowledged) - { - Acknowledged - } - else - { - Active - } - - @if (warning.CanInvestigate) - { - Investigate - } - @if (warning.Source == "Persisted" && warning.ContinuityWarningID.HasValue) - { - @if (warning.IsDismissed || warning.IsIntentional) - { -
- - - - - -
- } - else - { -
- - - - - -
-
- - - - - -
- } - } - else - { - @if (warning.IsAcknowledged) - { -
- - - - - - - - - - - - - - - - -
- } - else - { -
- - - - - - - - - - - - - - - - - -
- } - } -
+
+ @foreach (var group in Model.WarningGroups) + { + var groupId = $"warning-group-{group.Severity.ToLowerInvariant().Replace(" ", "-")}"; +
+

+ +

+
+
+
+ + + + + + + + + + + + + + @foreach (var warning in group.Warnings) + { + + + + + + + + + + } + +
SeverityCategoryWarning TypeMessageSceneStatusActions
@warning.Severity@warning.Category@warning.WarningTypeDisplayName +
@warning.Message
+ @if (!string.IsNullOrWhiteSpace(warning.Details)) + { + @warning.Details + } + @if (!string.IsNullOrWhiteSpace(warning.PreviousKnowledgeState) && !string.IsNullOrWhiteSpace(warning.CurrentKnowledgeState)) + { + @warning.PreviousKnowledgeState → @warning.CurrentKnowledgeState + } + @if (warning.IsAcknowledged && !string.IsNullOrWhiteSpace(warning.AcknowledgementNotes)) + { + Note: @warning.AcknowledgementNotes + } +
+ @if (warning.PreviousSceneId.HasValue || warning.CurrentSceneId.HasValue) + { + @if (warning.PreviousSceneId.HasValue) + { + Previous + @warning.PreviousContextSceneDisplayName + } + @if (warning.CurrentSceneId.HasValue) + { + Current + @warning.CurrentContextSceneDisplayName + } + } + else if (warning.SceneID.HasValue) + { + @warning.ContextSceneDisplayName + } + else + { + Project-wide + } + + @if (warning.IsDismissed) + { + Dismissed + } + else if (warning.IsIntentional || warning.IsAcknowledged) + { + Acknowledged + } + else + { + Active + } + +
+ @if (warning.CanInvestigate) + { + Investigate + } + @if (warning.Source == "Persisted" && warning.ContinuityWarningID.HasValue) + { + @if (warning.IsDismissed || warning.IsIntentional) + { +
+ + + + + +
+ } + else + { +
+ + + + + +
+
+ + + + + +
+ } + } + else if (warning.IsAcknowledged) + { + Acknowledged +
+ + + + + + + + + + + + + + + + +
+ } + else + { +
+ + + + + + + + + + + + + + + + + + +
+ } +
+
+
+
+
+
+ }
}