using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PlotLine.Services; using PlotLine.ViewModels; namespace PlotLine.Controllers; [Authorize] public sealed class FloorPlansController(IFloorPlanService floorPlans) : Controller { public async Task Index(int projectId) { var model = await floorPlans.ListAsync(projectId); return model is null ? NotFound() : View(model); } public async Task Edit(int id) { var model = await floorPlans.GetEditorAsync(id); return model is null ? NotFound() : View(model); } [HttpPost] [ValidateAntiForgeryToken] public async Task Create(FloorPlanEditViewModel model) { if (!ModelState.IsValid) { return RedirectToAction(nameof(Index), new { projectId = model.ProjectID }); } var floorPlanId = await floorPlans.SavePlanAsync(model); return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task SavePlan(FloorPlanEditViewModel model) { if (!ModelState.IsValid) { var editor = await floorPlans.GetEditorAsync(model.FloorPlanID); if (editor is null) { return NotFound(); } editor.FloorPlan = model; return View("Edit", editor); } var floorPlanId = await floorPlans.SavePlanAsync(model); TempData["FloorPlanMessage"] = "Floor plan saved."; return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task SaveLayout(FloorPlanEditorViewModel model) { var isAsyncSave = string.Equals(Request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.OrdinalIgnoreCase); try { await floorPlans.SaveLayoutAsync(model); if (isAsyncSave) { return Json(new { ok = true, message = "Saved" }); } TempData["FloorPlanMessage"] = "Layout saved."; } catch (InvalidOperationException ex) { if (isAsyncSave) { Response.StatusCode = StatusCodes.Status400BadRequest; return Json(new { ok = false, message = ex.Message }); } TempData["FloorPlanError"] = ex.Message; } return RedirectToAction(nameof(Edit), new { id = model.FloorPlan.FloorPlanID }); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeletePlan(int id, int projectId) { await floorPlans.DeletePlanAsync(id); TempData["FloorPlanMessage"] = "Floor plan deleted."; return RedirectToAction(nameof(Index), new { projectId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddFloor(FloorPlanFloorEditViewModel model) { if (ModelState.IsValid) { await floorPlans.SaveFloorAsync(model); TempData["FloorPlanMessage"] = "Floor added."; } return RedirectToAction(nameof(Edit), new { id = model.FloorPlanID }); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteFloor(int floorPlanId, int floorPlanFloorId) { await floorPlans.DeleteFloorAsync(floorPlanFloorId); TempData["FloorPlanMessage"] = "Floor deleted."; return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task UploadBackground(int floorPlanId, int floorPlanFloorId, IFormFile? backgroundImage) { if (backgroundImage is null) { TempData["FloorPlanError"] = "Choose a background image to upload."; return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } var result = await floorPlans.UploadBackgroundAsync(floorPlanId, floorPlanFloorId, backgroundImage); TempData[result.Succeeded ? "FloorPlanMessage" : "FloorPlanError"] = result.Succeeded ? "Background image uploaded." : result.ErrorMessage ?? "Background image could not be uploaded."; return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task SaveBackgroundSettings(FloorPlanFloorBackgroundSettingsViewModel model) { var isAsyncSave = string.Equals(Request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.OrdinalIgnoreCase); try { await floorPlans.SaveBackgroundSettingsAsync(model); if (isAsyncSave) { return Json(new { ok = true, message = "Saved" }); } TempData["FloorPlanMessage"] = "Background settings saved."; } catch (InvalidOperationException ex) { if (isAsyncSave) { Response.StatusCode = StatusCodes.Status400BadRequest; return Json(new { ok = false, message = ex.Message }); } TempData["FloorPlanError"] = ex.Message; } return RedirectToAction(nameof(Edit), new { id = model.FloorPlanID }); } [HttpPost] [ValidateAntiForgeryToken] public async Task ResetBackground(int floorPlanId, int floorPlanFloorId) { await floorPlans.ResetBackgroundAsync(floorPlanId, floorPlanFloorId); TempData["FloorPlanMessage"] = "Background position reset."; return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task RemoveBackground(int floorPlanId, int floorPlanFloorId) { await floorPlans.RemoveBackgroundAsync(floorPlanId, floorPlanFloorId); TempData["FloorPlanMessage"] = "Background image removed."; return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddBlock(int floorPlanId, FloorPlanBlockEditViewModel model) { try { if (ModelState.IsValid) { var floorPlanBlockId = await floorPlans.SaveBlockAsync(model); TempData["FloorPlanMessage"] = "Block added."; return RedirectToAction(nameof(Edit), new { id = floorPlanId, selectedBlockId = floorPlanBlockId }); } } catch (InvalidOperationException ex) { TempData["FloorPlanError"] = ex.Message; } return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } [HttpPost] [ValidateAntiForgeryToken] public async Task DeleteBlock(int floorPlanId, int floorPlanBlockId) { await floorPlans.DeleteBlockAsync(floorPlanBlockId); TempData["FloorPlanMessage"] = "Block deleted."; return RedirectToAction(nameof(Edit), new { id = floorPlanId }); } }