project dashboard basic setup

This commit is contained in:
simon987 2019-02-06 20:39:43 -05:00
parent 920647b4aa
commit be8dce10ea
5 changed files with 245 additions and 44 deletions

View File

@ -838,6 +838,12 @@
"semver-intersect": "1.4.0" "semver-intersect": "1.4.0"
} }
}, },
"@types/chart.js": {
"version": "2.7.42",
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.42.tgz",
"integrity": "sha512-+0v+PV2J9wsV7u36Vqt5Oke7ugtiZqNeYJgNk5mgNMkEkqtjxo2Q9N5Q9znHlgmXeOTfQL6e1sfcAbTnPHAzlA==",
"dev": true
},
"@types/jasmine": { "@types/jasmine": {
"version": "2.8.15", "version": "2.8.15",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.15.tgz", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.15.tgz",

View File

@ -36,6 +36,7 @@
"@angular/cli": "~7.2.2", "@angular/cli": "~7.2.2",
"@angular/compiler-cli": "~7.2.0", "@angular/compiler-cli": "~7.2.0",
"@angular/language-service": "~7.2.0", "@angular/language-service": "~7.2.0",
"@types/chart.js": "^2.7.42",
"@types/jasmine": "~2.8.8", "@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4", "@types/node": "~8.9.4",

View File

@ -1,44 +1,14 @@
#status { #timeline-wrapper {
height: 360px; width: 100%;
position: relative;
width: 360px;
margin: 1em;
} }
#assignees { #status-pie-wrapper {
height: 360px; height: 50%;
position: relative; width: 400px;
width: 360px;
margin: 1em;
} }
.pie-label { #assignees-pie-wrapper {
left: 130px; margin-top: 1em;
padding: 10px; height: 50%;
position: absolute; width: 400px;
text-align: center;
top: 150px;
width: 80px;
z-index: 10;
font-family: Roboto, "Helvetica Neue", sans-serif;
font-weight: bold;
} }
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
display: none;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: center;
top: 95px;
width: 80px;
z-index: 10;
}
/*---------*/

View File

@ -1,5 +1,5 @@
<div class="container"> <div class="container">
<mat-card> <mat-card class="mat-elevation-z8">
<mat-card-title *ngIf="project">{{"dashboard.title" | translate}} "{{project.name}}"</mat-card-title> <mat-card-title *ngIf="project">{{"dashboard.title" | translate}} "{{project.name}}"</mat-card-title>
<mat-card-content style="padding: 2em 0 1em"> <mat-card-content style="padding: 2em 0 1em">
@ -11,10 +11,18 @@
<pre *ngIf="project">{{project.motd}}</pre> <pre *ngIf="project">{{project.motd}}</pre>
<div style="display: flex; align-items: center; justify-content: center"> <div style="display: flex; align-items: center; justify-content: center">
<div id="timeline"></div> <div id="timeline-wrapper">
<canvas id="timeline"></canvas>
</div>
<div id="status-pie"></div> <div>
<div id="assignees-pie"></div> <div id="status-pie-wrapper">
<canvas id="status-pie"></canvas>
</div>
<div id="assignees-pie-wrapper">
<canvas id="assignees-pie"></canvas>
</div>
</div>
</div> </div>
<mat-expansion-panel *ngIf="project" style="margin-top: 1em"> <mat-expansion-panel *ngIf="project" style="margin-top: 1em">
@ -26,7 +34,7 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button color="primary" <button mat-raised-button color="primary" *ngIf="project"
[routerLink]="'/project/' + project.id + '/update'">{{"project.update" | translate}}</button> [routerLink]="'/project/' + project.id + '/update'">{{"project.update" | translate}}</button>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>

View File

