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:
@@ -0,0 +1,84 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
CommandResult,
|
||||
CreateSprintInput,
|
||||
Sprint,
|
||||
SprintBurndown,
|
||||
SprintsGateway,
|
||||
UpdateSprintInput,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import {
|
||||
CloseSprintDocument,
|
||||
CreateSprintDocument,
|
||||
DeleteSprintDocument,
|
||||
SetIssueSprintDocument,
|
||||
SprintBurndownDocument,
|
||||
SprintsDocument,
|
||||
StartSprintDocument,
|
||||
UpdateSprintDocument,
|
||||
} from '../graphql/generated';
|
||||
import { GraphqlOperations } from './graphql-operations';
|
||||
import { toCommandResult } from './mappers';
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlSprintsGateway extends SprintsGateway {
|
||||
private readonly gql = inject(GraphqlOperations);
|
||||
|
||||
list(projectId: string): Observable<Sprint[]> {
|
||||
return this.gql
|
||||
.query(SprintsDocument, { projectId })
|
||||
.pipe(map((d) => d.sprints as Sprint[]));
|
||||
}
|
||||
|
||||
burndown(sprintId: string): Observable<SprintBurndown> {
|
||||
return this.gql
|
||||
.query(SprintBurndownDocument, { id: sprintId })
|
||||
.pipe(map((d) => d.sprint.burndown));
|
||||
}
|
||||
|
||||
create(input: CreateSprintInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(CreateSprintDocument, { input })
|
||||
.pipe(map((d) => toCommandResult(d.createSprint)));
|
||||
}
|
||||
|
||||
update(input: UpdateSprintInput): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(UpdateSprintDocument, { input })
|
||||
.pipe(map((d) => toCommandResult(d.updateSprint)));
|
||||
}
|
||||
|
||||
start(sprintId: string): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(StartSprintDocument, { sprintId })
|
||||
.pipe(map((d) => toCommandResult(d.startSprint)));
|
||||
}
|
||||
|
||||
close(
|
||||
sprintId: string,
|
||||
carryOverTo: string | null,
|
||||
): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(CloseSprintDocument, {
|
||||
sprintId,
|
||||
carryOverToSprintId: carryOverTo,
|
||||
})
|
||||
.pipe(map((d) => toCommandResult(d.closeSprint)));
|
||||
}
|
||||
|
||||
remove(sprintId: string): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(DeleteSprintDocument, { sprintId })
|
||||
.pipe(map((d) => toCommandResult(d.deleteSprint)));
|
||||
}
|
||||
|
||||
setIssueSprint(
|
||||
issueId: string,
|
||||
sprintId: string | null,
|
||||
): Observable<CommandResult> {
|
||||
return this.gql
|
||||
.mutate(SetIssueSprintDocument, { issueId, sprintId })
|
||||
.pipe(map((d) => toCommandResult(d.setIssueSprint)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user