Skip to content

Workflow Rules ​

Allowed moves ​

Implement HasKanbanTransitions on the status enum:

php
enum TaskStatus: string implements HasColor, HasIcon, HasKanbanTransitions, HasLabel
{
    public function getTransitions(): array
    {
        return match ($this) {
            self::Backlog => [self::Todo],
            self::Todo => [self::Backlog, self::InProgress],
            self::InProgress => [self::Todo, self::Review],
            self::Review => [self::InProgress, self::Done],
            self::Done => [self::Review],
        };
    }
}

Or define them on the board:

php
->transitions(['todo' => [TaskStatus::InProgress]])

Columns you don't list accept anything. Refused moves are flagged while you drag, before you drop:

A refused move shows why while dragging
The reason shows on the column while dragging.

WIP limits ​

php
->wipLimits(['in_progress' => 3])

The header shows 3 / 3, amber at the limit and red above it. Limits warn by default, like Jira. Refuse moves into a full column:

php
->wipLimits(['in_progress' => 3], enforce: true)
// or
->enforceWipLimits()

Your own rules ​

php
->validateUsing(fn (Task $record, TaskStatus $to, ?Member $lane): ?string => ...)

Return a message to refuse a move.

Hooks ​

php
->afterMove(fn (Task $record, TaskStatus $from, TaskStatus $to) => ...)

Runs after a move is saved, for notifications or activity logs.

Permissions ​

With a policy on the model, update controls dragging and editing, create controls adding, and delete the delete action. Locked cards show a lock and can't be picked up.

Every rule runs on the server for drags, keyboard moves, inline adds and both modals.