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:
@@ -39,12 +39,14 @@ import { ChangeIssueStatusCommand } from '../commands/change-issue-status.comman
|
||||
import { COMMAND_HANDLERS } from '../commands/command-handlers';
|
||||
import { CommandResult } from '../commands/command-result';
|
||||
import { CreateIssueCommand } from '../commands/create-issue.command';
|
||||
import { DeleteIssueCommand } from '../commands/delete-issue.command';
|
||||
import { CreateProjectCommand } from '../commands/create-project.command';
|
||||
import { CreateReleaseCommand } from '../commands/create-release.command';
|
||||
import { DeleteProjectCommand } from '../commands/delete-project.command';
|
||||
import { LinkIssuesCommand } from '../commands/link-issues.command';
|
||||
import { AssignIssueCommand } from '../commands/assign-issue.command';
|
||||
import { WriteAccess } from '../commands/write-access';
|
||||
import { IssueTree } from '../commands/issue-tree';
|
||||
import { FindIssuesRepository } from '../ports/find-issues.repository';
|
||||
import { IssueNumbersRepository } from '../ports/issue-numbers.repository';
|
||||
import { ProjectKeysRepository } from '../ports/project-keys.repository';
|
||||
@@ -72,6 +74,7 @@ async function setup() {
|
||||
ReleaseArchivedSaga,
|
||||
UserDeactivatedSaga,
|
||||
WriteAccess,
|
||||
IssueTree,
|
||||
AggregateRehydrator,
|
||||
{ provide: EventStore, useValue: store },
|
||||
{ provide: AttachmentStorage, useValue: files },
|
||||
@@ -178,6 +181,31 @@ describe('sagas', () => {
|
||||
).resolves.toMatchObject({ key: 'WEB' });
|
||||
});
|
||||
|
||||
it('a deleted ticket lets the tickets under it go back to the top', async () => {
|
||||
const { run, drain, rehydrator } = await setup();
|
||||
const web = (await run(new CreateProjectCommand('owner', 'WEB', 'Web'))).id;
|
||||
const epic = (await run(ticket(web, 'Epic'))).id;
|
||||
const child = (
|
||||
await run(
|
||||
new CreateIssueCommand(
|
||||
'owner',
|
||||
web,
|
||||
'TASK',
|
||||
'child',
|
||||
{},
|
||||
null,
|
||||
null,
|
||||
epic,
|
||||
),
|
||||
)
|
||||
).id;
|
||||
|
||||
await run(new DeleteIssueCommand('owner', epic));
|
||||
await drain();
|
||||
|
||||
expect((await rehydrator.rehydrate(child, Issue)).parentId).toBeNull();
|
||||
});
|
||||
|
||||
it('only the owner deletes a project', async () => {
|
||||
const { run } = await setup();
|
||||
const web = (await run(new CreateProjectCommand('owner', 'WEB', 'Web'))).id;
|
||||
|
||||
Reference in New Issue
Block a user