Files
task-stream/libs/web/tracker/domain/src/lib/models/models.ts
Josep Piguillem 6aec774c92 feat: estimate tickets and see the team's velocity
Story points can be set on the ticket page and when creating a ticket,
and show on cards, column headers, subtasks and the backlog. The Sprints
tab charts the points finished per closed sprint and warns when a planned
sprint holds more than the recent average.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 15:04:11 +02:00

310 lines
6.8 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;
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;
dueDate: string | null;
links: IssueLink[];
attachments: Attachment[];
/** People following the ticket. */
watchers: UserRef[];
checklist: ChecklistItem[];
allowedTransitions: string[];
createdAt: 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;
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;
}