PlotDirector Phase 17K - Character Attributes Editor Migration
This commit is contained in:
parent
a305065f1a
commit
c54da61524
@ -142,6 +142,15 @@ public sealed class ScenesController(
|
||||
: PartialView("~/Views/Shared/SceneInspectorV2/_SceneRelationshipsEditor.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> SceneCharacterAttributesEditor(int sceneId)
|
||||
{
|
||||
var model = await scenes.GetEditAsync(sceneId);
|
||||
return model is null
|
||||
? NotFound()
|
||||
: PartialView("~/Views/Shared/SceneInspectorV2/_SceneCharacterAttributesEditor.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> SceneLocationsEditor(int sceneId)
|
||||
{
|
||||
@ -1116,23 +1125,76 @@ public sealed class ScenesController(
|
||||
|
||||
if (model.CharacterID <= 0)
|
||||
{
|
||||
if (IsAjaxRequest())
|
||||
{
|
||||
return BadRequest(new { success = false, message = "Choose a character before adding an attribute." });
|
||||
}
|
||||
|
||||
TempData["CharacterAttributeAddError"] = "Choose a character before adding an attribute.";
|
||||
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
|
||||
}
|
||||
|
||||
if (model.CharacterAttributeTypeID <= 0)
|
||||
{
|
||||
if (IsAjaxRequest())
|
||||
{
|
||||
return BadRequest(new { success = false, message = "Choose an attribute type before saving." });
|
||||
}
|
||||
|
||||
TempData["CharacterAttributeAddError"] = "Choose an attribute type before saving.";
|
||||
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.AttributeValue))
|
||||
{
|
||||
if (IsAjaxRequest())
|
||||
{
|
||||
return BadRequest(new { success = false, message = "Enter an attribute value before saving." });
|
||||
}
|
||||
|
||||
TempData["CharacterAttributeAddError"] = "Enter an attribute value before saving.";
|
||||
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
|
||||
}
|
||||
|
||||
await characters.AddAttributeEventAsync(model);
|
||||
if (IsAjaxRequest())
|
||||
{
|
||||
return await SceneCharacterAttributesRefreshJsonAsync(model.SceneID);
|
||||
}
|
||||
|
||||
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> UpdateCharacterAttributeEvent(CharacterAttributeEventCreateViewModel model)
|
||||
{
|
||||
if (model.CharacterAttributeEventID <= 0 || model.SceneID <= 0)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
if (model.CharacterID <= 0)
|
||||
{
|
||||
return BadRequest(new { success = false, message = "Choose a character before saving this attribute." });
|
||||
}
|
||||
|
||||
if (model.CharacterAttributeTypeID <= 0)
|
||||
{
|
||||
return BadRequest(new { success = false, message = "Choose an attribute type before saving." });
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.AttributeValue))
|
||||
{
|
||||
return BadRequest(new { success = false, message = "Enter an attribute value before saving." });
|
||||
}
|
||||
|
||||
await characters.UpdateAttributeEventAsync(model);
|
||||
if (IsAjaxRequest())
|
||||
{
|
||||
return await SceneCharacterAttributesRefreshJsonAsync(model.SceneID);
|
||||
}
|
||||
|
||||
return RedirectForScene(model.SceneID, model.ReturnProjectID, model.ReturnBookID);
|
||||
}
|
||||
|
||||
@ -1441,6 +1503,31 @@ public sealed class ScenesController(
|
||||
return await ScenePeopleContinuityRefreshJsonAsync(sceneId);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> SceneCharacterAttributesRefreshJsonAsync(int sceneId)
|
||||
{
|
||||
var updated = await scenes.GetEditAsync(sceneId);
|
||||
if (updated is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Json(new
|
||||
{
|
||||
success = true,
|
||||
html = new
|
||||
{
|
||||
health = await RenderPartialViewToStringAsync("~/Views/Shared/SceneInspectorV2/_SceneInspectorHealthStrip.cshtml", updated),
|
||||
people = await RenderPartialViewToStringAsync("~/Views/Shared/SceneInspectorV2/_ScenePeopleCard.cshtml", updated),
|
||||
continuity = await RenderPartialViewToStringAsync("~/Views/Shared/SceneInspectorV2/_SceneContinuityCard.cshtml", updated)
|
||||
},
|
||||
editorHtml = await RenderPartialViewToStringAsync("~/Views/Shared/SceneInspectorV2/_SceneCharacterAttributesEditor.cshtml", updated),
|
||||
scene = new
|
||||
{
|
||||
updated.SceneID
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<IActionResult> ScenePeopleContinuityRefreshJsonAsync(int sceneId)
|
||||
{
|
||||
var updated = await scenes.GetEditAsync(sceneId);
|
||||
|
||||
@ -260,6 +260,7 @@ public interface ICharacterService
|
||||
Task AcceptSceneCharacterSuggestionAsync(int suggestionId);
|
||||
Task RejectSceneCharacterSuggestionAsync(int suggestionId);
|
||||
Task<int> AddAttributeEventAsync(CharacterAttributeEventCreateViewModel model);
|
||||
Task<int> UpdateAttributeEventAsync(CharacterAttributeEventCreateViewModel model);
|
||||
Task<int> AddKnowledgeAsync(CharacterKnowledgeCreateViewModel model);
|
||||
Task<int> UpdateKnowledgeAsync(CharacterKnowledgeCreateViewModel model);
|
||||
Task DeleteKnowledgeAsync(int characterKnowledgeId);
|
||||
@ -9514,8 +9515,13 @@ public sealed class CharacterService(
|
||||
}
|
||||
}
|
||||
|
||||
public Task<int> AddAttributeEventAsync(CharacterAttributeEventCreateViewModel model) => characters.SaveAttributeEventAsync(new CharacterAttributeEvent
|
||||
public Task<int> AddAttributeEventAsync(CharacterAttributeEventCreateViewModel model) => SaveAttributeEventAsync(model);
|
||||
|
||||
public Task<int> UpdateAttributeEventAsync(CharacterAttributeEventCreateViewModel model) => SaveAttributeEventAsync(model);
|
||||
|
||||
private Task<int> SaveAttributeEventAsync(CharacterAttributeEventCreateViewModel model) => characters.SaveAttributeEventAsync(new CharacterAttributeEvent
|
||||
{
|
||||
CharacterAttributeEventID = model.CharacterAttributeEventID,
|
||||
CharacterID = model.CharacterID,
|
||||
CharacterAttributeTypeID = model.CharacterAttributeTypeID,
|
||||
SceneID = model.SceneID,
|
||||
|
||||
@ -2014,6 +2014,7 @@ public sealed class SceneCharacterEditViewModel
|
||||
|
||||
public sealed class CharacterAttributeEventCreateViewModel
|
||||
{
|
||||
public int CharacterAttributeEventID { get; set; }
|
||||
public int SceneID { get; set; }
|
||||
public int CharacterID { get; set; }
|
||||
public int CharacterAttributeTypeID { get; set; }
|
||||
|
||||
@ -0,0 +1,126 @@
|
||||
@model SceneEditViewModel
|
||||
|
||||
<div class="scene-inspector-editor-form">
|
||||
<div class="scene-inspector-editor-fields">
|
||||
<section class="scene-structure-editor-section">
|
||||
<div class="scene-structure-editor-heading">
|
||||
<span>Character attribute events</span>
|
||||
<strong>@Model.CharacterAttributeEvents.Count</strong>
|
||||
</div>
|
||||
|
||||
@if (!Model.CharacterAttributeEvents.Any())
|
||||
{
|
||||
<p class="muted">No character attribute events are recorded for this scene yet.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="scene-structure-editor-list">
|
||||
@foreach (var item in Model.CharacterAttributeEvents)
|
||||
{
|
||||
<article class="scene-structure-editor-card">
|
||||
<div class="scene-structure-card-header">
|
||||
<div>
|
||||
<h4>@item.CharacterName</h4>
|
||||
<p>@item.AttributeName</p>
|
||||
</div>
|
||||
<span class="scene-structure-marker">@item.AttributeValue</span>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(item.Description))
|
||||
{
|
||||
<p>@item.Description</p>
|
||||
}
|
||||
|
||||
<form asp-controller="Scenes" asp-action="UpdateCharacterAttributeEvent" method="post" class="scene-structure-editor-form" data-scene-editor-form>
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="CharacterAttributeEventID" value="@item.CharacterAttributeEventID" />
|
||||
<input type="hidden" name="SceneID" value="@Model.SceneID" />
|
||||
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
|
||||
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
|
||||
|
||||
<div class="scene-inspector-editor-error" data-scene-editor-error hidden></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="AttributeEvent_@(item.CharacterAttributeEventID)__CharacterID">Character</label>
|
||||
<select class="form-control form-control-sm" id="AttributeEvent_@(item.CharacterAttributeEventID)__CharacterID" name="CharacterID">
|
||||
@foreach (var option in Model.CharacterOptions)
|
||||
{
|
||||
<option value="@option.Value" selected="@(option.Value == item.CharacterID.ToString())">@option.Text</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="AttributeEvent_@(item.CharacterAttributeEventID)__CharacterAttributeTypeID">Attribute type</label>
|
||||
<select class="form-control form-control-sm" id="AttributeEvent_@(item.CharacterAttributeEventID)__CharacterAttributeTypeID" name="CharacterAttributeTypeID">
|
||||
@foreach (var option in Model.CharacterAttributeTypeOptions)
|
||||
{
|
||||
<option value="@option.Value" selected="@(option.Value == item.CharacterAttributeTypeID.ToString())">@option.Text</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="AttributeEvent_@(item.CharacterAttributeEventID)__AttributeValue">Attribute value</label>
|
||||
<input class="form-control form-control-sm" id="AttributeEvent_@(item.CharacterAttributeEventID)__AttributeValue" name="AttributeValue" value="@item.AttributeValue" maxlength="500" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="AttributeEvent_@(item.CharacterAttributeEventID)__Description">Description</label>
|
||||
<textarea class="form-control form-control-sm" id="AttributeEvent_@(item.CharacterAttributeEventID)__Description" name="Description" rows="3">@item.Description</textarea>
|
||||
</div>
|
||||
|
||||
<div class="scene-structure-action-row">
|
||||
<button class="btn btn-sm btn-primary" type="submit" data-scene-editor-save>Save attribute</button>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
|
||||
<section class="scene-structure-editor-section">
|
||||
<div class="scene-structure-editor-heading">
|
||||
<span>Add attribute event</span>
|
||||
</div>
|
||||
|
||||
<form asp-controller="Scenes" asp-action="AddCharacterAttributeEvent" method="post" class="scene-structure-editor-card scene-structure-editor-form" data-scene-editor-form>
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="SceneID" value="@Model.SceneID" />
|
||||
<input type="hidden" name="ReturnProjectID" value="@Model.ReturnProjectID" />
|
||||
<input type="hidden" name="ReturnBookID" value="@Model.ReturnBookID" />
|
||||
|
||||
<div class="scene-inspector-editor-error" data-scene-editor-error hidden></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="AttributeCharacterID">Character</label>
|
||||
<select class="form-control form-control-sm" id="AttributeCharacterID" name="CharacterID" asp-items="Model.CharacterOptions"></select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="AttributeTypeID">Attribute type</label>
|
||||
<select class="form-control form-control-sm" id="AttributeTypeID" name="CharacterAttributeTypeID" asp-items="Model.CharacterAttributeTypeOptions"></select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="AttributeValue">Attribute value</label>
|
||||
<input class="form-control form-control-sm" id="AttributeValue" name="AttributeValue" maxlength="500" placeholder="Value, e.g. Bruised ribs" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="AttributeDescription">Description</label>
|
||||
<textarea class="form-control form-control-sm" id="AttributeDescription" name="Description" rows="3" placeholder="Scene-specific context"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="scene-structure-action-row">
|
||||
<button class="btn btn-sm btn-primary" type="submit" data-scene-editor-save>Add attribute</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-editor-footer">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-scene-editor-close>Close</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -49,10 +49,15 @@
|
||||
<span>Relationship changes</span>
|
||||
<strong>@Model.RelationshipEvents.Count</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Attribute changes</span>
|
||||
<strong>@Model.CharacterAttributeEvents.Count</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-actions">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="characters" data-scene-id="@Model.SceneID" data-scene-editor-url="@Url.Action("SceneCharactersEditor", "Scenes", new { sceneId = Model.SceneID })">Manage characters</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="attributes" data-scene-id="@Model.SceneID" data-scene-editor-url="@Url.Action("SceneCharacterAttributesEditor", "Scenes", new { sceneId = Model.SceneID })">Manage attributes</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="knowledge" data-scene-id="@Model.SceneID" data-scene-editor-url="@Url.Action("SceneKnowledgeEditor", "Scenes", new { sceneId = Model.SceneID })">Manage knowledge</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="relationships" data-scene-id="@Model.SceneID" data-scene-editor-url="@Url.Action("SceneRelationshipsEditor", "Scenes", new { sceneId = Model.SceneID })">Manage relationships</button>
|
||||
</div>
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
threads: "Manage threads",
|
||||
dependencies: "Manage dependencies",
|
||||
characters: "Manage characters",
|
||||
attributes: "Manage attributes",
|
||||
knowledge: "Manage knowledge",
|
||||
relationships: "Manage relationships",
|
||||
locations: "Manage locations",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user