Add editable status column and submission markers to the Gantt chart

Adds a second frozen "Task Status" column (EditableTaskStatus) next to
the task name, and small white dot markers on each bar showing
submission dates - backed by a new lightweight GET /tasks/submission-dates
endpoint (task_id + submitted_at only, to avoid an N+1 fetch across
potentially hundreds of scheduled tasks).

Restructures the chart's scroll handling from a single scrolling
container with sticky-positioned columns to two independently-scrolled
panes (frozen name/status columns, and the timeline), synced on
vertical scroll. This fixes several issues that came with the sticky
approach - transparency bleed-through on hover and on group-header
rows, row heights not matching between columns, and the horizontal
scrollbar spanning the frozen columns instead of just the timeline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 03:20:28 +08:00
parent 04c85be0f7
commit 7f260067a2
4 changed files with 266 additions and 103 deletions
+21 -1
View File
@@ -19,7 +19,7 @@ from schemas.task import (
TaskCreate, TaskUpdate, TaskResponse, TaskListResponse, TaskStatusUpdate, TaskAssignment,
ProductionNoteCreate, ProductionNoteUpdate, ProductionNoteResponse,
TaskAttachmentCreate, TaskAttachmentResponse,
SubmissionCreate, SubmissionUpdate, SubmissionResponse,
SubmissionCreate, SubmissionUpdate, SubmissionResponse, SubmissionDateInfo,
BulkStatusUpdate, BulkAssignment, BulkActionResult
)
from utils.auth import get_current_user_from_token, _get_user_from_db, require_role, require_permission, user_has_permission
@@ -682,6 +682,26 @@ async def bulk_assign_tasks(
)
@router.get("/submission-dates", response_model=List[SubmissionDateInfo])
async def get_submission_dates(
project_id: int = Query(..., description="Project ID to fetch submission dates for"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
Get a lightweight list of (task_id, submitted_at) for every non-deleted
submission on a project's tasks, for rendering submission markers (e.g.
on the Schedule Gantt chart) without fetching full submission payloads.
"""
submissions = db.query(Submission.task_id, Submission.submitted_at).join(Task).filter(
Task.project_id == project_id,
Task.deleted_at.is_(None),
Submission.deleted_at.is_(None)
).all()
return [SubmissionDateInfo(task_id=task_id, submitted_at=submitted_at) for task_id, submitted_at in submissions]
@router.get("/{task_id}", response_model=TaskResponse)
async def get_task(
task_id: int,
+9
View File
@@ -195,6 +195,15 @@ class SubmissionResponse(SubmissionBase):
from_attributes = True
class SubmissionDateInfo(BaseModel):
"""Minimal per-task submission date, for lightweight bulk lookups (e.g. Gantt markers)."""
task_id: int
submitted_at: datetime
class Config:
from_attributes = True
# Review schemas
class ReviewBase(BaseModel):
decision: ReviewDecision