mirror of
https://github.com/simon987/task_tracker.git
synced 2025-04-19 18:16:45 +00:00
project dashboard basic setup
This commit is contained in:
parent
920647b4aa
commit
be8dce10ea
6
web/angular/package-lock.json
generated
6
web/angular/package-lock.json
generated
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*---------*/
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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>{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user