File
Implements
Metadata
selector |
app-create-issue-dialog |
styleUrls |
./create-issue-dialog.component.scss |
templateUrl |
./create-issue-dialog.component.html |
Methods
afterAlertClose
|
afterAlertClose()
|
|
|
onNoClick
|
onNoClick(showConfirmDialog: boolean)
|
|
Parameters :
Name |
Type |
Optional |
showConfirmDialog |
boolean
|
No
|
|
Private
updateSelectedItems
|
updateSelectedItems()
|
|
Updates items to be selected in the Create Issue page,
including 1) components and 2) locations.
|
body
|
Decorators :
@ViewChild('body')
|
|
category
|
Default value : new FormControl('', [Validators.required])
|
|
Public
data
|
Type : DialogData
|
Decorators :
@Inject(MAT_DIALOG_DATA)
|
|
Public
issueData
|
Type : LocalIssueData
|
Default value : {
components: [],
locations: [],
labels: [],
assignees: [],
linksToIssues: []
}
|
|
Public
loading
|
Default value : false
|
|
Public
saveFailed
|
Default value : false
|
|
title
|
Default value : new FormControl('', [CCIMSValidators.nameValidator, Validators.required])
|
|
import {Component, Inject, OnInit, ViewChild} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog';
import {FormControl, Validators} from '@angular/forms';
import {IssueCategory} from '../../../generated/graphql';
import {UserNotifyService} from '@app/user-notify/user-notify.service';
import {CCIMSValidators} from '@app/utils/validators';
import {CreateIssueInput} from '../../../generated/graphql-dgql';
import {NodeId, NodeType} from '@app/data-dgql/id';
import DataService from '@app/data-dgql';
import {LocalIssueData} from '@app/issue-detail/issue-sidebar.component';
import {RemoveDialogComponent} from '@app/dialogs/remove-dialog/remove-dialog.component';
@Component({
selector: 'app-create-issue-dialog',
templateUrl: './create-issue-dialog.component.html',
styleUrls: ['./create-issue-dialog.component.scss']
})
/**
* This component opens a dialog for the issue creation.
*/
export class CreateIssueDialogComponent implements OnInit {
@ViewChild('body') body;
public loading = false;
public saveFailed = false;
constructor(
public dialogRef: MatDialogRef<CreateIssueDialogComponent>,
private dialog: MatDialog,
private dataService: DataService,
@Inject(MAT_DIALOG_DATA) public data: DialogData,
private notify: UserNotifyService
) {}
// form controls for the form fields
title = new FormControl('', [CCIMSValidators.nameValidator, Validators.required]);
category = new FormControl('', [Validators.required]);
public issueData: LocalIssueData = {
components: [],
locations: [],
labels: [],
assignees: [],
linksToIssues: []
};
ngOnInit(): void {
// sets up the issue category as Unclassified
this.category.setValue(IssueCategory.Unclassified);
this.dialogRef.disableClose = true;
// updates items to be selected
this.updateSelectedItems();
}
/**
* Updates items to be selected in the Create Issue page,
* including 1) components and 2) locations.
*/
private updateSelectedItems() {
// updates components
for (const componentId of this.data.components) {
this.issueData.components.push(componentId);
this.issueData.locations.push(componentId);
}
if (this.data.locations) {
// updates locations
for (const componentId of this.data.locations) {
this.issueData.locations.push(componentId);
}
}
}
onNoClick(showConfirmDialog: boolean): void {
if (showConfirmDialog) {
this.dialog
.open(RemoveDialogComponent, {
data: {
title: 'Really discard issue?',
messages: ['Are you sure you want to discard this issue?'],
confirmButtonText: 'Confirm'
}
})
.afterClosed()
.subscribe((close) => {
if (close) {
this.dialogRef.close();
}
});
} else {
this.dialogRef.close();
}
}
afterAlertClose(): void {
this.saveFailed = false;
}
onCreate(): void {
const issueData: CreateIssueInput = {
title: this.title.value,
body: this.body.code,
category: this.category.value,
clientMutationID: Math.random().toString(36),
components: this.issueData.components.map((node) => node.id),
locations: this.issueData.locations.map((node) => node.id),
labels: this.issueData.labels.map((node) => node.id),
assignees: this.issueData.assignees.map((node) => node.id)
};
this.loading = true;
this.saveFailed = false;
this.dataService.mutations
.createIssue(issueData)
.then(async (result) => {
const issueId = {type: NodeType.Issue, id: result.id};
const promises = [];
for (const linked of this.issueData.linksToIssues) {
promises.push(
this.dataService.mutations.linkIssue(Math.random().toString(), issueId, linked).catch((err) => {
this.notify.notifyError('Failed to link issue!', err);
// aborting on this error would cause weird non-recoverable state so we won't rethrow it
})
);
}
await Promise.all(promises);
this.dialogRef.close(true);
})
.catch((err) => {
this.notify.notifyError('Failed to create issue!', err);
this.saveFailed = true;
})
.finally(() => {
this.loading = false;
});
}
}
/**
* Interface that defines what data is injected to the dialog.
*/
export interface DialogData {
projectId: string;
// initial state of the issue's component list
components: NodeId[];
// initial state of the issue's location list
locations: NodeId[];
}
<h1 mat-dialog-title>Create Issue</h1>
<div mat-dialog-content>
<form>
<!-- Issue title -->
<mat-form-field class="stretch" appearance="outline">
<mat-label>Title</mat-label>
<input matInput [formControl]="title" required />
<mat-error *ngIf="title.invalid">Invalid issue title</mat-error>
</mat-form-field>
<!-- Issue description -->
<app-markdown-editor #body></app-markdown-editor>
<!-- Issue category -->
<!-- FIXME: Extract into component (used in issue details page as well) -->
<mat-button-toggle-group class="category-selector" [formControl]="category" required>
<mat-button-toggle value="UNCLASSIFIED">
<mat-icon [svgIcon]="'issue-uncategorized'"></mat-icon>
Unclassified
</mat-button-toggle>
<mat-button-toggle value="BUG">
<mat-icon [svgIcon]="'issue-bug'"></mat-icon>
Bug
</mat-button-toggle>
<mat-button-toggle value="FEATURE_REQUEST">
<mat-icon [svgIcon]="'issue-feature'"></mat-icon>
Feature Request
</mat-button-toggle>
</mat-button-toggle-group>
<!-- Issue data -->
<app-issue-sidebar [(localIssue)]="issueData" [projectId]="data.projectId"></app-issue-sidebar>
<!-- Save failed error message -->
<!-- FIXME: This should use material styling -->
<nz-alert
*ngIf="this.saveFailed"
class="error-message"
nzType="error"
nzMessage="Something went wrong"
nzShowIcon
nzCloseable
(nzOnClose)="afterAlertClose()"
></nz-alert>
</form>
</div>
<div mat-dialog-actions class="actions">
<!-- Cancel button, stops the issue creation process -->
<button mat-raised-button color="warn" (click)="onNoClick(body.code?.length > 0 || title.value.length > 0)">Cancel</button>
<!-- Create button, finishes the issue creation process -->
<button
mat-raised-button
color="success"
[class.spinner]="loading"
[disabled]="loading || title.invalid || category.invalid"
(click)="onCreate()"
>
Create
</button>
</div>
@import "src/styles/dialog";
.category-selector {
width: 100%;
margin-bottom: 8px;
mat-button-toggle {
width: 100%;
::ng-deep &.mat-button-toggle-checked {
font-weight: 600;
}
}
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
}
Legend
Html element with directive