using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using PlotLine.Services; using PlotLine.ViewModels; namespace PlotLine.Controllers; [Authorize] public sealed class StoryAssetsController(IAssetService assets) : Controller { public async Task Index(int projectId) { var model = await assets.GetAssetsAsync(projectId); return model is null ? NotFound() : View(model); } public async Task Create(int projectId) { var model = await assets.GetCreateAssetAsync(projectId); return model is null ? NotFound() : View("Edit", model); } public async Task Edit(int id) { var model = await assets.GetEditAssetAsync(id); return model is null ? NotFound() : View(model); } public async Task Details(int id) { var model = await assets.GetAssetDetailAsync(id); return model is null ? NotFound() : View(model); } [HttpPost] [ValidateAntiForgeryToken] public async Task Save(StoryAssetEditViewModel model) { if (!ModelState.IsValid) { var invalidModel = await RehydrateEditModel(model); return invalidModel is null ? NotFound() : View("Edit", invalidModel); } int assetId; try { assetId = await assets.SaveAssetAsync(model); } catch (EntityImageUploadException ex) { TempData["ImageError"] = ex.Message; return RedirectToAction(nameof(Edit), new { id = ex.EntityId }); } catch (InvalidOperationException ex) { ModelState.AddModelError(string.Empty, ex.Message); var editModel = await RehydrateEditModel(model); return editModel is null ? NotFound() : View("Edit", editModel); } return RedirectToAction(nameof(Details), new { id = assetId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task Archive(int id, int projectId) { await assets.ArchiveAssetAsync(id); TempData["ArchiveMessage"] = "Asset archived. You can restore it from Archived Items."; return RedirectToAction(nameof(Index), new { projectId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddDependency(AssetDependencyEditViewModel model, int returnAssetId) { await assets.SaveDependencyAsync(model); return RedirectToAction(nameof(Details), new { id = returnAssetId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task DropDependency(AssetDependencyEditViewModel model) { if (model.SourceAssetID != model.TargetAssetID) { await assets.SaveDependencyAsync(model); } return RedirectToAction(nameof(Index), new { projectId = model.ProjectID }); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteDependency(int id, int returnAssetId) { await assets.DeleteDependencyAsync(id); return RedirectToAction(nameof(Details), new { id = returnAssetId }); } private async Task RehydrateEditModel(StoryAssetEditViewModel posted) { var model = posted.StoryAssetID == 0 ? await assets.GetCreateAssetAsync(posted.ProjectID) : await assets.GetEditAssetAsync(posted.StoryAssetID); if (model is null) { return null; } model.AssetName = posted.AssetName; model.Aliases = posted.Aliases; model.AssetKindID = posted.AssetKindID; model.Description = posted.Description; model.Importance = posted.Importance; model.CurrentStateID = posted.CurrentStateID; model.CurrentLocationID = posted.CurrentLocationID; model.IsResolved = posted.IsResolved; model.ShowInQuickAddBar = posted.ShowInQuickAddBar; return model; } }