Every ticket of a project can be downloaded as CSV, and a CSV exported from TaskStream or from Jira can be imported: types, statuses, priorities and people are matched by id or name, the rest falls back with a warning, and parents are rebuilt. The settings page previews an import before it creates anything. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
369 lines
8.4 KiB
TypeScript
369 lines
8.4 KiB
TypeScript
import {
|
|
NotificationKind,
|
|
IssueLinkType,
|
|
IssuePriority,
|
|
ProjectRole,
|
|
ReleaseStatus,
|
|
SprintStatus,
|
|
StatusCategory,
|
|
} from './enums';
|
|
import { 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;
|
|
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;
|
|
}
|
|
|
|
/** 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;
|
|
}
|
|
|
|
/** 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;
|
|
/** 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;
|
|
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;
|
|
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;
|
|
}
|
|
|
|
/** 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. */
|
|
export interface GitIntegration {
|
|
enabled: boolean;
|
|
/** Only for owners and admins. */
|
|
secret: string | null;
|
|
webhookPath: string;
|
|
}
|
|
|
|
export interface Comment {
|
|
id: string;
|
|
issueId: string;
|
|
author: UserRef;
|
|
body: string;
|
|
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;
|
|
}
|
|
|
|
export interface NotificationSettings {
|
|
/** Copies by e-mail. */
|
|
email: boolean;
|
|
/** Whether this installation can send e-mail at all. */
|
|
emailAvailable: boolean;
|
|
}
|