@ -3,6 +3,8 @@ import {ApiService} from "../api.service";
import {Project} from "../models/project"; import {Project} from "../models/project";
import {ActivatedRoute} from "@angular/router"; import {ActivatedRoute} from "@angular/router";
import {Chart, ChartData, Point} from "chart.js";
@Component({ @Component({
selector: 'app-project-dashboard', selector: 'app-project-dashboard',
@ -13,7 +15,22 @@ export class ProjectDashboardComponent implements OnInit {
private projectId; private projectId;
project: Project; project: Project;
private timeline: Chart;
private statusPie: Chart;
private assigneesPir: Chart;
private colors = {
new: "#76FF03",
failed: "#FF3D00",
closed: "#E0E0E0",
awaiting: "#FFB74D"
};
tmpLabels = [];
tmpNew = [];
tmpFailed = [];
tmpClosed = [];
tmpAwaiting = [];
constructor(private apiService: ApiService, private route: ActivatedRoute) { constructor(private apiService: ApiService, private route: ActivatedRoute) {
} }
@ -23,9 +40,208 @@ export class ProjectDashboardComponent implements OnInit {
this.route.params.subscribe(params => { this.route.params.subscribe(params => {
this.projectId = params["id"]; this.projectId = params["id"];
this.getProject(); this.getProject();
});
let n = 40;
for (let i = 0; i < n; i++) {
this.tmpLabels.push((1549501926 + 600 * i) * 1000);
this.tmpNew.push(Math.ceil(Math.random() * 30))
this.tmpClosed.push(Math.ceil(Math.random() * 100))
this.tmpFailed.push(Math.ceil(Math.random() * 13))
this.tmpAwaiting.push(Math.ceil(Math.random() * 40))
}
this.setupTimeline();
this.setupStatusPie();
this.setupAssigneesPie();
}
private setupTimeline() {
let elem = document.getElementById("timeline") as any;
let ctx = elem.getContext("2d");
this.timeline = new Chart(ctx, {
type: "bar",
data: {
labels: this.tmpLabels,
datasets: [
{
label: "New",
type: "line",
fill: false,
borderColor: this.colors.new,
backgroundColor: this.colors.new,
data: this.tmpNew,
pointRadius: 0,
lineTension: 0.2,
},
{
label: "Failed",
type: "line",
fill: false,
borderColor: this.colors.failed,
backgroundColor: this.colors.failed,
data: this.tmpFailed,
pointRadius: 0,
lineTension: 0.2,
},
{
label: "Closed",
type: "line",
fill: false,
borderColor: this.colors.closed,
backgroundColor: this.colors.closed,
pointRadius: 0,
data: this.tmpClosed,
lineTension: 0.2,
},
{
label: "Awaiting verification",
type: "line",
fill: false,
borderColor: this.colors.awaiting,
backgroundColor: this.colors.awaiting,
data: this.tmpAwaiting,
pointRadius: 0,
lineTension: 0.2,
},
],
},
options: {
title: {
display: true,
text: "Task status timeline",
position: "bottom"
},
legend: {
position: 'left',
},
scales: {
xAxes: [{
type: "time",
distribution: "series",
ticks: {
source: "auto"
},
time: {
unit: "minute",
unitStepSize: 10,
}
}]
},
tooltips: {
enabled: true,
intersect: false,
mode: "index",
position: "nearest",
},
}
}) })
} }
private setupStatusPie() {
let elem = document.getElementById("status-pie") as any;
let ctx = elem.getContext("2d");
this.statusPie = new Chart(ctx, {
type: "doughnut",
data: {
labels: [
"New",
"Failed",
"Closed",
"Awaiting verification",
],
datasets: [
{
label: "Task status",
data: [
10,
24,
301,
90,
],
backgroundColor: [
this.colors.new,
this.colors.failed,
this.colors.closed,
this.colors.awaiting
],
}
],
},
options: {
responsive: true,
legend: {
position: 'left',
},
title: {
display: true,
text: "Current task status",
position: "bottom"
},
animation: {
animateScale: true,
animateRotate: true
}
}
});
}
private setupAssigneesPie() {
let elem = document.getElementById("assignees-pie") as any;
let ctx = elem.getContext("2d");
this.statusPie = new Chart(ctx, {
type: "doughnut",
data: {
labels: [
"marc",
"simon",
"bernie",
"natasha",
],
datasets: [
{
label: "Task status",
data: [
10,
24,
1,
23,
],
backgroundColor: [
this.colors.new,
this.colors.failed,
this.colors.closed,
this.colors.awaiting
],
}
],
},
options: {
responsive: true,
legend: {
position: 'left',
},
title: {
display: true,
text: "Task assignment",
position: "bottom"
},
animation: {
animateScale: true,
animateRotate: true
}
}
});
}
private getProject() { private getProject() {
this.apiService.getProject(this.projectId).subscribe(data => { this.apiService.getProject(this.projectId).subscribe(data => {
this.project = <Project>{ this.project = <Project>{