303 lines
9.1 KiB
C#
303 lines
9.1 KiB
C#
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
|
|
};
|
|
}
|
|
} |