Files
predavanja/Events-MVC/Events.MVC/Controllers/EventsController.cs
2026-04-26 01:01:37 +02:00

242 lines
7.3 KiB
C#

using System.Text.Json;
#if POSTGRES
using Events.EF.Data.Postgres;
#else
using Events.EF.Data.MSSQL;
#endif
using Events.EF.Models;
using Events.MVC.Models;
using Events.MVC.Models.Events;
using Events.MVC.Util.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Sieve.Models;
using Sieve.Services;
namespace Events.MVC.Controllers;
public class EventsController : Controller
{
private readonly EventsContext ctx;
private readonly ISieveProcessor sieveProcessor;
private readonly PagingSettings pagingSettings;
public EventsController(EventsContext ctx, ISieveProcessor sieveProcessor, IOptions<PagingSettings> pagingSettings)
{
this.ctx = ctx;
this.sieveProcessor = sieveProcessor;
this.pagingSettings = pagingSettings.Value;
}
public async Task<IActionResult> Index(SieveModel sieveModel)
{
var viewModel = await BuildEventsListAsync(sieveModel);
if (Request.Headers.ContainsKey(Constants.HtmxHeaders.Request))
{
return PartialView("_EventsList", viewModel);
}
return View(viewModel);
}
[HttpGet]
public async Task<IActionResult> Row(int id)
{
var eventModel = await ctx.Events
.AsNoTracking()
.Select(e => new EventViewModel
{
Id = e.Id,
Name = e.Name,
EventDate = e.EventDate,
ParticipantsCount = e.Registrations.Count
})
.FirstOrDefaultAsync(e => e.Id == id);
if (eventModel is null)
{
return NotFound();
}
return PartialView("_EventRow", eventModel);
}
[HttpGet]
public async Task<IActionResult> EditRow(int id)
{
var eventModel = await ctx.Events
.AsNoTracking()
.Select(e => new EventViewModel
{
Id = e.Id,
Name = e.Name,
EventDate = e.EventDate
})
.FirstOrDefaultAsync(e => e.Id == id);
if (eventModel is null)
{
return NotFound();
}
return PartialView("_EventEditRow", eventModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(EventViewModel model, SieveModel sieveModel)
{
if (!ModelState.IsValid)
{
Response.Headers[Constants.HtmxHeaders.Retarget] = "#create-event-form";
Response.Headers[Constants.HtmxHeaders.Reswap] = Constants.HtmxSwap.OuterHtml;
return PartialView("_CreateEventForm", model);
}
var eventEntity = new Event
{
Name = model.Name,
EventDate = model.EventDate
};
ctx.Events.Add(eventEntity);
await ctx.SaveChangesAsync();
Response.Headers[Constants.HtmxHeaders.Trigger] = JsonSerializer.Serialize(new Dictionary<string, object?>
{
[Constants.HtmxEvents.EventCreated] = true,
[Constants.HtmxEvents.ShowToast] = new
{
variant = Constants.ToastVariants.Success,
title = Constants.ToastTitles.Success,
message = $"Event '{model.Name}' was added successfully."
}
});
var viewModel = await BuildEventsListAsync(sieveModel);
return PartialView("_EventsList", viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, EventViewModel model)
{
if (id != model.Id)
{
return BadRequest();
}
if (!ModelState.IsValid)
{
return PartialView("_EventEditRow", model);
}
var existingEvent = await ctx.Events.FirstOrDefaultAsync(e => e.Id == id);
if (existingEvent is null)
{
return NotFound();
}
existingEvent.Name = model.Name;
existingEvent.EventDate = model.EventDate;
await ctx.SaveChangesAsync();
return PartialView("_EventRow", new EventViewModel
{
Id = existingEvent.Id,
Name = existingEvent.Name,
EventDate = existingEvent.EventDate,
ParticipantsCount = await ctx.Registrations.CountAsync(r => r.EventId == existingEvent.Id)
});
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int id, SieveModel sieveModel)
{
var eventEntity = await ctx.Events
.Include(e => e.Registrations)
.FirstOrDefaultAsync(e => e.Id == id);
if (eventEntity is null)
{
return NotFound();
}
if (eventEntity.Registrations.Count > 0)
{
Response.StatusCode = StatusCodes.Status409Conflict;
return Content("The event cannot be deleted because registrations exist.");
}
ctx.Events.Remove(eventEntity);
var deletedName = eventEntity.Name;
await ctx.SaveChangesAsync();
Response.Headers[Constants.HtmxHeaders.Trigger] = JsonSerializer.Serialize(new Dictionary<string, object?>
{
[Constants.HtmxEvents.ShowToast] = new
{
variant = Constants.ToastVariants.Success,
title = Constants.ToastTitles.Success,
message = $"Event '{deletedName}' was deleted successfully."
}
});
var viewModel = await BuildEventsListAsync(sieveModel);
return PartialView("_EventsList", viewModel);
}
private async Task<PagedList<EventViewModel>> BuildEventsListAsync(SieveModel sieveModel)
{
sieveModel.SetDefaultPagingAndSorting(pagingSettings.PageSize, "EventDate");
var normalizedFilters = sieveModel.Filters?.Trim() ?? string.Empty;
var nameFilter = SieveModelExtensions.ExtractFilterValue(normalizedFilters, "Name");
var baseQuery = ctx.Events
.AsNoTracking()
.Select(e => new EventViewModel
{
Id = e.Id,
Name = e.Name,
EventDate = e.EventDate,
ParticipantsCount = e.Registrations.Count
});
var totalCount = await baseQuery.CountAsync();
var filteredCount = string.IsNullOrWhiteSpace(normalizedFilters)
? totalCount
: await sieveProcessor
.Apply(
sieveModel,
baseQuery,
applyFiltering: true,
applySorting: false,
applyPagination: false)
.CountAsync();
var pagingInfo = new PagingInfo
{
FilteredItemsCount = filteredCount,
TotalItemsCount = totalCount,
ItemsPerPage = sieveModel.PageSize!.Value,
CurrentPage = sieveModel.Page!.Value,
Sorts = sieveModel.Sorts ?? "EventDate",
Filters = normalizedFilters,
NameFilter = nameFilter
};
if (pagingInfo.CurrentPage > pagingInfo.TotalPages)
{
pagingInfo.CurrentPage = pagingInfo.TotalPages;
sieveModel.Page = pagingInfo.CurrentPage;
}
var events = await sieveProcessor
.Apply(sieveModel, baseQuery)
.ToListAsync();
return new PagedList<EventViewModel>(events, pagingInfo);
}
}