Skip to content

Tasks

Tasks are the core work item in Planner. They support a full lifecycle with status workflow, priorities, assignments, due dates, subtasks, threaded comments, an audit history log, and a dedicated task chat.

Model

Task
├── title, description
├── status: todo | in_progress | in_review | done | cancelled
├── priority: low | medium | high | urgent
├── assigneeId?, dueDate?
├── parentId? (subtasks)
├── projectId (required, cascade delete)
├── comments (threaded with replies)
├── history (audit log)
└── chat (get-or-create task conversation)

API Endpoints

Task CRUD (/projects/:projectId/tasks)

MethodPathDescription
GET/api/projects/:projectId/tasksList project tasks
POST/api/projects/:projectId/tasksCreate task
GET/api/projects/:projectId/tasks/:taskIdGet task with details
PATCH/api/projects/:projectId/tasks/:taskIdUpdate task
PATCH/api/projects/:projectId/tasks/:taskId/statusUpdate status only
DELETE/api/projects/:projectId/tasks/:taskIdDelete task

My Tasks

MethodPathDescription
GET/api/tasks/myTasks assigned to current user (dashboard)

Team Task Board

MethodPathDescription
GET/api/teams/by-slug/:teamSlug/tasksTeam convenience endpoint

Task Comments (/projects/:projectId/tasks/:taskId/comments)

MethodPathDescription
GET/api/projects/:projectId/tasks/:taskId/commentsList comments (flat, with parentId for threading)
POST/api/projects/:projectId/tasks/:taskId/commentsCreate comment (optional parentId for reply)
PATCH/api/projects/:projectId/tasks/:taskId/comments/:commentIdEdit comment
DELETE/api/projects/:projectId/tasks/:taskId/comments/:commentIdDelete comment (blocked if has replies)

Task History (/projects/:projectId/tasks/:taskId/history)

MethodPathDescription
GET/api/projects/:projectId/tasks/:taskId/historyGet task audit log

Task Chat

MethodPathDescription
POST/api/chats/task/:taskIdGet or create task conversation

Attachments

MethodPathDescription
POST/api/projects/:projectId/tasks/:taskId/comments/attachmentsUpload comment attachments

Status Workflow

todo ──► in_progress ──► in_review ──► done
  │                        │
  └────────────────────────┘
       cancelled

Task History (Audit Log)

Every change is automatically logged:

ActionTriggerData
CREATEDTask creationWho created, when
UPDATEDField changeField name, old value, new value
STATUS_CHANGEDStatus updateOld status, new status
ASSIGNEDAssignee changeOld assignee, new assignee
DELETEDTask deletionSnapshot of deleted task

The history is displayed as a timeline with color-coded entries.

Threaded Comments

Comments support replies via the parentId field:

  • Top-level comment: parentId is null
  • Reply: parentId references the parent comment
  • Nesting: Unlimited levels (rendered with indentation)
  • Delete: Cannot delete a comment that has replies — must delete replies first
  • Notifications: Reply to a comment notifies the parent comment author

Frontend Component

The TaskComments.vue component builds a comment tree from the flat API response and renders it recursively using CommentNode.vue. Each comment has:

  • Reply button (opens inline form)
  • Edit button (own comments only)
  • Delete button (own comments only, hidden if has replies)

Frontend Stores

Tasks Store

typescript
const store = useTasksStore()
const { tasks, currentTask } = storeToRefs(store)

await store.fetchTasks(projectId)
await store.createTask(projectId, { title, status, priority, assigneeId, dueDate, parentId })
await store.updateTask(projectId, taskId, { title, status, priority, assigneeId, dueDate })
await store.updateTaskStatus(projectId, taskId, status)
await store.deleteTask(projectId, taskId)

Comments Store

typescript
const store = useCommentsStore()
const { comments } = storeToRefs(store)

await store.fetchComments(projectId, taskId)
await store.createComment(projectId, taskId, { body, parentId, attachments })
await store.updateComment(projectId, taskId, commentId, { body })
await store.deleteComment(projectId, taskId, commentId)
await store.uploadAttachments(projectId, taskId, files)

Task History Store

typescript
const store = useTaskHistoryStore()
const { history, loading } = storeToRefs(store)

await store.fetchHistory(projectId, taskId)

Key Implementation Details

  • Task chat is separate from comments — use POST /api/chats/task/:taskId to get or create a conversation
  • The chat auto-adds the requesting user as a participant
  • Task history is served from a separate TaskHistoryController (not embedded in task response)
  • Attachments are uploaded before comment creation, then referenced by URL in the comment