Appearance
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)
| Method | Path | Description |
|---|---|---|
GET | /api/projects/:projectId/tasks | List project tasks |
POST | /api/projects/:projectId/tasks | Create task |
GET | /api/projects/:projectId/tasks/:taskId | Get task with details |
PATCH | /api/projects/:projectId/tasks/:taskId | Update task |
PATCH | /api/projects/:projectId/tasks/:taskId/status | Update status only |
DELETE | /api/projects/:projectId/tasks/:taskId | Delete task |
My Tasks
| Method | Path | Description |
|---|---|---|
GET | /api/tasks/my | Tasks assigned to current user (dashboard) |
Team Task Board
| Method | Path | Description |
|---|---|---|
GET | /api/teams/by-slug/:teamSlug/tasks | Team convenience endpoint |
Task Comments (/projects/:projectId/tasks/:taskId/comments)
| Method | Path | Description |
|---|---|---|
GET | /api/projects/:projectId/tasks/:taskId/comments | List comments (flat, with parentId for threading) |
POST | /api/projects/:projectId/tasks/:taskId/comments | Create comment (optional parentId for reply) |
PATCH | /api/projects/:projectId/tasks/:taskId/comments/:commentId | Edit comment |
DELETE | /api/projects/:projectId/tasks/:taskId/comments/:commentId | Delete comment (blocked if has replies) |
Task History (/projects/:projectId/tasks/:taskId/history)
| Method | Path | Description |
|---|---|---|
GET | /api/projects/:projectId/tasks/:taskId/history | Get task audit log |
Task Chat
| Method | Path | Description |
|---|---|---|
POST | /api/chats/task/:taskId | Get or create task conversation |
Attachments
| Method | Path | Description |
|---|---|---|
POST | /api/projects/:projectId/tasks/:taskId/comments/attachments | Upload comment attachments |
Status Workflow
todo ──► in_progress ──► in_review ──► done
│ │
└────────────────────────┘
cancelledTask History (Audit Log)
Every change is automatically logged:
| Action | Trigger | Data |
|---|---|---|
CREATED | Task creation | Who created, when |
UPDATED | Field change | Field name, old value, new value |
STATUS_CHANGED | Status update | Old status, new status |
ASSIGNED | Assignee change | Old assignee, new assignee |
DELETED | Task deletion | Snapshot 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:
parentIdis null - Reply:
parentIdreferences 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/:taskIdto 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