feat: epics, subtasks and checklists

Tickets can hang from another one of the same project, three levels deep
at most (epic → ticket → subtask). The ticket page lists its subtasks with
their progress, creates new ones and moves the ticket under an epic; a new
Épicas tab shows every epic and how far it has got. Each ticket also gets a
checklist for the small steps that don't deserve a ticket.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 13:52:43 +02:00
parent 75323115fb
commit d4da33d4e8
84 changed files with 4719 additions and 79 deletions

View File

@@ -13,6 +13,11 @@ type Activity {
type: String!
}
input AddChecklistItemInput {
issueId: ID!
text: String!
}
type AreaOption {
archived: Boolean!
id: String!
@@ -57,6 +62,23 @@ input ChangeIssueStatusInput {
status: String!
}
"""A step inside a ticket that does not deserve a ticket of its own"""
type ChecklistItem {
done: Boolean!
id: ID!
text: String!
}
"""How far the tickets hanging from one have got"""
type ChildProgress {
"""Done or cancelled"""
finished: Int!
finishedStoryPoints: Int!
percent: Int!
storyPoints: Int!
total: Int!
}
"""
Writes are acknowledged immediately; the read model catches up asynchronously. Wait for a subscription event with version >= this version to see the change.
"""
@@ -121,6 +143,9 @@ input CreateIssueInput {
"""Figma frame for UI changes"""
figmaUrl: String
labels: [String!]
"""Epic or ticket of the same project the new one breaks down"""
parentId: ID
priority: IssuePriority
projectId: ID!
releaseId: ID
@@ -172,6 +197,12 @@ type DurationStats {
p85Days: Float
}
"""A top-level ticket that groups others, with their progress"""
type Epic {
issue: Issue!
progress: ChildProgress!
}
type FlowSeries {
counts: [Int!]!
status: String!
@@ -185,6 +216,7 @@ type Issue {
area: String
assignee: UserRef
attachments: [Attachment!]!
checklist: [ChecklistItem!]!
commentCount: Int!
comments(limit: Int! = 50, offset: Int! = 0): CommentPage!
createdAt: String!
@@ -204,6 +236,9 @@ type Issue {
linkedIssues: [Issue!]!
links: [IssueLink!]!
number: Int!
"""The epic or ticket this one breaks down"""
parent: IssueParent
priority: IssuePriority!
projectId: ID!
projectKey: String!
@@ -237,6 +272,9 @@ input IssueFilterInput {
assigneeId: ID
categories: [StatusCategory!]
labels: [String!]
"""Only the tickets hanging from this one"""
parentId: ID
priorities: [IssuePriority!]
projectId: ID!
releaseId: ID
@@ -269,6 +307,13 @@ type IssuePage {
total: Int!
}
"""The ticket another one hangs from"""
type IssueParent {
id: ID!
key: String!
title: String!
}
enum IssuePriority {
CRITICAL
HIGH
@@ -317,6 +362,8 @@ type Me {
}
type Mutation {
"""Add a step to a ticket's checklist"""
addChecklistItem(input: AddChecklistItemInput!): CommandResult!
addComment(body: String!, issueId: ID!): CommandResult!
"""Add a Pireon user to the project"""
@@ -357,14 +404,23 @@ type Mutation {
Remove a file from a ticket (whoever uploaded it, or an owner/admin). Upload with the REST API.
"""
removeAttachment(attachmentId: ID!, issueId: ID!): CommandResult!
removeChecklistItem(issueId: ID!, itemId: ID!): CommandResult!
removeProjectMember(projectId: ID!, userId: ID!): CommandResult!
"""
Place a ticket under an epic or another ticket of the same project (null: back to the top). Three levels at most: epic → ticket → subtask.
"""
setIssueParent(issueId: ID!, parentId: ID): CommandResult!
"""Put a ticket in a release (null removes it)"""
setIssueRelease(issueId: ID!, releaseId: ID): CommandResult!
unlinkIssues(input: LinkIssuesInput!): CommandResult!
"""Stop following a ticket"""
unwatchIssue(issueId: ID!): CommandResult!
"""Reword, tick or untick a checklist step"""
updateChecklistItem(input: UpdateChecklistItemInput!): CommandResult!
updateIssue(input: UpdateIssueInput!): CommandResult!
updateNotificationSettings(email: Boolean!): NotificationSettings!
updateProject(input: UpdateProjectInput!): CommandResult!
@@ -541,6 +597,11 @@ type Query {
limit: Int! = 50
projectId: ID!
): [Activity!]!
"""
A project's epics: top-level tickets that group others, with the progress of those
"""
epics(projectId: ID!): [Epic!]!
issue(
"""Issue id or key (e.g. "TS-42")"""
idOrKey: String!
@@ -677,6 +738,13 @@ type Throughput {
week: String!
}
input UpdateChecklistItemInput {
done: Boolean
issueId: ID!
itemId: ID!
text: String
}
input UpdateIssueInput {
"""An area id"""
area: String