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:
2026-10-08 14:14:38 +02:00
parent 0606f53026
commit 298383f19f
46 changed files with 2922 additions and 26 deletions

View File

@@ -7,6 +7,7 @@ import {
IssueTimelineEntry,
ProjectReadModel,
ReleaseReadModel,
SavedFilterReadModel,
implicitRank,
} from '@task-stream/api/tracker/domain';
import { Schema } from 'mongoose';
@@ -26,6 +27,7 @@ export type ReleaseDoc = Doc<ReleaseReadModel>;
export type ActivityDoc = Doc<ActivityReadModel>;
export type TimelineDoc = Doc<IssueTimelineEntry>;
export type NotificationDoc = Doc<NotificationReadModel>;
export type SavedFilterDoc = Doc<SavedFilterReadModel>;
export interface NotificationSettingsDoc {
_id: string;
email: boolean;
@@ -40,6 +42,7 @@ export const ReadModels = {
Timeline: 'IssueTimelineReadModel',
Notification: 'NotificationReadModel',
NotificationSettings: 'NotificationSettings',
SavedFilter: 'SavedFilter',
} as const;
const userRef = { _id: false, id: String, name: String };
@@ -160,6 +163,20 @@ export const NotificationSettingsSchema = new Schema<NotificationSettingsDoc>(
{ ...loose, collection: 'notification_settings' },
);
export const SavedFilterSchema = new Schema<SavedFilterDoc>(
{
_id: String,
projectId: String,
owner: userRef,
name: String,
shared: Boolean,
createdAt: String,
},
{ ...loose, collection: 'saved_filters' },
);
SavedFilterSchema.index({ projectId: 1, 'owner.id': 1 });
SavedFilterSchema.index({ projectId: 1, shared: 1 });
export const READ_MODEL_SCHEMAS = [
{ name: ReadModels.Project, schema: ProjectSchema },
{ name: ReadModels.Issue, schema: IssueSchema },
@@ -169,6 +186,7 @@ export const READ_MODEL_SCHEMAS = [
{ name: ReadModels.Timeline, schema: TimelineSchema },
{ name: ReadModels.Notification, schema: NotificationSchema },
{ name: ReadModels.NotificationSettings, schema: NotificationSettingsSchema },
{ name: ReadModels.SavedFilter, schema: SavedFilterSchema },
];
export function fromDoc<T extends { _id: string }>(