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:
2026-10-21 21:05:44 +02:00
parent 429b52e6f5
commit 285bd5c454
50 changed files with 1953 additions and 79 deletions

View File

@@ -3683,6 +3683,108 @@
]
}
},
"/api/issues/{issue}/visit": {
"put": {
"description": "Answers when I had opened it before (null: never), to show what others did since. Private to me.",
"operationId": "IssuesController_visit",
"parameters": [
{
"name": "issue",
"required": true,
"in": "path",
"description": "Ticket id or key",
"schema": {
"example": "WEB-12",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/IssueVisitDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "I opened this ticket now",
"tags": [
"Issues"
]
}
},
"/api/issues/{issue}/follow-tree": {
"put": {
"description": "Told of its changes and of every ticket hanging from it, at any depth, also the ones added later. Private to me.",
@@ -8436,6 +8538,116 @@
]
}
},
"/api/comments/{commentId}/issue": {
"post": {
"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.",
"operationId": "CommentsController_turnIntoIssue",
"parameters": [
{
"name": "commentId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TurnCommentIntoIssueDto"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CommandResultDto"
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"401": {
"description": "Missing or rejected Pireon token",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"403": {
"description": "Not a member of the project (or not allowed)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"404": {
"description": "Not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"409": {
"description": "Someone changed it at the same time: retry",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
},
"422": {
"description": "Business rule (e.g. a transition the workflow forbids)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorDto"
}
}
}
}
},
"security": [
{
"pireon-oauth": []
},
{
"pireon": []
}
],
"summary": "Make a ticket of a comment",
"tags": [
"Comments"
]
}
},
"/api/comments/{commentId}/resolution": {
"put": {
"description": "Whoever can change the ticket. A conclusion is added as a reply marked as a decision. The web folds a settled conversation.",
@@ -17708,6 +17920,20 @@
"verdict"
]
},
"IssueVisitDto": {
"type": "object",
"properties": {
"previousVisit": {
"type": "string",
"format": "date-time",
"nullable": true,
"description": "My visit before this one (null: first time)"
}
},
"required": [
"previousVisit"
]
},
"IssuePageDto": {
"type": "object",
"properties": {
@@ -18190,6 +18416,22 @@
"winner"
]
},
"CommentSpawnDto": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"key": {
"type": "string",
"example": "WEB-45"
}
},
"required": [
"id",
"key"
]
},
"CommentDto": {
"type": "object",
"properties": {
@@ -18272,6 +18514,13 @@
"nullable": true,
"format": "date-time"
},
"spawned": {
"description": "Tickets made from it («Convertir en ticket»), oldest first",
"type": "array",
"items": {
"$ref": "#/components/schemas/CommentSpawnDto"
}
},
"createdAt": {
"type": "string",
"format": "date-time"
@@ -18300,6 +18549,7 @@
"resolved",
"resolvedBy",
"resolvedAt",
"spawned",
"createdAt",
"editedAt",
"version"
@@ -18476,6 +18726,29 @@
}
}
},
"TurnCommentIntoIssueDto": {
"type": "object",
"properties": {
"title": {
"type": "string",
"example": "También falla en Android",
"maxLength": 200
},
"type": {
"type": "string",
"description": "One of the project's ticket type ids"
},
"asSubtask": {
"type": "boolean",
"default": false,
"description": "Hang it from the ticket of the comment; otherwise it is linked to it (RELATES_TO)"
}
},
"required": [
"title",
"type"
]
},
"ResolveThreadDto": {
"type": "object",
"properties": {