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, IOptionsSnapshot pagingSettings) { this.ctx = ctx; this.sieveProcessor = sieveProcessor; this.pagingSettings = pagingSettings.Value; } public async Task 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 Row(int id) { var eventModel = await ctx.Events .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 EditRow(int id) { var eventModel = await ctx.Events .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 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 { [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 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 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 { [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> 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 .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(events, pagingInfo); } }