feat: move unfinished tickets between releases
- Bulk editing gets «Release…» (open releases or none), and the list view a release filter, also reachable from each release («Ver en la lista», ?release=). - Archiving a release asks where its unfinished tickets go, as closing a sprint does: ReleaseArchivedEvent carries `moveTo` (an open release of the same project, or none) and the saga moves them there; finished ones stay. In GraphQL, REST and the MCP's archive_release. - Chapter 10 lists the next features and the ideas to weigh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,11 @@ import {
|
||||
type PiSelectFieldOption,
|
||||
} from '@pi/ui';
|
||||
import { ProjectFacade } from '@task-stream/web/tracker/application';
|
||||
import { BulkChange, IssuePriority } from '@task-stream/web/tracker/domain';
|
||||
import {
|
||||
BulkChange,
|
||||
IssuePriority,
|
||||
ReleaseStatus,
|
||||
} from '@task-stream/web/tracker/domain';
|
||||
import { options, PRIORITY } from './copy';
|
||||
|
||||
/** "Nobody" / "no sprint" in the selects. */
|
||||
@@ -62,6 +66,9 @@ const NOBODY = '__none__';
|
||||
@case ('sprint') {
|
||||
<pi-select-field [options]="sprintOptions()" [(value)]="value" />
|
||||
}
|
||||
@case ('release') {
|
||||
<pi-select-field [options]="releaseOptions()" [(value)]="value" />
|
||||
}
|
||||
@case ('priority') {
|
||||
<pi-select-field [options]="priorityOptions" [(value)]="value" />
|
||||
}
|
||||
@@ -112,6 +119,7 @@ export class BulkBarComponent {
|
||||
{ value: 'status', label: 'Mover a…' },
|
||||
{ value: 'assignee', label: 'Asignar a…' },
|
||||
{ value: 'sprint', label: 'Sprint…' },
|
||||
{ value: 'release', label: 'Release…' },
|
||||
{ value: 'priority', label: 'Prioridad…' },
|
||||
{ value: 'label', label: 'Añadir etiqueta…' },
|
||||
];
|
||||
@@ -141,6 +149,16 @@ export class BulkBarComponent {
|
||||
.map((s) => ({ value: s.id, label: s.name })),
|
||||
],
|
||||
);
|
||||
/** Only open releases take tickets. */
|
||||
protected readonly releaseOptions = computed<PiSelectFieldOption<string>[]>(
|
||||
() => [
|
||||
{ value: NOBODY, label: 'Ninguna release' },
|
||||
...this.project
|
||||
.releases()
|
||||
.filter((r) => r.status === ReleaseStatus.PLANNED)
|
||||
.map((r) => ({ value: r.id, label: r.name })),
|
||||
],
|
||||
);
|
||||
protected readonly priorityOptions = options(PRIORITY);
|
||||
protected readonly canApply = computed(() =>
|
||||
this.field() === 'label' ? !!this.label().trim() : !!this.value(),
|
||||
@@ -159,6 +177,9 @@ export class BulkBarComponent {
|
||||
case 'sprint':
|
||||
change.sprintId = value === NOBODY ? null : value;
|
||||
break;
|
||||
case 'release':
|
||||
change.releaseId = value === NOBODY ? null : value;
|
||||
break;
|
||||
case 'priority':
|
||||
if (value) change.priority = value as IssuePriority;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user