Boards & Tasks
Last updated on 2026-09-07
The board and task system is the centerpiece of the kit. Tasks live in the tasks table in Supabase and can be viewed through five different layouts -- board, list, calendar, timeline, and backlog. All mutations use Server Actions and every drag-and-drop is persisted to the database.
Kanban Board
Route: /projects/[id]/board
The Kanban board provides a visual, drag-and-drop interface for managing tasks across status columns -- with every drag persisted to Supabase.
- Drag-and-drop -- powered by
@hello-pangea/dnd, task cards can be dragged between columns to update their status - Database persistence -- when a card is dropped, the
updateTaskStatusServer Action writes the new status to Supabase andrevalidatePath()refreshes the page data - Six status columns -- Backlog, Todo, In Progress, In Review, Done, and Cancelled, each with a distinct color indicator
- WIP limits -- Todo (8), In Progress (5), and In Review (3) columns have work-in-progress limits displayed in the column header
- Task cards -- each card shows the task ID badge (e.g.,
PRJ-42), title, priority indicator, assignee avatar, due date, label pills, and subtask progress - Column totals -- each column header displays the task count
- Filter popover -- filter tasks by priority (Urgent, High, Medium, Low) and assignee using checkboxes
- Optimistic UI -- columns update instantly on drag; the Server Action runs in the background
- Toast notifications -- a success toast confirms the move (e.g., 'Moved "Fix login bug" to In Progress')
Status Columns
| Status | Color | WIP Limit |
|---|---|---|
| Backlog | #6b7280 (gray) |
-- |
| Todo | #8b5cf6 (violet) |
8 |
| In Progress | #f59e0b (amber) |
5 |
| In Review | #3b82f6 (blue) |
3 |
| Done | #22c55e (green) |
-- |
| Cancelled | #ef4444 (red) |
-- |
How Data Flows
1. Server Component fetches tasks from Supabase via getTasksByProject()
2. Passes tasks, members, labels to BoardPageClient
3. BoardPageClient renders KanbanBoard with grouped-by-status columns
4. User drags a card to a new column
5. onDragEnd fires -> optimistic UI update via setColumns()
6. Server Action updateTaskStatus() writes new status to Supabase
7. Toast confirms the move
8. revalidatePath() refreshes server data
Kanban Board Architecture
KanbanBoard (DragDropContext)
├── Filter bar (priority + assignee popover)
├── KanbanColumn (Droppable) x 6 statuses
│ ├── Column header (title, count, WIP limit indicator)
│ ├── KanbanCard (Draggable) x N tasks
│ │ ├── Task ID badge (prefix-number)
│ │ ├── Title
│ │ ├── Priority indicator
│ │ ├── Assignee avatar
│ │ ├── Due date
│ │ ├── Label pills
│ │ └── Subtask progress
│ └── Column footer
└── onDragEnd handler
├── Optimistic state update
├── Toast notification
└── Server Action -> Supabase write
// kanban-board.tsx (simplified)
import { DragDropContext, type DropResult } from "@hello-pangea/dnd"
import { updateTaskStatus } from "@/lib/actions/tasks"
function KanbanBoard({ tasks, members, labels, projectPrefix, projectId }: KanbanBoardProps) {
const [columns, setColumns] = useState(groupTasksByStatus(tasks))
function onDragEnd(result: DropResult) {
const { source, destination } = result
if (!destination) return
if (source.droppableId === destination.droppableId
&& source.index === destination.index) return
const sourceStatus = source.droppableId as TaskStatusType
const destStatus = destination.droppableId as TaskStatusType
const movedTask = columns[sourceStatus][source.index]
// Optimistic UI update
setColumns(prev => {
const next = { ...prev }
const sourceTasks = [...prev[sourceStatus]]
const [moved] = sourceTasks.splice(source.index, 1)
const updatedTask = { ...moved, status: destStatus }
const destTasks = [...prev[destStatus]]
destTasks.splice(destination.index, 0, updatedTask)
next[sourceStatus] = sourceTasks
next[destStatus] = destTasks
return next
})
// Persist to database
toast.success(`Moved "${movedTask.title}" to ${destStatus}`)
void updateTaskStatus(movedTask.id, destStatus)
}
return (
<DragDropContext onDragEnd={onDragEnd}>
{COLUMN_ORDER.map(({ status, color, wipLimit }) => (
<KanbanColumn key={status} id={status} title={status}
tasks={columns[status]} color={color} wipLimit={wipLimit}
members={members} labels={labels}
projectPrefix={projectPrefix} projectId={projectId} />
))}
</DragDropContext>
)
}
List View
Route: /projects/[id]/list
A tabular view of all tasks in the project, grouped by status, with inline editing and bulk actions.
- Grouped by status -- tasks are organized under collapsible status group headers
- Columns -- task ID, title, status select, priority select, assignee, due date, labels
- Inline editing -- status and priority can be changed via select dropdowns that call
updateTaskServer Action - Bulk actions -- select multiple tasks via checkboxes for batch operations
- Row actions -- hover to reveal edit, assign, move, and delete actions via a dropdown menu
- Pagination -- previous/next controls for large task sets
- Sorting -- sortable column headers
Calendar View
Route: /projects/[id]/calendar
A monthly calendar showing tasks as color-coded pills on their due dates.
- Monthly grid -- standard calendar layout with navigation (previous/next month)
- Task pills -- tasks appear as colored pills on their due date, color-coded by priority
- Today highlight -- the current date cell is visually highlighted
- Quick create -- click the "+" button on any day to create a task pre-set to that date
- Task creation -- creates tasks via the
createTaskServer Action with the selected date asdue_date - Day cells -- cells for days outside the current month are dimmed
Timeline View
Route: /projects/[id]/timeline
A Gantt-chart-style timeline showing tasks as horizontal bars across a date range.
- Zoom levels -- switch between day, week, and month granularity
- Task bars -- horizontal bars spanning from
start_datetodue_date, color-coded by status - Status colors -- Backlog (gray), Todo (violet), In Progress (amber), In Review (blue), Done (green), Cancelled (red)
- Date headers -- column headers showing the date range at the current zoom level
- Today marker -- a vertical red line marking the current date
Backlog View
Route: /projects/[id]/backlog
A triage view focused on unscheduled tasks that need to be prioritized and assigned to sprints.
- Backlog tasks -- tasks not assigned to any cycle, filterable by priority
- Sprint assignment -- button to move tasks into the active or upcoming cycle via
addTaskToCycleServer Action - Task table -- tabular format with task ID, title, priority, assignee, due date, and labels
- Subtask progress -- progress bar showing completed subtasks
- Summary cards -- KPI cards showing total backlog count, unassigned tasks, and overdue tasks
Task Detail
Route: /projects/[id]/tasks/[taskId]
A comprehensive view of an individual task with breadcrumb navigation, a content area, and a property sidebar.
- Breadcrumb -- navigates from project name to board to task ID (e.g.,
PRJ-42) - Task ID badge -- monospace badge with the project prefix and task number
- Title -- heading-styled task title
- Description -- task description text
- Subtasks -- rendered via the
SubtaskListcomponent, showing each subtask with a checkbox, title, assignee, and due date; toggle completion viaupdateSubtaskServer Action - Comments and activity -- rendered via the
CommentThreadcomponent, showing comments and task activity in chronological order; add comments viaaddCommentServer Action
Task Detail Sidebar
The right sidebar (TaskDetailSidebar) displays and allows editing of task properties:
- Status -- select dropdown to change status (calls
updateTask) - Priority -- select dropdown to change priority
- Assignee -- select dropdown with team member avatars
- Labels -- multi-select label picker with color indicators
- Due date -- date picker
- Start date -- date picker
- Cycle -- select dropdown to assign to a sprint cycle
- Module -- select dropdown to assign to a module
- Estimate -- numeric input for story point estimate
Task CRUD Operations
| Operation | Server Action | Description |
|---|---|---|
| Create task | createTask |
Auto-generates next task number per project; writes to tasks and task_labels tables |
| Read task | getTask |
Fetches task with subtasks and comments via parallel queries |
| Update task | updateTask |
Updates task fields and syncs label associations |
| Update status | updateTaskStatus |
Updates only the status field (used by drag-and-drop) |
| Delete task | deleteTask |
Deletes task and revalidates project path |
| Create subtask | createSubtask |
Adds subtask linked to parent task |
| Update subtask | updateSubtask |
Toggle completion or edit title/assignee |
| Delete subtask | deleteSubtask |
Removes subtask |
| Add comment | addComment |
Creates comment with authenticated user as author |
| Update comment | updateComment |
Edits comment content |
| Delete comment | deleteComment |
Removes comment |
| Add label | addTaskLabel |
Creates task-label association |
| Remove label | removeTaskLabel |
Removes task-label association |
| Search tasks | searchTasks |
Full-text search on title and description via ilike |
Data Sources
| Data | Source | How |
|---|---|---|
| Tasks | tasks table |
Server Component query with subtask joins |
| Team members | profiles table |
Via project_members join |
| Labels | labels table |
Ordered by group and name |
| Comments | task_comments table |
Filtered by task_id, ordered by created_at |
| Subtasks | subtasks table |
Joined on tasks via tasks(*, subtasks(*)) |
Next Steps
- Sprints & Modules -- sprint cycles, modules, goals, and wiki pages
- Analytics & Planning -- analytics dashboards, roadmap, and workload
- Server Actions -- complete Server Action reference