feat: saved board views and richer board filters
The board now filters by text, type, priority and unassigned tickets as well. Any combination can be saved as a named view, private or shared with the project, and opened again with one click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1007,6 +1007,58 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Ticket type ids",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "priority",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/IssuePriority"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Matches key, title or description",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unassigned",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Only tickets without assignee",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "perColumn",
|
||||
"required": false,
|
||||
@@ -5933,6 +5985,293 @@
|
||||
"Users"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/projects/{project}/filters": {
|
||||
"get": {
|
||||
"operationId": "SavedFiltersController_list",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "project",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"description": "Project id or key",
|
||||
"schema": {
|
||||
"example": "WEB",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SavedFilterDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"pireon-oauth": []
|
||||
},
|
||||
{
|
||||
"pireon": []
|
||||
}
|
||||
],
|
||||
"summary": "My views of the project's board, then the shared ones",
|
||||
"tags": [
|
||||
"Saved filters"
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"description": "Project members only. `shared` makes it visible to the whole project (not for viewers).",
|
||||
"operationId": "SavedFiltersController_save",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "project",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"description": "Project id or key",
|
||||
"schema": {
|
||||
"example": "WEB",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SaveFilterDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SavedFilterIdDto"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": "Save board filters under a name",
|
||||
"tags": [
|
||||
"Saved filters"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/filters/{id}": {
|
||||
"delete": {
|
||||
"operationId": "SavedFiltersController_delete",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
},
|
||||
"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": "Delete a saved view (whoever saved it, or an admin if shared)",
|
||||
"tags": [
|
||||
"Saved filters"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
@@ -8267,6 +8606,174 @@
|
||||
"required": [
|
||||
"email"
|
||||
]
|
||||
},
|
||||
"SavedFilterCriteriaResponseDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"areas": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"priorities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/IssuePriority"
|
||||
}
|
||||
},
|
||||
"assigneeId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"unassigned": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"releaseId": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"format": "uuid"
|
||||
},
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"SavedFilterDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "#/components/schemas/UserRefDto"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "Bugs críticos de frontend"
|
||||
},
|
||||
"shared": {
|
||||
"type": "boolean",
|
||||
"description": "Visible to the whole project"
|
||||
},
|
||||
"criteria": {
|
||||
"$ref": "#/components/schemas/SavedFilterCriteriaResponseDto"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"projectId",
|
||||
"owner",
|
||||
"name",
|
||||
"shared",
|
||||
"criteria",
|
||||
"createdAt"
|
||||
]
|
||||
},
|
||||
"SavedFilterCriteriaDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"types": {
|
||||
"description": "Ticket type ids",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"areas": {
|
||||
"description": "Area ids",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"priorities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/IssuePriority"
|
||||
}
|
||||
},
|
||||
"assigneeId": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Pireon user id"
|
||||
},
|
||||
"unassigned": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"releaseId": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"format": "uuid"
|
||||
},
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"maxLength": 200
|
||||
}
|
||||
}
|
||||
},
|
||||
"SaveFilterDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"example": "Bugs críticos de frontend",
|
||||
"maxLength": 60
|
||||
},
|
||||
"shared": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
"description": "Visible to the whole project (members, not viewers, share)"
|
||||
},
|
||||
"criteria": {
|
||||
"$ref": "#/components/schemas/SavedFilterCriteriaDto"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"criteria"
|
||||
]
|
||||
},
|
||||
"SavedFilterIdDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,6 +399,9 @@ type Mutation {
|
||||
Delete the project (owner only), with its tickets, comments and releases. `confirmKey` must be the project key.
|
||||
"""
|
||||
deleteProject(confirmKey: String!, projectId: ID!): CommandResult!
|
||||
|
||||
"""Delete a view: whoever saved it, or an admin if it is shared"""
|
||||
deleteSavedFilter(id: ID!): Boolean!
|
||||
editComment(body: String!, commentId: ID!): CommandResult!
|
||||
|
||||
"""Relate tickets, e.g. FE DEPENDS_ON BE, QA TESTS FE"""
|
||||
@@ -422,6 +425,9 @@ type Mutation {
|
||||
removeChecklistItem(issueId: ID!, itemId: ID!): CommandResult!
|
||||
removeProjectMember(projectId: ID!, userId: ID!): CommandResult!
|
||||
|
||||
"""Save the current board filters under a name (members of the project)"""
|
||||
saveFilter(input: SaveFilterInput!): ID!
|
||||
|
||||
"""
|
||||
Place a ticket under an epic or another ticket of the same project (null: back to the top). Three levels at most: epic → ticket → subtask.
|
||||
"""
|
||||
@@ -501,7 +507,19 @@ type Project {
|
||||
analytics(days: Int! = 90): ProjectAnalytics!
|
||||
|
||||
"""Kanban board, one column per status"""
|
||||
board(area: String, assigneeId: ID, perColumn: Int! = 50, releaseId: ID): [BoardColumn!]!
|
||||
board(
|
||||
area: String
|
||||
assigneeId: ID
|
||||
labels: [String!]
|
||||
perColumn: Int! = 50
|
||||
priorities: [IssuePriority!]
|
||||
releaseId: ID
|
||||
|
||||
"""Matches key, title or description"""
|
||||
search: String
|
||||
types: [String!]
|
||||
unassigned: Boolean
|
||||
): [BoardColumn!]!
|
||||
createdAt: String!
|
||||
description: String
|
||||
id: ID!
|
||||
@@ -648,6 +666,9 @@ type Query {
|
||||
publicProjects: [Project!]!
|
||||
release(id: ID!): Release!
|
||||
|
||||
"""My views of the project's board, then the shared ones"""
|
||||
savedFilters(projectId: ID!): [SavedFilter!]!
|
||||
|
||||
"""Planned releases of all my projects, soonest target date first"""
|
||||
upcomingReleases(limit: Int! = 10): [Release!]!
|
||||
user(id: String!): User
|
||||
@@ -718,6 +739,51 @@ enum ReleaseStatus {
|
||||
RELEASED
|
||||
}
|
||||
|
||||
input SaveFilterInput {
|
||||
criteria: SavedFilterCriteriaInput!
|
||||
name: String!
|
||||
projectId: ID!
|
||||
|
||||
"""Visible to the whole project (members, not viewers, share)"""
|
||||
shared: Boolean! = false
|
||||
}
|
||||
|
||||
"""
|
||||
A named view of the board: private to whoever saved it, or shared with the project
|
||||
"""
|
||||
type SavedFilter {
|
||||
createdAt: String!
|
||||
criteria: SavedFilterCriteria!
|
||||
id: ID!
|
||||
name: String!
|
||||
owner: UserRef!
|
||||
projectId: ID!
|
||||
shared: Boolean!
|
||||
}
|
||||
|
||||
"""What a saved view narrows the board to; empty: everything"""
|
||||
type SavedFilterCriteria {
|
||||
areas: [String!]
|
||||
assigneeId: ID
|
||||
labels: [String!]
|
||||
priorities: [IssuePriority!]
|
||||
releaseId: ID
|
||||
search: String
|
||||
types: [String!]
|
||||
unassigned: Boolean
|
||||
}
|
||||
|
||||
input SavedFilterCriteriaInput {
|
||||
areas: [String!]
|
||||
assigneeId: ID
|
||||
labels: [String!]
|
||||
priorities: [IssuePriority!]
|
||||
releaseId: ID
|
||||
search: String
|
||||
types: [String!]
|
||||
unassigned: Boolean
|
||||
}
|
||||
|
||||
"""
|
||||
What a status means, whatever each project calls it. DONE and CANCELLED are finished.
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user