Nick Beckley 34a8d94cb4 Implemented the Visual Identity infrastructure pass.
Changed
Added image fields for Characters and Story Assets in models, view models, repositories, services, and save flows.
Added reusable avatar/initials rendering via [AvatarHelper.cs](C:/Source/PlotLine/PlotLine/Services/AvatarHelper.cs) and [_Avatar.cshtml](C:/Source/PlotLine/PlotLine/Views/Shared/_Avatar.cshtml).
Added upload/remove processing through existing upload storage in [VisualIdentityImageService.cs](C:/Source/PlotLine/PlotLine/Services/VisualIdentityImageService.cs).
Updated Character and Story Asset list/detail/edit views with thumbnails, initials fallback, upload, replace, and remove controls.
Added styling in [site.css](C:/Source/PlotLine/PlotLine/wwwroot/css/site.css) and refreshed site.min.css.
Database
Added new migration [079_VisualIdentityCharacterAssetImages.sql](C:/Source/PlotLine/PlotLine/Sql/079_VisualIdentityCharacterAssetImages.sql).
Adds nullable ImagePath and ThumbnailPath to Characters and StoryAssets.
Applied locally and confirmed the columns exist.
Stored Procedures
Updated active procedure definitions only:
Character_ListByProject
Character_Get
Character_Save
Character_UpdateImage
StoryAsset_ListByProject
StoryAsset_Get
StoryAsset_Save
StoryAsset_UpdateImage
2026-06-22 10:10:39 +01:00

102 lines
4.7 KiB
Plaintext

@model CharacterEditViewModel
@{
ViewData["Title"] = Model.CharacterID == 0 ? "New Character" : "Edit Character";
}
<div class="page-heading compact">
<div>
<p class="eyebrow">Character</p>
<h1>@ViewData["Title"] <help-icon key="characters.edit" /></h1>
</div>
</div>
@if (TempData["ImageError"] is string imageError)
{
<div class="alert alert-warning">@imageError</div>
}
<form asp-action="Save" method="post" class="edit-panel" enctype="multipart/form-data">
<input asp-for="CharacterID" type="hidden" />
<input asp-for="ProjectID" type="hidden" />
<input asp-for="ImagePath" type="hidden" />
<input asp-for="ThumbnailPath" type="hidden" />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row g-3">
<div class="col-md-8">
<label asp-for="CharacterName" class="form-label"></label>
<input asp-for="CharacterName" class="form-control" />
<span asp-validation-for="CharacterName" class="text-danger"></span>
</div>
<div class="col-md-4">
<label asp-for="ShortName" class="form-label"></label>
<input asp-for="ShortName" class="form-control" />
</div>
<div class="col-md-4">
<label asp-for="Sex" class="form-label"></label>
<input asp-for="Sex" class="form-control" />
</div>
<div class="col-md-4">
<label asp-for="BirthDate" class="form-label"></label>
<input asp-for="BirthDate" class="form-control" />
</div>
<div class="col-md-4">
<label asp-for="AgeAtSeriesStart" class="form-label"></label>
<input asp-for="AgeAtSeriesStart" class="form-control" />
</div>
<div class="col-md-6">
<label asp-for="Height" class="form-label"></label>
<input asp-for="Height" class="form-control" />
</div>
<div class="col-md-6">
<label asp-for="EyeColour" class="form-label"></label>
<input asp-for="EyeColour" class="form-control" />
</div>
<div class="col-md-6">
<label asp-for="CharacterImportance" class="form-label">Story importance <help-icon key="characters.fields.storyImportance" mode="inline" /></label>
<select asp-for="CharacterImportance" asp-items="Model.ImportanceOptions" class="form-select"></select>
<div class="form-text">Controls timeline grouping for major, secondary/supporting, and minor characters.</div>
</div>
<div class="col-md-6 d-flex align-items-end">
<div class="form-check mb-2">
<input asp-for="ShowInQuickAddBar" class="form-check-input" />
<label asp-for="ShowInQuickAddBar" class="form-check-label"></label>
<div class="form-text">Makes this item available as a draggable shortcut on the Timeline page.</div>
</div>
</div>
<div class="col-12">
<label asp-for="DefaultDescription" class="form-label"></label>
<textarea asp-for="DefaultDescription" class="form-control" rows="5"></textarea>
</div>
<div class="col-12">
<div class="visual-image-editor">
<div>
<p class="eyebrow">Character Image</p>
<partial name="_Avatar" model="@(new AvatarViewModel { DisplayName = Model.CharacterName, ImagePath = Model.ImagePath, ThumbnailPath = Model.ThumbnailPath, Size = "lg", Lazy = false })" />
</div>
<div class="visual-image-editor__controls">
<label asp-for="ImageUpload" class="form-label"></label>
<input asp-for="ImageUpload" class="form-control" type="file" accept=".jpg,.jpeg,.png,.webp,image/jpeg,image/png,image/webp" />
<div class="form-text">JPG, PNG or WebP. A 128 x 128 thumbnail is generated automatically.</div>
@if (!string.IsNullOrWhiteSpace(Model.ImagePath) || !string.IsNullOrWhiteSpace(Model.ThumbnailPath))
{
<label class="form-check mt-2">
<input asp-for="RemoveImage" class="form-check-input" />
<span class="form-check-label">Remove image</span>
</label>
}
</div>
</div>
</div>
</div>
<div class="button-row mt-3">
<button class="btn btn-primary" type="submit">Save character</button>
<a class="btn btn-outline-secondary" asp-action="Index" asp-route-projectId="@Model.ProjectID">Cancel</a>
</div>
</form>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}