Files
raceplanner/backend/Controllers/RegistrationsController.cs
2026-04-03 21:06:38 +02:00

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
};
}
}