using AutoMapper; using MediatR; using mws.backend.dotnet.application.Audit; using mws.backend.dotnet.application.Common; using mws.backend.dotnet.domain.Tasks; using TaskPriority = mws.backend.dotnet.domain.Tasks.TaskPriority; using TaskStatus = mws.backend.dotnet.domain.Tasks.TaskStatus; namespace mws.backend.dotnet.application.Tasks; public record CreateTaskCommand(Guid UserId, Guid ProjectId, CreateTaskRequest Request) : IRequest, IAuditableRequest { public string Action => "Task.Create"; public string EntityType => "Task"; public string? EntityName => Request.Title; } public class CreateTaskHandler(IUnitOfWork uow, IMapper mapper) : IRequestHandler { public async Task Handle(CreateTaskCommand command, CancellationToken ct) { await TaskAccess.EnsureMemberAccessAsync(uow, command.UserId, command.ProjectId, ct); var request = command.Request; if (string.IsNullOrWhiteSpace(request.Title)) { throw new BadRequestException("Task title is required"); } if (request.AssigneeId.HasValue && !await uow.Projects.IsMemberAsync(command.ProjectId, request.AssigneeId.Value, ct)) { throw new BadRequestException("Assignee must be a member of the project"); } var now = DateTime.UtcNow; var task = new TaskItem { Id = Guid.NewGuid(), ProjectId = command.ProjectId, Title = request.Title.Trim(), Description = request.Description, Status = request.Status ?? TaskStatus.Todo, Priority = request.Priority ?? TaskPriority.Medium, AssigneeId = request.AssigneeId, DueDate = request.DueDate, CreatedBy = command.UserId, CreatedAt = now, UpdatedAt = now, }; uow.Tasks.Add(task); await uow.SaveChangesAsync(ct); return await TaskAccess.GetDtoAsync(uow, mapper, task.Id, ct); } }