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

@@ -147,6 +147,25 @@ export type RankIssueInput = {
export type ReleaseStatus = 'ARCHIVED' | 'PLANNED' | 'RELEASED';
export type SaveFilterInput = {
criteria: SavedFilterCriteriaInput;
name: string;
projectId: string;
/** Visible to the whole project (members, not viewers, share) */
shared?: boolean;
};
export type SavedFilterCriteriaInput = {
areas?: Array<string> | null | undefined;
assigneeId?: string | null | undefined;
labels?: Array<string> | null | undefined;
priorities?: Array<IssuePriority> | null | undefined;
releaseId?: string | null | undefined;
search?: string | null | undefined;
types?: Array<string> | null | undefined;
unassigned?: boolean | null | undefined;
};
/** What a status means, whatever each project calls it. DONE and CANCELLED are finished. */
export type StatusCategory =
| 'CANCELLED'
@@ -335,6 +354,11 @@ export type BoardQueryVariables = Exact<{
releaseId?: string | null | undefined;
assigneeId?: string | null | undefined;
area?: string | null | undefined;
types?: Array<string> | string | null | undefined;
priorities?: Array<IssuePriority> | IssuePriority | null | undefined;
labels?: Array<string> | string | null | undefined;
search?: string | null | undefined;
unassigned?: boolean | null | undefined;
}>;
export type BoardQuery = {
@@ -1138,6 +1162,62 @@ export type ReleaseBurnupQuery = {
};
};
export type SavedFilterFieldsFragment = {
id: string;
projectId: string;
name: string;
shared: boolean;
createdAt: string;
owner: { id: string; name: string };
criteria: {
types: Array<string> | null;
areas: Array<string> | null;
priorities: Array<IssuePriority> | null;
assigneeId: string | null;
unassigned: boolean | null;
releaseId: string | null;
labels: Array<string> | null;
search: string | null;
};
};
export type SavedFiltersQueryVariables = Exact<{
projectId: string;
}>;
export type SavedFiltersQuery = {
savedFilters: Array<{
id: string;
projectId: string;
name: string;
shared: boolean;
createdAt: string;
owner: { id: string; name: string };
criteria: {
types: Array<string> | null;
areas: Array<string> | null;
priorities: Array<IssuePriority> | null;
assigneeId: string | null;
unassigned: boolean | null;
releaseId: string | null;
labels: Array<string> | null;
search: string | null;
};
}>;
};
export type SaveFilterMutationVariables = Exact<{
input: SaveFilterInput;
}>;
export type SaveFilterMutation = { saveFilter: string };
export type DeleteSavedFilterMutationVariables = Exact<{
id: string;
}>;
export type DeleteSavedFilterMutation = { deleteSavedFilter: boolean };
export type IssueChangedSubscriptionVariables = Exact<{
projectId: string;
}>;
@@ -1949,6 +2029,74 @@ export const NotificationFieldsFragmentDoc = {
},
],
} as unknown as DocumentNode<NotificationFieldsFragment, unknown>;
export const SavedFilterFieldsFragmentDoc = {
kind: 'Document',
definitions: [
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'SavedFilterFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'SavedFilter' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'projectId' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
{ kind: 'Field', name: { kind: 'Name', value: 'shared' } },
{ kind: 'Field', name: { kind: 'Name', value: 'createdAt' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'owner' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'UserRefFields' },
},
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'criteria' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'types' } },
{ kind: 'Field', name: { kind: 'Name', value: 'areas' } },
{ kind: 'Field', name: { kind: 'Name', value: 'priorities' } },
{ kind: 'Field', name: { kind: 'Name', value: 'assigneeId' } },
{ kind: 'Field', name: { kind: 'Name', value: 'unassigned' } },
{ kind: 'Field', name: { kind: 'Name', value: 'releaseId' } },
{ kind: 'Field', name: { kind: 'Name', value: 'labels' } },
{ kind: 'Field', name: { kind: 'Name', value: 'search' } },
],
},
},
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'UserRefFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'UserRef' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
],
},
},
],
} as unknown as DocumentNode<SavedFilterFieldsFragment, unknown>;
export const BoardDocument = {
kind: 'Document',
definitions: [
@@ -1992,6 +2140,73 @@ export const BoardDocument = {
variable: { kind: 'Variable', name: { kind: 'Name', value: 'area' } },
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } },
},
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'types' },
},
type: {
kind: 'ListType',
type: {
kind: 'NonNullType',
type: {
kind: 'NamedType',
name: { kind: 'Name', value: 'String' },
},
},
},
},
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'priorities' },
},
type: {
kind: 'ListType',
type: {
kind: 'NonNullType',
type: {
kind: 'NamedType',
name: { kind: 'Name', value: 'IssuePriority' },
},
},
},
},
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'labels' },
},
type: {
kind: 'ListType',
type: {
kind: 'NonNullType',
type: {
kind: 'NamedType',
name: { kind: 'Name', value: 'String' },
},
},
},
},
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'search' },
},
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } },
},
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'unassigned' },
},
type: { kind: 'NamedType', name: { kind: 'Name', value: 'Boolean' } },
},
],
selectionSet: {
kind: 'SelectionSet',
@@ -2041,6 +2256,46 @@ export const BoardDocument = {
name: { kind: 'Name', value: 'area' },
},
},
{
kind: 'Argument',
name: { kind: 'Name', value: 'types' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'types' },
},
},
{
kind: 'Argument',
name: { kind: 'Name', value: 'priorities' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'priorities' },
},
},
{
kind: 'Argument',
name: { kind: 'Name', value: 'labels' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'labels' },
},
},
{
kind: 'Argument',
name: { kind: 'Name', value: 'search' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'search' },
},
},
{
kind: 'Argument',
name: { kind: 'Name', value: 'unassigned' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'unassigned' },
},
},
{
kind: 'Argument',
name: { kind: 'Name', value: 'perColumn' },
@@ -7168,6 +7423,207 @@ export const ReleaseBurnupDocument = {
},
],
} as unknown as DocumentNode<ReleaseBurnupQuery, ReleaseBurnupQueryVariables>;
export const SavedFiltersDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
name: { kind: 'Name', value: 'SavedFilters' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'projectId' },
},
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'savedFilters' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'projectId' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'projectId' },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'SavedFilterFields' },
},
],
},
},
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'UserRefFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'UserRef' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
],
},
},
{
kind: 'FragmentDefinition',
name: { kind: 'Name', value: 'SavedFilterFields' },
typeCondition: {
kind: 'NamedType',
name: { kind: 'Name', value: 'SavedFilter' },
},
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
{ kind: 'Field', name: { kind: 'Name', value: 'projectId' } },
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
{ kind: 'Field', name: { kind: 'Name', value: 'shared' } },
{ kind: 'Field', name: { kind: 'Name', value: 'createdAt' } },
{
kind: 'Field',
name: { kind: 'Name', value: 'owner' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: { kind: 'Name', value: 'UserRefFields' },
},
],
},
},
{
kind: 'Field',
name: { kind: 'Name', value: 'criteria' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'types' } },
{ kind: 'Field', name: { kind: 'Name', value: 'areas' } },
{ kind: 'Field', name: { kind: 'Name', value: 'priorities' } },
{ kind: 'Field', name: { kind: 'Name', value: 'assigneeId' } },
{ kind: 'Field', name: { kind: 'Name', value: 'unassigned' } },
{ kind: 'Field', name: { kind: 'Name', value: 'releaseId' } },
{ kind: 'Field', name: { kind: 'Name', value: 'labels' } },
{ kind: 'Field', name: { kind: 'Name', value: 'search' } },
],
},
},
],
},
},
],
} as unknown as DocumentNode<SavedFiltersQuery, SavedFiltersQueryVariables>;
export const SaveFilterDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'mutation',
name: { kind: 'Name', value: 'SaveFilter' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {
kind: 'Variable',
name: { kind: 'Name', value: 'input' },
},
type: {
kind: 'NonNullType',
type: {
kind: 'NamedType',
name: { kind: 'Name', value: 'SaveFilterInput' },
},
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'saveFilter' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'input' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'input' },
},
},
],
},
],
},
},
],
} as unknown as DocumentNode<SaveFilterMutation, SaveFilterMutationVariables>;
export const DeleteSavedFilterDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'mutation',
name: { kind: 'Name', value: 'DeleteSavedFilter' },
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: { kind: 'Variable', name: { kind: 'Name', value: 'id' } },
type: {
kind: 'NonNullType',
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: { kind: 'Name', value: 'deleteSavedFilter' },
arguments: [
{
kind: 'Argument',
name: { kind: 'Name', value: 'id' },
value: {
kind: 'Variable',
name: { kind: 'Name', value: 'id' },
},
},
],
},
],
},
},
],
} as unknown as DocumentNode<
DeleteSavedFilterMutation,
DeleteSavedFilterMutationVariables
>;
export const IssueChangedDocument = {
kind: 'Document',
definitions: [