← All requests
0

workspace-scoped custom priorities

OpenRainyPixel (GitHub) · · edited

Motivation

Task priorities are currently a fixed set of five hardcoded values — no-priority, low, medium, high, urgent — defined in apps/api/src/task/validate-task-fields.ts and mirrored in eight or more places across the frontend, API, i18n, and integration plugins. Teams that want "Critical", "Minor", "P0…P3", or an internal priority scheme have no way to express that without patching the source.

The ask is to let each workspace define its own priority list — similar to how labelTable already works for workspace-scoped labels.

Current state of the code

Priority is just a text column on task (apps/api/src/database/schema.ts) with a default of \"low\". The value is validated at runtime against a hardcoded constant and rendered by a handful of components that each know the five possible values.

Locations where the priority enum lives (API):

  • apps/api/src/task/validate-task-fields.ts — VALID_PRIORITIES constant, assertValidPriority, coercePriority
  • apps/api/src/schemas.ts — Valibot picklist in taskSchema
  • apps/api/src/task/controllers/get-tasks.ts — SQL CASE expression for priority sort order
  • apps/api/src/task/controllers/create-task.ts, update-task-priority.ts, import-tasks.ts
  • apps/api/src/plugins/github/events/task-priority-changed.ts + utils/format.ts
  • apps/api/src/plugins/gitea/events/task-priority-changed.ts

Locations on the frontend:

  • apps/web/src/components/task/task-priority-popover.tsx — hardcoded priorityOptions array
  • apps/web/src/components/board/board-toolbar.tsx — hardcoded priority list in filters
  • apps/web/src/components/bulk-selection/backlog-bulk-toolbar.tsx — memoized priority options
  • apps/web/src/lib/priority.tsx — getPriorityIcon switch
  • apps/web/src/lib/i18n/domain.ts — getPriorityLabel
  • apps/web/src/hooks/use-task-filters.ts — in-memory priority filter

i18n: tasks.priority namespace in all nine locales (i18n/*.json).

Proposed approach

Option A — just add more built-in priorities (not what was asked, but cheap)

Extend VALID_PRIORITIES, the Valibot picklist, the SQL sort CASE, and the eight frontend/i18n touch-points. 1–2 days. This is listed for completeness; it does not actually solve the request.

Option B — workspace-scoped custom priorities (recommended, what was asked)

Schema

New table modelled loosely on labelTable:

export const priorityTable = pgTable(\"priority\", {
  id: text(\"id\").$defaultFn(() => createId()).primaryKey(),
  workspaceId: text(\"workspace_id\").notNull().references(() => workspaceTable.id, {
    onDelete: \"cascade\",
    onUpdate: \"cascade\",
  }),
  name: text(\"name\").notNull(),
  color: text(\"color\"),
  order: integer(\"order\").notNull().default(0),
  isDefault: boolean(\"is_default\").default(false).notNull(),
  createdAt: timestamp(\"created_at\", { mode: \"date\" }).defaultNow().notNull(),
  updatedAt: timestamp(\"updated_at\", { mode: \"date\" }).defaultNow().$onUpdate(() => new Date()).notNull(),
}, (table) => [
  index(\"priority_workspaceId_idx\").on(table.workspaceId),
  unique(\"priority_workspace_name_unique\").on(table.workspaceId, table.name),
]);
  • taskTable.priority changes from text(\"priority\") to text(\"priority_id\").references(() => priorityTable.id, { onDelete: \"set null\" }).
  • Migration needs to:
    1. Create priorityTable.
    2. For every existing workspace, insert five default rows matching no-priority / low / medium / high / urgent (with sensible order and isDefault for medium or whatever the team picks).
    3. Backfill task.priority_id from the current task.priority text value → matching new row id.
    4. Drop the old text column.

Backend

  • New CRUD endpoints under apps/api/src/priority/ (GET/POST/PATCH/DELETE by workspace). Follow the same shape as the existing label routes.
  • assertValidPriority becomes async and takes the workspace id.
  • get-tasks.ts needs a new priority sort approach — either ORDER BY priority.order via join, or fetch the priority list for the project's workspace once per request and build a dynamic CASE.
  • taskSchema in schemas.ts loses the Valibot picklist; priority becomes nullable(string) (the id).
  • GitHub/Gitea plugins need updating — they translate our internal priority values to provider-specific labels, and that mapping now needs to go through a user-defined name instead of a fixed enum.

Frontend

  • New query hook useGetPriorities(workspaceId) with TanStack Query.
  • Replace the hardcoded arrays in task-priority-popover.tsx, board-toolbar.tsx, backlog-bulk-toolbar.tsx with data from the query.
  • getPriorityIcon becomes getPriorityIcon(priority) where priority is a row from priorityTable — render a colored dot or a user-provided icon.
  • New settings screen in the workspace settings area to create/reorder/delete priorities.
  • i18n strings become display strings for the settings UI only — priority names themselves are user data, not translated.

Risk / scope

  • 2–3 weeks of work for one person.
  • Moderate risk: the async validator change ripples through every task mutation controller, and the migration needs to be careful to avoid data loss on busy instances.
  • Backward compatibility for API consumers: API responses change from priority: \"high\" to priority: { id, name, color, order } (or just priorityId + separate lookup). This is a breaking change for any external API client.

Open questions

  • Should the five existing values be "locked" defaults that every workspace starts with and cannot delete, or fully user-editable including the seeded rows?
  • What happens to a task whose priority is deleted? ON DELETE SET NULL makes it "no priority" — acceptable?
  • Do we want a workspace-level "default priority for new tasks" setting (replaces the current default(\"low\") behaviour)?
  • Export/import (export-tasks.ts, import-tasks.ts): do we export priority names (portable across workspaces) or ids (breaks on import into a different workspace)?
  • How do the GitHub and Gitea integrations handle custom priorities that don't map to any provider-native label?

Acceptance criteria

  • A workspace admin can create, rename, recolor, reorder, and delete priorities from workspace settings.
  • Every task creation, update, and filter UI reads priorities from the workspace instead of a hardcoded list.
  • Existing tasks migrate without data loss and existing workspaces end up with the same five priorities they had before.
  • Board sort-by-priority respects the workspace-defined order.
  • GitHub and Gitea integrations degrade gracefully for priorities they don't recognize.
  • Import/export round-trips priorities in a predictable way (decision documented in the PR description).

Related

  • Feature: #1166 (task author sticker — similar task-metadata surface, unrelated scope)

Originally requested by @RainyPixel on 2026-04-07. Original GitHub request #1171.

Issue #1171

Discussion 1

Sign in to comment.

Keyboard shortcuts

Anywhere

Go to requests
gh
Go to changelog
gc
Go to notifications
gn
Next or previous page
norp
Leave a text field or close a menu
Esc
Show this list
?

Requests

Move to the next or previous request
jork
Open the selected request
oorEnter
Vote on the selected request
v
Search
/
New request
c

Request

Vote
v
Write a comment
c
Edit
e