Add assigned column to task list with name labels, fix user_name null
- TaskRow: Replace tiny avatar-only display with proper assigned column showing avatar + name (single assignee) or avatar + "N people" (multi). Hidden on mobile, right-aligned, 96px width matching other columns. - Load options: Chain selectinload(ProjectTaskAssignment.user) so the user relationship is available for serialization. - TaskAssignmentResponse: Add model_validator to resolve user_name from eagerly loaded user relationship (same pattern as TaskCommentResponse). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f42175b3fe
commit
990c660fbf
@ -44,8 +44,8 @@ def _project_load_options():
|
|||||||
selectinload(Project.tasks).selectinload(ProjectTask.comments).selectinload(TaskComment.user),
|
selectinload(Project.tasks).selectinload(ProjectTask.comments).selectinload(TaskComment.user),
|
||||||
selectinload(Project.tasks).selectinload(ProjectTask.subtasks).selectinload(ProjectTask.comments).selectinload(TaskComment.user),
|
selectinload(Project.tasks).selectinload(ProjectTask.subtasks).selectinload(ProjectTask.comments).selectinload(TaskComment.user),
|
||||||
selectinload(Project.tasks).selectinload(ProjectTask.subtasks).selectinload(ProjectTask.subtasks),
|
selectinload(Project.tasks).selectinload(ProjectTask.subtasks).selectinload(ProjectTask.subtasks),
|
||||||
selectinload(Project.tasks).selectinload(ProjectTask.assignments),
|
selectinload(Project.tasks).selectinload(ProjectTask.assignments).selectinload(ProjectTaskAssignment.user),
|
||||||
selectinload(Project.tasks).selectinload(ProjectTask.subtasks).selectinload(ProjectTask.assignments),
|
selectinload(Project.tasks).selectinload(ProjectTask.subtasks).selectinload(ProjectTask.assignments).selectinload(ProjectTaskAssignment.user),
|
||||||
selectinload(Project.members),
|
selectinload(Project.members),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -56,8 +56,8 @@ def _task_load_options():
|
|||||||
selectinload(ProjectTask.comments).selectinload(TaskComment.user),
|
selectinload(ProjectTask.comments).selectinload(TaskComment.user),
|
||||||
selectinload(ProjectTask.subtasks).selectinload(ProjectTask.comments),
|
selectinload(ProjectTask.subtasks).selectinload(ProjectTask.comments),
|
||||||
selectinload(ProjectTask.subtasks).selectinload(ProjectTask.subtasks),
|
selectinload(ProjectTask.subtasks).selectinload(ProjectTask.subtasks),
|
||||||
selectinload(ProjectTask.assignments),
|
selectinload(ProjectTask.assignments).selectinload(ProjectTaskAssignment.user),
|
||||||
selectinload(ProjectTask.subtasks).selectinload(ProjectTask.assignments),
|
selectinload(ProjectTask.subtasks).selectinload(ProjectTask.assignments).selectinload(ProjectTaskAssignment.user),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
@ -17,3 +17,19 @@ class TaskAssignmentResponse(BaseModel):
|
|||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
@model_validator(mode="before")
|
||||||
|
@classmethod
|
||||||
|
def resolve_user_name(cls, data): # type: ignore[override]
|
||||||
|
"""Populate user_name from eagerly loaded user relationship."""
|
||||||
|
if hasattr(data, "user") and data.user is not None:
|
||||||
|
if not getattr(data, "user_name", None):
|
||||||
|
data = dict(
|
||||||
|
id=data.id,
|
||||||
|
task_id=data.task_id,
|
||||||
|
user_id=data.user_id,
|
||||||
|
assigned_by=data.assigned_by,
|
||||||
|
user_name=data.user.username,
|
||||||
|
created_at=data.created_at,
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|||||||
@ -135,12 +135,21 @@ export default function TaskRow({
|
|||||||
{hasSubtasks ? `${completedSubtasks}/${task.subtasks.length}` : '—'}
|
{hasSubtasks ? `${completedSubtasks}/${task.subtasks.length}` : '—'}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Assignee avatars */}
|
{/* Assigned column */}
|
||||||
{task.assignments && task.assignments.length > 0 && (
|
<span className="hidden sm:flex items-center gap-1.5 shrink-0 w-24 justify-end">
|
||||||
<span className="hidden sm:flex shrink-0">
|
{task.assignments && task.assignments.length > 0 ? (
|
||||||
<AssigneeAvatars assignments={task.assignments} max={3} />
|
<>
|
||||||
</span>
|
<AssigneeAvatars assignments={task.assignments} max={2} />
|
||||||
)}
|
<span className="text-[11px] text-muted-foreground truncate max-w-[60px]">
|
||||||
|
{task.assignments.length === 1
|
||||||
|
? (task.assignments[0].user_name ?? 'Unknown')
|
||||||
|
: `${task.assignments.length} people`}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span className="text-[11px] text-transparent">—</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
|
||||||
{/* Mobile-only: compact priority dot + overdue indicator */}
|
{/* Mobile-only: compact priority dot + overdue indicator */}
|
||||||
<div className="flex items-center gap-1.5 sm:hidden shrink-0">
|
<div className="flex items-center gap-1.5 sm:hidden shrink-0">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user