feat: epics, subtasks and checklists
Tickets can hang from another one of the same project, three levels deep at most (epic → ticket → subtask). The ticket page lists its subtasks with their progress, creates new ones and moves the ticket under an epic; a new Épicas tab shows every epic and how far it has got. Each ticket also gets a checklist for the small steps that don't deserve a ticket. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
EpicReadModel,
|
||||
IssuePriority,
|
||||
IssueReadModel,
|
||||
ProjectStatsReadModel,
|
||||
@@ -18,6 +19,8 @@ export interface IssueFilter {
|
||||
unassigned?: boolean;
|
||||
releaseId?: string;
|
||||
labels?: string[];
|
||||
/** Only the tickets hanging from this one. */
|
||||
parentId?: string;
|
||||
search?: string;
|
||||
limit: number;
|
||||
offset: number;
|
||||
@@ -51,6 +54,8 @@ export abstract class FindIssuesRepository {
|
||||
abstract findOpenAssignedTo(userId: string): Promise<IssueReadModel[]>;
|
||||
/** Every ticket of a project (statistics). */
|
||||
abstract findByProject(projectId: string): Promise<IssueReadModel[]>;
|
||||
/** Top-level tickets with others hanging from them, by key. */
|
||||
abstract epics(projectId: string): Promise<EpicReadModel[]>;
|
||||
abstract releaseProgress(
|
||||
releaseId: string,
|
||||
): Promise<ReleaseProgressReadModel>;
|
||||
|
||||
@@ -10,4 +10,6 @@ export abstract class TrackerStreamsRepository {
|
||||
abstract commentIdsOfIssue(issueId: string): Promise<string[]>;
|
||||
/** Tickets that were ever linked to `issueId` (they may have unlinked since). */
|
||||
abstract issueIdsLinkingTo(issueId: string): Promise<string[]>;
|
||||
/** Tickets that were ever placed under `issueId` (they may have left since). */
|
||||
abstract issueIdsEverUnder(issueId: string): Promise<string[]>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
AttachmentReadModel,
|
||||
ChecklistItemReadModel,
|
||||
IssueLinkReadModel,
|
||||
IssueReadModel,
|
||||
StatusCategory,
|
||||
@@ -13,7 +14,7 @@ export abstract class UpsertMaterializedIssueRepository {
|
||||
Partial<
|
||||
Omit<
|
||||
IssueReadModel,
|
||||
'links' | 'commentCount' | 'attachments' | 'watchers'
|
||||
'links' | 'commentCount' | 'attachments' | 'watchers' | 'checklist'
|
||||
>
|
||||
>,
|
||||
): Promise<void>;
|
||||
@@ -35,6 +36,19 @@ export abstract class UpsertMaterializedIssueRepository {
|
||||
/** Adds a follower (idempotent). */
|
||||
abstract addWatcher(issueId: string, user: UserRefReadModel): Promise<void>;
|
||||
abstract removeWatcher(issueId: string, userId: string): Promise<void>;
|
||||
/** Adds a checklist step (idempotent). */
|
||||
abstract addChecklistItem(
|
||||
issueId: string,
|
||||
item: ChecklistItemReadModel,
|
||||
): Promise<void>;
|
||||
abstract updateChecklistItem(
|
||||
issueId: string,
|
||||
itemId: string,
|
||||
changes: Partial<Pick<ChecklistItemReadModel, 'text' | 'done'>>,
|
||||
): Promise<void>;
|
||||
abstract removeChecklistItem(issueId: string, itemId: string): Promise<void>;
|
||||
/** Keeps the denormalised parent title of a ticket's children in sync. */
|
||||
abstract renameParent(parentId: string, title: string): Promise<void>;
|
||||
abstract setCommentCount(
|
||||
issueId: string,
|
||||
commentCount: number,
|
||||
|
||||
Reference in New Issue
Block a user