Phase 12F: Continuity Warning Management.
This commit is contained in:
parent
279c8f182c
commit
738b9f496d
@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlotLine.Models;
|
||||
using PlotLine.Services;
|
||||
using PlotLine.ViewModels;
|
||||
|
||||
@ -25,4 +26,58 @@ public sealed class ContinuityExplorerController(IStoryStateService storyState)
|
||||
{
|
||||
return RedirectToAction(nameof(Index), filter);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AcknowledgeWarning(
|
||||
string warningType,
|
||||
int? sceneId,
|
||||
int? characterId,
|
||||
int? assetId,
|
||||
string? notes,
|
||||
[Bind(Prefix = "ReturnFilter")] ContinuityFilter returnFilter)
|
||||
{
|
||||
if (returnFilter.ProjectID == 0 || string.IsNullOrWhiteSpace(warningType))
|
||||
{
|
||||
return BadRequest("Warning identity is required.");
|
||||
}
|
||||
|
||||
await storyState.AcknowledgeWarningAsync(new ContinuityWarningAcknowledgement
|
||||
{
|
||||
ProjectID = returnFilter.ProjectID,
|
||||
WarningType = warningType,
|
||||
SceneID = sceneId,
|
||||
CharacterID = characterId,
|
||||
AssetID = assetId,
|
||||
Notes = notes
|
||||
});
|
||||
|
||||
return RedirectToAction(nameof(Index), returnFilter);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RestoreWarning(
|
||||
string warningType,
|
||||
int? sceneId,
|
||||
int? characterId,
|
||||
int? assetId,
|
||||
[Bind(Prefix = "ReturnFilter")] ContinuityFilter returnFilter)
|
||||
{
|
||||
if (returnFilter.ProjectID == 0 || string.IsNullOrWhiteSpace(warningType))
|
||||
{
|
||||
return BadRequest("Warning identity is required.");
|
||||
}
|
||||
|
||||
await storyState.RestoreWarningAsync(new ContinuityWarningAcknowledgement
|
||||
{
|
||||
ProjectID = returnFilter.ProjectID,
|
||||
WarningType = warningType,
|
||||
SceneID = sceneId,
|
||||
CharacterID = characterId,
|
||||
AssetID = assetId
|
||||
});
|
||||
|
||||
return RedirectToAction(nameof(Index), returnFilter);
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,6 +223,14 @@ public interface IWarningRepository
|
||||
Task FinaliseSceneDependencyWarningsAsync(int projectId, int? bookId);
|
||||
}
|
||||
|
||||
public interface IContinuityWarningAcknowledgementRepository
|
||||
{
|
||||
Task<IReadOnlyList<ContinuityWarningAcknowledgement>> ListByProjectAsync(int projectId);
|
||||
Task<ContinuityWarningAcknowledgement?> GetAsync(int projectId, string warningType, int? sceneId, int? characterId, int? assetId);
|
||||
Task SaveAsync(ContinuityWarningAcknowledgement acknowledgement);
|
||||
Task DeleteAsync(int projectId, string warningType, int? sceneId, int? characterId, int? assetId);
|
||||
}
|
||||
|
||||
public interface ISceneDependencyRepository
|
||||
{
|
||||
Task<SceneDependencyLookupData> GetLookupsAsync();
|
||||
@ -606,6 +614,69 @@ public sealed class WarningRepository(ISqlConnectionFactory connectionFactory) :
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ContinuityWarningAcknowledgementRepository(ISqlConnectionFactory connectionFactory) : IContinuityWarningAcknowledgementRepository
|
||||
{
|
||||
public async Task<IReadOnlyList<ContinuityWarningAcknowledgement>> ListByProjectAsync(int projectId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<ContinuityWarningAcknowledgement>(
|
||||
"dbo.ContinuityWarningAcknowledgement_ListByProject",
|
||||
new { ProjectID = projectId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
public async Task<ContinuityWarningAcknowledgement?> GetAsync(int projectId, string warningType, int? sceneId, int? characterId, int? assetId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
return await connection.QuerySingleOrDefaultAsync<ContinuityWarningAcknowledgement>(
|
||||
"dbo.ContinuityWarningAcknowledgement_Get",
|
||||
new
|
||||
{
|
||||
ProjectID = projectId,
|
||||
WarningType = warningType,
|
||||
SceneID = sceneId,
|
||||
CharacterID = characterId,
|
||||
AssetID = assetId
|
||||
},
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task SaveAsync(ContinuityWarningAcknowledgement acknowledgement)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
await connection.ExecuteAsync(
|
||||
"dbo.ContinuityWarningAcknowledgement_Save",
|
||||
new
|
||||
{
|
||||
acknowledgement.ProjectID,
|
||||
acknowledgement.WarningType,
|
||||
acknowledgement.SceneID,
|
||||
acknowledgement.CharacterID,
|
||||
acknowledgement.AssetID,
|
||||
acknowledgement.AcknowledgedByUserID,
|
||||
acknowledgement.Notes
|
||||
},
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int projectId, string warningType, int? sceneId, int? characterId, int? assetId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
await connection.ExecuteAsync(
|
||||
"dbo.ContinuityWarningAcknowledgement_Delete",
|
||||
new
|
||||
{
|
||||
ProjectID = projectId,
|
||||
WarningType = warningType,
|
||||
SceneID = sceneId,
|
||||
CharacterID = characterId,
|
||||
AssetID = assetId
|
||||
},
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AnalyticsData
|
||||
{
|
||||
public IReadOnlyList<AnalyticsMetricPoint> MetricPoints { get; init; } = [];
|
||||
|
||||
@ -1087,6 +1087,19 @@ public sealed class ContinuityWarning
|
||||
public DateTime LastDetectedDate { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ContinuityWarningAcknowledgement
|
||||
{
|
||||
public int ContinuityWarningAcknowledgementID { get; set; }
|
||||
public int ProjectID { get; set; }
|
||||
public string WarningType { get; set; } = string.Empty;
|
||||
public int? SceneID { get; set; }
|
||||
public int? CharacterID { get; set; }
|
||||
public int? AssetID { get; set; }
|
||||
public DateTime AcknowledgedDate { get; set; }
|
||||
public int? AcknowledgedByUserID { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public sealed class WarningLookupData
|
||||
{
|
||||
public IReadOnlyList<WarningType> WarningTypes { get; init; } = [];
|
||||
|
||||
@ -92,6 +92,7 @@ public class Program
|
||||
builder.Services.AddScoped<ICharacterRepository, CharacterRepository>();
|
||||
builder.Services.AddScoped<ILocationRepository, LocationRepository>();
|
||||
builder.Services.AddScoped<IWarningRepository, WarningRepository>();
|
||||
builder.Services.AddScoped<IContinuityWarningAcknowledgementRepository, ContinuityWarningAcknowledgementRepository>();
|
||||
builder.Services.AddScoped<ISceneDependencyRepository, SceneDependencyRepository>();
|
||||
builder.Services.AddScoped<IAnalyticsRepository, AnalyticsRepository>();
|
||||
builder.Services.AddScoped<IStoryBibleRepository, StoryBibleRepository>();
|
||||
|
||||
@ -119,6 +119,10 @@ public interface IStoryStateService
|
||||
Task<List<AssetJourneyViewModel>> GetAssetJourneyAsync(int projectId, int assetId, ContinuityFilter filter);
|
||||
Task<List<LocationActivityViewModel>> GetLocationActivityAsync(int projectId, int locationId, ContinuityFilter filter);
|
||||
Task<List<ContinuityWarningViewModel>> GetContinuityWarningsAsync(int projectId, ContinuityFilter filter);
|
||||
Task AcknowledgeWarningAsync(ContinuityWarningAcknowledgement acknowledgement);
|
||||
Task RestoreWarningAsync(ContinuityWarningAcknowledgement acknowledgement);
|
||||
Task<bool> IsAcknowledgedAsync(int projectId, string warningType, int? sceneId, int? characterId, int? assetId);
|
||||
Task<List<ContinuityWarningAcknowledgement>> GetAcknowledgementsAsync(int projectId);
|
||||
}
|
||||
|
||||
public interface IPlotService
|
||||
@ -2216,7 +2220,9 @@ public sealed class StoryStateService(
|
||||
ITimelineRepository timelineRepository,
|
||||
IAssetRepository assets,
|
||||
ICharacterRepository characters,
|
||||
ILocationRepository locations) : IStoryStateService
|
||||
ILocationRepository locations,
|
||||
IContinuityWarningAcknowledgementRepository warningAcknowledgements,
|
||||
ICurrentUserService currentUser) : IStoryStateService
|
||||
{
|
||||
public async Task<StoryStateViewModel> GetStoryStateAsync(int sceneId)
|
||||
{
|
||||
@ -2550,7 +2556,11 @@ public sealed class StoryStateService(
|
||||
warnings.AddRange(BuildUnknownAssetLocationWarnings(assetTimeline.Assets, assetTimeline.Events, allAssetLocations, custodyCharacters, enabledAssetIds, context));
|
||||
}
|
||||
|
||||
var acknowledgements = await GetAcknowledgementsAsync(projectId);
|
||||
ApplyAcknowledgements(warnings, acknowledgements);
|
||||
|
||||
return warnings
|
||||
.Where(x => filter.ShowAcknowledgedWarnings || !x.IsAcknowledged)
|
||||
.OrderBy(x => WarningSeveritySort(x.Severity))
|
||||
.ThenBy(x => x.BookDisplayName)
|
||||
.ThenBy(x => x.ChapterDisplayName)
|
||||
@ -2560,6 +2570,35 @@ public sealed class StoryStateService(
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task AcknowledgeWarningAsync(ContinuityWarningAcknowledgement acknowledgement)
|
||||
{
|
||||
acknowledgement.WarningType = acknowledgement.WarningType.Trim();
|
||||
acknowledgement.Notes = string.IsNullOrWhiteSpace(acknowledgement.Notes) ? null : acknowledgement.Notes.Trim();
|
||||
acknowledgement.AcknowledgedByUserID = currentUser.UserId;
|
||||
await warningAcknowledgements.SaveAsync(acknowledgement);
|
||||
}
|
||||
|
||||
public Task RestoreWarningAsync(ContinuityWarningAcknowledgement acknowledgement)
|
||||
{
|
||||
return warningAcknowledgements.DeleteAsync(
|
||||
acknowledgement.ProjectID,
|
||||
acknowledgement.WarningType.Trim(),
|
||||
acknowledgement.SceneID,
|
||||
acknowledgement.CharacterID,
|
||||
acknowledgement.AssetID);
|
||||
}
|
||||
|
||||
public async Task<bool> IsAcknowledgedAsync(int projectId, string warningType, int? sceneId, int? characterId, int? assetId)
|
||||
{
|
||||
var acknowledgement = await warningAcknowledgements.GetAsync(projectId, warningType, sceneId, characterId, assetId);
|
||||
return acknowledgement is not null;
|
||||
}
|
||||
|
||||
public async Task<List<ContinuityWarningAcknowledgement>> GetAcknowledgementsAsync(int projectId)
|
||||
{
|
||||
return (await warningAcknowledgements.ListByProjectAsync(projectId)).ToList();
|
||||
}
|
||||
|
||||
private async Task<RangeContext> GetRangeContextAsync(int projectId, ContinuityFilter filter)
|
||||
{
|
||||
NormaliseFilter(filter);
|
||||
@ -3102,6 +3141,7 @@ public sealed class StoryStateService(
|
||||
var sceneContext = sceneId.HasValue ? context.SceneContexts.GetValueOrDefault(sceneId.Value) : null;
|
||||
return new ContinuityWarningViewModel
|
||||
{
|
||||
WarningKey = BuildWarningKey(warningType, sceneId, characterId, assetId),
|
||||
WarningType = warningType,
|
||||
Severity = severity,
|
||||
Message = message,
|
||||
@ -3114,6 +3154,47 @@ public sealed class StoryStateService(
|
||||
};
|
||||
}
|
||||
|
||||
private static void ApplyAcknowledgements(
|
||||
IReadOnlyList<ContinuityWarningViewModel> warnings,
|
||||
IReadOnlyList<ContinuityWarningAcknowledgement> acknowledgements)
|
||||
{
|
||||
var acknowledgementsByKey = acknowledgements.ToDictionary(
|
||||
x => BuildWarningKey(x.WarningType, x.SceneID, x.CharacterID, x.AssetID),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var warning in warnings)
|
||||
{
|
||||
if (!acknowledgementsByKey.TryGetValue(warning.WarningKey, out var acknowledgement))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
warning.IsAcknowledged = true;
|
||||
warning.AcknowledgementNotes = acknowledgement.Notes;
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildWarningKey(string warningType, int? sceneId, int? characterId, int? assetId)
|
||||
{
|
||||
var parts = new List<string> { warningType.Trim() };
|
||||
if (sceneId.HasValue)
|
||||
{
|
||||
parts.Add($"Scene:{sceneId.Value}");
|
||||
}
|
||||
|
||||
if (characterId.HasValue)
|
||||
{
|
||||
parts.Add($"Character:{characterId.Value}");
|
||||
}
|
||||
|
||||
if (assetId.HasValue)
|
||||
{
|
||||
parts.Add($"Asset:{assetId.Value}");
|
||||
}
|
||||
|
||||
return string.Join("|", parts);
|
||||
}
|
||||
|
||||
private static int WarningSeveritySort(string severity)
|
||||
{
|
||||
return severity switch
|
||||
|
||||
152
PlotLine/Sql/065_Phase12F_ContinuityWarningAcknowledgements.sql
Normal file
152
PlotLine/Sql/065_Phase12F_ContinuityWarningAcknowledgements.sql
Normal file
@ -0,0 +1,152 @@
|
||||
SET ANSI_NULLS ON;
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
GO
|
||||
|
||||
IF OBJECT_ID(N'dbo.ContinuityWarningAcknowledgements', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.ContinuityWarningAcknowledgements
|
||||
(
|
||||
ContinuityWarningAcknowledgementID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ContinuityWarningAcknowledgements PRIMARY KEY,
|
||||
ProjectID int NOT NULL,
|
||||
WarningType nvarchar(100) NOT NULL,
|
||||
SceneID int NULL,
|
||||
CharacterID int NULL,
|
||||
AssetID int NULL,
|
||||
AcknowledgedDate datetime2 NOT NULL CONSTRAINT DF_ContinuityWarningAcknowledgements_AcknowledgedDate DEFAULT SYSUTCDATETIME(),
|
||||
AcknowledgedByUserID int NULL,
|
||||
Notes nvarchar(500) NULL,
|
||||
SceneIDKey AS ISNULL(SceneID, 0) PERSISTED,
|
||||
CharacterIDKey AS ISNULL(CharacterID, 0) PERSISTED,
|
||||
AssetIDKey AS ISNULL(AssetID, 0) PERSISTED,
|
||||
CONSTRAINT FK_ContinuityWarningAcknowledgements_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID),
|
||||
CONSTRAINT FK_ContinuityWarningAcknowledgements_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID),
|
||||
CONSTRAINT FK_ContinuityWarningAcknowledgements_Characters FOREIGN KEY (CharacterID) REFERENCES dbo.Characters(CharacterID),
|
||||
CONSTRAINT FK_ContinuityWarningAcknowledgements_Assets FOREIGN KEY (AssetID) REFERENCES dbo.StoryAssets(StoryAssetID),
|
||||
CONSTRAINT FK_ContinuityWarningAcknowledgements_AppUser FOREIGN KEY (AcknowledgedByUserID) REFERENCES dbo.AppUser(UserID)
|
||||
);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'UX_ContinuityWarningAcknowledgements_Identity' AND object_id = OBJECT_ID(N'dbo.ContinuityWarningAcknowledgements'))
|
||||
BEGIN
|
||||
CREATE UNIQUE INDEX UX_ContinuityWarningAcknowledgements_Identity
|
||||
ON dbo.ContinuityWarningAcknowledgements(ProjectID, WarningType, SceneIDKey, CharacterIDKey, AssetIDKey);
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ContinuityWarningAcknowledgement_ListByProject
|
||||
@ProjectID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT ContinuityWarningAcknowledgementID,
|
||||
ProjectID,
|
||||
WarningType,
|
||||
SceneID,
|
||||
CharacterID,
|
||||
AssetID,
|
||||
AcknowledgedDate,
|
||||
AcknowledgedByUserID,
|
||||
Notes
|
||||
FROM dbo.ContinuityWarningAcknowledgements
|
||||
WHERE ProjectID = @ProjectID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ContinuityWarningAcknowledgement_Get
|
||||
@ProjectID int,
|
||||
@WarningType nvarchar(100),
|
||||
@SceneID int = NULL,
|
||||
@CharacterID int = NULL,
|
||||
@AssetID int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT ContinuityWarningAcknowledgementID,
|
||||
ProjectID,
|
||||
WarningType,
|
||||
SceneID,
|
||||
CharacterID,
|
||||
AssetID,
|
||||
AcknowledgedDate,
|
||||
AcknowledgedByUserID,
|
||||
Notes
|
||||
FROM dbo.ContinuityWarningAcknowledgements
|
||||
WHERE ProjectID = @ProjectID
|
||||
AND WarningType = @WarningType
|
||||
AND ISNULL(SceneID, 0) = ISNULL(@SceneID, 0)
|
||||
AND ISNULL(CharacterID, 0) = ISNULL(@CharacterID, 0)
|
||||
AND ISNULL(AssetID, 0) = ISNULL(@AssetID, 0);
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ContinuityWarningAcknowledgement_Save
|
||||
@ProjectID int,
|
||||
@WarningType nvarchar(100),
|
||||
@SceneID int = NULL,
|
||||
@CharacterID int = NULL,
|
||||
@AssetID int = NULL,
|
||||
@AcknowledgedByUserID int = NULL,
|
||||
@Notes nvarchar(500) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
DECLARE @TrimmedWarningType nvarchar(100) = NULLIF(LTRIM(RTRIM(@WarningType)), N'');
|
||||
DECLARE @TrimmedNotes nvarchar(500) = NULLIF(LTRIM(RTRIM(@Notes)), N'');
|
||||
|
||||
IF @ProjectID <= 0 OR @TrimmedWarningType IS NULL
|
||||
RETURN;
|
||||
|
||||
IF EXISTS
|
||||
(
|
||||
SELECT 1
|
||||
FROM dbo.ContinuityWarningAcknowledgements
|
||||
WHERE ProjectID = @ProjectID
|
||||
AND WarningType = @TrimmedWarningType
|
||||
AND ISNULL(SceneID, 0) = ISNULL(@SceneID, 0)
|
||||
AND ISNULL(CharacterID, 0) = ISNULL(@CharacterID, 0)
|
||||
AND ISNULL(AssetID, 0) = ISNULL(@AssetID, 0)
|
||||
)
|
||||
BEGIN
|
||||
UPDATE dbo.ContinuityWarningAcknowledgements
|
||||
SET AcknowledgedDate = SYSUTCDATETIME(),
|
||||
AcknowledgedByUserID = @AcknowledgedByUserID,
|
||||
Notes = @TrimmedNotes
|
||||
WHERE ProjectID = @ProjectID
|
||||
AND WarningType = @TrimmedWarningType
|
||||
AND ISNULL(SceneID, 0) = ISNULL(@SceneID, 0)
|
||||
AND ISNULL(CharacterID, 0) = ISNULL(@CharacterID, 0)
|
||||
AND ISNULL(AssetID, 0) = ISNULL(@AssetID, 0);
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
INSERT dbo.ContinuityWarningAcknowledgements
|
||||
(ProjectID, WarningType, SceneID, CharacterID, AssetID, AcknowledgedByUserID, Notes)
|
||||
VALUES
|
||||
(@ProjectID, @TrimmedWarningType, @SceneID, @CharacterID, @AssetID, @AcknowledgedByUserID, @TrimmedNotes);
|
||||
END;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ContinuityWarningAcknowledgement_Delete
|
||||
@ProjectID int,
|
||||
@WarningType nvarchar(100),
|
||||
@SceneID int = NULL,
|
||||
@CharacterID int = NULL,
|
||||
@AssetID int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
DELETE FROM dbo.ContinuityWarningAcknowledgements
|
||||
WHERE ProjectID = @ProjectID
|
||||
AND WarningType = @WarningType
|
||||
AND ISNULL(SceneID, 0) = ISNULL(@SceneID, 0)
|
||||
AND ISNULL(CharacterID, 0) = ISNULL(@CharacterID, 0)
|
||||
AND ISNULL(AssetID, 0) = ISNULL(@AssetID, 0);
|
||||
END;
|
||||
GO
|
||||
@ -289,6 +289,7 @@ public sealed class ContinuityFilter
|
||||
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);
|
||||
@ -316,6 +317,7 @@ public sealed class ContinuityExplorerViewModel
|
||||
|
||||
public sealed class ContinuityWarningViewModel
|
||||
{
|
||||
public string WarningKey { get; set; } = string.Empty;
|
||||
public string WarningType { get; set; } = string.Empty;
|
||||
public string Severity { get; set; } = "Information";
|
||||
public string Message { get; set; } = string.Empty;
|
||||
@ -325,6 +327,8 @@ public sealed class ContinuityWarningViewModel
|
||||
public string SceneDisplayName { 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 sealed class CharacterJourneyViewModel
|
||||
|
||||
@ -161,6 +161,10 @@
|
||||
<div class="col-lg-8">
|
||||
<label class="form-label" for="SelectedSceneID">Story State scene</label>
|
||||
<select class="form-select form-select-sm" id="SelectedSceneID" name="SelectedSceneID" asp-items="Model.SceneOptions"></select>
|
||||
<label class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="ShowAcknowledgedWarnings" value="true" checked="@Model.Filter.ShowAcknowledgedWarnings" />
|
||||
<span class="form-check-label">Show acknowledged warnings</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -377,9 +381,11 @@ else if (Model.Filter.DisplayMode == "ContinuityWarnings")
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Severity</th>
|
||||
<th>Status</th>
|
||||
<th>Warning Type</th>
|
||||
<th>Message</th>
|
||||
<th>Scene</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -387,8 +393,24 @@ else if (Model.Filter.DisplayMode == "ContinuityWarnings")
|
||||
{
|
||||
<tr>
|
||||
<td><span class="badge @WarningBadgeClass(warning.Severity)">@warning.Severity</span></td>
|
||||
<td>
|
||||
@if (warning.IsAcknowledged)
|
||||
{
|
||||
<span class="badge bg-secondary">Acknowledged</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-warning text-dark">Active</span>
|
||||
}
|
||||
</td>
|
||||
<td>@warning.WarningType</td>
|
||||
<td>@warning.Message</td>
|
||||
<td>
|
||||
<div>@warning.Message</div>
|
||||
@if (warning.IsAcknowledged && !string.IsNullOrWhiteSpace(warning.AcknowledgementNotes))
|
||||
{
|
||||
<small class="text-muted">Note: @warning.AcknowledgementNotes</small>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (warning.SceneID.HasValue)
|
||||
{
|
||||
@ -399,6 +421,79 @@ else if (Model.Filter.DisplayMode == "ContinuityWarnings")
|
||||
<span class="muted">Project-wide</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (warning.IsAcknowledged)
|
||||
{
|
||||
<form asp-action="RestoreWarning" method="post" class="d-inline">
|
||||
<input type="hidden" name="WarningType" value="@warning.WarningType" />
|
||||
<input type="hidden" name="SceneID" value="@warning.SceneID" />
|
||||
<input type="hidden" name="CharacterID" value="@warning.CharacterID" />
|
||||
<input type="hidden" name="AssetID" value="@warning.AssetID" />
|
||||
<input type="hidden" name="ReturnFilter.ProjectID" value="@Model.Filter.ProjectID" />
|
||||
@foreach (var entityType in Model.Filter.EntityTypes)
|
||||
{
|
||||
<input type="hidden" name="ReturnFilter.EntityTypes" value="@entityType" />
|
||||
}
|
||||
@foreach (var characterId in Model.Filter.CharacterIDs)
|
||||
{
|
||||
<input type="hidden" name="ReturnFilter.CharacterIDs" value="@characterId" />
|
||||
}
|
||||
@foreach (var assetId in Model.Filter.AssetIDs)
|
||||
{
|
||||
<input type="hidden" name="ReturnFilter.AssetIDs" value="@assetId" />
|
||||
}
|
||||
@foreach (var locationId in Model.Filter.LocationIDs)
|
||||
{
|
||||
<input type="hidden" name="ReturnFilter.LocationIDs" value="@locationId" />
|
||||
}
|
||||
<input type="hidden" name="ReturnFilter.SceneRangeMode" value="@Model.Filter.SceneRangeMode" />
|
||||
<input type="hidden" name="ReturnFilter.BookID" value="@Model.Filter.BookID" />
|
||||
<input type="hidden" name="ReturnFilter.ChapterID" value="@Model.Filter.ChapterID" />
|
||||
<input type="hidden" name="ReturnFilter.StartSceneID" value="@Model.Filter.StartSceneID" />
|
||||
<input type="hidden" name="ReturnFilter.EndSceneID" value="@Model.Filter.EndSceneID" />
|
||||
<input type="hidden" name="ReturnFilter.DisplayMode" value="@Model.Filter.DisplayMode" />
|
||||
<input type="hidden" name="ReturnFilter.SelectedSceneID" value="@Model.Filter.SelectedSceneID" />
|
||||
<input type="hidden" name="ReturnFilter.ShowAcknowledgedWarnings" value="@Model.Filter.ShowAcknowledgedWarnings" />
|
||||
<button class="btn btn-outline-secondary btn-sm" type="submit">Restore</button>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form asp-action="AcknowledgeWarning" method="post" class="d-flex gap-1 align-items-center">
|
||||
<input type="hidden" name="WarningType" value="@warning.WarningType" />
|
||||
<input type="hidden" name="SceneID" value="@warning.SceneID" />
|
||||
<input type="hidden" name="CharacterID" value="@warning.CharacterID" />
|
||||
<input type="hidden" name="AssetID" value="@warning.AssetID" />
|
||||
<input type="hidden" name="ReturnFilter.ProjectID" value="@Model.Filter.ProjectID" />
|
||||
@foreach (var entityType in Model.Filter.EntityTypes)
|
||||
{
|
||||
<input type="hidden" name="ReturnFilter.EntityTypes" value="@entityType" />
|
||||
}
|
||||
@foreach (var characterId in Model.Filter.CharacterIDs)
|
||||
{
|
||||
<input type="hidden" name="ReturnFilter.CharacterIDs" value="@characterId" />
|
||||
}
|
||||
@foreach (var assetId in Model.Filter.AssetIDs)
|
||||
{
|
||||
<input type="hidden" name="ReturnFilter.AssetIDs" value="@assetId" />
|
||||
}
|
||||
@foreach (var locationId in Model.Filter.LocationIDs)
|
||||
{
|
||||
<input type="hidden" name="ReturnFilter.LocationIDs" value="@locationId" />
|
||||
}
|
||||
<input type="hidden" name="ReturnFilter.SceneRangeMode" value="@Model.Filter.SceneRangeMode" />
|
||||
<input type="hidden" name="ReturnFilter.BookID" value="@Model.Filter.BookID" />
|
||||
<input type="hidden" name="ReturnFilter.ChapterID" value="@Model.Filter.ChapterID" />
|
||||
<input type="hidden" name="ReturnFilter.StartSceneID" value="@Model.Filter.StartSceneID" />
|
||||
<input type="hidden" name="ReturnFilter.EndSceneID" value="@Model.Filter.EndSceneID" />
|
||||
<input type="hidden" name="ReturnFilter.DisplayMode" value="@Model.Filter.DisplayMode" />
|
||||
<input type="hidden" name="ReturnFilter.SelectedSceneID" value="@Model.Filter.SelectedSceneID" />
|
||||
<input type="hidden" name="ReturnFilter.ShowAcknowledgedWarnings" value="@Model.Filter.ShowAcknowledgedWarnings" />
|
||||
<input class="form-control form-control-sm" type="text" name="Notes" maxlength="500" placeholder="Optional note" />
|
||||
<button class="btn btn-outline-primary btn-sm" type="submit">Acknowledge</button>
|
||||
</form>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user