Add dashboard prototype
This commit is contained in:
		
							
								
								
									
										26911
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										26911
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -22,6 +22,7 @@
 | 
			
		||||
    "@angular/router": "~9.1.9",
 | 
			
		||||
    "@ng-bootstrap/ng-bootstrap": "^6.0.0",
 | 
			
		||||
    "bootstrap": "^4.4.0",
 | 
			
		||||
    "chart.js": "^2.9.3",
 | 
			
		||||
    "rxjs": "~6.5.4",
 | 
			
		||||
    "tslib": "^1.10.0",
 | 
			
		||||
    "zone.js": "~0.10.2"
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,13 @@ import { Routes, RouterModule } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { TaskListComponent } from './task-list/task-list.component';
 | 
			
		||||
import { UserstoryListComponent } from './userstory-list/userstory-list.component';
 | 
			
		||||
import { DashboardComponent } from './dashboard/dashboard.component';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
  { path: 'tasks', component: TaskListComponent },
 | 
			
		||||
  { path: 'userstories', component: UserstoryListComponent },
 | 
			
		||||
  { path: 'dashboard', component: DashboardComponent },
 | 
			
		||||
  { path: '', redirectTo: '/tasks', pathMatch: 'full' },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,8 @@ import { TaskListComponent } from './task-list/task-list.component';
 | 
			
		||||
import { TaskFormComponent } from './task-form/task-form.component';
 | 
			
		||||
import { UserstoryListComponent } from './userstory-list/userstory-list.component';
 | 
			
		||||
import { UserstoryFormComponent } from './userstory-form/userstory-form.component';
 | 
			
		||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
