PlotDirector/PlotLine/Controllers/WriterController.cs
2026-06-12 22:00:10 +01:00

142 lines
5.0 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using PlotLine.Services;
using PlotLine.ViewModels;
namespace PlotLine.Controllers;
[Authorize]
public sealed class WriterController(
IWriterWorkspaceService writerWorkspace,
IWritingScheduleService writingSchedule) : Controller
{
public async Task<IActionResult> Index(int? projectId)
{
var model = await writerWorkspace.GetDashboardAsync(projectId);
model.WritingSchedule = await writingSchedule.GetTodayDashboardAsync();
return View(model);
}
public async Task<IActionResult> Chapter(int id)
{
var model = await writerWorkspace.GetChapterWorkflowAsync(id);
return model is null ? NotFound() : View(model);
}
public async Task<IActionResult> SchedulePreview(int? writingPlanId)
{
var model = await writingSchedule.GetSchedulePreviewAsync(writingPlanId);
return View(model);
}
public async Task<IActionResult> ScheduleSetup(int? projectId)
{
var model = await writingSchedule.GetScheduleSetupAsync(projectId);
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ScheduleSetup(WritingScheduleSetupViewModel model)
{
if (!Request.Form.ContainsKey(nameof(model.SelectedWritingDays)))
{
model.SelectedWritingDays = [];
}
if (!model.SelectedWritingDays.Any())
{
ModelState.AddModelError(string.Empty, "Select at least one writing day.");
ModelState.AddModelError(nameof(model.SelectedWritingDays), "Select at least one writing day.");
}
if (model.DeadlineDate.Date < model.StartDate.Date)
{
ModelState.AddModelError(nameof(model.DeadlineDate), "Deadline date must be on or after the start date.");
}
if (!ModelState.IsValid)
{
await writingSchedule.PopulateScheduleSetupOptionsAsync(model);
return View(model);
}
try
{
var result = await writingSchedule.CreateScheduleFromSetupAsync(model);
if (string.IsNullOrWhiteSpace(result.ValidationMessage))
{
TempData["SchedulePreviewMessage"] = $"Writing plan created. {result.GeneratedItemCount} item{(result.GeneratedItemCount == 1 ? "" : "s")} generated.";
}
else
{
TempData["SchedulePreviewError"] = result.ValidationMessage;
}
return RedirectToAction(nameof(SchedulePreview), new { writingPlanId = result.WritingPlanID });
}
catch (InvalidOperationException ex)
{
ModelState.AddModelError(string.Empty, ex.Message);
await writingSchedule.PopulateScheduleSetupOptionsAsync(model);
return View(model);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> GenerateSchedule(int writingPlanId)
{
try
{
var items = await writingSchedule.GenerateSchedule(writingPlanId);
TempData["SchedulePreviewMessage"] = $"{items.Count} item{(items.Count == 1 ? "" : "s")} generated.";
}
catch (InvalidOperationException ex)
{
TempData["SchedulePreviewError"] = ex.Message;
}
return RedirectToAction(nameof(SchedulePreview), new { writingPlanId });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteGeneratedSchedule(int writingPlanId)
{
await writingSchedule.DeleteScheduleAsync(writingPlanId);
TempData["SchedulePreviewMessage"] = "Generated schedule deleted.";
return RedirectToAction(nameof(SchedulePreview), new { writingPlanId });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> MarkScheduleItemComplete(int writingScheduleItemId, int? projectId)
{
var updated = await writingSchedule.MarkScheduleItemCompleteAsync(writingScheduleItemId);
TempData[updated ? "WriterWorkspaceMessage" : "WriterWorkspaceError"] = updated
? "Writing task marked complete."
: "Only planned writing tasks can be marked complete.";
return RedirectToAction(nameof(Index), new { projectId });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SkipScheduleItem(int writingScheduleItemId, int? projectId)
{
var updated = await writingSchedule.SkipScheduleItemAsync(writingScheduleItemId);
TempData[updated ? "WriterWorkspaceMessage" : "WriterWorkspaceError"] = updated
? "Writing task skipped."
: "Only planned writing tasks can be skipped.";
return RedirectToAction(nameof(Index), new { projectId });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveChapterGoal(ChapterGoalEditViewModel model)
{
await writerWorkspace.SaveChapterGoalAsync(model);
return RedirectToAction(nameof(Chapter), new { id = model.ChapterID });
}
}