Files
task-stream/libs/web/tracker/domain/src/lib/models/models.ts
Josep Piguillem 1076205acc feat: import and export tickets as CSV
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>
2026-10-08 15:43:02 +02:00

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;
}