import { DashboardComponent } from './dashboard/dashboard.component';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@@ -19,7 +20,8 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
    TaskListComponent,
 | 
			
		||||
    TaskFormComponent,
 | 
			
		||||
    UserstoryListComponent,
 | 
			
		||||
    UserstoryFormComponent
 | 
			
		||||
    UserstoryFormComponent,
 | 
			
		||||
    DashboardComponent
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    BrowserModule,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								src/app/dashboard/dashboard.component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/app/dashboard/dashboard.component.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
.text-2em {
 | 
			
		||||
  font-size: 2rem;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								src/app/dashboard/dashboard.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/app/dashboard/dashboard.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
<div class="container-fluid">
 | 
			
		||||
 | 
			
		||||
    <div class="row px-3 py-2">
 | 
			
		||||
      <h1>Dashboard</h1>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="row px-3 py-2">
 | 
			
		||||
      <h2>Current sprint</h2>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="row">
 | 
			
		||||
 | 
			
		||||
      <div class="p-3 xl-col-6 lg-col-6 md-col-12 sm-col-12 xs-col-12">
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <div class="card-header">
 | 
			
		||||
            Userstories
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="card-body">
 | 
			
		||||
            <canvas id="done-stories-chart"></canvas>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>      
 | 
			
		||||
 | 
			
		||||
      <div class="p-3 xl-col-6 lg-col-6 md-col-12 sm-col-12 xs-col-12">
 | 
			
		||||
        <div class="card-deck">
 | 
			
		||||
          
 | 
			
		||||
        <div 
 | 
			
		||||
          class="card"
 | 
			
		||||
          [class.border-danger]="getSprintUrgency() == 0"
 | 
			
		||||
          [class.border-warning]="getSprintUrgency() == 1"
 | 
			
		||||
          [class.border-success]="getSprintUrgency() == 2"
 | 
			
		||||
          *ngIf="getRemainingDaysInSprint() !== undefined"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="card-header">
 | 
			
		||||
            Days remaining
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="card-body text-center">
 | 
			
		||||
            <span class="text-2em">
 | 
			
		||||
              {{getRemainingDaysInSprint()}}
 | 
			
		||||
            </span>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="card" *ngFor="let status of usedStatus">
 | 
			
		||||
          <div class="card-header">
 | 
			
		||||
            Userstories: {{status.title}}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="card-body text-center">
 | 
			
		||||
            <span class="text-2em">
 | 
			
		||||
              {{getNumberOfUserstoriesByStatus(status)}}
 | 
			
		||||
            </span>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										127
									
								
								src/app/dashboard/dashboard.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/app/dashboard/dashboard.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
import {Component, OnInit} from '@angular/core';
 | 
			
		||||
import Chart from 'chart.js';
 | 
			
		||||
import {BackendService, ScrumStatus, ScrumUser, ScrumUserstory, ScrumSprint} from '../services/backend.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-dashboard',
 | 
			
		||||
  templateUrl: 'dashboard.component.html',
 | 
			
		||||
  styleUrls: ['./dashboard.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class DashboardComponent implements OnInit {
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the status that are used by at least one userstory.
 | 
			
		||||
   */
 | 
			
		||||
  private get usedStatus(): ScrumStatus[] {
 | 
			
		||||
    return this.status.filter(s => this.userstories.find(us => us.status === s.id) !== undefined);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private status: ScrumStatus[];
 | 
			
		||||
  private userstories: ScrumUserstory[];
 | 
			
		||||
  private sprints: ScrumSprint[];
 | 
			
		||||
 | 
			
		||||
  constructor(private backendService: BackendService) {
 | 
			
		||||
    // backendService.getUserstories().subscribe(response => {
 | 
			
		||||
    //   if (response.status > 399) {
 | 
			
		||||
    //     alert('Fehler');
 | 
			
		||||
    //   } else {
 | 
			
		||||
    //     this.userstories.push(...response.body);
 | 
			
		||||
    //   }
 | 
			
		||||
    // });
 | 
			
		||||
    // backendService.getAllStatus().subscribe(response => {
 | 
			
		||||
    //   if (response.status > 399) {
 | 
			
		||||
    //     alert('Fehler');
 | 
			
		||||
    //   } else {
 | 
			
		||||
    //     this.status.push(...response.body);
 | 
			
		||||
    //   }
 | 
			
		||||
    // });
 | 
			
		||||
    // backendService.getSprints().subscribe(response => {
 | 
			
		||||
    //   if (response.status > 399) {
 | 
			
		||||
    //     alert('Fehler');
 | 
			
		||||
    //   } else {
 | 
			
		||||
    //     this.sprints.push(...response.body);
 | 
			
		||||
    //   }
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
    this.status = [
 | 
			
		||||
      {id: 0, title: "In progress", description:""},
 | 
			
		||||
      {id: 1, title: "Done", description:""},
 | 
			
		||||
    ];
 | 
			
		||||
    this.userstories = [
 | 
			
		||||
      {status: 0, title:""},
 | 
			
		||||
      {status: 0, title:""},
 | 
			
		||||
      {status: 0, title:""},
 | 
			
		||||
      {status: 1, title:""},
 | 
			
		||||
      {status: 1, title:""},
 | 
			
		||||
    ];
 | 
			
		||||
    this.sprints = [
 | 
			
		||||
      {description:"", title:"", project: 0, startDate: new Date(2020, 5, 22), endDate: new Date(2020, 5, 28)},
 | 
			
		||||
      {description:"", title:"", project: 0, startDate: new Date(2020, 5, 29), endDate: new Date(2020, 6, 5)},
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const context = document.getElementById('done-stories-chart').getContext('2d');
 | 
			
		||||
    const chart = new Chart(context, {
 | 
			
		||||
      type: 'pie',
 | 
			
		||||
      data: {
 | 
			
		||||
        labels: this.usedStatus.map(s => s.title),
 | 
			
		||||
        datasets: [{
 | 
			
		||||
          label: 'Done stories',
 | 
			
		||||
          data: this.usedStatus.map(s => this.getNumberOfUserstoriesByStatus(s)),
 | 
			
		||||
          backgroundColor: this.getBackgroundColors(),
 | 
			
		||||
        }]
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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.userstories.filter(us => us.status === status.id).length;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getRemainingDaysInSprint(): number {
 | 
			
		||||
    const now = new Date();
 | 
			
		||||
    const currentSprint = this.sprints.find(s => s.endDate > now && s.startDate < now);
 | 
			
		||||
    if (currentSprint === undefined) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
    const daysDelta = Math.floor((currentSprint.endDate.getTime() - now.getTime()) / 86400000);
 | 
			
		||||
    return daysDelta;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getSprintUrgency(): number {
 | 
			
		||||
    const now = new Date();
 | 
			
		||||
    const currentSprint = this.sprints.find(s => s.endDate > now && s.startDate < now);
 | 
			
		||||
    if (currentSprint === undefined) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
    const deltaFromNow = currentSprint.endDate.getTime() - now.getTime();
 | 
			
		||||
    const deltaFromStart = currentSprint.endDate.getTime() - currentSprint.startDate.getTime();
 | 
			
		||||
    return Math.floor(3 * deltaFromNow / deltaFromStart);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user