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>
481 lines
12 KiB
TypeScript
481 lines
12 KiB
TypeScript
import {
|
|
NotificationKind,
|
|
IssueLinkType,
|
|
IssuePriority,
|
|
ProjectRole,
|
|
ReleaseStatus,
|
|
SprintStatus,
|
|
StatusCategory,
|
|
} from './enums';
|
|
import { CustomFieldValue, ProjectSettings } from './workflow';
|
|
|
|
export interface UserRef {
|
|
id: string;
|
|
name: string;
|
|
}
|
|
|
|
export interface ProjectMember {
|
|
user: UserRef;
|
|
role: ProjectRole;
|
|
}
|
|
|
|
export interface Project {
|
|
id: string;
|
|
key: string;
|
|
name: string;
|
|
description: string | null;
|
|
/** Anyone can read it, also without signing in. */
|
|
isPublic: boolean;
|
|
/** Takes bug reports and ideas from anyone (while public). */
|
|
acceptsProposals: boolean;
|
|
owner: UserRef;
|
|
members: ProjectMember[];
|
|
settings: ProjectSettings;
|
|
updatedAt: string;
|
|
version: number;
|
|
}
|
|
|
|
export interface IssueLink {
|
|
type: IssueLinkType;
|
|
/** OUTWARD: this issue → other. INWARD: other → this issue. */
|
|
direction: 'OUTWARD' | 'INWARD';
|
|
issueId: string;
|
|
issueKey: string;
|
|
}
|
|
|
|
/** The ticket another one hangs from. */
|
|
export interface IssueParent {
|
|
id: string;
|
|
key: string;
|
|
title: string;
|
|
}
|
|
|
|
/** A step inside a ticket that does not deserve a ticket of its own. */
|
|
export interface ChecklistItem {
|
|
id: string;
|
|
text: string;
|
|
done: boolean;
|
|
}
|
|
|
|
/** How far the tickets hanging from one have got. */
|
|
export interface ChildProgress {
|
|
total: number;
|
|
/** Done or cancelled. */
|
|
finished: number;
|
|
percent: number;
|
|
storyPoints: number;
|
|
finishedStoryPoints: number;
|
|
}
|
|
|
|
/** A top-level ticket that groups others. */
|
|
export interface Epic {
|
|
issue: IssueSummary;
|
|
progress: ChildProgress;
|
|
/** Its relations: «bloquea» / «depende de» are the roadmap's arrows. */
|
|
links: IssueLink[];
|
|
}
|
|
|
|
/** What a saved view narrows the board to; empty: everything. */
|
|
export interface SavedFilterCriteria {
|
|
types?: string[] | null;
|
|
areas?: string[] | null;
|
|
priorities?: IssuePriority[] | null;
|
|
assigneeId?: string | null;
|
|
unassigned?: boolean | null;
|
|
releaseId?: string | null;
|
|
labels?: string[] | null;
|
|
search?: string | null;
|
|
}
|
|
|
|
/** What a view of «my work» narrows to, across my projects. */
|
|
export interface MyViewCriteria {
|
|
/** Only these projects (empty: all of mine). */
|
|
projectIds?: string[] | null;
|
|
assignedToMe?: boolean | null;
|
|
watching?: boolean | null;
|
|
categories?: StatusCategory[] | null;
|
|
priorities?: IssuePriority[] | null;
|
|
/** Only types drawn with the bug icon. */
|
|
bugsOnly?: boolean | null;
|
|
/** Only open tickets past their due date. */
|
|
overdue?: boolean | null;
|
|
search?: string | null;
|
|
}
|
|
|
|
/** A named view of «my work», mine only. */
|
|
export interface MyView {
|
|
id: string;
|
|
name: string;
|
|
criteria: MyViewCriteria;
|
|
createdAt: string;
|
|
}
|
|
|
|
/** A named view of the board: private, or shared with the project. */
|
|
export interface SavedFilter {
|
|
id: string;
|
|
projectId: string;
|
|
owner: UserRef;
|
|
name: string;
|
|
shared: boolean;
|
|
criteria: SavedFilterCriteria;
|
|
createdAt: string;
|
|
}
|
|
|
|
/** What boards and lists show. */
|
|
export interface IssueSummary {
|
|
id: string;
|
|
projectId: string;
|
|
key: string;
|
|
/** Ids of the project's settings. */
|
|
type: string;
|
|
area: string | null;
|
|
title: string;
|
|
status: string;
|
|
statusCategory: StatusCategory;
|
|
priority: IssuePriority;
|
|
assignee: UserRef | null;
|
|
release: { id: string; name: string } | null;
|
|
sprint: { id: string; name: string } | null;
|
|
labels: string[];
|
|
/** The epic or ticket this one breaks down. */
|
|
parent: IssueParent | null;
|
|
/** Open tickets it waits for: the ones that block it or it depends on. */
|
|
blockedBy: IssueParent[];
|
|
/** How many want it done, and whether I am one of them. */
|
|
votes: number;
|
|
voted: boolean;
|
|
/** Place in the backlog and in its board column (ascending, as text). */
|
|
rank: string;
|
|
/** Estimate; null when nobody estimated it. */
|
|
storyPoints: number | null;
|
|
/** Planned start and end (`YYYY-MM-DD`), for the roadmap. */
|
|
startDate: string | null;
|
|
dueDate: string | null;
|
|
/** Values of the project's custom fields (only those set). */
|
|
customFields: CustomFieldValue[];
|
|
/** In the trash since then (null: not in it). */
|
|
trashedAt: string | null;
|
|
commentCount: number;
|
|
updatedAt: string;
|
|
version: number;
|
|
}
|
|
|
|
/** A page of tickets: the ones returned and how many match in total. */
|
|
export interface IssuePage {
|
|
items: IssueSummary[];
|
|
total: number;
|
|
}
|
|
|
|
/** A file on a ticket (or under one of its comments). */
|
|
export interface Attachment {
|
|
id: string;
|
|
fileName: string;
|
|
mimeType: string;
|
|
/** Bytes. */
|
|
size: number;
|
|
uploadedBy: UserRef;
|
|
uploadedAt: string;
|
|
commentId: string | null;
|
|
}
|
|
|
|
export interface Issue extends IssueSummary {
|
|
description: string | null;
|
|
reporter: UserRef;
|
|
figmaUrl: string | null;
|
|
links: IssueLink[];
|
|
attachments: Attachment[];
|
|
/** People following the ticket. */
|
|
watchers: UserRef[];
|
|
checklist: ChecklistItem[];
|
|
/** Commits, branches and pull requests that mention it. */
|
|
devLinks: DevLink[];
|
|
allowedTransitions: string[];
|
|
createdAt: string;
|
|
}
|
|
|
|
/** A commit, branch or pull request that mentions a ticket. */
|
|
export interface DevLink {
|
|
kind: 'COMMIT' | 'BRANCH' | 'PULL_REQUEST';
|
|
url: string;
|
|
title: string;
|
|
/** Short SHA, branch or PR number. */
|
|
ref: string;
|
|
state: 'OPEN' | 'MERGED' | 'CLOSED' | null;
|
|
provider: string;
|
|
/** The project's name for the repository it came from. */
|
|
repository: string | null;
|
|
author: string | null;
|
|
at: string;
|
|
}
|
|
|
|
export interface WebhookDelivery {
|
|
at: string;
|
|
event: string;
|
|
/** HTTP status, null when it never answered. */
|
|
status: number | null;
|
|
ok: boolean;
|
|
error: string | null;
|
|
}
|
|
|
|
/** Where the project tells another system what happened. */
|
|
export interface Webhook {
|
|
id: string;
|
|
url: string;
|
|
/** Event names, `family.*` or `*`. */
|
|
events: string[];
|
|
active: boolean;
|
|
createdBy: UserRef;
|
|
createdAt: string;
|
|
lastDelivery: WebhookDelivery | null;
|
|
}
|
|
|
|
export type AutomationTriggerKind =
|
|
| 'ISSUE_CREATED'
|
|
| 'STATUS_CHANGED'
|
|
| 'ISSUE_ASSIGNED'
|
|
| 'COMMENT_ADDED'
|
|
| 'PULL_REQUEST_MERGED';
|
|
|
|
export type AutomationActionKind =
|
|
| 'SET_STATUS'
|
|
| 'ASSIGN'
|
|
| 'ASSIGN_REPORTER'
|
|
| 'ADD_LABEL'
|
|
| 'SET_PRIORITY'
|
|
| 'ADD_COMMENT'
|
|
| 'ADD_TO_ACTIVE_SPRINT';
|
|
|
|
/** «When this happens to a ticket, if it is like this, do that.» */
|
|
export interface Automation {
|
|
id: string;
|
|
name: string;
|
|
active: boolean;
|
|
trigger: { kind: AutomationTriggerKind; status?: string | null };
|
|
conditions: {
|
|
types?: string[] | null;
|
|
priorities?: IssuePriority[] | null;
|
|
areas?: string[] | null;
|
|
label?: string | null;
|
|
};
|
|
action: { kind: AutomationActionKind; value?: string | null };
|
|
createdBy: UserRef;
|
|
runs: number;
|
|
lastRunAt: string | null;
|
|
lastError: string | null;
|
|
}
|
|
|
|
export type AutomationInput = Pick<
|
|
Automation,
|
|
'name' | 'trigger' | 'conditions' | 'action'
|
|
>;
|
|
|
|
/** What an import did (or, as a preview, would do). */
|
|
export interface ImportReport {
|
|
format: 'taskstream' | 'jira';
|
|
dryRun: boolean;
|
|
rows: number;
|
|
created: number;
|
|
keys: string[];
|
|
skipped: { line: number; reason?: string }[];
|
|
warnings: { line: number; message?: string }[];
|
|
}
|
|
|
|
/** The project's webhook for GitHub, Gitea or GitLab. */
|
|
/** A repository whose webhooks the project accepts. */
|
|
export interface GitRepository {
|
|
id: string;
|
|
/** The project's name for it: api, web, admin… */
|
|
name: string;
|
|
/** Only for owners and admins. */
|
|
secret: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface GitIntegration {
|
|
/** On while there is at least one repository. */
|
|
enabled: boolean;
|
|
/** The same for every repository: the secret tells them apart. */
|
|
webhookPath: string;
|
|
repositories: GitRepository[];
|
|
}
|
|
|
|
export interface Comment {
|
|
id: string;
|
|
issueId: string;
|
|
author: UserRef;
|
|
/** Empty on a deleted comment kept for its replies. */
|
|
body: string;
|
|
/** The top-level comment it answers; null for a top-level one. */
|
|
replyTo: string | null;
|
|
/** Deleted, but kept (without its text) while it has replies. */
|
|
deleted: boolean;
|
|
/** 👍… and who chose each (only the ones somebody chose). */
|
|
reactions: { emoji: string; users: UserRef[] }[];
|
|
createdAt: string;
|
|
editedAt: string | null;
|
|
}
|
|
|
|
export interface SprintProgress {
|
|
total: number;
|
|
/** Done or cancelled. */
|
|
finished: number;
|
|
percent: number;
|
|
storyPoints: number;
|
|
finishedStoryPoints: number;
|
|
}
|
|
|
|
/** A time-boxed iteration of a project. */
|
|
export interface Sprint {
|
|
id: string;
|
|
projectId: string;
|
|
name: string;
|
|
goal: string | null;
|
|
/** `YYYY-MM-DD`, both included. */
|
|
startDate: string;
|
|
endDate: string;
|
|
status: SprintStatus;
|
|
startedAt: string | null;
|
|
closedAt: string | null;
|
|
progress: SprintProgress;
|
|
}
|
|
|
|
/** Per day of a sprint; null for the days to come. */
|
|
export interface SprintBurndown {
|
|
sprintId: string;
|
|
dates: string[];
|
|
scope: (number | null)[];
|
|
remaining: (number | null)[];
|
|
remainingPoints: (number | null)[];
|
|
ideal: number[];
|
|
}
|
|
|
|
export interface Release {
|
|
id: string;
|
|
projectId: string;
|
|
name: string;
|
|
description: string | null;
|
|
targetDate: string | null;
|
|
status: ReleaseStatus;
|
|
releasedAt: string | null;
|
|
/** What shipped, told for people (Markdown). */
|
|
notes: string | null;
|
|
progress: { total: number; finished: number; percent: number };
|
|
}
|
|
|
|
export interface Activity {
|
|
id: string;
|
|
type: string;
|
|
issueId: string | null;
|
|
issueKey: string | null;
|
|
actor: UserRef;
|
|
summary: string;
|
|
occurredAt: string;
|
|
}
|
|
|
|
export interface CountBy<T = string> {
|
|
key: T;
|
|
count: number;
|
|
}
|
|
|
|
export interface ProjectStats {
|
|
total: number;
|
|
open: number;
|
|
byStatus: CountBy[];
|
|
byType: CountBy[];
|
|
byPriority: CountBy[];
|
|
byArea: CountBy[];
|
|
byAssignee: { user: UserRef | null; count: number }[];
|
|
}
|
|
|
|
export interface BoardColumn {
|
|
status: string;
|
|
issues: IssueSummary[];
|
|
total: number;
|
|
}
|
|
|
|
export interface DurationStats {
|
|
count: number;
|
|
averageDays: number | null;
|
|
/** 85% of tickets take at most this long. */
|
|
p85Days: number | null;
|
|
}
|
|
|
|
/** Statistics of a project over the last days. */
|
|
export interface ProjectAnalytics {
|
|
from: string;
|
|
to: string;
|
|
/** Per week (Monday, YYYY-MM-DD). */
|
|
createdVsResolved: { week: string; created: number; resolved: number }[];
|
|
/** Tickets in each status at the end of every day. */
|
|
cumulativeFlow: {
|
|
dates: string[];
|
|
series: { status: string; counts: number[] }[];
|
|
};
|
|
/** Start of work → done. */
|
|
cycleTime: DurationStats;
|
|
/** Creation → done. */
|
|
leadTime: DurationStats;
|
|
throughput: { week: string; user: UserRef | null; count: number }[];
|
|
qa: { passed: number; failed: number; failureRate: number | null };
|
|
}
|
|
|
|
/** Scope vs finished tickets of a release, day by day. */
|
|
export interface ReleaseBurnup {
|
|
releaseId: string;
|
|
targetDate: string | null;
|
|
dates: string[];
|
|
scope: number[];
|
|
done: number[];
|
|
}
|
|
|
|
export interface UserDirectoryEntry {
|
|
id: string;
|
|
name: string;
|
|
email: string;
|
|
}
|
|
|
|
/** Ack of a command: the read model catches up asynchronously. */
|
|
export interface CommandResult {
|
|
id: string;
|
|
key?: string | null;
|
|
version: number;
|
|
}
|
|
|
|
/** Something the signed-in person should know. */
|
|
export interface Notification {
|
|
id: string;
|
|
kind: NotificationKind;
|
|
projectId: string;
|
|
projectKey: string;
|
|
issueId: string | null;
|
|
issueKey: string | null;
|
|
actor: UserRef;
|
|
summary: string;
|
|
/** Null: unread. */
|
|
readAt: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
/** My calendar subscription: a secret URL calendar apps read (iCalendar). */
|
|
export interface CalendarFeed {
|
|
/** Absolute: what goes into Google Calendar, Outlook or Apple Calendar. */
|
|
url: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface NotificationSettings {
|
|
/** Copies by e-mail. */
|
|
email: boolean;
|
|
/** Whether this installation can send e-mail at all. */
|
|
emailAvailable: boolean;
|
|
/** VAPID key to subscribe a device to push with; null: no push here. */
|
|
pushPublicKey: string | null;
|
|
}
|
|
|
|
/** A browser's Web Push subscription, as the API takes it. */
|
|
export interface PushDeviceSubscription {
|
|
endpoint: string;
|
|
p256dh: string;
|
|
auth: string;
|
|
}
|