feat: what is new since my last visit, and turning a comment into a ticket
Opening a ticket notes the visit and answers the one before (issue_visits), so the page marks others' comments and changes since. A comment can become a ticket of its own, as a subtask or linked; it remembers where it went. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
ClosePollDto,
|
||||
ReactToCommentDto,
|
||||
ResolveThreadDto,
|
||||
TurnCommentIntoIssueDto,
|
||||
VotePollDto,
|
||||
} from './dto/requests.dto';
|
||||
import { CommandResultDto } from './dto/responses.dto';
|
||||
@@ -117,6 +118,28 @@ export class CommentsController {
|
||||
return this.comments.closePoll(me.id, commentId, body.decide ?? true);
|
||||
}
|
||||
|
||||
@Post(':commentId/issue')
|
||||
@ApiOperation({
|
||||
summary: 'Make a ticket of a comment',
|
||||
description:
|
||||
'Its text becomes the description, as a subtask of its ticket or linked to it (RELATES_TO); the comment shows where it went. Answers the new ticket. Whoever can change tickets.',
|
||||
})
|
||||
@ApiOkResponse({ type: CommandResultDto })
|
||||
@ApiCommandErrors()
|
||||
turnIntoIssue(
|
||||
@CurrentUser() me: User,
|
||||
@Param('commentId') commentId: string,
|
||||
@Body() body: TurnCommentIntoIssueDto,
|
||||
) {
|
||||
return this.comments.turnIntoIssue(
|
||||
me.id,
|
||||
commentId,
|
||||
body.title,
|
||||
body.type,
|
||||
body.asSubtask ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@Put(':commentId/resolution')
|
||||
@ApiOperation({
|
||||
summary: 'Settle a conversation (a top-level comment and its replies)',
|
||||
|
||||
@@ -1640,6 +1640,27 @@ export class SetReminderDto {
|
||||
note?: string;
|
||||
}
|
||||
|
||||
export class TurnCommentIntoIssueDto {
|
||||
@ApiProperty({ example: 'También falla en Android', maxLength: 200 })
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(200)
|
||||
title!: string;
|
||||
|
||||
@ApiProperty({ description: "One of the project's ticket type ids" })
|
||||
@IsString()
|
||||
type!: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
default: false,
|
||||
description:
|
||||
'Hang it from the ticket of the comment; otherwise it is linked to it (RELATES_TO)',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
asSubtask?: boolean;
|
||||
}
|
||||
|
||||
export class ResolveThreadDto {
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
|
||||
@@ -628,6 +628,10 @@ export class CommentPollDto {
|
||||
winner!: number | null;
|
||||
}
|
||||
|
||||
export class CommentSpawnDto {
|
||||
@ApiProperty() id!: string;
|
||||
@ApiProperty({ example: 'WEB-45' }) key!: string;
|
||||
}
|
||||
export class CommentDto {
|
||||
@ApiProperty({ format: 'uuid' }) id!: string;
|
||||
@ApiProperty({ format: 'uuid' }) issueId!: string;
|
||||
@@ -679,6 +683,11 @@ export class CommentDto {
|
||||
resolvedBy!: UserRefDto | null;
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
|
||||
resolvedAt!: string | null;
|
||||
@ApiProperty({
|
||||
type: () => [CommentSpawnDto],
|
||||
description: 'Tickets made from it («Convertir en ticket»), oldest first',
|
||||
})
|
||||
spawned!: CommentSpawnDto[];
|
||||
@ApiProperty({ format: 'date-time' }) createdAt!: string;
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
|
||||
editedAt!: string | null;
|
||||
@@ -1447,6 +1456,16 @@ export class IssueReminderDto {
|
||||
@ApiProperty({ type: String, nullable: true }) note!: string | null;
|
||||
}
|
||||
|
||||
export class IssueVisitDto {
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
format: 'date-time',
|
||||
nullable: true,
|
||||
description: 'My visit before this one (null: first time)',
|
||||
})
|
||||
previousVisit!: string | null;
|
||||
}
|
||||
|
||||
export class ReminderOnIssueDto {
|
||||
@ApiProperty({ type: IssueReminderDto }) reminder!: IssueReminderDto;
|
||||
@ApiProperty({ type: () => IssueDto }) issue!: IssueDto;
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
PinsService,
|
||||
TreeFollowsService,
|
||||
IssueRemindersService,
|
||||
IssueVisitsService,
|
||||
MyDayService,
|
||||
PresenceService,
|
||||
ProjectsService,
|
||||
@@ -73,6 +74,7 @@ import {
|
||||
CommentPageDto,
|
||||
DecisionDto,
|
||||
IssueReminderDto,
|
||||
IssueVisitDto,
|
||||
MyDayDto,
|
||||
ReminderOnIssueDto,
|
||||
EpicDto,
|
||||
@@ -112,6 +114,7 @@ export class IssuesController {
|
||||
private readonly treeFollows: TreeFollowsService,
|
||||
private readonly reminders: IssueRemindersService,
|
||||
private readonly myDayService: MyDayService,
|
||||
private readonly visits: IssueVisitsService,
|
||||
) {}
|
||||
|
||||
@Get('me/home')
|
||||
@@ -382,6 +385,27 @@ export class IssuesController {
|
||||
);
|
||||
}
|
||||
|
||||
@Put('issues/:issue/visit')
|
||||
@ISSUE
|
||||
@ApiOperation({
|
||||
summary: 'I opened this ticket now',
|
||||
description:
|
||||
'Answers when I had opened it before (null: never), to show what others did since. Private to me.',
|
||||
})
|
||||
@ApiOkResponse({ type: IssueVisitDto })
|
||||
@ApiCommandErrors()
|
||||
async visit(
|
||||
@CurrentUser() me: User,
|
||||
@Param('issue') issue: string,
|
||||
): Promise<IssueVisitDto> {
|
||||
return {
|
||||
previousVisit: await this.visits.visit(
|
||||
me.id,
|
||||
await this.refs.issueId(me.id, issue),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@Put('issues/:issue/follow-tree')
|
||||
@HttpCode(204)
|
||||
@ISSUE
|
||||
|
||||
Reference in New Issue
Block a user