mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 17:43:12 +00:00
* Align EntityFrameworkCore.SqlServer and Tools to 8.0.8
* Add calendar/events backend API
Cherry-picked from main-backup (19994ef); scratch notes file removed.
* Require authentication on CalendarController
Security review found [Authorize] commented out, leaving all 6 calendar
endpoints anonymous. Enforce auth to match the API convention (17/23
controllers).
* Add CalendarController unit tests (xUnit + EF InMemory)
306 lines
12 KiB
C#
306 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Api.SeaHavenIndustries.DTOs;
|
|
using Data.SeaHavenIndustries;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Api.SeaHavenIndustries.Controllers
|
|
{
|
|
[Authorize]
|
|
[ApiController]
|
|
[Route("api/Calendar")]
|
|
public class CalendarController : Controller
|
|
{
|
|
private readonly UserManager<ApplicationUser> _userManager;
|
|
private readonly ApplicationDbContext _db;
|
|
|
|
public CalendarController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
|
|
{
|
|
_userManager = userManager;
|
|
_db = db;
|
|
}
|
|
|
|
// ===================================================================================
|
|
// CREATE EVENT
|
|
// ===================================================================================
|
|
[HttpPost]
|
|
[Route("AddEvent")]
|
|
public async Task<IActionResult> AddEvent(EditEvent_DTO eventDto)
|
|
{
|
|
try
|
|
{
|
|
var model = new Events
|
|
{
|
|
Title = eventDto.Title,
|
|
Description = eventDto.Description,
|
|
Location = eventDto.Location,
|
|
RecordID = eventDto.RecordID,
|
|
RecordName = eventDto.RecordName,
|
|
RecordTargetType = eventDto.RecordTargetType,
|
|
ReminderMinutes = eventDto.ReminderMinutes,
|
|
EventColor = eventDto.EventColor,
|
|
StartDate = eventDto.StartDate,
|
|
StartTime = eventDto.StartTime,
|
|
EndDate = eventDto.EndDate,
|
|
EndTime = eventDto.EndTime,
|
|
AllDay = eventDto.AllDay,
|
|
CreatedDate = DateTime.Now,
|
|
IsDeleted = false
|
|
};
|
|
|
|
_db.Events.Add(model);
|
|
await _db.SaveChangesAsync();
|
|
|
|
return Ok(new DataResponse { Message = "Event Created Successfully", Status = "200" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
|
}
|
|
}
|
|
|
|
// ===================================================================================
|
|
// UPDATE EVENT
|
|
// ===================================================================================
|
|
[HttpPost]
|
|
[Route("EditEvent")]
|
|
public async Task<IActionResult> EditEvent(EditEvent_DTO eventDto)
|
|
{
|
|
try
|
|
{
|
|
if (!eventDto.Id.HasValue)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = "Event ID is required for editing" });
|
|
}
|
|
|
|
var model = await _db.Events.FindAsync(eventDto.Id.Value);
|
|
if (model == null || model.IsDeleted == true)
|
|
{
|
|
return NotFound(new Response { Status = "Error", Message = "Event not found" });
|
|
}
|
|
|
|
model.Title = eventDto.Title;
|
|
model.Description = eventDto.Description;
|
|
model.Location = eventDto.Location;
|
|
model.RecordID = eventDto.RecordID;
|
|
model.RecordName = eventDto.RecordName;
|
|
model.RecordTargetType = eventDto.RecordTargetType;
|
|
model.ReminderMinutes = eventDto.ReminderMinutes;
|
|
model.EventColor = eventDto.EventColor;
|
|
model.StartDate = eventDto.StartDate;
|
|
model.StartTime = eventDto.StartTime;
|
|
model.EndDate = eventDto.EndDate;
|
|
model.EndTime = eventDto.EndTime;
|
|
model.AllDay = eventDto.AllDay;
|
|
model.LastModificationTime = DateTime.Now;
|
|
|
|
_db.Events.Update(model);
|
|
await _db.SaveChangesAsync();
|
|
|
|
return Ok(new DataResponse { Message = "Event Updated Successfully", Status = "200" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
|
}
|
|
}
|
|
|
|
// ===================================================================================
|
|
// GET EVENT BY ID
|
|
// ===================================================================================
|
|
[HttpGet]
|
|
[Route("GetEventById/{id}")]
|
|
public async Task<IActionResult> GetEventById(int id)
|
|
{
|
|
try
|
|
{
|
|
var model = await _db.Events
|
|
.Where(e => e.Id == id && e.IsDeleted != true)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (model == null)
|
|
{
|
|
return NotFound(new Response { Status = "Error", Message = "Event not found" });
|
|
}
|
|
|
|
var dto = new Event_DTO
|
|
{
|
|
Id = model.Id,
|
|
Title = model.Title,
|
|
Description = model.Description,
|
|
Location = model.Location,
|
|
RecordID = model.RecordID,
|
|
RecordName = model.RecordName,
|
|
RecordTargetType = model.RecordTargetType,
|
|
ReminderMinutes = model.ReminderMinutes,
|
|
EventColor = model.EventColor,
|
|
StartDate = model.StartDate,
|
|
StartTime = model.StartTime,
|
|
EndDate = model.EndDate,
|
|
EndTime = model.EndTime,
|
|
AllDay = model.AllDay,
|
|
CreatedDate = model.CreatedDate,
|
|
createdby = model.createdby
|
|
};
|
|
|
|
return Ok(new DataResponse { Message = "Success", Status = "200", Data = dto });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
|
}
|
|
}
|
|
|
|
// ===================================================================================
|
|
// DELETE EVENT
|
|
// ===================================================================================
|
|
[HttpDelete]
|
|
[Route("DeleteEvent/{id}")]
|
|
public async Task<IActionResult> DeleteEvent(int id)
|
|
{
|
|
try
|
|
{
|
|
var model = await _db.Events.FindAsync(id);
|
|
if (model == null)
|
|
{
|
|
return NotFound(new Response { Status = "Error", Message = "Event not found" });
|
|
}
|
|
|
|
// Soft delete
|
|
model.IsDeleted = true;
|
|
model.DeletionTime = DateTime.Now;
|
|
|
|
_db.Events.Update(model);
|
|
await _db.SaveChangesAsync();
|
|
|
|
return Ok(new DataResponse { Message = "Event Deleted Successfully", Status = "200" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
|
}
|
|
}
|
|
|
|
// ===================================================================================
|
|
// GET EVENTS LIST (WITH PAGINATION AND FILTERS)
|
|
// ===================================================================================
|
|
[HttpGet]
|
|
[Route("GetEventList")]
|
|
public IActionResult GetEventList(string? search = "", int page = 1, int pageSize = 10)
|
|
{
|
|
try
|
|
{
|
|
var query = _db.Events.Where(e => e.IsDeleted != true);
|
|
|
|
// Apply search filter if provided
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
var searchLower = search.ToLower();
|
|
query = query.Where(e =>
|
|
e.Title.ToLower().Contains(searchLower) ||
|
|
(e.Description != null && e.Description.ToLower().Contains(searchLower)) ||
|
|
(e.Location != null && e.Location.ToLower().Contains(searchLower))
|
|
);
|
|
}
|
|
|
|
// IMPORTANT: Calculate totalCount AFTER applying filters
|
|
var totalCount = query.Count();
|
|
|
|
// Apply pagination
|
|
var events = query
|
|
.OrderByDescending(e => e.StartDate)
|
|
.ThenByDescending(e => e.CreatedDate)
|
|
.Skip((page - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.Select(e => new Event_DTO
|
|
{
|
|
Id = e.Id,
|
|
Title = e.Title,
|
|
Description = e.Description,
|
|
Location = e.Location,
|
|
RecordID = e.RecordID,
|
|
RecordName = e.RecordName,
|
|
RecordTargetType = e.RecordTargetType,
|
|
ReminderMinutes = e.ReminderMinutes,
|
|
EventColor = e.EventColor,
|
|
StartDate = e.StartDate,
|
|
StartTime = e.StartTime,
|
|
EndDate = e.EndDate,
|
|
EndTime = e.EndTime,
|
|
AllDay = e.AllDay,
|
|
CreatedDate = e.CreatedDate,
|
|
createdby = e.createdby
|
|
})
|
|
.ToList();
|
|
|
|
return Ok(new DataResponse
|
|
{
|
|
Message = "Success",
|
|
Status = "200",
|
|
Data = new { rows = events, totalCount }
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
|
}
|
|
}
|
|
|
|
// ===================================================================================
|
|
// GET EVENTS FOR CALENDAR (Date Range Filter)
|
|
// ===================================================================================
|
|
[HttpGet]
|
|
[Route("GetEventsForCalendar")]
|
|
public IActionResult GetEventsForCalendar([FromQuery] DateTime? start, [FromQuery] DateTime? end)
|
|
{
|
|
try
|
|
{
|
|
var query = _db.Events.Where(e => e.IsDeleted != true);
|
|
|
|
// Filter by date range if provided
|
|
if (start.HasValue && end.HasValue)
|
|
{
|
|
query = query.Where(e =>
|
|
(e.StartDate >= start.Value && e.StartDate <= end.Value) ||
|
|
(e.EndDate >= start.Value && e.EndDate <= end.Value) ||
|
|
(e.StartDate <= start.Value && e.EndDate >= end.Value)
|
|
);
|
|
}
|
|
|
|
var events = query
|
|
.Select(e => new Event_DTO
|
|
{
|
|
Id = e.Id,
|
|
Title = e.Title,
|
|
Description = e.Description,
|
|
Location = e.Location,
|
|
RecordID = e.RecordID,
|
|
RecordName = e.RecordName,
|
|
RecordTargetType = e.RecordTargetType,
|
|
ReminderMinutes = e.ReminderMinutes,
|
|
EventColor = e.EventColor,
|
|
StartDate = e.StartDate,
|
|
StartTime = e.StartTime,
|
|
EndDate = e.EndDate,
|
|
EndTime = e.EndTime,
|
|
AllDay = e.AllDay,
|
|
CreatedDate = e.CreatedDate,
|
|
createdby = e.createdby
|
|
})
|
|
.ToList();
|
|
|
|
return Ok(new DataResponse { Message = "Success", Status = "200", Data = events });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|