using FastEndpoints; using Microsoft.EntityFrameworkCore; using Socialize.Api.Data; using Socialize.Api.Infrastructure.Security; using Socialize.Api.Modules.Campaigns.Data; namespace Socialize.Api.Modules.Campaigns.Handlers; public record CreateCampaignRequest( Guid WorkspaceId, Guid ClientId, string Name, DateTimeOffset StartDate, DateTimeOffset EndDate, string? Description, string? Notes); public class CreateCampaignRequestValidator : Validator { public CreateCampaignRequestValidator() { RuleFor(x => x.WorkspaceId).NotEmpty(); RuleFor(x => x.ClientId).NotEmpty(); RuleFor(x => x.Name).NotEmpty().MaximumLength(256); RuleFor(x => x.StartDate).NotEmpty(); RuleFor(x => x.EndDate) .NotEmpty() .GreaterThanOrEqualTo(x => x.StartDate); RuleFor(x => x.Description).MaximumLength(4000); RuleFor(x => x.Notes).MaximumLength(4000); } } public class CreateCampaignHandler( AppDbContext dbContext, AccessScopeService accessScopeService) : Endpoint { public override void Configure() { Post("/api/campaigns"); Options(o => o.WithTags("Campaigns")); } public override async Task HandleAsync(CreateCampaignRequest request, CancellationToken ct) { if (!await accessScopeService.CanManageWorkspaceAsync(User, request.WorkspaceId, ct)) { await SendForbiddenAsync(ct); return; } bool workspaceExists = await dbContext.Workspaces .AnyAsync(workspace => workspace.Id == request.WorkspaceId, ct); if (!workspaceExists) { AddError(request => request.WorkspaceId, "The selected workspace does not exist."); await SendErrorsAsync(StatusCodes.Status400BadRequest, ct); return; } bool clientExists = await dbContext.Clients .AnyAsync( client => client.Id == request.ClientId && client.WorkspaceId == request.WorkspaceId, ct); if (!clientExists) { AddError(request => request.ClientId, "The selected client does not belong to the active workspace."); await SendErrorsAsync(StatusCodes.Status400BadRequest, ct); return; } string normalizedName = request.Name.Trim(); bool duplicateCampaign = await dbContext.Campaigns .AnyAsync( campaign => campaign.ClientId == request.ClientId && campaign.Name == normalizedName, ct); if (duplicateCampaign) { AddError(request => request.Name, "A campaign with this name already exists for the selected client."); await SendErrorsAsync(StatusCodes.Status409Conflict, ct); return; } Campaign campaign = new() { Id = Guid.NewGuid(), WorkspaceId = request.WorkspaceId, ClientId = request.ClientId, Name = normalizedName, Description = string.IsNullOrWhiteSpace(request.Description) ? null : request.Description.Trim(), Notes = string.IsNullOrWhiteSpace(request.Notes) ? null : request.Notes.Trim(), Status = "Planned", StartDate = request.StartDate, EndDate = request.EndDate, CreatedAt = DateTimeOffset.UtcNow, }; dbContext.Campaigns.Add(campaign); await dbContext.SaveChangesAsync(ct); CampaignDto dto = new( campaign.Id, campaign.WorkspaceId, campaign.ClientId, campaign.Name, campaign.Description, campaign.Notes, campaign.Status, campaign.StartDate, campaign.EndDate); await SendAsync(dto, StatusCodes.Status201Created, ct); } }