feat: work-in-progress limits and swimlanes on the board

Each status can carry a WIP limit in the project settings; the board shows
it next to the count, marks a column that goes over it and says so when a
card is dropped there. The board can also be grouped in rows by person,
epic, priority or type, remembered per browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 15:09:21 +02:00
parent 6aec774c92
commit 09bd8c19a3
23 changed files with 563 additions and 74 deletions

View File

@@ -14,6 +14,7 @@ export interface SettingsPayload {
color: string;
requiresReason: boolean;
archived: boolean;
wipLimit?: number | null;
}[];
transitions: { from: string; to: string[] }[];
issueTypes: {

View File

@@ -425,6 +425,15 @@ export class WorkflowStatusInput {
@Field() @IsString() color!: string;
@Field({ defaultValue: false }) @IsBoolean() requiresReason!: boolean;
@Field({ defaultValue: false }) @IsBoolean() archived!: boolean;
@Field(() => Int, {
nullable: true,
description: 'Work-in-progress limit (null: none)',
})
@IsOptional()
@IsInt()
@Min(1)
@Max(999)
wipLimit?: number | null;
}
@InputType()

View File

@@ -33,6 +33,12 @@ export class WorkflowStatusGql {
requiresReason!: boolean;
@Field({ description: 'No new tickets or transitions into it' })
archived!: boolean;
@Field(() => Int, {
nullable: true,
description:
'Work-in-progress limit: most tickets the column should hold (a warning, not a lock)',
})
wipLimit?: number | null;
}
@ObjectType('StatusTransitions')

View File

@@ -142,6 +142,18 @@ export class WorkflowStatusBodyDto {
@IsOptional()
@IsBoolean()
archived = false;
@ApiPropertyOptional({
type: Number,
nullable: true,
minimum: 1,
maximum: 999,
description: 'Work-in-progress limit (null: none)',
})
@IsOptional()
@IsInt()
@Min(1)
@Max(999)
wipLimit?: number | null;
}
export class StatusTransitionsBodyDto {

View File

@@ -39,6 +39,12 @@ export class WorkflowStatusDto {
requiresReason!: boolean;
@ApiProperty({ description: 'No new tickets or transitions into it' })
archived!: boolean;
@ApiProperty({
type: Number,
nullable: true,
description: 'Work-in-progress limit (a warning, not a lock)',
})
wipLimit!: number | null;
}
export class StatusTransitionsDto {