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:
@@ -1188,6 +1188,44 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/me/views": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** My saved views of my work, across my projects (by name) */
|
||||
get: operations["SavedFiltersController_myViews"];
|
||||
put?: never;
|
||||
/**
|
||||
* Save a view of my work under a name
|
||||
* @description Run it with GET /api/me/issues and the same criteria (project, priority, bugsOnly, overdue…).
|
||||
*/
|
||||
post: operations["SavedFiltersController_saveMyView"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/me/views/{id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post?: never;
|
||||
/** Delete one of my views */
|
||||
delete: operations["SavedFiltersController_deleteMyView"];
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/projects/{project}/sprints": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -2879,6 +2917,44 @@ export interface components {
|
||||
/** Format: uuid */
|
||||
id: string;
|
||||
};
|
||||
MyViewCriteriaResponseDto: {
|
||||
projectIds?: string[];
|
||||
assignedToMe?: boolean;
|
||||
watching?: boolean;
|
||||
categories?: components["schemas"]["StatusCategory"][];
|
||||
priorities?: components["schemas"]["IssuePriority"][];
|
||||
bugsOnly?: boolean;
|
||||
overdue?: boolean;
|
||||
search?: string;
|
||||
};
|
||||
MyViewDto: {
|
||||
/** Format: uuid */
|
||||
id: string;
|
||||
/** @example Bugs críticos de todos mis proyectos */
|
||||
name: string;
|
||||
criteria: components["schemas"]["MyViewCriteriaResponseDto"];
|
||||
/** Format: date-time */
|
||||
createdAt: string;
|
||||
};
|
||||
MyViewCriteriaDto: {
|
||||
/** @description Only these of my projects (empty: all) */
|
||||
projectIds?: string[];
|
||||
assignedToMe?: boolean;
|
||||
/** @description Only the tickets I follow */
|
||||
watching?: boolean;
|
||||
categories?: components["schemas"]["StatusCategory"][];
|
||||
priorities?: components["schemas"]["IssuePriority"][];
|
||||
/** @description Only types with the bug icon */
|
||||
bugsOnly?: boolean;
|
||||
/** @description Only open tickets past due */
|
||||
overdue?: boolean;
|
||||
search?: string;
|
||||
};
|
||||
SaveMyViewDto: {
|
||||
/** @example Bugs críticos de todos mis proyectos */
|
||||
name: string;
|
||||
criteria: components["schemas"]["MyViewCriteriaDto"];
|
||||
};
|
||||
/** @enum {string} */
|
||||
SprintStatus: "PLANNED" | "ACTIVE" | "CLOSED";
|
||||
SprintDto: {
|
||||
@@ -4600,6 +4676,13 @@ export interface operations {
|
||||
category?: components["schemas"]["StatusCategory"][];
|
||||
/** @description Matches key, title or description */
|
||||
search?: string;
|
||||
/** @description Only these of my projects (ids). Repeat or separate with commas */
|
||||
project?: string[];
|
||||
priority?: components["schemas"]["IssuePriority"][];
|
||||
/** @description Only types with the bug icon */
|
||||
bugsOnly?: boolean;
|
||||
/** @description Only open tickets past their due date */
|
||||
overdue?: boolean;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
@@ -8458,6 +8541,211 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
SavedFiltersController_myViews: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["MyViewDto"][];
|
||||
};
|
||||
};
|
||||
/** @description Invalid input */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Missing or rejected Pireon token */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not a member of the project (or not allowed) */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
SavedFiltersController_saveMyView: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["SaveMyViewDto"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["SavedFilterIdDto"];
|
||||
};
|
||||
};
|
||||
/** @description Invalid input */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Missing or rejected Pireon token */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not a member of the project (or not allowed) */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Someone changed it at the same time: retry */
|
||||
409: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Business rule (e.g. a transition the workflow forbids) */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
SavedFiltersController_deleteMyView: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
204: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Invalid input */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Missing or rejected Pireon token */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not a member of the project (or not allowed) */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Someone changed it at the same time: retry */
|
||||
409: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Business rule (e.g. a transition the workflow forbids) */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
SprintsController_list: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
||||
@@ -88,6 +88,15 @@ export class IssuesResolver {
|
||||
.array(CATEGORY)
|
||||
.optional()
|
||||
.describe('Default: open ones (TODO, IN_PROGRESS, QA)'),
|
||||
priorities: z.array(PRIORITY).optional(),
|
||||
bugsOnly: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe('Only types drawn with the bug icon, in each project'),
|
||||
overdue: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe('Only open tickets past their due date'),
|
||||
limit: limit(200, 50),
|
||||
}),
|
||||
annotations: READ,
|
||||
@@ -97,6 +106,9 @@ export class IssuesResolver {
|
||||
assignedToMe: boolean;
|
||||
watching: boolean;
|
||||
categories?: z.infer<typeof CATEGORY>[];
|
||||
priorities?: z.infer<typeof PRIORITY>[];
|
||||
bugsOnly?: boolean;
|
||||
overdue?: boolean;
|
||||
limit: number;
|
||||
},
|
||||
ctx: McpContext,
|
||||
@@ -109,6 +121,9 @@ export class IssuesResolver {
|
||||
assignedToMe: params.assignedToMe,
|
||||
watching: params.watching,
|
||||
category: params.categories ?? ['TODO', 'IN_PROGRESS', 'QA'],
|
||||
priority: params.priorities,
|
||||
bugsOnly: params.bugsOnly,
|
||||
overdue: params.overdue,
|
||||
limit: params.limit,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user