Work items
Issues and tasks on pipelines, with identifiers, parents without cycles, several assignees, priority 0 to 4, estimates, start, end and due dates, locking, projects, iterations, batched edits and a progress tree.
work-items is the task and issue record of Linear, Jira, GitHub issues and monday items: a title, a body, a parent, assignees, a priority, an estimate, start, end and due dates, and a stage on a pipeline. Items can belong to a team, a project and an iteration (a cycle or a sprint). Their identifier is TASK-12, or the format of a work_item entity in number-sequences.
Reading needs work_items.read, creating, editing and assigning needs work_items.write, and projects, iterations and deleting someone else's item need work_items.manage. Admins get every permission, and members get work_items.read and work_items.write.
better-supabase sql add work-itemsThe module needs tenant, access, subjects and pipelines. Installing it binds the work_item type in the pipelines module with one pipeline per team (scope: "team_id") and the categories of Linear's workflow states:
| Category | Outcome | Default stages |
|---|---|---|
triage | open | Triage |
backlog | open | Backlog |
unstarted | open | Todo |
started | open | In progress, In review |
completed | success | Done |
canceled | failure | Canceled |
A stage's category sets the item's state, so a list or a policy reads state and never a stage name. Set sql.modules.pipelines.options.subjects.work_item to rename the stages or start with your own, such as CentraKit's Todo, In progress, On hold and Done; your keys are merged over the defaults.
import { workItemsSubjects } from "better-supabase/blocks/work-items";
export default defineConfig({
subjects: { ...workItemsSubjects({ url: "/tasks/{id}" }) },
});Create, edit and assign
import { createWorkItems } from "better-supabase/blocks/work-items";
const items = createWorkItems({ transport });
const epic = await items
.create(organizationId, { title: "Checkout v2", teamId, priority: 2 })
.orThrow();
const task = await items
.create(
organizationId,
{ title: "Payment API", parentId: epic.id, teamId, dueAt },
[userId],
)
.orThrow();
const { accepted, rejected } = await items
.update([
{ workItemId: task.id, field: "stageId", value: inProgressStageId },
{ workItemId: task.id, field: "endAt", value: friday },
])
.orThrow();update(changes) applies grid edits and Gantt drags one by one and returns the refused ones with their hint. The table refuses a parent that would put an item under itself (WORK_ITEM_PARENT_CYCLE) and date changes on a locked item (WORK_ITEM_LOCKED). Moving to a completed or canceled stage stamps closedAt and a close reason (completed, or not_planned for canceled; pass closeReason: "duplicate" with the move to mark a duplicate) and records work_item.closed. Moving back to an open stage clears both and records work_item.reopened.
assign(id, userIds) replaces the assignees. Each one watches the item, and the people newly assigned get a work_item.assigned notification. options.maxAssignees caps the list (WORK_ITEM_ASSIGNEES_LIMIT), and 1 gives the single-assignee shape of Linear and Jira.
Lists, trees, projects and iterations
list(organizationId, { teamId, projectId, iterationId, parentId, assigneeId, states, open, search }) returns active items, newest first, 50 per page; pass the last item's id as after for the next page. tree(rootId) returns an item and everything under it with its depth; an item without its own progress rolls up the share of its open and completed children that are completed, for a Gantt group.
Dependencies between items are relations: link({ kind: "blocks", source: design, target: build }) is a finish-to-start dependency, and edges(organizationId, "blocks") reads them for the chart.
saveProject(organizationId, { key, name, status, startOn, targetOn }) keeps a project (PAY) with the share of its items that are completed. saveIteration(organizationId, { name, teamId, startsOn, endsOn }) keeps a cycle or a sprint. Turn either table off with tables: { projects: null } or tables: { iterations: null }.
Extra columns
options.extraColumns adds columns to work_items, such as CentraKit's customer_id. Every item a read returns carries them in fields.
Events
| Event | Data |
|---|---|
work_item.created, work_item.updated, work_item.assigned, work_item.reopened, work_item.archived, work_item.deleted | organizationId, workItemId, subjectType, subjectId, title, fields, assigneeIds |
work_item.closed | the same, with state and closeReason |
Hooks
The module calls before_work_item_create(tenant, attrs jsonb), after_work_item_create(work_item uuid), before_work_item_update(work_item uuid, field text, value jsonb) and after_work_item_update(work_item uuid, field text). A before_ hook raises to refuse; in update, that refuses only its change.
Wire it to ReUI
The ReUI Kanban blocks commit a drag through onValueCommit: call rank.move("work_item", id, { previous, next, group: stageId }). The Gantt blocks commit a move or resize through onEventUpdate: send startAt and endAt through update, and return false when the change comes back in rejected (a locked bar). Read the Gantt rows from tree, and its arrows from the blocks relations.
Errors
| Hint | Cause |
|---|---|
WORK_ITEM_FORBIDDEN | the caller lacks the permission |
WORK_ITEM_TITLE_REQUIRED | the title is empty |
WORK_ITEM_ATTRS_INVALID | a field has an invalid value |
WORK_ITEM_NOT_FOUND | no item the caller can edit |
WORK_ITEM_FIELD_UNKNOWN | update names a field it doesn't write |
WORK_ITEM_PARENT_CYCLE | the parent would put the item under itself |
WORK_ITEM_PARENT_INVALID | the parent is in another organization |
WORK_ITEM_LOCKED | a locked item's dates don't change |
WORK_ITEM_ASSIGNEES_LIMIT | more assignees than maxAssignees |
WORK_ITEM_PROJECT_*, WORK_ITEM_ITERATION_* | a project or iteration is missing, taken or invalid |
Last updated on
Deals
Deals on pipelines with amounts in minor units, a company and contacts, a deal team, line items, forecast calls with attainment, close-date slips, a next step, locking and batched edits that reject per change.
Quotes
Quotes with line items and totals, a revision chain, a public link the customer accepts, rejects or asks changes through, expiry, and quotes made from deals.