PlotDirector/PlotLine/Controllers/StoryCalendarController.cs

111 lines
3.2 KiB
C#

using System.Globalization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using PlotLine.Services;
namespace PlotLine.Controllers;
[Authorize]
public sealed class StoryCalendarController(IStoryCalendarService calendar, ILogger<StoryCalendarController> logger) : Controller
{
public async Task<IActionResult> Index(int projectId, int? bookId, string? month, string? date)
{
if (projectId == 0)
{
return BadRequest("ProjectID is required.");
}
var selectedMonth = ParseMonth(month);
var selectedDate = ParseDate(date);
var model = await calendar.GetAsync(projectId, bookId, selectedMonth, selectedDate);
if (model is null)
{
return NotFound();
}
return IsAjaxRequest()
? PartialView("_StoryCalendarSurface", model)
: View(model);
}
[HttpGet]
public async Task<IActionResult> DayDetails(int projectId, int? bookId, string? date)
{
if (projectId == 0)
{
return BadRequest("ProjectID is required.");
}
var selectedDate = ParseDate(date);
if (!selectedDate.HasValue)
{
return BadRequest("Date is required.");
}
try
{
var model = await calendar.GetAsync(projectId, bookId, FirstOfMonth(selectedDate.Value), selectedDate);
return model is null ? NotFound() : PartialView("_StoryCalendarDayDetails", model.DayDetails);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to load Story Calendar day details for project {ProjectID} and date {StoryDate}.", projectId, date);
return StatusCode(StatusCodes.Status500InternalServerError, "The selected story date could not be loaded.");
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ApplyFilters(int projectId, int? bookId, string? month)
{
return RedirectToAction(nameof(Index), new
{
projectId,
bookId,
month
});
}
private static DateOnly? ParseMonth(string? value)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return DateOnly.TryParseExact(
$"{value.Trim()}-01",
"yyyy-MM-dd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out var parsed)
? new DateOnly(parsed.Year, parsed.Month, 1)
: null;
}
private static DateOnly? ParseDate(string? value)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return DateOnly.TryParseExact(
value.Trim(),
"yyyy-MM-dd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out var parsed)
? parsed
: null;
}
private static DateOnly FirstOfMonth(DateOnly date)
=> new(date.Year, date.Month, 1);
private bool IsAjaxRequest()
=> string.Equals(Request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.OrdinalIgnoreCase);
}