Merge branch 'Feature/program-manager/priority'

# Conflicts:
#	ProgramManager/src/Application/GozareshgirProgramManager.Application/Modules/Projects/Queries/ProjectBoardList/ProjectBoardListResponse.cs
This commit is contained in:
2026-01-08 14:00:49 +03:30
10 changed files with 89 additions and 20 deletions

View File

@@ -10,7 +10,7 @@ public record AddTaskToPhaseCommand(
Guid PhaseId,
string Name,
string? Description = null,
TaskPriority Priority = TaskPriority.Medium,
ProjectTaskPriority Priority = ProjectTaskPriority.Medium,
int OrderIndex = 0,
DateTime? DueDate = null
) : IBaseCommand;

View File

@@ -6,35 +6,103 @@ using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
namespace GozareshgirProgramManager.Application.Modules.Projects.Commands.ChangeTaskPriority;
/// <summary>
/// Command to change a task priority.
/// </summary>
public record ChangeTaskPriorityCommand(Guid TaskId, TaskPriority Priority) : IBaseCommand;
public record ChangeTaskPriorityCommand(
Guid Id,
ProjectHierarchyLevel Level,
ProjectTaskPriority Priority
) : IBaseCommand;
public class ChangeTaskPriorityCommandHandler : IBaseCommandHandler<ChangeTaskPriorityCommand>
{
private readonly IProjectTaskRepository _taskRepository;
private readonly IProjectPhaseRepository _phaseRepository;
private readonly IProjectRepository _projectRepository;
private readonly IUnitOfWork _unitOfWork;
public ChangeTaskPriorityCommandHandler(IProjectTaskRepository taskRepository, IUnitOfWork unitOfWork)
public ChangeTaskPriorityCommandHandler(
IProjectTaskRepository taskRepository,
IProjectPhaseRepository phaseRepository,
IProjectRepository projectRepository,
IUnitOfWork unitOfWork)
{
_taskRepository = taskRepository;
_phaseRepository = phaseRepository;
_projectRepository = projectRepository;
_unitOfWork = unitOfWork;
}
public async Task<OperationResult> Handle(ChangeTaskPriorityCommand request, CancellationToken cancellationToken)
{
var task = await _taskRepository.GetByIdAsync(request.TaskId, cancellationToken);
switch (request.Level)
{
case ProjectHierarchyLevel.Task:
return await HandleTaskLevelAsync(request.Id, request.Priority, cancellationToken);
case ProjectHierarchyLevel.Phase:
return await HandlePhaseLevelAsync(request.Id, request.Priority, cancellationToken);
case ProjectHierarchyLevel.Project:
return await HandleProjectLevelAsync(request.Id, request.Priority, cancellationToken);
default:
return OperationResult.Failure("سطح نامعتبر است");
}
}
// Task-level priority update
private async Task<OperationResult> HandleTaskLevelAsync(Guid taskId, ProjectTaskPriority priority, CancellationToken ct)
{
var task = await _taskRepository.GetByIdAsync(taskId, ct);
if (task is null)
return OperationResult.NotFound("تسک یافت نشد");
// Idempotent: if already same priority, skip extra work
if (task.Priority != request.Priority)
if (task.Priority != priority)
{
task.SetPriority(request.Priority);
task.SetPriority(priority);
}
await _unitOfWork.SaveChangesAsync(cancellationToken);
await _unitOfWork.SaveChangesAsync(ct);
return OperationResult.Success();
}
// Phase-level bulk priority update
private async Task<OperationResult> HandlePhaseLevelAsync(Guid phaseId, ProjectTaskPriority priority, CancellationToken ct)
{
var phase = await _phaseRepository.GetWithTasksAsync(phaseId);
if (phase is null)
return OperationResult.NotFound("فاز یافت نشد");
var tasks = phase.Tasks?.ToList() ?? new List<Domain.ProjectAgg.Entities.ProjectTask>();
foreach (var t in tasks)
{
if (t.Priority != priority)
{
t.SetPriority(priority);
}
}
await _unitOfWork.SaveChangesAsync(ct);
return OperationResult.Success();
}
// Project-level bulk priority update across all phases
private async Task<OperationResult> HandleProjectLevelAsync(Guid projectId, ProjectTaskPriority priority, CancellationToken ct)
{
var project = await _projectRepository.GetWithFullHierarchyAsync(projectId);
if (project is null)
return OperationResult.NotFound("پروژه یافت نشد");
var phases = project.Phases?.ToList() ?? new List<Domain.ProjectAgg.Entities.ProjectPhase>();
foreach (var phase in phases)
{
var tasks = phase.Tasks?.ToList() ?? new List<Domain.ProjectAgg.Entities.ProjectTask>();
foreach (var t in tasks)
{
if (t.Priority != priority)
{
t.SetPriority(priority);
}
}
}
await _unitOfWork.SaveChangesAsync(ct);
return OperationResult.Success();
}
}

