Skip to content

Commit 0bdbbb6

Browse files
Complete new dashboard
1 parent 188ca6e commit 0bdbbb6

File tree

15 files changed

+149
-152
lines changed

15 files changed

+149
-152
lines changed

e2e/pages/issues/list.po/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,3 @@ export const columns = {
1919
assignee: 'Assignee',
2020
externalIssue: 'External Issue'
2121
};
22-

src/app/elements/single-line-bar-chart/single-line-bar-chart.component.css

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
content: "";
1616
position: absolute;
1717
opacity: .4;
18+
filter: brightness(120%);
1819
z-index: -1;
1920
left: 50%;
2021
right: 50%;
@@ -25,40 +26,39 @@
2526
border-bottom-right-radius: inherit;
2627
border-top-left-radius: inherit;
2728
border-bottom-left-radius: inherit;
28-
filter: brightness(120%);
2929
-webkit-transition-property: left right;
3030
transition-property: left right;
31-
-webkit-transition-duration: 0.3s;
32-
transition-duration: 0.3s;
31+
-webkit-transition-duration: .3s;
32+
transition-duration: .3s;
3333
-webkit-transition-timing-function: ease-out;
3434
transition-timing-function: ease-out;
3535
}
3636

37-
.slc-part:hover:before {
37+
.slc-part.active:before {
3838
top: -3;
3939
bottom:-3;
40+
left: 0;
41+
right: 0;
42+
}
43+
44+
.slc-part.active:first-of-type:before {
4045
left: -3;
46+
border-top-left-radius: 13px;
47+
border-bottom-left-radius: 13px;
48+
}
49+
50+
.slc-part.active:last-of-type:before {
4151
right: -3;
52+
border-top-right-radius: 13px;
53+
border-bottom-right-radius: 13px;
4254
}
4355

44-
.slc-part:first-child {
56+
.slc-part:first-of-type {
4557
border-top-left-radius: 10px;
4658
border-bottom-left-radius: 10px;
4759
}
4860

49-
.slc-part:last-child {
61+
.slc-part:last-of-type {
5062
border-top-right-radius: 10px;
5163
border-bottom-right-radius: 10px;
52-
}
53-
54-
.slc-part:hover .slc-title {
55-
opacity: 1;
56-
}
57-
58-
.slc-title {
59-
transition: all .3s ease-in;
60-
opacity: 0;
61-
margin-left: 10px;
62-
bottom: -25px;
63-
position: absolute;
6464
}
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
<div class="row">
2-
<div class="col-sm-12">
3-
<div *ngFor="let item of dataToShow" class="slc-part" [style.width.%]="item.percent" [style.background-color]="item.color">
4-
<div class="slc-title text-centered">
5-
{{item.percent | number: '1.0-1'}}% {{item.label}}
6-
</div>
2+
<div class="col-sm-12" #lineHolder>
3+
<div *ngFor="let item of dataToShow" [class]="'slc-part slc-part-' + item.index" [style.width.%]="item.percent"
4+
[style.background-color]="item.color" (mouseenter)="setActive($event)" (mouseleave)="setInactive($event)"
5+
tooltip="{{item.percent | number: '1.0-1'}}% {{item.label}}" placement="bottom">
76
</div>
87
</div>
9-
</div>
8+
</div>

src/app/elements/single-line-bar-chart/single-line-bar-chart.component.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Component, OnInit, Input } from '@angular/core';
1+
import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
2+
import { TooltipModule } from 'ngx-bootstrap/tooltip';
23

34
@Component({
45
selector: 'app-single-line-bar-chart',
@@ -8,6 +9,8 @@ import { Component, OnInit, Input } from '@angular/core';
89
export class SingleLineBarChartComponent implements OnInit {
910

1011
@Input() data: SingleLineBarChartData[];
12+
@Input() activateParts: number[];
13+
@ViewChild('lineHolder') lineHolder: ElementRef;
1114
dataToShow: SingleLineBarChartData[];
1215

1316
constructor() { }
@@ -17,20 +20,51 @@ export class SingleLineBarChartComponent implements OnInit {
1720
}
1821

1922
ngOnChanges() {
20-
this.updateData()
23+
this.updateData();
24+
this.setActiveParts(this.activateParts);
2125
}
2226

2327
updateData() {
2428
if (this.data) {
2529
const sum = this.data.map(item => item.value).reduce((prev, next) => prev + next);
26-
this.data.forEach(element => {
30+
for (let i = 0; i < this.data.length; i++) {
31+
const element = this.data[i];
2732
element['percent'] = (element.value / sum) * 100;
28-
});
33+
element['index'] = i;
34+
}
2935

3036
this.dataToShow = this.data.filter(x => x.value !== 0)
3137
}
3238
}
3339

40+
setActiveParts(partIndexes: number[]) {
41+
if(this.data){const holder = this.lineHolder.nativeElement as HTMLElement;
42+
this.data.forEach(dataItem => {
43+
if (this.dataToShow.find(x => x['index'] === dataItem['index'])) {
44+
const part = holder.getElementsByClassName(`slc-part-${dataItem['index']}`).item(0);
45+
if(part) {
46+
if (partIndexes.includes(dataItem['index'])) {
47+
part.classList.add('active');
48+
part.dispatchEvent(new CustomEvent('mouseover'));
49+
} else {
50+
part.classList.remove('active');
51+
part.dispatchEvent(new CustomEvent('mouseout'));
52+
}
53+
}
54+
}
55+
});
56+
}
57+
}
58+
59+
setActive(event: MouseEvent) {
60+
const target = event.target as HTMLElement;
61+
target.classList.add('active');
62+
}
63+
64+
setInactive(event: MouseEvent) {
65+
const target = event.target as HTMLElement;
66+
target.classList.remove('active');
67+
}
3468
}
3569

3670
export class SingleLineBarChartData {

src/app/pages/project/import/import.component.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ ui-switch{
3030
padding-bottom: 4px;
3131
}
3232

33-
label.btn {
33+
.btn {
3434
margin: 0;
35+
height: 33px;
3536
}

src/app/pages/project/project-view/project-veiw.component.ts

Lines changed: 39 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,36 @@ import { TestRunService } from '../../../services/testRun.service';
66
import { ProjectService } from '../../../services/project.service';
77
import { TestRunStat } from '../../../shared/models/testrunStats';
88
import { AuditService } from '../../../services/audits.service';
9-
import { Audit, AuditNotification } from '../../../shared/models/audit';
9+
import { Audit } from '../../../shared/models/audit';
1010
import { GlobalDataService } from '../../../services/globaldata.service';
1111
import { TFColumn, TFColumnType } from '../../../elements/table/tfColumn';
1212
import { IssueService } from '../../../services/issue.service';
1313
import { Issue } from '../../../shared/models/issue';
1414
import { SingleLineBarChartData } from '../../../elements/single-line-bar-chart/single-line-bar-chart.component';
1515
import { TestSuiteService } from '../../../services/testSuite.service';
1616
import { TestSuite } from '../../../shared/models/testSuite';
17+
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
1718

1819
@Component({
1920
templateUrl: './project-view.component.html',
2021
styleUrls: ['./project-view.component.css']
2122
})
2223
export class ProjectViewComponent implements OnInit {
24+
icons = { faExclamationTriangle };
25+
activateParts: number[] = [];
2326
audits: Audit[];
2427
suites: TestSuite[];
2528
project: Project;
2629
testRuns: TestRun[];
2730
testRun: TestRun;
2831
testRunStats: TestRunStat[];
2932
hideAll = true;
30-
auditNotification: AuditNotification;
31-
notification: { text: string, type: string };
33+
notification: string;
3234
testRunColumns: TFColumn[] = [
3335
{ name: 'Build Name', property: 'build_name', type: TFColumnType.text },
3436
{ name: 'Execution Environment', property: 'execution_environment', type: TFColumnType.text },
37+
{ name: 'Suite', property: 'test_suite.name', type: TFColumnType.text },
3538
{ name: 'Start Time', property: 'start_time', type: TFColumnType.date, class: 'fit' },
36-
{ name: 'Finish Time', property: 'finish_time', type: TFColumnType.date, class: 'fit' },
3739
{ name: 'Pass Rate', property: 'failed', type: TFColumnType.percent, class: 'fit' },
3840
];
3941
issueColumns: TFColumn[] = [{ name: 'Id', property: 'id', type: TFColumnType.text, class: 'fit' },
@@ -73,25 +75,22 @@ export class ProjectViewComponent implements OnInit {
7375
this.testRun = { project_id: projectId, debug: 0 };
7476
this.project = { id: projectId };
7577

76-
this.allIssues = await this.issueService.getIssues({
77-
project_id: projectId
78-
});
78+
this.project = (await this.projectService.getProjects(this.project).toPromise())[0];
7979

80-
this.myIssues = this.allIssues.filter(x => x.status_id != 4 && x.status_id != 2 && x.assignee_id === this.globaldata.currentUser.id);
80+
[this.allIssues,this.suites, this.testRuns, this.testRunStats] = await Promise.all([
81+
this.issueService.getIssues({ project_id: projectId }),
82+
this.testSuiteService.getTestSuite({ project_id: projectId }),
83+
this.testrunService.getTestRun(this.testRun, 5),
84+
this.testrunService.getTestsRunStats(this.testRun)
85+
])
8186

82-
this.suites = await this.testSuiteService.getTestSuite({ project_id: projectId });
83-
this.project = (await this.projectService.getProjects(this.project).toPromise())[0];
87+
this.myIssues = this.allIssues.filter(x => x.status_id != 4 && x.status_id != 2 && x.assignee_id === this.globaldata.currentUser.id);
8488

8589
if (this.globaldata.auditModule) {
86-
this.auditService.getAudits({ project: { id: this.project.id } }).subscribe(audits => {
87-
this.audits = audits;
88-
this.generateAuditNotification();
89-
this.setAuditNotification();
90-
});
90+
this.audits = await this.auditService.getAudits({ project: { id: this.project.id } }).toPromise();
91+
this.notification = this.generateAuditNotification(this.audits);
9192
}
9293

93-
this.testRuns = await this.testrunService.getTestRun(this.testRun, 5);
94-
this.testRunStats = await this.testrunService.getTestsRunStats(this.testRun);
9594
this.testRuns.forEach(testrun => {
9695
testrun['failed'] = this.getNumberOfFails(testrun.id);
9796
});
@@ -114,77 +113,37 @@ export class ProjectViewComponent implements OnInit {
114113
return this.testrunService.getPassRate(stats);
115114
}
116115

117-
openTestRun(testRunId: number) {
118-
this.router.navigate(['/project/' + this.project.id + '/testrun/' + testRunId]);
116+
openTestRun(testRun: TestRun) {
117+
this.router.navigate([`/project/${testRun.project_id}/testrun/${testRun.id}`]);
119118
}
120119

121-
generateAuditNotification() {
122-
const latest: Audit = this.audits.filter(x => x.submitted).length > 0
123-
? this.audits.sort(function (a, b) { return new Date(b.submitted).getTime() - new Date(a.submitted).getTime(); })[0]
124-
: undefined;
125-
const opened: Audit = this.audits.find(x => x.status.id !== 4);
126-
this.auditNotification = {
127-
last_submitted: latest,
128-
has_opened_audit: opened !== undefined,
129-
next_due_date: opened
130-
? opened.due_date
131-
: latest
132-
? this.auditService.createDueDate(new Date(new Date(latest.submitted).setHours(0, 0, 0, 0)))
133-
: this.auditService.createDueDate(new Date(new Date(this.project.created).setHours(0, 0, 0, 0)))
134-
};
120+
openIssue(issue: Issue) {
121+
this.router.navigate([`/project/${issue.project_id}/issue/${issue.id}`]);
135122
}
136123

137-
setAuditNotification() {
138-
if (new Date(this.auditNotification.next_due_date)
139-
< new Date(new Date(new Date().setDate(new Date().getDate())).setHours(0, 0, 0, 0))) {
140-
this.notification = {
141-
text: `Next Audit should have been submitted on ${
142-
new Date(this.auditNotification.next_due_date).toDateString()
143-
} and is now overdue! Please contact your audit administrator to get more details on next audit progress.`,
144-
type: 'danger'
145-
};
146-
} else if (!this.auditNotification.has_opened_audit
147-
&& new Date(this.auditNotification.next_due_date.toString()) >= new Date(new Date().setHours(0, 0, 0, 0))
148-
&& new Date(this.auditNotification.next_due_date.toString()) <= new Date(new Date().setDate(new Date().getDate() + 14))) {
149-
const days = this.daysdifference(new Date(new Date().setHours(0, 0, 0, 0)),
150-
new Date(this.auditNotification.next_due_date.toString()));
151-
this.notification = {
152-
text: `Next Audit should be submitted ${
153-
days === 0 ? 'today' : `in ${days} ${days > 1 ? 'days' : 'day'}`
154-
} but is still not created in the system. Please contact your audit administrator to schedule a new Audit for your project.`,
155-
type: 'warning'
156-
};
157-
} else if (this.auditNotification.has_opened_audit && this.audits.length > 0) {
158-
this.notification = {
159-
text: `New Audit is created and planned to be finished by ${new Date(this.auditNotification.next_due_date).toDateString()}`,
160-
type: 'success'
161-
};
162-
}
163-
164-
if (this.auditNotification.last_submitted && this.audits.length > 0) {
165-
const message = `Last project Audit was submitted on ${
166-
new Date(this.auditNotification.last_submitted.submitted).toDateString()
167-
} with a result of ${this.auditNotification.last_submitted.result}%`;
168-
169-
if (!this.notification) { this.notification = { text: undefined, type: undefined }; }
170-
this.notification.text = this.notification.text
171-
? `${this.notification.text} \r\n ${message}`
172-
: message;
173-
174-
this.notification.type = this.notification.type ? this.notification.type : this.notification.type = 'success';
124+
generateAuditNotification(audits: Audit[]): string {
125+
if (!audits || audits.length === 0) {
126+
return `This project has no audits! Please contact your audit administrator to schedule a new Audit for your project.`
175127
}
176-
177-
if (!this.notification || !this.notification.text) {
178-
this.notification = {
179-
text: `This project has no audits! Please contact your audit administrator to schedule a new Audit for your project.`,
180-
type: 'warning'
181-
};
128+
const lastSubmitted: Audit = this.audits.filter(x => x.submitted).length > 0
129+
? this.audits.sort(function (a, b) { return new Date(b.submitted).getTime() - new Date(a.submitted).getTime(); })[0]
130+
: undefined;
131+
if (lastSubmitted) {
132+
const lastSubmittedDate = new Date(lastSubmitted.submitted);
133+
const nextDueDate = new Date(lastSubmittedDate).setMonth(lastSubmittedDate.getMonth() + 6);
134+
if (nextDueDate < new Date().getTime()) {
135+
return `Over half a year has passed since last submitted Audit. Please make sure the process of the new Audit is going well.`
136+
}
182137
}
183138
}
184139

185-
getDateAsText(date: Date) {
186-
return date.toDateString();
187-
}
140+
showMeaningful() {
141+
this.activateParts = [0, 1];
142+
};
143+
144+
hideMeaningful() {
145+
this.activateParts = [];
146+
};
188147

189148
IsHideAll() {
190149
if (this.testRunStats) {
@@ -195,14 +154,6 @@ export class ProjectViewComponent implements OnInit {
195154
return this.hideAll;
196155
}
197156

198-
daysdifference(date1, date2) {
199-
const ONEDAY = 1000 * 60 * 60 * 24;
200-
const date1_ms = date1.getTime();
201-
const date2_ms = date2.getTime();
202-
const difference_ms = Math.abs(date1_ms - date2_ms);
203-
return Math.round(difference_ms / ONEDAY);
204-
}
205-
206157
getQualityInfo(suites: TestSuite[], testRunStats: TestRunStat[]): { current: { quality: number, NA: number, testIssue: number, other: number, appIssue: number, passed: number, total: number }, average: number } {
207158
const testRunStatsBySuite = [];
208159
for (const suite of suites) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
.row {
22
margin-bottom: 80px;
3+
margin-left: 20px;
4+
margin-right: 20px;
5+
}
6+
7+
.warning-icon {
8+
color: var(--warning);
9+
}
10+
11+
.well {
12+
white-space: pre;
313
}

0 commit comments

Comments
 (0)