252 lines
7.3 KiB
C#
252 lines
7.3 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 AnnouncementsController : ControllerBase
|
||
|
|
{
|
||
|
|
private readonly RacePlannerDbContext _context;
|
||
|
|
|
||
|
|
public AnnouncementsController(RacePlannerDbContext context)
|
||
|
|
{
|
||
|
|
_context = context;
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpPost]
|
||
|
|
[Authorize(Roles = "Organizer")]
|
||
|
|
public async Task<ActionResult<AnnouncementDto>> CreateAnnouncement(CreateAnnouncementRequest request)
|
||
|
|
{
|
||
|
|
var userId = GetCurrentUserId();
|
||
|
|
if (userId == null)
|
||
|
|
{
|
||
|
|
return Unauthorized();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify event exists and user is organizer
|
||
|
|
var eventEntity = await _context.Events
|
||
|
|
.FirstOrDefaultAsync(e => e.Id == request.EventId);
|
||
|
|
|
||
|
|
if (eventEntity == null)
|
||
|
|
{
|
||
|
|
return NotFound(new { error = "Event not found" });
|
||
|
|
}
|
||
|
|
|
||
|
|
if (eventEntity.OrganizerId != userId.Value)
|
||
|
|
{
|
||
|
|
return Forbid();
|
||
|
|
}
|
||
|
|
|
||
|
|
var announcement = new Announcement
|
||
|
|
{
|
||
|
|
EventId = request.EventId,
|
||
|
|
Title = request.Title,
|
||
|
|
Content = request.Content,
|
||
|
|
AuthorId = userId.Value,
|
||
|
|
IsPublished = true
|
||
|
|
};
|
||
|
|
|
||
|
|
_context.Announcements.Add(announcement);
|
||
|
|
await _context.SaveChangesAsync();
|
||
|
|
|
||
|
|
// Load related data
|
||
|
|
await _context.Entry(announcement)
|
||
|
|
.Reference(a => a.Event)
|
||
|
|
.LoadAsync();
|
||
|
|
await _context.Entry(announcement)
|
||
|
|
.Reference(a => a.Author)
|
||
|
|
.LoadAsync();
|
||
|
|
|
||
|
|
// TODO: Send notifications to registered participants (Task 7.7)
|
||
|
|
|
||
|
|
return CreatedAtAction(
|
||
|
|
nameof(GetAnnouncement),
|
||
|
|
new { id = announcement.Id },
|
||
|
|
MapToAnnouncementDto(announcement));
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpGet("{id}")]
|
||
|
|
public async Task<ActionResult<AnnouncementDto>> GetAnnouncement(Guid id)
|
||
|
|
{
|
||
|
|
var announcement = await _context.Announcements
|
||
|
|
.Include(a => a.Event)
|
||
|
|
.Include(a => a.Author)
|
||
|
|
.FirstOrDefaultAsync(a => a.Id == id);
|
||
|
|
|
||
|
|
if (announcement == null)
|
||
|
|
{
|
||
|
|
return NotFound();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Only show published announcements to participants
|
||
|
|
if (!announcement.IsPublished)
|
||
|
|
{
|
||
|
|
var userId = GetCurrentUserId();
|
||
|
|
if (userId == null || announcement.Event.OrganizerId != userId.Value)
|
||
|
|
{
|
||
|
|
return NotFound();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return Ok(MapToAnnouncementDto(announcement));
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpGet("event/{eventId}")]
|
||
|
|
public async Task<ActionResult<IEnumerable<AnnouncementDto>>> GetEventAnnouncements(Guid eventId)
|
||
|
|
{
|
||
|
|
var userId = GetCurrentUserId();
|
||
|
|
var isOrganizer = false;
|
||
|
|
|
||
|
|
if (userId != null)
|
||
|
|
{
|
||
|
|
isOrganizer = await _context.Events
|
||
|
|
.AnyAsync(e => e.Id == eventId && e.OrganizerId == userId.Value);
|
||
|
|
}
|
||
|
|
|
||
|
|
var query = _context.Announcements
|
||
|
|
.Include(a => a.Event)
|
||
|
|
.Include(a => a.Author)
|
||
|
|
.Where(a => a.EventId == eventId)
|
||
|
|
.AsQueryable();
|
||
|
|
|
||
|
|
// Only show published announcements to non-organizers
|
||
|
|
if (!isOrganizer)
|
||
|
|
{
|
||
|
|
query = query.Where(a => a.IsPublished);
|
||
|
|
}
|
||
|
|
|
||
|
|
var announcements = await query
|
||
|
|
.OrderByDescending(a => a.CreatedAt)
|
||
|
|
.ToListAsync();
|
||
|
|
|
||
|
|
return Ok(announcements.Select(MapToAnnouncementDto));
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpPut("{id}")]
|
||
|
|
[Authorize(Roles = "Organizer")]
|
||
|
|
public async Task<ActionResult<AnnouncementDto>> UpdateAnnouncement(Guid id, UpdateAnnouncementRequest request)
|
||
|
|
{
|
||
|
|
var userId = GetCurrentUserId();
|
||
|
|
if (userId == null)
|
||
|
|
{
|
||
|
|
return Unauthorized();
|
||
|
|
}
|
||
|
|
|
||
|
|
var announcement = await _context.Announcements
|
||
|
|
.Include(a => a.Event)
|
||
|
|
.Include(a => a.Author)
|
||
|
|
.FirstOrDefaultAsync(a => a.Id == id);
|
||
|
|
|
||
|
|
if (announcement == null)
|
||
|
|
{
|
||
|
|
return NotFound();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Only organizer can update
|
||
|
|
if (announcement.Event.OrganizerId != userId.Value)
|
||
|
|
{
|
||
|
|
return Forbid();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update fields
|
||
|
|
if (request.Title != null) announcement.Title = request.Title;
|
||
|
|
if (request.Content != null) announcement.Content = request.Content;
|
||
|
|
if (request.IsPublished.HasValue) announcement.IsPublished = request.IsPublished.Value;
|
||
|
|
|
||
|
|
announcement.UpdatedAt = DateTime.UtcNow;
|
||
|
|
|
||
|
|
await _context.SaveChangesAsync();
|
||
|
|
|
||
|
|
return Ok(MapToAnnouncementDto(announcement));
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpDelete("{id}")]
|
||
|
|
[Authorize(Roles = "Organizer")]
|
||
|
|
public async Task<IActionResult> DeleteAnnouncement(Guid id)
|
||
|
|
{
|
||
|
|
var userId = GetCurrentUserId();
|
||
|
|
if (userId == null)
|
||
|
|
{
|
||
|
|
return Unauthorized();
|
||
|
|
}
|
||
|
|
|
||
|
|
var announcement = await _context.Announcements
|
||
|
|
.Include(a => a.Event)
|
||
|
|
.FirstOrDefaultAsync(a => a.Id == id);
|
||
|
|
|
||
|
|
if (announcement == null)
|
||
|
|
{
|
||
|
|
return NotFound();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Only organizer can delete
|
||
|
|
if (announcement.Event.OrganizerId != userId.Value)
|
||
|
|
{
|
||
|
|
return Forbid();
|
||
|
|
}
|
||
|
|
|
||
|
|
_context.Announcements.Remove(announcement);
|
||
|
|
await _context.SaveChangesAsync();
|
||
|
|
|
||
|
|
return NoContent();
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpGet("my-announcements")]
|
||
|
|
public async Task<ActionResult<IEnumerable<AnnouncementDto>>> GetMyAnnouncements()
|
||
|
|
{
|
||
|
|
var userId = GetCurrentUserId();
|
||
|
|
if (userId == null)
|
||
|
|
{
|
||
|
|
return Unauthorized();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get user registrations
|
||
|
|
var userRegistrationEventIds = await _context.Registrations
|
||
|
|
.Where(r => r.ParticipantId == userId.Value && r.Status != RegistrationStatus.Cancelled)
|
||
|
|
.Select(r => r.EventId)
|
||
|
|
.ToListAsync();
|
||
|
|
|
||
|
|
// Get announcements for those events
|
||
|
|
var announcements = await _context.Announcements
|
||
|
|
.Include(a => a.Event)
|
||
|
|
.Include(a => a.Author)
|
||
|
|
.Where(a => userRegistrationEventIds.Contains(a.EventId) && a.IsPublished)
|
||
|
|
.OrderByDescending(a => a.CreatedAt)
|
||
|
|
.ToListAsync();
|
||
|
|
|
||
|
|
return Ok(announcements.Select(MapToAnnouncementDto));
|
||
|
|
}
|
||
|
|
|
||
|
|
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 AnnouncementDto MapToAnnouncementDto(Announcement announcement)
|
||
|
|
{
|
||
|
|
return new AnnouncementDto
|
||
|
|
{
|
||
|
|
Id = announcement.Id,
|
||
|
|
EventId = announcement.EventId,
|
||
|
|
EventName = announcement.Event?.Name ?? string.Empty,
|
||
|
|
Title = announcement.Title,
|
||
|
|
Content = announcement.Content,
|
||
|
|
AuthorId = announcement.AuthorId,
|
||
|
|
AuthorName = announcement.Author?.Name ?? string.Empty,
|
||
|
|
CreatedAt = announcement.CreatedAt,
|
||
|
|
UpdatedAt = announcement.UpdatedAt,
|
||
|
|
IsPublished = announcement.IsPublished
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|