feat: sprints with burndown and carry-over
Plan sprints with dates and a goal, run one at a time per project, and put tickets in them from the ticket page. The Sprints tab follows the one under way with its progress and a burndown in tickets or points against the ideal line. Closing a sprint sends what was left to the next one or back to the backlog; the board can be filtered by sprint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -176,6 +176,17 @@ input CreateReleaseInput {
|
||||
targetDate: String
|
||||
}
|
||||
|
||||
input CreateSprintInput {
|
||||
"""YYYY-MM-DD, included"""
|
||||
endDate: String!
|
||||
goal: String
|
||||
name: String!
|
||||
projectId: ID!
|
||||
|
||||
"""YYYY-MM-DD"""
|
||||
startDate: String!
|
||||
}
|
||||
|
||||
type CreatedResolved {
|
||||
created: Int!
|
||||
resolved: Int!
|
||||
@@ -249,6 +260,7 @@ type Issue {
|
||||
rank: String!
|
||||
release: ReleaseRef
|
||||
reporter: UserRef!
|
||||
sprint: SprintRef
|
||||
|
||||
"""A status id of the project's settings"""
|
||||
status: String!
|
||||
@@ -278,6 +290,9 @@ input IssueFilterInput {
|
||||
categories: [StatusCategory!]
|
||||
labels: [String!]
|
||||
|
||||
"""Only tickets in no sprint"""
|
||||
noSprint: Boolean
|
||||
|
||||
"""Only the tickets hanging from this one"""
|
||||
parentId: ID
|
||||
priorities: [IssuePriority!]
|
||||
@@ -287,6 +302,7 @@ input IssueFilterInput {
|
||||
"""Matches key, title or description"""
|
||||
search: String
|
||||
sort: IssueSort = RECENT
|
||||
sprintId: ID
|
||||
statuses: [String!]
|
||||
types: [String!]
|
||||
unassigned: Boolean
|
||||
@@ -388,10 +404,18 @@ type Mutation {
|
||||
"""Move a ticket through the workflow (incl. QA)"""
|
||||
changeIssueStatus(input: ChangeIssueStatusInput!): CommandResult!
|
||||
changeProjectMemberRole(input: ProjectMemberInput!): CommandResult!
|
||||
|
||||
"""
|
||||
Close the sprint under way. Unfinished tickets move to carryOverToSprintId, or back to the backlog when it is null.
|
||||
"""
|
||||
closeSprint(carryOverToSprintId: ID, sprintId: ID!): CommandResult!
|
||||
createIssue(input: CreateIssueInput!): CommandResult!
|
||||
createProject(input: CreateProjectInput!): CommandResult!
|
||||
createRelease(input: CreateReleaseInput!): CommandResult!
|
||||
|
||||
"""Plan a sprint (owners and admins)"""
|
||||
createSprint(input: CreateSprintInput!): CommandResult!
|
||||
|
||||
"""Author or project admin"""
|
||||
deleteComment(commentId: ID!): CommandResult!
|
||||
|
||||
@@ -402,6 +426,9 @@ type Mutation {
|
||||
|
||||
"""Delete a view: whoever saved it, or an admin if it is shared"""
|
||||
deleteSavedFilter(id: ID!): Boolean!
|
||||
|
||||
"""Delete a sprint that never started; its tickets go back to the backlog"""
|
||||
deleteSprint(sprintId: ID!): CommandResult!
|
||||
editComment(body: String!, commentId: ID!): CommandResult!
|
||||
|
||||
"""Relate tickets, e.g. FE DEPENDS_ON BE, QA TESTS FE"""
|
||||
@@ -435,6 +462,12 @@ type Mutation {
|
||||
|
||||
"""Put a ticket in a release (null removes it)"""
|
||||
setIssueRelease(issueId: ID!, releaseId: ID): CommandResult!
|
||||
|
||||
"""Put a ticket in a sprint (null: back to the backlog)"""
|
||||
setIssueSprint(issueId: ID!, sprintId: ID): CommandResult!
|
||||
|
||||
"""Start a planned sprint (only one under way per project)"""
|
||||
startSprint(sprintId: ID!): CommandResult!
|
||||
unlinkIssues(input: LinkIssuesInput!): CommandResult!
|
||||
|
||||
"""Stop following a ticket"""
|
||||
@@ -451,6 +484,7 @@ type Mutation {
|
||||
"""
|
||||
updateProjectSettings(projectId: ID!, settings: ProjectSettingsInput!): CommandResult!
|
||||
updateRelease(input: UpdateReleaseInput!): CommandResult!
|
||||
updateSprint(input: UpdateSprintInput!): CommandResult!
|
||||
|
||||
"""
|
||||
Follow a ticket: be told when it moves, is reassigned or gets a comment
|
||||
@@ -517,6 +551,7 @@ type Project {
|
||||
|
||||
"""Matches key, title or description"""
|
||||
search: String
|
||||
sprintId: ID
|
||||
types: [String!]
|
||||
unassigned: Boolean
|
||||
): [BoardColumn!]!
|
||||
@@ -668,6 +703,12 @@ type Query {
|
||||
|
||||
"""My views of the project's board, then the shared ones"""
|
||||
savedFilters(projectId: ID!): [SavedFilter!]!
|
||||
sprint(id: ID!): Sprint!
|
||||
|
||||
"""
|
||||
A project's sprints: the one under way, the planned ones, then the closed ones
|
||||
"""
|
||||
sprints(projectId: ID!): [Sprint!]!
|
||||
|
||||
"""Planned releases of all my projects, soonest target date first"""
|
||||
upcomingReleases(limit: Int! = 10): [Release!]!
|
||||
@@ -784,6 +825,73 @@ input SavedFilterCriteriaInput {
|
||||
unassigned: Boolean
|
||||
}
|
||||
|
||||
"""A time-boxed iteration: the tickets the team commits to"""
|
||||
type Sprint {
|
||||
burndown: SprintBurndown!
|
||||
closedAt: String
|
||||
createdAt: String!
|
||||
|
||||
"""Last day (included), YYYY-MM-DD"""
|
||||
endDate: String!
|
||||
goal: String
|
||||
id: ID!
|
||||
|
||||
"""Tickets in this sprint, in backlog order"""
|
||||
issues(limit: Int! = 100, offset: Int! = 0): IssuePage!
|
||||
name: String!
|
||||
progress: SprintProgress!
|
||||
projectId: ID!
|
||||
|
||||
"""First day, YYYY-MM-DD"""
|
||||
startDate: String!
|
||||
startedAt: String
|
||||
status: SprintStatus!
|
||||
version: Int!
|
||||
}
|
||||
|
||||
"""
|
||||
Per day of the sprint: tickets in it, still open, and their points (null: days to come). `ideal` falls from the first day to zero.
|
||||
"""
|
||||
type SprintBurndown {
|
||||
dates: [String!]!
|
||||
ideal: [Float!]!
|
||||
remaining: [Int]!
|
||||
remainingPoints: [Int]!
|
||||
scope: [Int]!
|
||||
sprintId: ID!
|
||||
}
|
||||
|
||||
type SprintChange {
|
||||
action: String!
|
||||
actorId: ID!
|
||||
|
||||
"""Null: deleted"""
|
||||
sprint: Sprint
|
||||
sprintId: ID!
|
||||
version: Int!
|
||||
}
|
||||
|
||||
type SprintProgress {
|
||||
"""Done or cancelled"""
|
||||
finished: Int!
|
||||
finishedStoryPoints: Int!
|
||||
percent: Int!
|
||||
storyPoints: Int!
|
||||
total: Int!
|
||||
}
|
||||
|
||||
type SprintRef {
|
||||
id: ID!
|
||||
name: String!
|
||||
}
|
||||
|
||||
"""PLANNED, ACTIVE (one per project at a time) or CLOSED"""
|
||||
enum SprintStatus {
|
||||
ACTIVE
|
||||
CLOSED
|
||||
PLANNED
|
||||
}
|
||||
|
||||
"""
|
||||
What a status means, whatever each project calls it. DONE and CANCELLED are finished.
|
||||
"""
|
||||
@@ -820,6 +928,7 @@ type Subscription {
|
||||
notificationAdded: Notification!
|
||||
projectChanged(projectId: ID!): ProjectChange!
|
||||
releaseChanged(projectId: ID!): ReleaseChange!
|
||||
sprintChanged(projectId: ID!): SprintChange!
|
||||
}
|
||||
|
||||
type Throughput {
|
||||
@@ -865,6 +974,14 @@ input UpdateReleaseInput {
|
||||
targetDate: String
|
||||
}
|
||||
|
||||
input UpdateSprintInput {
|
||||
endDate: String
|
||||
goal: String
|
||||
name: String
|
||||
sprintId: ID!
|
||||
startDate: String
|
||||
}
|
||||
|
||||
"""User managed by the Pireon identity server"""
|
||||
type User {
|
||||
email: String!
|
||||
|
||||
Reference in New Issue
Block a user