Add Events, Registrations, and Payments controllers with DTOs
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RacePlannerApi.Data;
|
||||
using RacePlannerApi.DTOs;
|
||||
using RacePlannerApi.Models;
|
||||
|
||||
namespace RacePlannerApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Authorize]
|
||||
public class RegistrationsController : ControllerBase
|
||||
{
|
||||
private readonly RacePlannerDbContext _context;
|
||||
|
||||
public RegistrationsController(RacePlannerDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<RegistrationDto>> CreateRegistration(CreateRegistrationRequest request)
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
if (userId == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
// Check if event exists and is published
|
||||
var eventEntity = await _context.Events
|
||||
.FirstOrDefaultAsync(e => e.Id == request.EventId);
|
||||
|
||||
if (eventEntity == null)
|
||||
{
|
||||
return NotFound(new { error = "Event not found" });
|
||||
}
|
||||
|
||||
if (eventEntity.Status != EventStatus.Published)
|
||||
{
|
||||
return BadRequest(new { error = "Event is not open for registration" });
|
||||
}
|
||||
|
||||
// Check if already registered
|
||||
var existingRegistration = await _context.Registrations
|
||||
.FirstOrDefaultAsync(r => r.EventId == request.EventId && r.ParticipantId == userId.Value);
|
||||
|
||||
if (existingRegistration != null)
|
||||
{
|
||||
return Conflict(new { error = "Already registered for this event" });
|
||||
}
|
||||
|
||||
// Check if event is full
|
||||
if (eventEntity.MaxParticipants.HasValue)
|
||||
{
|
||||
var registrationCount = await _context.Registrations
|
||||
.CountAsync(r => r.EventId == request.EventId && r.Status != RegistrationStatus.Cancelled);
|
||||
|
||||
if (registrationCount >= eventEntity.MaxParticipants.Value)
|
||||
{
|
||||
return BadRequest(new { error = "Event is full" });
|
||||
}
|
||||
}
|
||||
|
||||
var registration = new Registration
|
||||
{
|
||||
EventId = request.EventId,
|
||||
ParticipantId = userId.Value,
|
||||
Status = RegistrationStatus.Pending,
|
||||
Category = request.Category,
|
||||
EmergencyContact = request.EmergencyContact
|
||||
};
|
||||
|
||||
_context.Registrations.Add(registration);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Load related data
|
||||
await _context.Entry(registration)
|
||||
.Reference(r => r.Event)
|
||||
.LoadAsync();
|
||||
await _context.Entry(registration)
|
||||
.Reference(r => r.Participant)
|
||||
.LoadAsync();
|
||||
await _context.Entry(registration)
|
||||
.Collection(r => r.Payments)
|
||||
.LoadAsync();
|
||||
|
||||
return CreatedAtAction(
|
||||
nameof(GetRegistration),
|
||||
new { id = registration.Id },
|
||||
MapToRegistrationDto(registration));
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<RegistrationDto>> GetRegistration(Guid id)
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
if (userId == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var registration = await _context.Registrations
|
||||
.Include(r => r.Event)
|
||||
.Include(r => r.Participant)
|
||||
.Include(r => r.Payments)
|
||||
.FirstOrDefaultAsync(r => r.Id == id);
|
||||
|
||||
if (registration == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Only participant or event organizer can view
|
||||
var isOrganizer = await _context.Events
|
||||
.AnyAsync(e => e.Id == registration.EventId && e.OrganizerId == userId.Value);
|
||||
|
||||
if (registration.ParticipantId != userId.Value && !isOrganizer)
|
||||
{
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
return Ok(MapToRegistrationDto(registration));
|
||||
}
|
||||
|
||||
[HttpGet("my-registrations")]
|
||||
public async Task<ActionResult<IEnumerable<RegistrationDto>>> GetMyRegistrations()
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
if (userId == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var registrations = await _context.Registrations
|
||||
.Include(r => r.Event)
|
||||
.Include(r => r.Participant)
|
||||
.Include(r => r.Payments)
|
||||
.Where(r => r.ParticipantId == userId.Value)
|
||||
.OrderBy(r => r.Event.EventDate)
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(registrations.Select(MapToRegistrationDto));
|
||||
}
|
||||
|
||||
[HttpGet("event/{eventId}")]
|
||||
[Authorize(Roles = "Organizer")]
|
||||
public async Task<ActionResult<IEnumerable<RegistrationDto>>> GetEventRegistrations(Guid eventId)
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
if (userId == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
// Verify user is organizer of this event
|
||||
var eventEntity = await _context.Events
|
||||
.FirstOrDefaultAsync(e => e.Id == eventId && e.OrganizerId == userId.Value);
|
||||
|
||||
if (eventEntity == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var registrations = await _context.Registrations
|
||||
.Include(r => r.Event)
|
||||
.Include(r => r.Participant)
|
||||
.Include(r => r.Payments)
|
||||
.Where(r => r.EventId == eventId)
|
||||
.OrderBy(r => r.CreatedAt)
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(registrations.Select(MapToRegistrationDto));
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult<RegistrationDto>> UpdateRegistration(Guid id, UpdateRegistrationRequest request)
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
if (userId == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var registration = await _context.Registrations
|
||||
.Include(r => r.Event)
|
||||
.FirstOrDefaultAsync(r => r.Id == id);
|
||||
|
||||
if (registration == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Participants can only update their own registrations
|
||||
// Organizers can update any registration for their events
|
||||
var isOrganizer = registration.Event.OrganizerId == userId.Value;
|
||||
|
||||
if (registration.ParticipantId != userId.Value && !isOrganizer)
|
||||
{
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
// Update fields
|
||||
if (request.Status.HasValue && isOrganizer)
|
||||
{
|
||||
registration.Status = request.Status.Value;
|
||||
}
|
||||
|
||||
if (request.Category != null)
|
||||
{
|
||||
registration.Category = request.Category;
|
||||
}
|
||||
|
||||
if (request.EmergencyContact != null)
|
||||
{
|
||||
registration.EmergencyContact = request.EmergencyContact;
|
||||
}
|
||||
|
||||
registration.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Reload related data
|
||||
await _context.Entry(registration)
|
||||
.Reference(r => r.Participant)
|
||||
.LoadAsync();
|
||||
await _context.Entry(registration)
|
||||
.Collection(r => r.Payments)
|
||||
.LoadAsync();
|
||||
|
||||
return Ok(MapToRegistrationDto(registration));
|
||||
}
|
||||
|
||||
[HttpPost("{id}/cancel")]
|
||||
public async Task<ActionResult<RegistrationDto>> CancelRegistration(Guid id)
|
||||
{
|
||||
var userId = GetCurrentUserId();
|
||||
if (userId == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var registration = await _context.Registrations
|
||||
.Include(r => r.Event)
|
||||
.Include(r => r.Participant)
|
||||
.Include(r => r.Payments)
|
||||
.FirstOrDefaultAsync(r => r.Id == id);
|
||||
|
||||
if (registration == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Only participant or event organizer can cancel
|
||||
var isOrganizer = registration.Event.OrganizerId == userId.Value;
|
||||
|
||||
if (registration.ParticipantId != userId.Value && !isOrganizer)
|
||||
{
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
registration.Status = RegistrationStatus.Cancelled;
|
||||
registration.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(MapToRegistrationDto(registration));
|
||||
}
|
||||
|
||||
private Guid? GetCurrentUserId()
|
||||
{
|
||||
var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
if (Guid.TryParse(userIdClaim, out var userId))
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static RegistrationDto MapToRegistrationDto(Registration registration)
|
||||
{
|
||||
var totalPaid = registration.Payments?.Sum(p => p.Amount) ?? 0;
|
||||
|
||||
return new RegistrationDto
|
||||
{
|
||||
Id = registration.Id,
|
||||
EventId = registration.EventId,
|
||||
EventName = registration.Event?.Name ?? string.Empty,
|
||||
EventDate = registration.Event?.EventDate ?? DateTime.MinValue,
|
||||
ParticipantId = registration.ParticipantId,
|
||||
ParticipantName = registration.Participant?.Name ?? string.Empty,
|
||||
ParticipantEmail = registration.Participant?.Email ?? string.Empty,
|
||||
Status = registration.Status.ToString(),
|
||||
Category = registration.Category,
|
||||
EmergencyContact = registration.EmergencyContact,
|
||||
CreatedAt = registration.CreatedAt,
|
||||
UpdatedAt = registration.UpdatedAt,
|
||||
TotalPaid = totalPaid,
|
||||
AmountDue = 0 // Will be calculated based on event fee if needed
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user