Task management system development
Task Management is narrower than Project Management. Focus: task flow — create, assign, execute, control. Typical custom scenarios: operational teams with non-standard workflow, internal request processing, production process trackers tied to physical objects (equipment, contracts, clients).
Domain model
Minimal viable task model:
CREATE TABLE tasks (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(500) NOT NULL,
description TEXT,
status VARCHAR(50) NOT NULL DEFAULT 'todo',
priority SMALLINT NOT NULL DEFAULT 2, -- 1=low, 2=medium, 3=high, 4=critical
assignee_id BIGINT REFERENCES users(id),
reporter_id BIGINT NOT NULL REFERENCES users(id),
team_id BIGINT REFERENCES teams(id),
due_date DATE,
completed_at TIMESTAMPTZ,
parent_id BIGINT REFERENCES tasks(id),
position INTEGER, -- order in list/column
metadata JSONB DEFAULT '{}', -- custom fields
created_at TIMESTAMPTZ DEFAULT now()
);
metadata (JSONB) solves custom fields without schema changes. Different task types have different fields: marketing task has campaign_id, HR task has position_id. Index as: CREATE INDEX ON tasks ((metadata->>'campaign_id')).
Workflow and statuses
Key difference from Trello — configurable workflow with transition rules. Not just drag-and-drop to any status, but strict state machine with guards:
- Can move to "In Review" only if assignee exists
- "Closed" requires filled "Result" field
- "Cancelled" available only to manager or creator
// XState config
const taskMachine = createMachine({
id: 'task',
initial: 'todo',
states: {
todo: { on: { START: 'in_progress', CANCEL: 'cancelled' } },
in_progress: { on: { REVIEW: 'in_review', BLOCK: 'blocked' } },
blocked: { on: { UNBLOCK: 'in_progress' } },
in_review: { on: { APPROVE: 'done', REJECT: 'in_progress' } },
done: { on: { REOPEN: 'todo' } },
cancelled: { type: 'final' },
},
});
Store config in DB (JSON), edit via visual builder.
Views: list, Kanban, spreadsheet
List — main view. Virtualized scroll (TanStack Virtual) for > 100 tasks, grouping by any field, multi-field sort.
Kanban: columns = workflow statuses. Drag-and-drop via @dnd-kit/sortable. Client-side transition validation before request.
Spreadsheet: row = task, columns = fields. Inline edit. Bulk operations: select 20 tasks, assign to person, change deadline. Use TanStack Table with row selection and custom editors.
Bulk operations and automation
Bulk operations often overlooked:
- Reassign group of tasks to another person
- Bulk close by filter (all tasks > 30 days in "Postponed")
- Copy/move tasks between projects
Automation (triggered rules): "If task not taken within 2 hours of assignment — remind assignee and notify manager". Via scheduled jobs checking conditions and executing actions. Rules stored in DB, edited via UI.
Notifications and SLA control
For operational systems, SLA control is critical: task must be taken within N hours of creation.
class CheckTaskSlaJob implements ShouldQueue
{
public function handle(): void
{
$overdueTask = Task::query()
->where('status', 'todo')
->where('created_at', '<', now()->subHours($this->slaHours))
->whereNull('assignee_id')
->get();
foreach ($overdueTask as $task) {
Notification::send($task->team->managers, new SlaBreachedNotification($task));
}
}
}
Escalation matrix: 1h overdue → email assignee, 4h → email + Slack manager, 8h → director notification.
Timeline
- Design workflows and data — 1–2 weeks
- Backend (tasks, permissions, API) — 3–4 weeks
- Frontend (list + Kanban + table) — 3–4 weeks
- Notifications, SLA, automation — 2 weeks
- Testing and launch — 1 week
Total: 10–13 weeks. First working version with basic workflow and two views — 6–7 weeks.







