feat: a trash for tickets
Deleting a ticket now sends it to the trash, for whoever opened it or an admin: it disappears from boards, searches and statistics, cannot change, and comes back from the ticket page or the trash page for 30 days. After that it is deleted for good with the usual cascade. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,7 @@ fragment IssueSummaryFields on Issue {
|
||||
storyPoints
|
||||
startDate
|
||||
dueDate
|
||||
trashedAt
|
||||
}
|
||||
|
||||
fragment IssueFields on Issue {
|
||||
|
||||
@@ -481,6 +481,7 @@ export type IssueSummaryFieldsFragment = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
@@ -509,6 +510,7 @@ export type IssueFieldsFragment = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
reporter: { id: string; name: string };
|
||||
links: Array<{
|
||||
type: IssueLinkType;
|
||||
@@ -652,6 +654,7 @@ export type BoardQuery = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
@@ -688,6 +691,7 @@ export type IssueQuery = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
reporter: { id: string; name: string };
|
||||
links: Array<{
|
||||
type: IssueLinkType;
|
||||
@@ -747,6 +751,7 @@ export type SearchIssuesQuery = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
@@ -903,6 +908,7 @@ export type MyIssuesQuery = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
@@ -935,6 +941,7 @@ export type IssueChildrenQuery = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
@@ -967,6 +974,7 @@ export type EpicsQuery = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
@@ -1041,6 +1049,7 @@ export type BacklogQuery = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
@@ -1068,6 +1077,53 @@ export type BulkUpdateIssuesMutation = {
|
||||
};
|
||||
};
|
||||
|
||||
export type TrashIssueMutationVariables = Exact<{
|
||||
issueId: string;
|
||||
}>;
|
||||
|
||||
export type TrashIssueMutation = {
|
||||
trashIssue: { id: string; key: string | null; version: number };
|
||||
};
|
||||
|
||||
export type RestoreIssueMutationVariables = Exact<{
|
||||
issueId: string;
|
||||
}>;
|
||||
|
||||
export type RestoreIssueMutation = {
|
||||
restoreIssue: { id: string; key: string | null; version: number };
|
||||
};
|
||||
|
||||
export type TrashQueryVariables = Exact<{
|
||||
projectId: string;
|
||||
}>;
|
||||
|
||||
export type TrashQuery = {
|
||||
trash: Array<{
|
||||
id: string;
|
||||
projectId: string;
|
||||
key: string;
|
||||
type: string;
|
||||
area: string | null;
|
||||
title: string;
|
||||
status: string;
|
||||
statusCategory: StatusCategory;
|
||||
priority: IssuePriority;
|
||||
labels: Array<string>;
|
||||
commentCount: number;
|
||||
updatedAt: string;
|
||||
version: number;
|
||||
rank: string;
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
parent: { id: string; key: string; title: string } | null;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type NotificationFieldsFragment = {
|
||||
id: string;
|
||||
kind: NotificationKind;
|
||||
@@ -1714,6 +1770,7 @@ export type IssueChangedSubscription = {
|
||||
storyPoints: number | null;
|
||||
startDate: string | null;
|
||||
dueDate: string | null;
|
||||
trashedAt: string | null;
|
||||
assignee: { id: string; name: string } | null;
|
||||
release: { id: string; name: string } | null;
|
||||
sprint: { id: string; name: string } | null;
|
||||
@@ -2323,6 +2380,7 @@ export const IssueSummaryFieldsFragmentDoc = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -2555,6 +2613,7 @@ export const IssueFieldsFragmentDoc = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -4010,6 +4069,7 @@ export const BoardDocument = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -4156,6 +4216,7 @@ export const IssueDocument = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -4436,6 +4497,7 @@ export const SearchIssuesDocument = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -5682,6 +5744,7 @@ export const MyIssuesDocument = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -5842,6 +5905,7 @@ export const IssueChildrenDocument = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -6020,6 +6084,7 @@ export const EpicsDocument = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -6502,6 +6567,7 @@ export const BacklogDocument = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -6646,6 +6712,287 @@ export const BulkUpdateIssuesDocument = {
|
||||
BulkUpdateIssuesMutation,
|
||||
BulkUpdateIssuesMutationVariables
|
||||
>;
|
||||
export const TrashIssueDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'mutation',
|
||||
name: { kind: 'Name', value: 'TrashIssue' },
|
||||
variableDefinitions: [
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
|
||||
},
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'trashIssue' },
|
||||
arguments: [
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
},
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'FragmentSpread',
|
||||
name: { kind: 'Name', value: 'CommandResultFields' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'FragmentDefinition',
|
||||
name: { kind: 'Name', value: 'CommandResultFields' },
|
||||
typeCondition: {
|
||||
kind: 'NamedType',
|
||||
name: { kind: 'Name', value: 'CommandResult' },
|
||||
},
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'version' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
} as unknown as DocumentNode<TrashIssueMutation, TrashIssueMutationVariables>;
|
||||
export const RestoreIssueDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'mutation',
|
||||
name: { kind: 'Name', value: 'RestoreIssue' },
|
||||
variableDefinitions: [
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
|
||||
},
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'restoreIssue' },
|
||||
arguments: [
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'issueId' },
|
||||
},
|
||||
},
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'FragmentSpread',
|
||||
name: { kind: 'Name', value: 'CommandResultFields' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'FragmentDefinition',
|
||||
name: { kind: 'Name', value: 'CommandResultFields' },
|
||||
typeCondition: {
|
||||
kind: 'NamedType',
|
||||
name: { kind: 'Name', value: 'CommandResult' },
|
||||
},
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'version' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
} as unknown as DocumentNode<
|
||||
RestoreIssueMutation,
|
||||
RestoreIssueMutationVariables
|
||||
>;
|
||||
export const TrashDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'query',
|
||||
name: { kind: 'Name', value: 'Trash' },
|
||||
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: 'trash' },
|
||||
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: 'IssueSummaryFields' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
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: 'IssueSummaryFields' },
|
||||
typeCondition: {
|
||||
kind: 'NamedType',
|
||||
name: { kind: 'Name', value: 'Issue' },
|
||||
},
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'projectId' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'type' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'area' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'title' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'status' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'statusCategory' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'priority' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'labels' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'commentCount' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'updatedAt' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'version' } },
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'assignee' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'FragmentSpread',
|
||||
name: { kind: 'Name', value: 'UserRefFields' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'release' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'sprint' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'parent' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'id' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'key' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'title' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'rank' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
} as unknown as DocumentNode<TrashQuery, TrashQueryVariables>;
|
||||
export const MyNotificationsDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
@@ -10421,6 +10768,7 @@ export const IssueChangedDocument = {
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'storyPoints' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'startDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'dueDate' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'trashedAt' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -221,3 +221,21 @@ mutation BulkUpdateIssues($input: BulkUpdateIssuesInput!) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation TrashIssue($issueId: ID!) {
|
||||
trashIssue(issueId: $issueId) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
mutation RestoreIssue($issueId: ID!) {
|
||||
restoreIssue(issueId: $issueId) {
|
||||
...CommandResultFields
|
||||
}
|
||||
}
|
||||
|
||||
query Trash($projectId: ID!) {
|
||||
trash(projectId: $projectId) {
|
||||
...IssueSummaryFields
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user