feat: pinned tickets, at hand on the home page and in search

Each person pins up to 20 tickets from the ticket's page («Fijar»). They
show as a strip under the projects on the home page and in the search
palette before typing. Private and without history, like the views of
«my work» (Mongo `pinned_issues`); tickets in the trash or out of reach
are left out, and when the list is full the unreadable ones are dropped
first.

API: GET /api/me/pins, PUT/DELETE /api/issues/{issue}/pin; GraphQL
myPins, pinIssue, unpinIssue and Issue.pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 10:15:28 +02:00
parent 423f82c1a3
commit dfa42a8a1a
32 changed files with 1612 additions and 46 deletions

View File

@@ -43,6 +43,7 @@ export const issuePageEvents = eventGroup({
replyTo?: string | null;
}>(),
watchRequested: type<{ issueId: string; watching: boolean }>(),
pinRequested: type<{ issueId: string; pinned: boolean }>(),
trashRequested: type<{ issueId: string; trashed: boolean }>(),
/** Place the ticket under another one by its key (e.g. WEB-3); null: out. */
parentRequested: type<{ issueId: string; parentKey: string | null }>(),

View File

@@ -120,6 +120,13 @@ export class IssueFacade {
);
}
/** Pin the ticket for me (or unpin it): at hand on my home page and in search. */
setPinned(pinned: boolean): void {
this.withIssue((issueId) =>
this.dispatch.pinRequested({ issueId, pinned }),
);
}
/** Places the open ticket under another one by its key; null: back to the top. */
/**
* Tickets to link to or hang the open one from: the most recently updated

View File

@@ -337,6 +337,20 @@ export const IssueStore = signalStore(
),
),
),
events
.on(issuePageEvents.pinRequested)
.pipe(
mergeMap(({ payload }) =>
runCommand(
issues.pin(payload.issueId, payload.pinned),
payload.pinned
? 'Fijado: lo tienes a mano en el inicio y al buscar'
: 'Ya no está fijado',
done,
failed,
),
),
),
events
.on(issuePageEvents.watchRequested)
.pipe(