feat: saved views of my work, across my projects

A person saves named views of «my work»: projects, assigned to me, that I
follow, status categories, priorities, bugs only, overdue and text. They
are private (Mongo my_views, no history), listed and deleted by their
owner (GraphQL myViews / saveMyView / deleteMyView, REST /api/me/views),
and run as the cross-project search, which gains project, priority,
bugsOnly and overdue filters (also in MCP my_issues). The home page shows
them as a bar above «my work», with a drawer to save a new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 14:22:20 +02:00
parent e14f445e6b
commit 5b0396bc10
46 changed files with 2648 additions and 35 deletions

View File

@@ -500,6 +500,38 @@ export class MyIssuesQueryDto extends PageQueryDto {
@IsString()
@MaxLength(200)
search?: string;
@ApiPropertyOptional({
type: [String],
description:
'Only these of my projects (ids). Repeat or separate with commas',
})
@IsOptional()
@Transform(toList)
@IsUUID('all', { each: true })
project?: string[];
@ApiPropertyOptional({
enum: IssuePriority,
enumName: 'IssuePriority',
isArray: true,
})
@IsOptional()
@Transform(toList)
@IsEnum(IssuePriority, { each: true })
priority?: IssuePriority[];
@ApiPropertyOptional({ description: 'Only types with the bug icon' })
@IsOptional()
@Transform(toBoolean)
@IsBoolean()
bugsOnly?: boolean;
@ApiPropertyOptional({ description: 'Only open tickets past their due date' })
@IsOptional()
@Transform(toBoolean)
@IsBoolean()
overdue?: boolean;
}
export class UpcomingReleasesQueryDto {
@@ -898,6 +930,69 @@ export class SavedFilterCriteriaDto {
search?: string | null;
}
export class MyViewCriteriaDto {
@ApiPropertyOptional({
type: [String],
format: 'uuid',
description: 'Only these of my projects (empty: all)',
})
@IsOptional()
@IsArray()
@ArrayMaxSize(50)
@IsUUID('all', { each: true })
projectIds?: string[];
@ApiPropertyOptional() @IsOptional() @IsBoolean() assignedToMe?: boolean;
@ApiPropertyOptional({ description: 'Only the tickets I follow' })
@IsOptional()
@IsBoolean()
watching?: boolean;
@ApiPropertyOptional({
enum: StatusCategory,
enumName: 'StatusCategory',
isArray: true,
})
@IsOptional()
@IsEnum(StatusCategory, { each: true })
categories?: StatusCategory[];
@ApiPropertyOptional({
enum: IssuePriority,
enumName: 'IssuePriority',
isArray: true,
})
@IsOptional()
@IsEnum(IssuePriority, { each: true })
priorities?: IssuePriority[];
@ApiPropertyOptional({ description: 'Only types with the bug icon' })
@IsOptional()
@IsBoolean()
bugsOnly?: boolean;
@ApiPropertyOptional({ description: 'Only open tickets past due' })
@IsOptional()
@IsBoolean()
overdue?: boolean;
@ApiPropertyOptional({ maxLength: 200 })
@IsOptional()
@IsString()
@MaxLength(200)
search?: string;
}
export class SaveMyViewDto {
@ApiProperty({
example: 'Bugs críticos de todos mis proyectos',
maxLength: 60,
})
@IsString()
@MinLength(1)
@MaxLength(60)
name!: string;
@ApiProperty({ type: MyViewCriteriaDto })
@ValidateNested()
@Type(() => MyViewCriteriaDto)
criteria!: MyViewCriteriaDto;
}
export class SaveFilterDto {
@ApiProperty({ example: 'Bugs críticos de frontend', maxLength: 60 })
@IsString()