using Socialize.Infrastructure.Security; using Socialize.Modules.ContentItems.Data; using Socialize.Modules.Notifications.Contracts; namespace Socialize.Modules.ContentItems.Handlers; public record UpdateContentItemStatusRequest(string Status); public class UpdateContentItemStatusRequestValidator : Validator { public UpdateContentItemStatusRequestValidator() { RuleFor(x => x.Status).NotEmpty().MaximumLength(64); } } public class UpdateContentItemStatusHandler( AppDbContext dbContext, AccessScopeService accessScopeService, INotificationEventWriter notificationEventWriter) : Endpoint { private static readonly HashSet AllowedStatuses = [ "Draft", "In internal review", "Changes requested internally", "Internal changes in progress", "Ready for client review", "In client review", "Changes requested by client", "Client changes in progress", "Approved", "Rejected", "Ready to publish", "Published", "Archived", ]; public override void Configure() { Post("/api/content-items/{id}/status"); Options(o => o.WithTags("Content Items")); } public override async Task HandleAsync(UpdateContentItemStatusRequest request, CancellationToken ct) { Guid id = Route("id"); ContentItem? item = await dbContext.ContentItems.SingleOrDefaultAsync(candidate => candidate.Id == id, ct); if (item is null) { await SendNotFoundAsync(ct); return; } if (!accessScopeService.CanManageWorkspace(User, item.WorkspaceId)) { await SendForbiddenAsync(ct); return; } string normalizedStatus = request.Status.Trim(); if (!AllowedStatuses.Contains(normalizedStatus)) { AddError(request => request.Status, "The requested status is not valid."); await SendErrorsAsync(StatusCodes.Status400BadRequest, ct); return; } item.Status = normalizedStatus; await dbContext.SaveChangesAsync(ct); await notificationEventWriter.WriteAsync( new NotificationEventWriteModel( item.WorkspaceId, item.Id, "content-item.status.updated", "ContentItem", item.Id, $"Status changed to {item.Status} for {item.Title}.", User.GetUserId(), User.GetEmail(), $$"""{"status":"{{item.Status}}"}"""), ct); ContentItemDetailDto dto = new( item.Id, item.WorkspaceId, item.ClientId, item.ProjectId, item.Title, item.PublicationMessage, item.PublicationTargets, item.Hashtags, item.Status, item.DueDate, item.CurrentRevisionLabel, item.CurrentRevisionNumber, item.CreatedAt); await SendOkAsync(dto, ct); } }