feat: estimate tickets and see the team's velocity
Story points can be set on the ticket page and when creating a ticket, and show on cards, column headers, subtasks and the backlog. The Sprints tab charts the points finished per closed sprint and warns when a planned sprint holds more than the recent average. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
untracked,
|
||||
@@ -19,7 +20,7 @@ import {
|
||||
ProjectFacade,
|
||||
provideBacklogFacade,
|
||||
} from '@task-stream/web/tracker/application';
|
||||
import { IssueSummary } from '@task-stream/web/tracker/domain';
|
||||
import { IssueSummary, sumPoints } from '@task-stream/web/tracker/domain';
|
||||
import { initials, PRIORITY, statusLook, typeLook } from '../ui/copy';
|
||||
|
||||
/** /p/:key/backlog — what is still to start, in the order the team takes it. */
|
||||
@@ -43,6 +44,11 @@ import { initials, PRIORITY, statusLook, typeLook } from '../ui/copy';
|
||||
Lo que aún no ha empezado, en el orden en que se va a coger. Arrastra
|
||||
para reordenar; las columnas del tablero siguen este mismo orden.
|
||||
</p>
|
||||
@if (facade.loaded() && facade.issues().length) {
|
||||
<span class="ts-muted ts-mono"
|
||||
>{{ points() }} pts estimados · {{ unestimated() }} sin estimar</span
|
||||
>
|
||||
}
|
||||
@if (facade.loaded() && facade.total() > facade.issues().length) {
|
||||
<span class="ts-muted ts-mono"
|
||||
>{{ facade.issues().length }} de {{ facade.total() }}</span
|
||||
@@ -92,6 +98,9 @@ import { initials, PRIORITY, statusLook, typeLook } from '../ui/copy';
|
||||
<span class="ts-backlog__priority ts-muted">{{
|
||||
priorityLabel(issue)
|
||||
}}</span>
|
||||
<span class="ts-backlog__points ts-mono" title="Estimación">{{
|
||||
issue.storyPoints ?? '—'
|
||||
}}</span>
|
||||
<span
|
||||
class="ts-backlog__who"
|
||||
[title]="issue.assignee?.name ?? 'Sin asignar'"
|
||||
@@ -138,7 +147,10 @@ import { initials, PRIORITY, statusLook, typeLook } from '../ui/copy';
|
||||
}
|
||||
.ts-backlog__row {
|
||||
display: grid;
|
||||
grid-template-columns: 2rem auto auto minmax(0, 1fr) auto auto auto auto;
|
||||
grid-template-columns: 2rem auto auto minmax(
|
||||
0,
|
||||
1fr
|
||||
) auto auto 2rem auto auto;
|
||||
gap: 0.625rem;
|
||||
align-items: center;
|
||||
padding: 0.375rem 0.875rem;
|
||||
@@ -151,6 +163,7 @@ import { initials, PRIORITY, statusLook, typeLook } from '../ui/copy';
|
||||
@media (max-width: 720px) {
|
||||
grid-template-columns: 2rem auto minmax(0, 1fr) auto;
|
||||
.ts-backlog__type,
|
||||
.ts-backlog__points,
|
||||
.ts-pill,
|
||||
.ts-backlog__priority,
|
||||
.ts-backlog__who {
|
||||
@@ -180,6 +193,10 @@ import { initials, PRIORITY, statusLook, typeLook } from '../ui/copy';
|
||||
.ts-backlog__priority {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.ts-backlog__points {
|
||||
font-size: 0.8125rem;
|
||||
text-align: right;
|
||||
}
|
||||
.ts-backlog__who {
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
@@ -205,6 +222,10 @@ export class BacklogPage {
|
||||
protected readonly facade = inject(BacklogFacade);
|
||||
protected readonly project = inject(ProjectFacade);
|
||||
protected readonly initialsOf = initials;
|
||||
protected readonly points = computed(() => sumPoints(this.facade.issues()));
|
||||
protected readonly unestimated = computed(
|
||||
() => this.facade.issues().filter((i) => i.storyPoints === null).length,
|
||||
);
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
|
||||
Reference in New Issue
Block a user