Merge pull request 'Choix type issue' (#34) from feat/34-choix-type-issue into develop
Reviewed-on: Bonsai/Bonsai-webapp#34
This commit is contained in:
@@ -83,6 +83,44 @@
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Type dropdown (inline issue creation) */
|
||||||
|
.type-dropdown-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: calc(100% + 0.3rem);
|
||||||
|
min-width: 12rem;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create issue split button dropdown */
|
||||||
|
.create-issue-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-type-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: calc(100% + 0.3rem);
|
||||||
|
min-width: 12rem;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
/* Dependency badges */
|
/* Dependency badges */
|
||||||
.dep-badge {
|
.dep-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@@ -334,10 +334,31 @@
|
|||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
<div class="card-footer bg-white">
|
<div class="card-footer bg-white pt-3">
|
||||||
@if (showCreateInEpic) {
|
@if (showCreateInEpic) {
|
||||||
<div class="d-flex gap-2 flex-wrap">
|
<div class="d-flex gap-2 flex-wrap align-items-center">
|
||||||
|
<div class="type-dropdown-wrapper">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary d-flex align-items-center gap-1" (click)="toggleTypeDropdown()">
|
||||||
|
<span class="type-icon" [style.background]="typeIcon(newIssueType).bg" [title]="newIssueType">{{ typeIcon(newIssueType).letter }}</span>
|
||||||
|
{{ newIssueType }}
|
||||||
|
<span class="dropdown-toggle ms-1"></span>
|
||||||
|
</button>
|
||||||
|
@if (showTypeDropdown) {
|
||||||
|
<div class="type-backdrop" (click)="closeTypeDropdown()"></div>
|
||||||
|
<ul class="type-dropdown dropdown-menu show">
|
||||||
|
@for (type of typeOptions; track type) {
|
||||||
|
<li>
|
||||||
|
<button type="button" class="dropdown-item d-flex align-items-center gap-2" [class.active]="newIssueType === type" (click)="selectNewIssueType(type)">
|
||||||
|
<span class="type-icon" [style.background]="typeIcon(type).bg">{{ typeIcon(type).letter }}</span>
|
||||||
|
{{ type }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
|
#newIssueInput
|
||||||
aria-label="Titre de la nouvelle issue"
|
aria-label="Titre de la nouvelle issue"
|
||||||
class="form-control form-control-sm dep-select"
|
class="form-control form-control-sm dep-select"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -362,7 +383,25 @@
|
|||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button type="button" class="btn btn-sm btn-primary" (click)="openCreateInEpic()">+ Créer une issue</button>
|
<div class="btn-group create-issue-wrapper">
|
||||||
|
<button type="button" class="btn btn-sm btn-primary" (click)="openCreateInEpic()">+ Créer une issue</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split" (click)="toggleCreateDropdown()">
|
||||||
|
<span class="visually-hidden">Choisir le type</span>
|
||||||
|
</button>
|
||||||
|
@if (showCreateDropdown) {
|
||||||
|
<div class="create-backdrop" (click)="closeCreateDropdown()"></div>
|
||||||
|
<ul class="create-type-dropdown dropdown-menu show">
|
||||||
|
@for (type of typeOptions; track type) {
|
||||||
|
<li>
|
||||||
|
<button type="button" class="dropdown-item d-flex align-items-center gap-2" (click)="openCreateInEpic(type)">
|
||||||
|
<span class="type-icon" [style.background]="typeIcon(type).bg">{{ typeIcon(type).letter }}</span>
|
||||||
|
{{ type }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary" (click)="openAddToEpic()">Ajouter une existante</button>
|
<button type="button" class="btn btn-sm btn-outline-secondary" (click)="openAddToEpic()">Ajouter une existante</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -737,6 +737,12 @@ describe('IssueDetail — existing issue', () => {
|
|||||||
expect((component as any).showAddToEpic).toBe(false);
|
expect((component as any).showAddToEpic).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('openCreateInEpic resets newIssueType to Story', () => {
|
||||||
|
(component as any).newIssueType = 'Bug';
|
||||||
|
(component as any).openCreateInEpic();
|
||||||
|
expect((component as any).newIssueType).toBe('Story');
|
||||||
|
});
|
||||||
|
|
||||||
it('cancelCreateInEpic hides the form and clears the name', () => {
|
it('cancelCreateInEpic hides the form and clears the name', () => {
|
||||||
(component as any).showCreateInEpic = true;
|
(component as any).showCreateInEpic = true;
|
||||||
(component as any).newIssueName = 'Draft';
|
(component as any).newIssueName = 'Draft';
|
||||||
@@ -745,7 +751,13 @@ describe('IssueDetail — existing issue', () => {
|
|||||||
expect((component as any).newIssueName).toBe('');
|
expect((component as any).newIssueName).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('confirmCreateInEpic creates a child issue linked to the epic', () => {
|
it('cancelCreateInEpic resets newIssueType to Story', () => {
|
||||||
|
(component as any).newIssueType = 'Task';
|
||||||
|
(component as any).cancelCreateInEpic();
|
||||||
|
expect((component as any).newIssueType).toBe('Story');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('confirmCreateInEpic creates a child issue with Story type by default', () => {
|
||||||
(component as any).newIssueName = 'Child Issue';
|
(component as any).newIssueName = 'Child Issue';
|
||||||
const before = store.issues().length;
|
const before = store.issues().length;
|
||||||
(component as any).confirmCreateInEpic();
|
(component as any).confirmCreateInEpic();
|
||||||
@@ -755,6 +767,14 @@ describe('IssueDetail — existing issue', () => {
|
|||||||
expect(created?.type).toBe('Story');
|
expect(created?.type).toBe('Story');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('confirmCreateInEpic creates a child issue with the selected type', async () => {
|
||||||
|
(component as any).newIssueName = 'Child Bug';
|
||||||
|
(component as any).newIssueType = 'Bug';
|
||||||
|
await (component as any).confirmCreateInEpic();
|
||||||
|
const created = store.issues().find((i) => i.name === 'Child Bug');
|
||||||
|
expect(created?.type).toBe('Bug');
|
||||||
|
});
|
||||||
|
|
||||||
it('confirmCreateInEpic resets the form', async () => {
|
it('confirmCreateInEpic resets the form', async () => {
|
||||||
(component as any).newIssueName = 'Child Issue';
|
(component as any).newIssueName = 'Child Issue';
|
||||||
await (component as any).confirmCreateInEpic();
|
await (component as any).confirmCreateInEpic();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, effect, inject } from '@angular/core';
|
import { Component, effect, ElementRef, inject, ViewChild } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
@@ -76,8 +76,13 @@ export class IssueDetail {
|
|||||||
private _descriptionBeforeEdit = '';
|
private _descriptionBeforeEdit = '';
|
||||||
protected showAddToEpic = false;
|
protected showAddToEpic = false;
|
||||||
protected selectedEpicCandidateId: number | null = null;
|
protected selectedEpicCandidateId: number | null = null;
|
||||||
|
@ViewChild('newIssueInput') private newIssueInput?: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
protected showCreateInEpic = false;
|
protected showCreateInEpic = false;
|
||||||
|
protected showCreateDropdown = false;
|
||||||
protected newIssueName = '';
|
protected newIssueName = '';
|
||||||
|
protected newIssueType: IssueEntity['type'] = 'Story';
|
||||||
|
protected showTypeDropdown = false;
|
||||||
|
|
||||||
protected readonly statusOptions = this.statusesStore.statuses;
|
protected readonly statusOptions = this.statusesStore.statuses;
|
||||||
|
|
||||||
@@ -221,15 +226,43 @@ export class IssueDetail {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected openCreateInEpic(): void {
|
protected openCreateInEpic(type: IssueEntity['type'] = 'Story'): void {
|
||||||
this.newIssueName = '';
|
this.newIssueName = '';
|
||||||
|
this.newIssueType = type;
|
||||||
|
this.showTypeDropdown = false;
|
||||||
|
this.showCreateDropdown = false;
|
||||||
this.showCreateInEpic = true;
|
this.showCreateInEpic = true;
|
||||||
this.showAddToEpic = false;
|
this.showAddToEpic = false;
|
||||||
|
setTimeout(() => this.newIssueInput?.nativeElement.focus(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected cancelCreateInEpic(): void {
|
protected cancelCreateInEpic(): void {
|
||||||
this.showCreateInEpic = false;
|
this.showCreateInEpic = false;
|
||||||
this.newIssueName = '';
|
this.newIssueName = '';
|
||||||
|
this.newIssueType = 'Story';
|
||||||
|
this.showTypeDropdown = false;
|
||||||
|
this.showCreateDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toggleCreateDropdown(): void {
|
||||||
|
this.showCreateDropdown = !this.showCreateDropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected closeCreateDropdown(): void {
|
||||||
|
this.showCreateDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toggleTypeDropdown(): void {
|
||||||
|
this.showTypeDropdown = !this.showTypeDropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected closeTypeDropdown(): void {
|
||||||
|
this.showTypeDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected selectNewIssueType(type: IssueEntity['type']): void {
|
||||||
|
this.newIssueType = type;
|
||||||
|
this.showTypeDropdown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async confirmCreateInEpic(): Promise<void> {
|
protected async confirmCreateInEpic(): Promise<void> {
|
||||||
@@ -237,7 +270,7 @@ export class IssueDetail {
|
|||||||
if (!name) return;
|
if (!name) return;
|
||||||
const created = await this.issuesStore.upsert({
|
const created = await this.issuesStore.upsert({
|
||||||
id: 0,
|
id: 0,
|
||||||
type: 'Story',
|
type: this.newIssueType,
|
||||||
assignee: '',
|
assignee: '',
|
||||||
epic: this.issue.name,
|
epic: this.issue.name,
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -43,6 +43,42 @@
|
|||||||
z-index: 9;
|
z-index: 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.create-issue-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-type-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: calc(100% + 0.3rem);
|
||||||
|
min-width: 12rem;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-dropdown-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: calc(100% + 0.3rem);
|
||||||
|
min-width: 12rem;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
/* Issue name link in table */
|
/* Issue name link in table */
|
||||||
.issue-name-btn {
|
.issue-name-btn {
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -229,18 +229,38 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="card-body" [class.pt-0]="linkedIssues.length > 0">
|
<div class="card-body" [class.pt-2]="linkedIssues.length > 0">
|
||||||
@if (showCreateIssue) {
|
@if (showCreateIssue) {
|
||||||
<div class="d-flex gap-2 flex-wrap">
|
<div class="d-flex gap-2 align-items-center">
|
||||||
|
<div class="type-dropdown-wrapper">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary d-flex align-items-center gap-1" (click)="toggleTypeDropdown()">
|
||||||
|
<span class="type-icon" [style.background]="typeIcon(newIssueType).bg" [title]="newIssueType">{{ typeIcon(newIssueType).letter }}</span>
|
||||||
|
{{ newIssueType }}
|
||||||
|
<span class="dropdown-toggle ms-1"></span>
|
||||||
|
</button>
|
||||||
|
@if (showTypeDropdown) {
|
||||||
|
<div class="type-backdrop" (click)="closeTypeDropdown()"></div>
|
||||||
|
<ul class="type-dropdown dropdown-menu show">
|
||||||
|
@for (type of typeOptions; track type) {
|
||||||
|
<li>
|
||||||
|
<button type="button" class="dropdown-item d-flex align-items-center gap-2" [class.active]="newIssueType === type" (click)="selectNewIssueType(type)">
|
||||||
|
<span class="type-icon" [style.background]="typeIcon(type).bg">{{ typeIcon(type).letter }}</span>
|
||||||
|
{{ type }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
|
#newIssueInput
|
||||||
aria-label="Titre de la nouvelle issue"
|
aria-label="Titre de la nouvelle issue"
|
||||||
class="form-control form-control-sm"
|
class="form-control form-control-sm dep-select"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Titre de l'issue..."
|
placeholder="Titre de l'issue..."
|
||||||
[(ngModel)]="newIssueName"
|
[(ngModel)]="newIssueName"
|
||||||
(keydown.enter)="confirmCreateIssue()"
|
(keydown.enter)="confirmCreateIssue()"
|
||||||
(keydown.escape)="cancelCreateIssue()"
|
(keydown.escape)="cancelCreateIssue()"
|
||||||
autofocus
|
|
||||||
/>
|
/>
|
||||||
<button type="button" class="btn btn-sm btn-primary text-nowrap" (click)="confirmCreateIssue()" [disabled]="!newIssueName.trim()">Créer</button>
|
<button type="button" class="btn btn-sm btn-primary text-nowrap" (click)="confirmCreateIssue()" [disabled]="!newIssueName.trim()">Créer</button>
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary text-nowrap" (click)="cancelCreateIssue()">Annuler</button>
|
<button type="button" class="btn btn-sm btn-outline-secondary text-nowrap" (click)="cancelCreateIssue()">Annuler</button>
|
||||||
@@ -283,7 +303,25 @@
|
|||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button type="button" class="btn btn-sm btn-primary" (click)="openCreateIssue()">+ Créer une issue</button>
|
<div class="btn-group create-issue-wrapper">
|
||||||
|
<button type="button" class="btn btn-sm btn-primary" (click)="openCreateIssue()">+ Créer une issue</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split" (click)="toggleCreateDropdown()">
|
||||||
|
<span class="visually-hidden">Choisir le type</span>
|
||||||
|
</button>
|
||||||
|
@if (showCreateDropdown) {
|
||||||
|
<div class="create-backdrop" (click)="closeCreateDropdown()"></div>
|
||||||
|
<ul class="create-type-dropdown dropdown-menu show">
|
||||||
|
@for (type of typeOptions; track type) {
|
||||||
|
<li>
|
||||||
|
<button type="button" class="dropdown-item d-flex align-items-center gap-2" (click)="openCreateIssue(type)">
|
||||||
|
<span class="type-icon" [style.background]="typeIcon(type).bg">{{ typeIcon(type).letter }}</span>
|
||||||
|
{{ type }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-outline-secondary"
|
class="btn btn-sm btn-outline-secondary"
|
||||||
|
|||||||
@@ -370,6 +370,12 @@ describe('MilestoneDetail', () => {
|
|||||||
expect((component as any).showAddIssue).toBe(false);
|
expect((component as any).showAddIssue).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('openCreateIssue resets newIssueType to Story', () => {
|
||||||
|
(component as any).newIssueType = 'Bug';
|
||||||
|
(component as any).openCreateIssue();
|
||||||
|
expect((component as any).newIssueType).toBe('Story');
|
||||||
|
});
|
||||||
|
|
||||||
it('cancelCreateIssue hides the form and clears name', () => {
|
it('cancelCreateIssue hides the form and clears name', () => {
|
||||||
(component as any).showCreateIssue = true;
|
(component as any).showCreateIssue = true;
|
||||||
(component as any).newIssueName = 'Draft';
|
(component as any).newIssueName = 'Draft';
|
||||||
@@ -377,6 +383,12 @@ describe('MilestoneDetail', () => {
|
|||||||
expect((component as any).showCreateIssue).toBe(false);
|
expect((component as any).showCreateIssue).toBe(false);
|
||||||
expect((component as any).newIssueName).toBe('');
|
expect((component as any).newIssueName).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('cancelCreateIssue resets newIssueType to Story', () => {
|
||||||
|
(component as any).newIssueType = 'Task';
|
||||||
|
(component as any).cancelCreateIssue();
|
||||||
|
expect((component as any).newIssueType).toBe('Story');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('navigateToIssue', () => {
|
describe('navigateToIssue', () => {
|
||||||
@@ -437,6 +449,21 @@ describe('MilestoneDetail', () => {
|
|||||||
expect((component as any).newIssueName).toBe('');
|
expect((component as any).newIssueName).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('creates an issue with the selected type', async () => {
|
||||||
|
(component as any).newIssueName = 'New Bug';
|
||||||
|
(component as any).newIssueType = 'Bug';
|
||||||
|
await (component as any).confirmCreateIssue();
|
||||||
|
const created = issuesStore.issues().find((i) => i.name === 'New Bug');
|
||||||
|
expect(created?.type).toBe('Bug');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a Story by default', async () => {
|
||||||
|
(component as any).newIssueName = 'Default Story';
|
||||||
|
await (component as any).confirmCreateIssue();
|
||||||
|
const created = issuesStore.issues().find((i) => i.name === 'Default Story');
|
||||||
|
expect(created?.type).toBe('Story');
|
||||||
|
});
|
||||||
|
|
||||||
it('does nothing when name is blank', async () => {
|
it('does nothing when name is blank', async () => {
|
||||||
(component as any).newIssueName = ' ';
|
(component as any).newIssueName = ' ';
|
||||||
await (component as any).confirmCreateIssue();
|
await (component as any).confirmCreateIssue();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, inject } from '@angular/core';
|
import { Component, ElementRef, inject, ViewChild } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
@@ -28,10 +28,19 @@ export class MilestoneDetail {
|
|||||||
protected milestone: MilestoneEntity = this.buildMilestone();
|
protected milestone: MilestoneEntity = this.buildMilestone();
|
||||||
protected readonly issues = this.issuesStore.issues;
|
protected readonly issues = this.issuesStore.issues;
|
||||||
|
|
||||||
|
@ViewChild('newIssueInput') private newIssueInput?: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
protected editingDescription = false;
|
protected editingDescription = false;
|
||||||
protected showAddIssue = false;
|
protected showAddIssue = false;
|
||||||
protected showCreateIssue = false;
|
protected showCreateIssue = false;
|
||||||
|
protected showCreateDropdown = false;
|
||||||
protected newIssueName = '';
|
protected newIssueName = '';
|
||||||
|
protected newIssueType: IssueEntity['type'] = 'Story';
|
||||||
|
protected showTypeDropdown = false;
|
||||||
|
|
||||||
|
protected readonly typeOptions: IssueEntity['type'][] = [
|
||||||
|
'Epic', 'Bug', 'Study', 'Story', 'Task', 'Technical Story',
|
||||||
|
];
|
||||||
protected issueSearchQuery = '';
|
protected issueSearchQuery = '';
|
||||||
protected showIssueSuggestions = false;
|
protected showIssueSuggestions = false;
|
||||||
protected moreMenuOpen = false;
|
protected moreMenuOpen = false;
|
||||||
@@ -200,15 +209,43 @@ export class MilestoneDetail {
|
|||||||
).slice(0, 8);
|
).slice(0, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected openCreateIssue(): void {
|
protected openCreateIssue(type: IssueEntity['type'] = 'Story'): void {
|
||||||
this.newIssueName = '';
|
this.newIssueName = '';
|
||||||
|
this.newIssueType = type;
|
||||||
|
this.showTypeDropdown = false;
|
||||||
|
this.showCreateDropdown = false;
|
||||||
this.showCreateIssue = true;
|
this.showCreateIssue = true;
|
||||||
this.showAddIssue = false;
|
this.showAddIssue = false;
|
||||||
|
setTimeout(() => this.newIssueInput?.nativeElement.focus(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected cancelCreateIssue(): void {
|
protected cancelCreateIssue(): void {
|
||||||
this.showCreateIssue = false;
|
this.showCreateIssue = false;
|
||||||
this.newIssueName = '';
|
this.newIssueName = '';
|
||||||
|
this.newIssueType = 'Story';
|
||||||
|
this.showTypeDropdown = false;
|
||||||
|
this.showCreateDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toggleCreateDropdown(): void {
|
||||||
|
this.showCreateDropdown = !this.showCreateDropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected closeCreateDropdown(): void {
|
||||||
|
this.showCreateDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected toggleTypeDropdown(): void {
|
||||||
|
this.showTypeDropdown = !this.showTypeDropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected closeTypeDropdown(): void {
|
||||||
|
this.showTypeDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected selectNewIssueType(type: IssueEntity['type']): void {
|
||||||
|
this.newIssueType = type;
|
||||||
|
this.showTypeDropdown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async confirmCreateIssue(): Promise<void> {
|
protected async confirmCreateIssue(): Promise<void> {
|
||||||
@@ -216,7 +253,7 @@ export class MilestoneDetail {
|
|||||||
if (!name) return;
|
if (!name) return;
|
||||||
const created = await this.issuesStore.upsert({
|
const created = await this.issuesStore.upsert({
|
||||||
id: 0,
|
id: 0,
|
||||||
type: 'Story',
|
type: this.newIssueType,
|
||||||
assignee: '',
|
assignee: '',
|
||||||
epic: '',
|
epic: '',
|
||||||
name,
|
name,
|
||||||
|
|||||||
Reference in New Issue
Block a user