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

@@ -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;