diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index f7cc9da..22c8b5f 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -1,119 +1,124 @@ -
-
-

Dashboard

- -
-
- -

Alle Userstories

- Lege einen Sprint an, um Userstories zu organisieren. -
- - -

- Aktueller Sprint: -

-

- Sprint: -

-

{{ selectedSprint.title }}

-

- {{ toDateString(selectedSprint.startDate) }} - - {{ toDateString(selectedSprint.endDate) }} -

- - - - -

- Verbleibende Tage: {{ getRemainingDaysInSprint() }} -

-
-
- -
-
-
-
- - Userstories - -
-
-
- Zum Sprint "{{ selectedSprint.title }}" sind aktuell keine - Userstories vorhanden. -
- -
-
-
-
-
- - {{ status.title }} - -
-
- - {{ getNumberOfUserstoriesByStatus(status) }} - -
-
-
-
- -
-
-
- -
-
-
-
-
-
-
+
+
+

Dashboard

+ +
+
+ + + +

Alle Userstories

+ Lege einen Sprint an, um Userstories zu organisieren. +
+ + +

+ Aktueller Sprint: +

+

+ Sprint: +

+

{{ selectedSprint.title }}

+

+ {{ toDateString(selectedSprint.startDate) }} - + {{ toDateString(selectedSprint.endDate) }} +

+ + + + + +

+ Verbleibende Tage: {{ getRemainingDaysInSprint() }} +