View File

@@ -15,7 +15,7 @@ public class GetTaskDto
// Task-specific fields
public TimeSpan SpentTime { get; init; }
public TimeSpan RemainingTime { get; init; }
public TaskPriority Priority { get; set; }
public ProjectTaskPriority Priority { get; set; }
public List<GetTaskSectionDto> Sections { get; init; }
}

View File

@@ -105,6 +105,7 @@ public class ProjectBoardListQueryHandler : IBaseQueryHandler<ProjectBoardListQu
ProjectName = x.Task.Phase.Project.Name,
TaskName = x.Task.Name,
SectionStatus = x.Status,
TaskPriority = x.Task.Priority,
Progress = new ProjectProgressDto()
{
CompleteSecond = x.FinalEstimatedHours.TotalSeconds,

View File

@@ -13,7 +13,7 @@ public class ProjectBoardListResponse
public string? AssignedUser { get; set; }
public string OriginalUser { get; set; }
public string SkillName { get; set; }
public ProjectTaskPriority TaskPriority { get; set; }
public Guid TaskId { get; set; }
}

View File

@@ -20,7 +20,7 @@ public class ProjectTask : ProjectHierarchyNode
{
PhaseId = phaseId;
_sections = new List<TaskSection>();
Priority = TaskPriority.Medium;
Priority = ProjectTaskPriority.Medium;
AddDomainEvent(new TaskCreatedEvent(Id, phaseId, name));
}
@@ -30,7 +30,7 @@ public class ProjectTask : ProjectHierarchyNode
// Task-specific properties
public Enums.TaskStatus Status { get; private set; } = Enums.TaskStatus.NotStarted;
public TaskPriority Priority { get; private set; }
public ProjectTaskPriority Priority { get; private set; }
public DateTime? StartDate { get; private set; }
public DateTime? EndDate { get; private set; }
public DateTime? DueDate { get; private set; }
@@ -119,7 +119,7 @@ public class ProjectTask : ProjectHierarchyNode
AddDomainEvent(new TaskStatusUpdatedEvent(Id, status));
}
public void SetPriority(TaskPriority priority)
public void SetPriority(ProjectTaskPriority priority)
{
Priority = priority;
AddDomainEvent(new TaskPriorityUpdatedEvent(Id, priority));

View File

@@ -3,7 +3,7 @@ namespace GozareshgirProgramManager.Domain.ProjectAgg.Enums;
/// <summary>
/// اولویت تسک
/// </summary>
public enum TaskPriority
public enum ProjectTaskPriority
{
/// <summary>
/// پایین

View File

@@ -78,7 +78,7 @@ public record TaskStatusUpdatedEvent(Guid TaskId, TaskStatus Status) : IDomainEv
public DateTime OccurredOn { get; init; } = DateTime.Now;
}
public record TaskPriorityUpdatedEvent(Guid TaskId, TaskPriority Priority) : IDomainEvent
public record TaskPriorityUpdatedEvent(Guid TaskId, ProjectTaskPriority Priority) : IDomainEvent
{
public DateTime OccurredOn { get; init; } = DateTime.Now;
}

View File

@@ -36,7 +36,7 @@ public interface IProjectTaskRepository : IRepository<Guid, ProjectTask>
/// <summary>
/// Get tasks by priority
/// </summary>
Task<List<ProjectTask>> GetByPriorityAsync(ProjectAgg.Enums.TaskPriority priority);
Task<List<ProjectTask>> GetByPriorityAsync(ProjectAgg.Enums.ProjectTaskPriority priority);
/// <summary>
/// Get tasks assigned to user

View File

@@ -58,7 +58,7 @@ public class ProjectTaskRepository : RepositoryBase<Guid, ProjectTask>, IProject
.ToListAsync();
}
public Task<List<ProjectTask>> GetByPriorityAsync(TaskPriority priority)
public Task<List<ProjectTask>> GetByPriorityAsync(ProjectTaskPriority priority)
{
return _context.ProjectTasks
.Where(t => t.Priority == priority)