feat: Enrich DTOs and UI to display member names instead of UUIDs for task assignees, creators, and shift signups.

This commit is contained in:
WorkClub Automation
2026-03-18 14:15:33 +01:00
parent 65fea5d48b
commit da70cf4b13
17 changed files with 453 additions and 157 deletions
+139 -93
View File
@@ -30,67 +30,89 @@ public class TaskService
}
}
var total = await query.CountAsync();
var total = await query.CountAsync();
var items = await query
.OrderBy(w => w.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
var items = await query
.OrderBy(w => w.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
Guid? memberId = null;
if (currentExternalUserId != null)
{
var tenantId = _tenantProvider.GetTenantId();
memberId = await _context.Members
.Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
.Select(m => m.Id)
.FirstOrDefaultAsync();
}
// Get current member ID for IsAssignedToMe check
Guid? currentMemberId = null;
if (currentExternalUserId != null)
{
var tenantId = _tenantProvider.GetTenantId();
currentMemberId = await _context.Members
.Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
.Select(m => m.Id)
.FirstOrDefaultAsync();
}
var itemDtos = items.Select(w => new TaskListItemDto(
w.Id,
w.Title,
w.Status.ToString(),
w.AssigneeId,
w.CreatedAt,
memberId != null && w.AssigneeId == memberId
)).ToList();
// Get all assignee IDs to fetch their names in bulk
var assigneeIds = items.Where(w => w.AssigneeId.HasValue).Select(w => w.AssigneeId!.Value).Distinct().ToList();
var assigneeNames = await _context.Members
.Where(m => assigneeIds.Contains(m.Id))
.Select(m => new { m.Id, m.DisplayName })
.ToDictionaryAsync(m => m.Id, m => m.DisplayName);
return new TaskListDto(itemDtos, total, page, pageSize);
}
var itemDtos = items.Select(w => new TaskListItemDto(
w.Id,
w.Title,
w.Status.ToString(),
w.AssigneeId,
w.AssigneeId.HasValue && assigneeNames.TryGetValue(w.AssigneeId.Value, out var name) ? name : null,
w.CreatedAt,
currentMemberId != null && w.AssigneeId == currentMemberId
)).ToList();
public async Task<TaskDetailDto?> GetTaskByIdAsync(Guid id, string? currentExternalUserId = null)
{
var workItem = await _context.WorkItems.FindAsync(id);
return new TaskListDto(itemDtos, total, page, pageSize);
}
if (workItem == null)
return null;
public async Task<TaskDetailDto?> GetTaskByIdAsync(Guid id, string? currentExternalUserId = null)
{
var workItem = await _context.WorkItems.FindAsync(id);
Guid? memberId = null;
if (currentExternalUserId != null)
{
var tenantId = _tenantProvider.GetTenantId();
memberId = await _context.Members
.Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
.Select(m => m.Id)
.FirstOrDefaultAsync();
}
if (workItem == null)
return null;
return new TaskDetailDto(
workItem.Id,
workItem.Title,
workItem.Description,
workItem.Status.ToString(),
workItem.AssigneeId,
workItem.CreatedById,
workItem.ClubId,
workItem.DueDate,
workItem.CreatedAt,
workItem.UpdatedAt,
memberId != null && workItem.AssigneeId == memberId
);
}
// Get current member ID for IsAssignedToMe check
Guid? currentMemberId = null;
if (currentExternalUserId != null)
{
var tenantId = _tenantProvider.GetTenantId();
currentMemberId = await _context.Members
.Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
.Select(m => m.Id)
.FirstOrDefaultAsync();
}
// Fetch assignee and creator names
var memberIds = new List<Guid>();
if (workItem.AssigneeId.HasValue) memberIds.Add(workItem.AssigneeId.Value);
memberIds.Add(workItem.CreatedById);
var memberNames = await _context.Members
.Where(m => memberIds.Contains(m.Id))
.Select(m => new { m.Id, m.DisplayName })
.ToDictionaryAsync(m => m.Id, m => m.DisplayName);
return new TaskDetailDto(
workItem.Id,
workItem.Title,
workItem.Description,
workItem.Status.ToString(),
workItem.AssigneeId,
workItem.AssigneeId.HasValue && memberNames.TryGetValue(workItem.AssigneeId.Value, out var assigneeName) ? assigneeName : null,
workItem.CreatedById,
memberNames.TryGetValue(workItem.CreatedById, out var createdByName) ? createdByName : null,
workItem.ClubId,
workItem.DueDate,
workItem.CreatedAt,
workItem.UpdatedAt,
currentMemberId != null && workItem.AssigneeId == currentMemberId
);
}
public async Task<(TaskDetailDto? task, string? error)> CreateTaskAsync(CreateTaskRequest request, Guid createdById)
{
@@ -111,24 +133,35 @@ public class TaskService
UpdatedAt = DateTimeOffset.UtcNow
};
_context.WorkItems.Add(workItem);
await _context.SaveChangesAsync();
_context.WorkItems.Add(workItem);
await _context.SaveChangesAsync();
var dto = new TaskDetailDto(
workItem.Id,
workItem.Title,
workItem.Description,
workItem.Status.ToString(),
workItem.AssigneeId,
workItem.CreatedById,
workItem.ClubId,
workItem.DueDate,
workItem.CreatedAt,
workItem.UpdatedAt,
false
);
// Fetch creator and assignee names
var memberIds = new List<Guid> { createdById };
if (workItem.AssigneeId.HasValue) memberIds.Add(workItem.AssigneeId.Value);
return (dto, null);
var memberNames = await _context.Members
.Where(m => memberIds.Contains(m.Id))
.Select(m => new { m.Id, m.DisplayName })
.ToDictionaryAsync(m => m.Id, m => m.DisplayName);
var dto = new TaskDetailDto(
workItem.Id,
workItem.Title,
workItem.Description,
workItem.Status.ToString(),
workItem.AssigneeId,
workItem.AssigneeId.HasValue && memberNames.TryGetValue(workItem.AssigneeId.Value, out var assigneeName) ? assigneeName : null,
workItem.CreatedById,
memberNames.TryGetValue(workItem.CreatedById, out var createdByName) ? createdByName : null,
workItem.ClubId,
workItem.DueDate,
workItem.CreatedAt,
workItem.UpdatedAt,
false
);
return (dto, null);
}
public async Task<(TaskDetailDto? task, string? error, bool isConflict)> UpdateTaskAsync(Guid id, UpdateTaskRequest request, string? currentExternalUserId = null)
@@ -176,32 +209,45 @@ public class TaskService
return (null, "Task was modified by another user. Please refresh and try again.", true);
}
Guid? memberId = null;
if (currentExternalUserId != null)
{
var tenantId = _tenantProvider.GetTenantId();
memberId = await _context.Members
.Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
.Select(m => m.Id)
.FirstOrDefaultAsync();
}
// Get current member ID for IsAssignedToMe check
Guid? currentMemberId = null;
if (currentExternalUserId != null)
{
var tenantId = _tenantProvider.GetTenantId();
currentMemberId = await _context.Members
.Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
.Select(m => m.Id)
.FirstOrDefaultAsync();
}
var dto = new TaskDetailDto(
workItem.Id,
workItem.Title,
workItem.Description,
workItem.Status.ToString(),
workItem.AssigneeId,
workItem.CreatedById,
workItem.ClubId,
workItem.DueDate,
workItem.CreatedAt,
workItem.UpdatedAt,
memberId != null && workItem.AssigneeId == memberId
);
// Fetch assignee and creator names
var memberIds = new List<Guid>();
if (workItem.AssigneeId.HasValue) memberIds.Add(workItem.AssigneeId.Value);
memberIds.Add(workItem.CreatedById);
return (dto, null, false);
}
var memberNames = await _context.Members
.Where(m => memberIds.Contains(m.Id))
.Select(m => new { m.Id, m.DisplayName })
.ToDictionaryAsync(m => m.Id, m => m.DisplayName);
var dto = new TaskDetailDto(
workItem.Id,
workItem.Title,
workItem.Description,
workItem.Status.ToString(),
workItem.AssigneeId,
workItem.AssigneeId.HasValue && memberNames.TryGetValue(workItem.AssigneeId.Value, out var assigneeName) ? assigneeName : null,
workItem.CreatedById,
memberNames.TryGetValue(workItem.CreatedById, out var createdByName) ? createdByName : null,
workItem.ClubId,
workItem.DueDate,
workItem.CreatedAt,
workItem.UpdatedAt,
currentMemberId != null && workItem.AssigneeId == currentMemberId
);
return (dto, null, false);
}
public async Task<bool> DeleteTaskAsync(Guid id)
{