+
+
+ +
+ + +
+
+
+ + Userstories + +
+
+
+ Zum Sprint "{{ selectedSprint.title }}" sind aktuell keine + Userstories vorhanden. +
+ +
+
+
+
+
+ + {{ status.title }} + +
+
+ + {{ getNumberOfUserstoriesByStatus(status) }} + +
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index b52c9ec..e6710ce 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -1,187 +1,215 @@ -import { Component, OnChanges } from '@angular/core'; -import { forkJoin } from 'rxjs'; -import Chart from 'chart.js'; -import { - BackendService, - ScrumSprint, - ScrumStatus, - ScrumUserstory, -} from '../../services/backend.service'; - -@Component({ - selector: 'app-dashboard', - templateUrl: 'dashboard.component.html', - styleUrls: ['./dashboard.component.css'], -}) -export class DashboardComponent { - /** - * Returns the status that are used by at least one userstory. - */ - public get usedStatus(): ScrumStatus[] { - return this.status.filter( - (s) => - this.selectedSprintUserstories.find((us) => us.statusid === s.id) !== - undefined - ); - } - - public get selectedSprintUserstories(): ScrumUserstory[] { - if (this.selectedSprint === undefined) { - return this.userstories; - } - return this.userstories.filter( - (us) => us.sprintid === this.selectedSprint.id - ); - } - - public get currentSprint(): ScrumSprint { - const now = Date.now(); - return this.sprints.find( - (s) => Date.parse(s.startDate) < now && Date.parse(s.endDate) > now - ); - } - - private _selectedSprint: ScrumSprint; - public get selectedSprint(): ScrumSprint { - if (this._selectedSprint === undefined) { - if (this.currentSprint === undefined) { - return this.sprints[0]; - } - return this.currentSprint; - } - return this._selectedSprint; - } - - public set selectedSprint(value) { - this._selectedSprint = value; - this.createChart(); - } - - public status: ScrumStatus[] = []; - public userstories: ScrumUserstory[] = []; - public sprints: ScrumSprint[] = []; - - public chart: Chart; - - constructor(private backendService: BackendService) { - // download userstories, status and sprints and update - // the chart whenever a new response is there - forkJoin([ - backendService.getUserstories(), - backendService.getAllStatus(), - backendService.getSprints(), - ]).subscribe((results) => { - const [userstoryResponse, statusResponse, sprintResponse] = results; - if ( - userstoryResponse.status > 399 || - statusResponse.status > 399 || - sprintResponse.status > 399 - ) { - alert('Fehler'); - } else { - this.userstories.push(...userstoryResponse.body); - this.status.push(...statusResponse.body); - this.sprints.push(...sprintResponse.body); - this.createChart(); - } - }); - } - - /** - * Returns the date in the following format: 1.7.2020 - */ - public toDateString(isoFormatString) { - const date = new Date(isoFormatString); - return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`; - } - - private createChart() { - // @ts-ignore - const context = document.getElementById('done-stories-chart').getContext('2d'); - - if (this.usedStatus.length === 0) { - this.chart.destroy(); - } else { - this.chart = new Chart(context, { - type: 'pie', - data: { - labels: this.usedStatus.map((s) => s.title), - datasets: [ - { - data: this.usedStatus.map((s) => - this.getNumberOfUserstoriesByStatus(s) - ), - backgroundColor: this.getBackgroundColors(), - options: { - legend: { - display: true, - position: 'right', - labels: { - fontColor: 'rgba(255, 255, 255, 0.8)', - }, - }, - }, - }, - ], - }, - }); - this.chart.update(); - this.chart.render(); - } - } - - private getBackgroundColors(): string[] { - const baseColors = [ - 'rgb(255, 153, 102)', - 'rgb(255, 102, 102)', - 'rgb(153, 204, 255)', - 'rgb(102, 153, 102)', - 'rgb(204, 204, 153)', - 'rgb(153, 102, 204)', - 'rgb(204, 102, 102)', - 'rgb(255, 204, 153)', - 'rgb(153, 102, 255)', - 'rgb(204, 204, 204)', - 'rgb(102, 255, 204)', - 'rgb(102, 153, 255)', - 'rgb(153, 102, 153)', - 'rgb(204, 204, 255)', - ]; - const colors = []; - while (colors.length < this.usedStatus.length) { - colors.push(...baseColors); - } - return colors; - } - - public getNumberOfUserstoriesByStatus(status: ScrumStatus): number { - return this.selectedSprintUserstories.filter( - (us) => us.statusid === status.id - ).length; - } - - public getRemainingDaysInSprint(): number { - if (this.selectedSprint === undefined) { - return undefined; - } - return Math.floor( - (Date.parse(this.selectedSprint.endDate) - Date.now()) / 86400000 - ); - } - - /** - * Returns the "urgency" of the current sprint (ie what percentage of the sprint is remaining) - * as an integer from 0 (most urgent) to 2 (least urgent). - */ - public getSprintUrgency(): number { - const now = Date.now(); - const sprint = this.selectedSprint; - if (sprint === undefined) { - return undefined; - } - const deltaFromNow = Date.parse(sprint.endDate) - now; - const deltaFromStart = - Date.parse(sprint.endDate) - Date.parse(sprint.startDate); - return Math.floor((3 * deltaFromNow) / deltaFromStart); - } -} +import { Component } from '@angular/core'; +import { forkJoin } from 'rxjs'; +import Chart from 'chart.js'; +import { BackendService, ScrumSprint, ScrumStatus, ScrumUserstory } from '../../services/backend.service'; + +@Component({ + selector: 'app-dashboard', + templateUrl: 'dashboard.component.html', + styleUrls: ['./dashboard.component.css'], +}) +export class DashboardComponent { + /** + * Returns the status that are used by at least one userstory. + */ + public get usedStatus(): ScrumStatus[] { + return this.status.filter( + (s) => + this.selectedSprintUserstories.find((us) => us.statusid === s.id) !== + undefined + ); + } + + /** + * Returns all userstories in the selected sprint. + */ + public get selectedSprintUserstories(): ScrumUserstory[] { + if (this.selectedSprint === undefined) { + return this.userstories; + } + return this.userstories.filter( + (us) => us.sprintid === this.selectedSprint.id + ); + } + + /** + * Returns the currently active sprint. + * If multiple sprints are active at the current time, any one of them may be returned. + */ + public get currentSprint(): ScrumSprint { + const now = Date.now(); + return this.sprints.find( + (s) => Date.parse(s.startDate) < now && Date.parse(s.endDate) > now + ); + } + + private _selectedSprint: ScrumSprint; + + /** + * Returns the sprint selected by the user. + */ + public get selectedSprint(): ScrumSprint { + if (this._selectedSprint === undefined) { + if (this.currentSprint === undefined) { + return this.sprints[0]; + } + return this.currentSprint; + } + return this._selectedSprint; + } + + /** + * Sets the sprint selected by the user. + */ + public set selectedSprint(value) { + this._selectedSprint = value; + this.createChart(); + } + + /** All status. */ + public status: ScrumStatus[] = []; + + /** All userstories. */ + public userstories: ScrumUserstory[] = []; + + /** All sprints. */ + public sprints: ScrumSprint[] = []; + + /** + * The pie chart showing the userstories in the selected sprint. + */ + public chart: Chart; + + constructor(private backendService: BackendService) { + // download userstories, status and sprints and update + // the chart whenever a new response is there + forkJoin([ + backendService.getUserstories(), + backendService.getAllStatus(), + backendService.getSprints(), + ]).subscribe((results) => { + const [userstoryResponse, statusResponse, sprintResponse] = results; + if ( + userstoryResponse.status > 399 || + statusResponse.status > 399 || + sprintResponse.status > 399 + ) { + alert('Fehler'); + } else { + this.userstories.push(...userstoryResponse.body); + this.status.push(...statusResponse.body); + this.sprints.push(...sprintResponse.body); + this.createChart(); + } + }); + } + + /** + * Returns the date in the following format: 1.7.2020 + * @param dateValue an integer representing the number of milliseconds since the + * ECMAScript epoch -or- a string in a format recognized by the Date.parse() method. + */ + public toDateString(dateValue) { + const date = new Date(dateValue); + return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`; + } + + private createChart() { + // @ts-ignore + const context = document.getElementById('done-stories-chart').getContext('2d'); + + if (this.usedStatus.length === 0) { + this.chart.destroy(); + } else { + this.chart = new Chart(context, { + type: 'pie', + data: { + labels: this.usedStatus.map((s) => s.title), + datasets: [ + { + data: this.usedStatus.map((s) => + this.getNumberOfUserstoriesByStatus(s) + ), + backgroundColor: this.getBackgroundColors(), + options: { + legend: { + display: true, + position: 'right', + labels: { + fontColor: 'rgba(255, 255, 255, 0.8)', + }, + }, + }, + }, + ], + }, + }); + this.chart.update(); + this.chart.render(); + } + } + + /** + * Returns a sufficiently large array of background colors to be used in the chart. + */ + private getBackgroundColors(): string[] { + const baseColors = [ + 'rgb(255, 153, 102)', + 'rgb(255, 102, 102)', + 'rgb(153, 204, 255)', + 'rgb(102, 153, 102)', + 'rgb(204, 204, 153)', + 'rgb(153, 102, 204)', + 'rgb(204, 102, 102)', + 'rgb(255, 204, 153)', + 'rgb(153, 102, 255)', + 'rgb(204, 204, 204)', + 'rgb(102, 255, 204)', + 'rgb(102, 153, 255)', + 'rgb(153, 102, 153)', + 'rgb(204, 204, 255)', + ]; + const colors = []; + while (colors.length < this.usedStatus.length) { + colors.push(...baseColors); + } + return colors; + } + + /** + * Returns the number of userstories in the selected sprint that have the given status. + */ + public getNumberOfUserstoriesByStatus(status: ScrumStatus): number { + return this.selectedSprintUserstories.filter( + (us) => us.statusid === status.id + ).length; + } + + /** + * Returns the days remaining until the end date of the selected sprint. + */ + public getRemainingDaysInSprint(): number { + if (this.selectedSprint === undefined) { + return undefined; + } + return Math.floor( + (Date.parse(this.selectedSprint.endDate) - Date.now()) / 86400000 + ); + } + + /** + * Returns the "urgency" of the current sprint (ie what percentage of the sprint is remaining) + * as an integer from 0 (most urgent) to 2 (least urgent). + */ + public getSprintUrgency(): number { + const now = Date.now(); + const sprint = this.selectedSprint; + if (sprint === undefined) { + return undefined; + } + const deltaFromNow = Date.parse(sprint.endDate) - now; + const deltaFromStart = + Date.parse(sprint.endDate) - Date.parse(sprint.startDate); + return Math.floor((3 * deltaFromNow) / deltaFromStart); + } +}