feat: dashboard weighs quality and splits work by a list field

The insights reading adds quality (open bugs per priority, bugs created
and closed per week for 8 weeks, QA returns and passes per area over 90
days, bugs reported in the 14 days after each of the last three
releases) and, for each list custom field, open tickets per option. The
dashboard shows both; the field to split by can be picked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 13:06:11 +02:00
parent 0d158a2ed7
commit 76312c079d
22 changed files with 1264 additions and 74 deletions

View File

@@ -1006,6 +1006,56 @@ export class BacklogHealthDto {
@ApiProperty() staleDays!: number;
}
export class PriorityCountDto {
@ApiProperty({ enum: IssuePriority, enumName: 'IssuePriority' })
priority!: IssuePriority;
@ApiProperty() count!: number;
}
export class BugWeekDto {
@ApiProperty({ format: 'date', description: 'Monday (UTC)' }) week!: string;
@ApiProperty() created!: number;
@ApiProperty() closed!: number;
}
export class QaByAreaDto {
@ApiProperty({ type: String, nullable: true, description: 'Null: no area' })
area!: string | null;
@ApiProperty() returned!: number;
@ApiProperty() passed!: number;
}
export class EscapedBugsDto {
@ApiProperty({ format: 'uuid' }) releaseId!: string;
@ApiProperty() name!: string;
@ApiProperty() releasedAt!: string;
@ApiProperty({ description: 'Bugs reported in the 14 days after' })
bugs!: number;
}
export class QualityDto {
@ApiProperty({ type: [PriorityCountDto] })
openBugsByPriority!: PriorityCountDto[];
@ApiProperty({
type: [BugWeekDto],
description: 'Last 8 weeks, oldest first',
})
bugWeeks!: BugWeekDto[];
@ApiProperty({ type: [QaByAreaDto] }) qaByArea!: QaByAreaDto[];
@ApiProperty({ type: [EscapedBugsDto] }) escaped!: EscapedBugsDto[];
}
export class OptionCountDto {
@ApiProperty() value!: string;
@ApiProperty() count!: number;
}
export class FieldBreakdownDto {
@ApiProperty() fieldId!: string;
@ApiProperty({ type: [OptionCountDto] }) counts!: OptionCountDto[];
@ApiProperty({ description: 'Open tickets with no value' }) none!: number;
}
export class ProjectInsightsDto {
@ApiProperty() generatedAt!: string;
@ApiProperty({ type: SprintPulseDto, nullable: true })
@@ -1022,4 +1072,10 @@ export class ProjectInsightsDto {
changes!: ChangesSinceDto | null;
@ApiProperty({ type: [TeamLoadDto] }) team!: TeamLoadDto[];
@ApiProperty({ type: BacklogHealthDto }) backlog!: BacklogHealthDto;
@ApiProperty({ type: QualityDto }) quality!: QualityDto;
@ApiProperty({
type: [FieldBreakdownDto],
description: 'One per active list custom field',
})
fields!: FieldBreakdownDto[];
}