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:
@@ -39,6 +39,13 @@ export enum ReleaseStatus {
|
||||
ARCHIVED = 'ARCHIVED',
|
||||
}
|
||||
|
||||
export enum SprintStatus {
|
||||
PLANNED = 'PLANNED',
|
||||
/** Under way: one per project at a time. */
|
||||
ACTIVE = 'ACTIVE',
|
||||
CLOSED = 'CLOSED',
|
||||
}
|
||||
|
||||
/** Why someone is told. */
|
||||
export enum NotificationKind {
|
||||
ASSIGNED = 'ASSIGNED',
|
||||
|
||||
@@ -86,4 +86,22 @@ export interface BoardFilter {
|
||||
labels?: string[];
|
||||
/** Matches key, title or description. */
|
||||
search?: string;
|
||||
sprintId?: string;
|
||||
}
|
||||
|
||||
export interface CreateSprintInput {
|
||||
projectId: string;
|
||||
name: string;
|
||||
goal?: string | null;
|
||||
/** `YYYY-MM-DD`. */
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
}
|
||||
|
||||
export interface UpdateSprintInput {
|
||||
sprintId: string;
|
||||
name?: string;
|
||||
goal?: string | null;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
IssuePriority,
|
||||
ProjectRole,
|
||||
ReleaseStatus,
|
||||
SprintStatus,
|
||||
StatusCategory,
|
||||
} from './enums';
|
||||
import { ProjectSettings } from './workflow';
|
||||
@@ -107,6 +108,7 @@ export interface IssueSummary {
|
||||
priority: IssuePriority;
|
||||
assignee: UserRef | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
labels: string[];
|
||||
/** The epic or ticket this one breaks down. */
|
||||
parent: IssueParent | null;
|
||||
@@ -159,6 +161,40 @@ export interface Comment {
|
||||
editedAt: string | null;
|
||||
}
|
||||
|
||||
export interface SprintProgress {
|
||||
total: number;
|
||||
/** Done or cancelled. */
|
||||
finished: number;
|
||||
percent: number;
|
||||
storyPoints: number;
|
||||
finishedStoryPoints: number;
|
||||
}
|
||||
|
||||
/** A time-boxed iteration of a project. */
|
||||
export interface Sprint {
|
||||
id: string;
|
||||
projectId: string;
|
||||
name: string;
|
||||
goal: string | null;
|
||||
/** `YYYY-MM-DD`, both included. */
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
status: SprintStatus;
|
||||
startedAt: string | null;
|
||||
closedAt: string | null;
|
||||
progress: SprintProgress;
|
||||
}
|
||||
|
||||
/** Per day of a sprint; null for the days to come. */
|
||||
export interface SprintBurndown {
|
||||
sprintId: string;
|
||||
dates: string[];
|
||||
scope: (number | null)[];
|
||||
remaining: (number | null)[];
|
||||
remainingPoints: (number | null)[];
|
||||
ideal: number[];
|
||||
}
|
||||
|
||||
export interface Release {
|
||||
id: string;
|
||||
projectId: string;
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
Comment,
|
||||
Epic,
|
||||
SavedFilter,
|
||||
Sprint,
|
||||
SprintBurndown,
|
||||
SavedFilterCriteria,
|
||||
CommandResult,
|
||||
Issue,
|
||||
@@ -32,6 +34,8 @@ import {
|
||||
MyIssuesFilter,
|
||||
UpdateIssueInput,
|
||||
UpdateProjectInput,
|
||||
CreateSprintInput,
|
||||
UpdateSprintInput,
|
||||
} from '../models/inputs';
|
||||
|
||||
/*
|
||||
@@ -173,6 +177,25 @@ export abstract class ReleasesGateway {
|
||||
abstract burnup(releaseId: string): Observable<ReleaseBurnup>;
|
||||
}
|
||||
|
||||
export abstract class SprintsGateway {
|
||||
/** With their progress: under way, planned, then closed. */
|
||||
abstract list(projectId: string): Observable<Sprint[]>;
|
||||
abstract burndown(sprintId: string): Observable<SprintBurndown>;
|
||||
abstract create(input: CreateSprintInput): Observable<CommandResult>;
|
||||
abstract update(input: UpdateSprintInput): Observable<CommandResult>;
|
||||
abstract start(sprintId: string): Observable<CommandResult>;
|
||||
/** Unfinished tickets go to `carryOverTo` (null: the backlog). */
|
||||
abstract close(
|
||||
sprintId: string,
|
||||
carryOverTo: string | null,
|
||||
): Observable<CommandResult>;
|
||||
abstract remove(sprintId: string): Observable<CommandResult>;
|
||||
abstract setIssueSprint(
|
||||
issueId: string,
|
||||
sprintId: string | null,
|
||||
): Observable<CommandResult>;
|
||||
}
|
||||
|
||||
export abstract class ActivityGateway {
|
||||
abstract list(projectId: string, issueId?: string): Observable<Activity[]>;
|
||||
}
|
||||
@@ -219,6 +242,8 @@ export abstract class TrackerRealtimeGateway {
|
||||
abstract commentChanges(issueId: string): Observable<CommentChanged>;
|
||||
abstract activity(projectId: string): Observable<Activity>;
|
||||
abstract releaseChanges(projectId: string): Observable<Release>;
|
||||
/** Something about a sprint of the project changed (or it was deleted). */
|
||||
abstract sprintChanges(projectId: string): Observable<{ sprintId: string }>;
|
||||
abstract projectChanges(projectId: string): Observable<ProjectChanged>;
|
||||
abstract myProjectsChanges(): Observable<ProjectChanged>;
|
||||
abstract notifications(): Observable<Notification>;
|
||||
|
||||
Reference in New Issue
Block a user