Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 57 additions & 92 deletions SEBS-API/Controllers/Booking/AdminBookingController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,112 +76,77 @@ public async Task<ActionResult<BookingResponseDTO>> GetBookingById(int id)
[HttpPatch, Route("{id}/status")]
public async Task<ActionResult<BookingResponseDTO>> UpdateBookingStatus(int id, [FromBody] BookingStatusUpdateDTO statusDto)
{
try
{
var booking = await Context.Bookings
.Include(b => b.Event)
.ThenInclude(e => e!.EventServices)
.FirstOrDefaultAsync(b => b.BookingID == id);

if (booking == null)
{
return NotFound("Booking not found");
}

if (!IsValidStatusTransition(booking.Status, statusDto.Status))
{
return BadRequest($"Cannot change status from {booking.Status} to {statusDto.Status}");
}
var booking = await GetBookingWithIncludesById(id);

if (booking == null)
return NotFound("Booking not found");

// Set admin approval info for status changes that require admin action
if (statusDto.Status == BookingStatus.Confirmed ||
statusDto.Status == BookingStatus.Cancelled ||
statusDto.Status == BookingStatus.Declined ||
statusDto.Status == BookingStatus.Completed ||
statusDto.Status == BookingStatus.NoShow)
{
var adminUserId = GetCurrentUserId();
if (adminUserId == null)
return Unauthorized("Invalid admin token");
if (!IsValidStatusTransition(booking.Status, statusDto.Status))
return BadRequest($"Cannot change status from {booking.Status} to {statusDto.Status}");

booking.ApprovedByUserId = adminUserId.Value;
booking.ApprovedDate = DateTime.UtcNow;
// Set admin approval info
var adminUserId = GetCurrentUserId();
if (adminUserId == null)
return Unauthorized("Invalid admin token");

// Update event status if needed
if (booking.Event != null)
{
booking.Event.Status = statusDto.Status switch
{
BookingStatus.Confirmed => EventStatus.Scheduled,
BookingStatus.Cancelled => EventStatus.Cancelled,
BookingStatus.Completed => EventStatus.Completed,
BookingStatus.NoShow => EventStatus.NoShow,
BookingStatus.Declined => EventStatus.Cancelled,
_ => booking.Event.Status
};
booking.Event.UpdatedAt = DateTime.UtcNow;
}
}

booking.Status = statusDto.Status;
booking.ApprovedByUserId = adminUserId.Value;
booking.ApprovedDate = DateTime.UtcNow;
booking.Status = statusDto.Status;

if (!await SaveChangesWithValidation())
// Update event status if needed
if (booking.Event != null)
{
booking.Event.Status = statusDto.Status switch
{
return BadRequest("Failed to update booking status. Please try again later.");
}
BookingStatus.Confirmed => EventStatus.Scheduled,
BookingStatus.Cancelled => EventStatus.Cancelled,
BookingStatus.Declined => EventStatus.Cancelled,
BookingStatus.Completed => EventStatus.Completed,
BookingStatus.NoShow => EventStatus.NoShow,
_ => booking.Event.Status
};
booking.Event.UpdatedAt = DateTime.UtcNow;
}

if (!await SaveChangesWithValidation())
return BadRequest("Failed to update booking status");

// Send emails (don't let email failures break the status update)
// Send email notification (don't fail if email fails)
_ = Task.Run(async () =>
{
try
{
var adminUserId2 = GetCurrentUserId()!.Value;
var adminUser = await Context.Users.FirstAsync(u => u.UserId == adminUserId2);
var customer = new User { Name = booking.CustomerName, Email = booking.CustomerEmail };

// Send appropriate email based on status
switch (statusDto.Status)
{
case BookingStatus.Confirmed:
var approvalData = EmailTemplateMapper.ToApprovalTemplate(booking, customer, booking.Event!, adminUser);
await EmailService.SendBookingApprovalAsync(customer.Email, customer.Name, approvalData);
break;

case BookingStatus.Cancelled:
case BookingStatus.Declined:
var rejectionData = EmailTemplateMapper.ToRejectionTemplate(
booking, customer, booking.Event!, adminUser,
statusDto.Notes ?? $"Booking {statusDto.Status.ToString().ToLower()} by admin"
);
await EmailService.SendBookingRejectionAsync(customer.Email, customer.Name, rejectionData);
break;

case BookingStatus.Completed:
case BookingStatus.NoShow:
// For completion/no-show status changes, send confirmation template
if (booking.Event?.EventServices != null)
{
var services = await Context.Services
.Where(s => booking.Event.EventServices.Select(es => es.ServiceID).Contains(s.ServiceID))
.ToListAsync();
var confirmationData = EmailTemplateMapper.ToConfirmationTemplate(booking, customer, booking.Event!, services);
await EmailService.SendBookingConfirmationAsync(customer.Email, customer.Name, confirmationData);
}
break;
}
await SendStatusUpdateEmail(booking, statusDto);
}
catch (Exception emailEx)
catch (Exception ex)
{
// Log email error but don't fail the status update
Console.WriteLine($"Email sending failed: {emailEx.Message}");
Console.WriteLine($"Email sending failed: {ex.Message}");
}
});

// Reload booking with all related data
booking = await GetBookingWithIncludesById(id);
return Ok(new BookingResponseDTO(booking));
}

return Ok(new BookingResponseDTO(booking!));
}
catch (Exception ex)
private async Task SendStatusUpdateEmail(Booking booking, BookingStatusUpdateDTO statusDto)
{
var adminUser = await Context.Users.FirstAsync(u => u.UserId == booking.ApprovedByUserId);
var customer = new User { Name = booking.CustomerName, Email = booking.CustomerEmail };

switch (statusDto.Status)
{
return BadRequest($"Error updating booking status: {ex.Message}");
case BookingStatus.Confirmed:
var approvalData = EmailTemplateMapper.ToApprovalTemplate(booking, customer, booking.Event!, adminUser);
await EmailService.SendBookingApprovalAsync(customer.Email, customer.Name, approvalData);
break;

case BookingStatus.Cancelled:
case BookingStatus.Declined:
var rejectionData = EmailTemplateMapper.ToRejectionTemplate(
booking, customer, booking.Event!, adminUser,
statusDto.Notes ?? $"Booking {statusDto.Status.ToString().ToLower()} by admin"
);
await EmailService.SendBookingRejectionAsync(customer.Email, customer.Name, rejectionData);
break;
}
}

Expand Down
5 changes: 3 additions & 2 deletions SEBS-API/Controllers/PublicController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ public PublicController(SEBSDbContext db, S3Service s3)
/// Get all available services for display on the website.
/// </summary>
[HttpGet, Route("services")]
public async Task<ActionResult<List<ServiceDTO>>> GetServices()
public async Task<ActionResult<List<ServiceWithImageDTO>>> GetServices()
{
var services = await _db.Services
.Include(s => s.Image)
.AsNoTracking()
.OrderBy(s => s.Name)
.ToListAsync();

var result = services.Select(s => new ServiceDTO(s)).ToList();
var result = services.Select(s => new ServiceWithImageDTO(s)).ToList();
return Ok(result);
}

Expand Down
Loading