feature/new-raceplanner-app #1

Merged
MasterMito merged 12 commits from feature/new-raceplanner-app into main 2026-04-03 21:47:59 +02:00
6 changed files with 995 additions and 0 deletions
Showing only changes of commit 30d573d1f8 - Show all commits
+242
View File
@@ -0,0 +1,242 @@
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 EventsController : ControllerBase
{
private readonly RacePlannerDbContext _context;
public EventsController(RacePlannerDbContext context)
{
_context = context;
}
[HttpPost]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<EventDto>> CreateEvent(CreateEventRequest request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var eventEntity = new Event
{
Name = request.Name,
Description = request.Description,
EventDate = request.EventDate,
Location = request.Location,
Category = request.Category,
Tags = request.Tags ?? new List<string>(),
MaxParticipants = request.MaxParticipants,
OrganizerId = userId.Value,
Status = EventStatus.Draft
};
_context.Events.Add(eventEntity);
await _context.SaveChangesAsync();
// Load organizer for response
await _context.Entry(eventEntity).Reference(e => e.Organizer).LoadAsync();
return CreatedAtAction(
nameof(GetEvent),
new { id = eventEntity.Id },
MapToEventDto(eventEntity));
}
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult<IEnumerable<EventDto>>> GetEvents(
[FromQuery] EventFilterRequest? filter = null)
{
var query = _context.Events
.Include(e => e.Organizer)
.Include(e => e.Registrations)
.AsQueryable();
// Apply filters
if (filter?.Category != null)
{
query = query.Where(e => e.Category == filter.Category);
}
if (filter?.Tags != null && filter.Tags.Any())
{
query = query.Where(e => e.Tags.Any(t => filter.Tags.Contains(t)));
}
if (filter?.FromDate != null)
{
query = query.Where(e => e.EventDate >= filter.FromDate);
}
if (filter?.ToDate != null)
{
query = query.Where(e => e.EventDate <= filter.ToDate);
}
if (filter?.Status != null && Enum.TryParse<EventStatus>(filter.Status, out var status))
{
query = query.Where(e => e.Status == status);
}
if (filter?.OrganizerId != null)
{
query = query.Where(e => e.OrganizerId == filter.OrganizerId);
}
// Default to showing published events only for anonymous users
if (!User.Identity?.IsAuthenticated ?? true)
{
query = query.Where(e => e.Status == EventStatus.Published);
}
var events = await query
.OrderBy(e => e.EventDate)
.ToListAsync();
return Ok(events.Select(MapToEventDto));
}
[HttpGet("{id}")]
[AllowAnonymous]
public async Task<ActionResult<EventDto>> GetEvent(Guid id)
{
var eventEntity = await _context.Events
.Include(e => e.Organizer)
.Include(e => e.Registrations)
.FirstOrDefaultAsync(e => e.Id == id);
if (eventEntity == null)
{
return NotFound();
}
// Draft events only visible to organizers
if (eventEntity.Status == EventStatus.Draft)
{
var userId = GetCurrentUserId();
if (userId == null || eventEntity.OrganizerId != userId)
{
return NotFound();
}
}
return Ok(MapToEventDto(eventEntity));
}
[HttpPut("{id}")]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<EventDto>> UpdateEvent(Guid id, UpdateEventRequest request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var eventEntity = await _context.Events
.Include(e => e.Organizer)
.FirstOrDefaultAsync(e => e.Id == id);
if (eventEntity == null)
{
return NotFound();
}
// Only organizer can update their event
if (eventEntity.OrganizerId != userId)
{
return Forbid();
}
// Update fields
if (request.Name != null) eventEntity.Name = request.Name;
if (request.Description != null) eventEntity.Description = request.Description;
if (request.EventDate != null) eventEntity.EventDate = request.EventDate.Value;
if (request.Location != null) eventEntity.Location = request.Location;
if (request.Status != null) eventEntity.Status = request.Status.Value;
if (request.Category != null) eventEntity.Category = request.Category;
if (request.Tags != null) eventEntity.Tags = request.Tags;
if (request.MaxParticipants != null) eventEntity.MaxParticipants = request.MaxParticipants;
eventEntity.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return Ok(MapToEventDto(eventEntity));
}
[HttpDelete("{id}")]
[Authorize(Roles = "Organizer")]
public async Task<IActionResult> DeleteEvent(Guid id)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var eventEntity = await _context.Events.FindAsync(id);
if (eventEntity == null)
{
return NotFound();
}
if (eventEntity.OrganizerId != userId)
{
return Forbid();
}
_context.Events.Remove(eventEntity);
await _context.SaveChangesAsync();
return NoContent();
}
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 EventDto MapToEventDto(Event eventEntity)
{
return new EventDto
{
Id = eventEntity.Id,
Name = eventEntity.Name,
Description = eventEntity.Description,
EventDate = eventEntity.EventDate,
Location = eventEntity.Location,
Status = eventEntity.Status.ToString(),
Category = eventEntity.Category,
Tags = eventEntity.Tags,
MaxParticipants = eventEntity.MaxParticipants,
CurrentRegistrations = eventEntity.Registrations?.Count ?? 0,
CreatedAt = eventEntity.CreatedAt,
UpdatedAt = eventEntity.UpdatedAt,
Organizer = new UserSummaryDto
{
Id = eventEntity.Organizer.Id,
Name = eventEntity.Organizer.Name,
Email = eventEntity.Organizer.Email
}
};
}
}
+258
View File
@@ -0,0 +1,258 @@
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 PaymentsController : ControllerBase
{
private readonly RacePlannerDbContext _context;
public PaymentsController(RacePlannerDbContext context)
{
_context = context;
}
[HttpPost]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<PaymentDto>> RecordPayment(CreatePaymentRequest request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
// Load registration with event
var registration = await _context.Registrations
.Include(r => r.Event)
.FirstOrDefaultAsync(r => r.Id == request.RegistrationId);
if (registration == null)
{
return NotFound(new { error = "Registration not found" });
}
// Verify user is organizer of the event
if (registration.Event.OrganizerId != userId.Value)
{
return Forbid();
}
// Check registration is not cancelled
if (registration.Status == RegistrationStatus.Cancelled)
{
return BadRequest(new { error = "Cannot record payment for cancelled registration" });
}
var payment = new Payment
{
RegistrationId = request.RegistrationId,
Amount = request.Amount,
Method = request.Method,
TransactionId = request.TransactionId,
Notes = request.Notes,
PaymentDate = request.PaymentDate ?? DateTime.UtcNow
};
_context.Payments.Add(payment);
await _context.SaveChangesAsync();
// Update registration status if fully paid (assuming a fixed fee amount would be known)
// For now, we'll just record the payment
return CreatedAtAction(
nameof(GetPayment),
new { id = payment.Id },
MapToPaymentDto(payment));
}
[HttpGet("{id}")]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<PaymentDto>> GetPayment(Guid id)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var payment = await _context.Payments
.Include(p => p.Registration)
.ThenInclude(r => r.Event)
.FirstOrDefaultAsync(p => p.Id == id);
if (payment == null)
{
return NotFound();
}
// Verify user is organizer
if (payment.Registration.Event.OrganizerId != userId.Value)
{
return Forbid();
}
return Ok(MapToPaymentDto(payment));
}
[HttpGet("registration/{registrationId}")]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<IEnumerable<PaymentDto>>> GetRegistrationPayments(Guid registrationId)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
// Verify organizer has access to this registration
var registration = await _context.Registrations
.Include(r => r.Event)
.FirstOrDefaultAsync(r => r.Id == registrationId);
if (registration == null)
{
return NotFound();
}
if (registration.Event.OrganizerId != userId.Value)
{
return Forbid();
}
var payments = await _context.Payments
.Where(p => p.RegistrationId == registrationId)
.OrderByDescending(p => p.PaymentDate)
.ToListAsync();
return Ok(payments.Select(MapToPaymentDto));
}
[HttpGet("registration/{registrationId}/status")]
[Authorize]
public async Task<ActionResult<object>> GetPaymentStatus(Guid registrationId)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
var registration = await _context.Registrations
.Include(r => r.Event)
.Include(r => r.Payments)
.FirstOrDefaultAsync(r => r.Id == registrationId);
if (registration == null)
{
return NotFound();
}
// Participant can view their own, organizer can view any
var isOrganizer = registration.Event.OrganizerId == userId.Value;
if (registration.ParticipantId != userId.Value && !isOrganizer)
{
return Forbid();
}
var totalPaid = registration.Payments.Sum(p => p.Amount);
var status = totalPaid > 0 ? "paid" : "unpaid";
return Ok(new
{
RegistrationId = registrationId,
TotalPaid = totalPaid,
PaymentCount = registration.Payments.Count,
Status = status,
Payments = registration.Payments.Select(MapToPaymentDto)
});
}
[HttpGet("event/{eventId}/report")]
[Authorize(Roles = "Organizer")]
public async Task<ActionResult<PaymentReportDto>> GetPaymentReport(Guid eventId)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return Unauthorized();
}
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.Participant)
.Include(r => r.Payments)
.Where(r => r.EventId == eventId && r.Status != RegistrationStatus.Cancelled)
.ToListAsync();
var totalCollected = registrations.Sum(r => r.Payments.Sum(p => p.Amount));
var totalRegistrations = registrations.Count;
var paidRegistrations = registrations.Count(r => r.Payments.Any());
var unpaidRegistrations = registrations.Count(r => !r.Payments.Any());
var report = new PaymentReportDto
{
EventId = eventId,
EventName = eventEntity.Name,
TotalCollected = totalCollected,
TotalPending = 0, // Would require event fee structure
TotalOutstanding = 0, // Would require event fee structure
TotalRegistrations = totalRegistrations,
PaidRegistrations = paidRegistrations,
PartialRegistrations = 0, // Would require partial payment logic
UnpaidRegistrations = unpaidRegistrations,
Registrations = registrations.Select(r => new RegistrationPaymentDto
{
RegistrationId = r.Id,
ParticipantName = r.Participant.Name,
Status = r.Status.ToString(),
TotalPaid = r.Payments.Sum(p => p.Amount),
AmountDue = 0, // Would require event fee structure
PaymentStatus = r.Payments.Any() ? "paid" : "unpaid",
Payments = r.Payments.Select(MapToPaymentDto).ToList()
}).ToList()
};
return Ok(report);
}
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 PaymentDto MapToPaymentDto(Payment payment)
{
return new PaymentDto
{
Id = payment.Id,
RegistrationId = payment.RegistrationId,
Amount = payment.Amount,
Method = payment.Method.ToString(),
TransactionId = payment.TransactionId,
Notes = payment.Notes,
PaymentDate = payment.PaymentDate,
CreatedAt = payment.CreatedAt
};
}
}
@@ -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
};
}
}
+85
View File
@@ -0,0 +1,85 @@
using System.ComponentModel.DataAnnotations;
using RacePlannerApi.Models;
namespace RacePlannerApi.DTOs;
public class CreateEventRequest
{
[Required]
[MaxLength(200)]
public string Name { get; set; } = string.Empty;
[MaxLength(2000)]
public string Description { get; set; } = string.Empty;
[Required]
public DateTime EventDate { get; set; }
[Required]
[MaxLength(500)]
public string Location { get; set; } = string.Empty;
[MaxLength(100)]
public string? Category { get; set; }
public List<string> Tags { get; set; } = new();
public int? MaxParticipants { get; set; }
}
public class UpdateEventRequest
{
[MaxLength(200)]
public string? Name { get; set; }
[MaxLength(2000)]
public string? Description { get; set; }
public DateTime? EventDate { get; set; }
[MaxLength(500)]
public string? Location { get; set; }
public EventStatus? Status { get; set; }
[MaxLength(100)]
public string? Category { get; set; }
public List<string>? Tags { get; set; }
public int? MaxParticipants { get; set; }
}
public class EventDto
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public DateTime EventDate { get; set; }
public string Location { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public string? Category { get; set; }
public List<string> Tags { get; set; } = new();
public int? MaxParticipants { get; set; }
public int CurrentRegistrations { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public UserSummaryDto Organizer { get; set; } = null!;
}
public class EventFilterRequest
{
public string? Category { get; set; }
public List<string>? Tags { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public string? Status { get; set; }
public Guid? OrganizerId { get; set; }
}
public class UserSummaryDto
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
+62
View File
@@ -0,0 +1,62 @@
using System.ComponentModel.DataAnnotations;
using RacePlannerApi.Models;
namespace RacePlannerApi.DTOs;
public class CreatePaymentRequest
{
[Required]
public Guid RegistrationId { get; set; }
[Required]
[Range(0.01, double.MaxValue)]
public decimal Amount { get; set; }
[Required]
public PaymentMethod Method { get; set; }
[MaxLength(100)]
public string? TransactionId { get; set; }
[MaxLength(500)]
public string? Notes { get; set; }
public DateTime? PaymentDate { get; set; }
}
public class PaymentDto
{
public Guid Id { get; set; }
public Guid RegistrationId { get; set; }
public decimal Amount { get; set; }
public string Method { get; set; } = string.Empty;
public string? TransactionId { get; set; }
public string? Notes { get; set; }
public DateTime PaymentDate { get; set; }
public DateTime CreatedAt { get; set; }
}
public class PaymentReportDto
{
public Guid EventId { get; set; }
public string EventName { get; set; } = string.Empty;
public decimal TotalCollected { get; set; }
public decimal TotalPending { get; set; }
public decimal TotalOutstanding { get; set; }
public int TotalRegistrations { get; set; }
public int PaidRegistrations { get; set; }
public int PartialRegistrations { get; set; }
public int UnpaidRegistrations { get; set; }
public List<RegistrationPaymentDto> Registrations { get; set; } = new();
}
public class RegistrationPaymentDto
{
public Guid RegistrationId { get; set; }
public string ParticipantName { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public decimal TotalPaid { get; set; }
public decimal AmountDue { get; set; }
public string PaymentStatus { get; set; } = string.Empty;
public List<PaymentDto> Payments { get; set; } = new();
}
+45
View File
@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
using RacePlannerApi.Models;
namespace RacePlannerApi.DTOs;
public class CreateRegistrationRequest
{
[Required]
public Guid EventId { get; set; }
[MaxLength(100)]
public string? Category { get; set; }
[MaxLength(200)]
public string? EmergencyContact { get; set; }
}
public class UpdateRegistrationRequest
{
public RegistrationStatus? Status { get; set; }
[MaxLength(100)]
public string? Category { get; set; }
[MaxLength(200)]
public string? EmergencyContact { get; set; }
}
public class RegistrationDto
{
public Guid Id { get; set; }
public Guid EventId { get; set; }
public string EventName { get; set; } = string.Empty;
public DateTime EventDate { get; set; }
public Guid ParticipantId { get; set; }
public string ParticipantName { get; set; } = string.Empty;
public string ParticipantEmail { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public string? Category { get; set; }
public string? EmergencyContact { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public decimal TotalPaid { get; set; }
public decimal AmountDue { get; set; }
}