feat(issues): add human-readable issue identifiers (e.g. JIA-1)
Add per-workspace auto-incrementing issue numbers with a configurable prefix, producing identifiers like "JIA-1" instead of truncated UUIDs. Database: - Add issue_prefix and issue_counter to workspace table - Add number column to issue table with UNIQUE(workspace_id, number) - Backfill existing issues with sequential numbers Backend: - Issue creation atomically increments counter in a transaction - API responses include number and identifier fields - Support issue lookup by identifier format (KEY-N) - Workspace prefix auto-generated from name, customizable via API Frontend: - Display identifier in list rows and issue detail breadcrumb - Add issue_prefix to Workspace type, number/identifier to Issue type
This commit is contained in:
parent
42f72371bd
commit
9fbac49f24
19 changed files with 335 additions and 54 deletions
|
|
@ -15,10 +15,10 @@ const createIssue = `-- name: CreateIssue :one
|
|||
INSERT INTO issue (
|
||||
workspace_id, title, description, status, priority,
|
||||
assignee_type, assignee_id, creator_type, creator_id,
|
||||
parent_issue_id, position, due_date
|
||||
parent_issue_id, position, due_date, number
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
|
||||
) RETURNING id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13
|
||||
) RETURNING id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at, number
|
||||
`
|
||||
|
||||
type CreateIssueParams struct {
|
||||
|
|
@ -34,6 +34,7 @@ type CreateIssueParams struct {
|
|||
ParentIssueID pgtype.UUID `json:"parent_issue_id"`
|
||||
Position float64 `json:"position"`
|
||||
DueDate pgtype.Timestamptz `json:"due_date"`
|
||||
Number int32 `json:"number"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateIssue(ctx context.Context, arg CreateIssueParams) (Issue, error) {
|
||||
|
|
@ -50,6 +51,7 @@ func (q *Queries) CreateIssue(ctx context.Context, arg CreateIssueParams) (Issue
|
|||
arg.ParentIssueID,
|
||||
arg.Position,
|
||||
arg.DueDate,
|
||||
arg.Number,
|
||||
)
|
||||
var i Issue
|
||||
err := row.Scan(
|
||||
|
|
@ -70,6 +72,7 @@ func (q *Queries) CreateIssue(ctx context.Context, arg CreateIssueParams) (Issue
|
|||
&i.DueDate,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Number,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -84,7 +87,7 @@ func (q *Queries) DeleteIssue(ctx context.Context, id pgtype.UUID) error {
|
|||
}
|
||||
|
||||
const getIssue = `-- name: GetIssue :one
|
||||
SELECT id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at FROM issue
|
||||
SELECT id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at, number FROM issue
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
|
|
@ -109,12 +112,49 @@ func (q *Queries) GetIssue(ctx context.Context, id pgtype.UUID) (Issue, error) {
|
|||
&i.DueDate,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Number,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getIssueByNumber = `-- name: GetIssueByNumber :one
|
||||
SELECT id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at, number FROM issue
|
||||
WHERE workspace_id = $1 AND number = $2
|
||||
`
|
||||
|
||||
type GetIssueByNumberParams struct {
|
||||
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
||||
Number int32 `json:"number"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetIssueByNumber(ctx context.Context, arg GetIssueByNumberParams) (Issue, error) {
|
||||
row := q.db.QueryRow(ctx, getIssueByNumber, arg.WorkspaceID, arg.Number)
|
||||
var i Issue
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.WorkspaceID,
|
||||
&i.Title,
|
||||
&i.Description,
|
||||
&i.Status,
|
||||
&i.Priority,
|
||||
&i.AssigneeType,
|
||||
&i.AssigneeID,
|
||||
&i.CreatorType,
|
||||
&i.CreatorID,
|
||||
&i.ParentIssueID,
|
||||
&i.AcceptanceCriteria,
|
||||
&i.ContextRefs,
|
||||
&i.Position,
|
||||
&i.DueDate,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Number,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listIssues = `-- name: ListIssues :many
|
||||
SELECT id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at FROM issue
|
||||
SELECT id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at, number FROM issue
|
||||
WHERE workspace_id = $1
|
||||
AND ($4::text IS NULL OR status = $4)
|
||||
AND ($5::text IS NULL OR priority = $5)
|
||||
|
|
@ -166,6 +206,7 @@ func (q *Queries) ListIssues(ctx context.Context, arg ListIssuesParams) ([]Issue
|
|||
&i.DueDate,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Number,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -189,7 +230,7 @@ UPDATE issue SET
|
|||
due_date = $9,
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
RETURNING id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at
|
||||
RETURNING id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at, number
|
||||
`
|
||||
|
||||
type UpdateIssueParams struct {
|
||||
|
|
@ -235,6 +276,7 @@ func (q *Queries) UpdateIssue(ctx context.Context, arg UpdateIssueParams) (Issue
|
|||
&i.DueDate,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Number,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
@ -244,7 +286,7 @@ UPDATE issue SET
|
|||
status = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
RETURNING id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at
|
||||
RETURNING id, workspace_id, title, description, status, priority, assignee_type, assignee_id, creator_type, creator_id, parent_issue_id, acceptance_criteria, context_refs, position, due_date, created_at, updated_at, number
|
||||
`
|
||||
|
||||
type UpdateIssueStatusParams struct {
|
||||
|
|
@ -273,6 +315,7 @@ func (q *Queries) UpdateIssueStatus(ctx context.Context, arg UpdateIssueStatusPa
|
|||
&i.DueDate,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Number,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue