Refine dashboard

This commit is contained in:
Jakob Fahr 2020-07-02 02:43:16 +02:00
parent a27de02468
commit 301407c0c4
No known key found for this signature in database
GPG Key ID: 8873416D8E4CEF6B
14 changed files with 2162 additions and 390 deletions

1764
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@
"@ng-bootstrap/ng-bootstrap": "^6.0.0",
"bootstrap": "^4.4.0",
"chart.js": "^2.9.3",
"component": "^1.1.0",
"rxjs": "~6.5.4",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"

View File

@ -14,7 +14,8 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { UserstoryTableComponent } from './userstory-table/userstory-table.component';
import { TaskTableComponent } from './task-table/task-table.component';
import { SprintTableComponent } from './sprint-table/sprint-table.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { UserstoryInnerTableComponent } from './userstory-inner-table/userstory-inner-table.component';
@NgModule({
declarations: [
@ -27,6 +28,7 @@ import { DashboardComponent } from './dashboard/dashboard.component';
SprintFormComponent,
SprintTableComponent,
DashboardComponent,
UserstoryInnerTableComponent,
],
imports: [
BrowserModule,

View File

@ -1,3 +1,7 @@
.text-2em {
font-size: 2rem;
.text-large {
font-size: 1.2rem;
}
.text-very-large {
font-size: 2.4rem;
}

View File

@ -1,60 +1,98 @@
<div class="container-fluid">
<div class="mx-5 my-3">
<div class="row px-3 py-2">
<h1>Dashboard</h1>
</div>
<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 px-3 py-2">
<div class="row">
<ng-container *ngIf="selectedSprint === undefined">
<h3 class="mr-3 text-primary">Alle Userstories</h3>
<a [routerLink]="['sprints']">Lege einen Sprint an, um Userstories zu organisieren.</a>
</ng-container>
<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">
<ng-container *ngIf="selectedSprint !== undefined">
<h3 class="mr-3 text-primary" *ngIf="selectedSprint === currentSprint">Aktueller Sprint:</h3>
<h3 class="mr-3 text-primary" *ngIf="selectedSprint !== currentSprint">Sprint:</h3>
<h3 class="mr-3 custom-text-secondary">{{selectedSprint.title}}</h3>
<h3 class="mr-3">{{toDateString(selectedSprint.startDate)}} - {{toDateString(selectedSprint.endDate)}}</h3>
<label class="mr-3">
<select class="select custom-select custom-text-secondary" [(ngModel)]="selectedSprint">
<option class="bg-secondary text-dark" [ngValue]="currentSprint">
{{currentSprint.title}} (aktuell)
</option>
<option value="" disabled="disabled">─────────────────────────</option>
<option class="text-dark" *ngFor="let sprint of sprints" [ngValue]="sprint">
<ng-container *ngIf="sprint === currentSprint">
{{sprint.title}} (aktuell)
</ng-container>
<ng-container *ngIf="sprint !== currentSprint">
{{sprint.title}} ({{toDateString(sprint.startDate)}} - {{toDateString(sprint.endDate)}})
</ng-container>
</option>
</select>
</label>
<span class="mr-5"></span>
<h3
*ngIf="selectedSprint === currentSprint"
class="mr-3 custom-text-secondary"
[class.text-success]="getSprintUrgency() === 2"
[class.text-warning]="getSprintUrgency() === 1"
[class.text-danger]="getSprintUrgency() === 0"
>
Verbleibende Tage: {{getRemainingDaysInSprint()}}
</h3>
</ng-container>
</div>
<div class="row">
<div class="p-3 col-12 col-xl-6 col-lg-6 col-md-12 col-sm-12">
<div class="card">
<div class="card-header">
<span class="text-large">
Userstories
</div>
<div class="card-body">
<canvas id="done-stories-chart"></canvas>
</div>
</span>
</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 class="card-body">
<div *ngIf="selectedSprint !== undefined && usedStatus.length === 0">
Zum Sprint "{{selectedSprint.title}}" sind aktuell keine Userstories vorhanden.
</div>
<canvas id="done-stories-chart"></canvas>
</div>
</div>
<div class="card-deck">
<div
class="card text-center"
*ngFor="let status of usedStatus"
>
<div class="card-header">
<span class="text-large">
{{status.title}}
</span>
</div>
<div class="card-body">
<span class="text-very-large">
<b>{{getNumberOfUserstoriesByStatus(status)}}</b>
</span>
</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 class="p-3 col-12 col-xl-6 col-lg-6 col-md-12 col-sm-12">
<div class="card">
<div class="card-body">
<app-userstory-inner-table [items]="selectedSprintUserstories"></app-userstory-inner-table>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,79 +1,111 @@
import {Component, OnInit} from '@angular/core';
import {Component, OnChanges} from '@angular/core';
import {forkJoin} from 'rxjs';
import Chart from 'chart.js';
import {BackendService, ScrumStatus, ScrumUser, ScrumUserstory, ScrumSprint} from '../services/backend.service';
import {BackendService, ScrumSprint, ScrumStatus, ScrumUserstory} from '../services/backend.service';
@Component({
selector: 'app-dashboard',
templateUrl: 'dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
export class DashboardComponent {
/**
* Returns the status that are used by at least one userstory.
*/
public get usedStatus(): ScrumStatus[] {
return this.status.filter(s => this.userstories.find(us => us.statusid === s.id) !== undefined);
return this.status.filter(s => this.selectedSprintUserstories.find(us => us.statusid === s.id) !== undefined);
}
private status: ScrumStatus[];
private userstories: ScrumUserstory[];
private sprints: ScrumSprint[];
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) {
// 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 = [
{statusid: 0, title:""},
{statusid: 0, title:""},
{statusid: 0, title:""},
{statusid: 1, title:""},
{statusid: 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)},
]
// 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()}`;
}
ngOnInit(): void {
private createChart() {
// @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(),
}]
}
});
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[] {
@ -101,27 +133,28 @@ export class DashboardComponent implements OnInit {
}
public getNumberOfUserstoriesByStatus(status: ScrumStatus): number {
return this.userstories.filter(us => us.statusid === status.id).length;
return this.selectedSprintUserstories.filter(us => us.statusid === 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) {
if (this.selectedSprint === undefined) {
return undefined;
}
const daysDelta = Math.floor((currentSprint.endDate.getTime() - now.getTime()) / 86400000);
return daysDelta;
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 = new Date();
const currentSprint = this.sprints.find(s => s.endDate > now && s.startDate < now);
if (currentSprint === undefined) {
const now = Date.now();
const sprint = this.selectedSprint;
if (sprint === undefined) {
return undefined;
}
const deltaFromNow = currentSprint.endDate.getTime() - now.getTime();
const deltaFromStart = currentSprint.endDate.getTime() - currentSprint.startDate.getTime();
const deltaFromNow = Date.parse(sprint.endDate) - now;
const deltaFromStart = Date.parse(sprint.endDate) - Date.parse(sprint.startDate);
return Math.floor(3 * deltaFromNow / deltaFromStart);
}
}

View File

@ -233,8 +233,8 @@ export interface ScrumSprint{
id?: number;
title: string;
description?: string;
startDate: Date;
endDate: Date;
startDate: string;
endDate: string;
project: number;
}

View File

@ -13,7 +13,7 @@ import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { getNumberForPriority } from '../services/sorting.service';
@Component({
selector: 'app-userstory-table',
selector: 'app-task-table',
templateUrl: './task-table.component.html',
styleUrls: ['./task-table.component.css'],
})

View File

@ -0,0 +1,90 @@
<button class="btn btn-secondary my-3" (click)="openUserstoryForm()">Neue Userstory</button>
<table class="table">
<thead>
<tr>
<th (click)="sortById()" class="sortable">
<span>ID</span>
<span *ngIf="sortBy === 'id'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByTitle()" class="sortable">
<span>Titel</span>
<span *ngIf="sortBy === 'title'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByTasks()" class="sortable">
<span>Tasks</span>
<span *ngIf="sortBy === 'tasks'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByStatus()" class="sortable">
<span>Status</span>
<span *ngIf="sortBy === 'statusid'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByPrio()" class="sortable">
<span>Priorität</span>
<label class="pl-3" (click)="$event.stopPropagation()">
<select [(ngModel)]="filterPriority">
<option [ngValue]="null" selected></option>
<option *ngFor="let p of getAllPriorities()" [ngValue]="p">{{p}}</option>
</select>
</label>
<span *ngIf="sortBy === 'priority'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByCategory()" class="sortable">
<span>Category</span>
<span *ngIf="sortBy === 'categoryid'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let userstory of filteredItems" [class.table-info]="userstory.id === highlightId">
<td>{{userstory.id}}</td>
<td>{{userstory.title}}</td>
<td>
<a [routerLink]="['/tasks', {userstoryId: userstory.id}]">
{{getNumberOfTasks(userstory)}} Tasks
</a>
</td>
<td>
<a [routerLink]="['/status', {id: userstory.statusid}]">
{{getStatusTitleById(userstory.statusid)}}
</a>
</td>
<td>{{userstory.priority}}</td>
<td>
<a [routerLink]="['/categories', {id: userstory.categoryid}]">
{{getCategoryTitleById(userstory.categoryid)}}
</a>
</td>
<td>
<button type="button" rel="tooltip" (click)="openUserstoryForm(userstory)" class="btn btn-success btn-sm btn-icon">
<i class="fa fa-pencil-alt"></i>
</button>
<button type="button" rel="tooltip" (click)="deleteUserstory(userstory)" class="btn btn-danger btn-sm btn-icon">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,143 @@
import { Component, Input } from '@angular/core';
import {
BackendService,
ScrumTask,
ScrumUserstory,
ScrumStatus,
ScrumCategory,
} from '../services/backend.service';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TableComponentBase } from '../services/table-component.base';
import { getNumberForPriority } from '../services/sorting.service';
import { UserstoryFormComponent } from '../userstory-form/userstory-form.component';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
@Component({
selector: 'app-userstory-inner-table',
templateUrl: './userstory-inner-table.component.html',
styleUrls: ['./userstory-inner-table.component.css']
})
export class UserstoryInnerTableComponent extends TableComponentBase<ScrumUserstory> {
public tasks: ScrumTask[] = [];
public filterPriority: string | null = null;
public highlightId: number;
public status: ScrumStatus[] = [];
public categories: ScrumCategory[] = [];
@Input() public items: ScrumUserstory[] = [];
public get filteredItems() {
return this.items.filter(
(task) =>
this.filterPriority === null || task.priority === this.filterPriority
);
}
constructor(
private backendService: BackendService,
private modalService: NgbModal,
private route: ActivatedRoute,
private router: Router
) {
super();
this.applyFilterParameters(this.route.snapshot.paramMap);
this.route.paramMap.subscribe((map) => this.applyFilterParameters(map));
backendService.getTasks().subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
} else {
this.tasks.push(...response.body);
}
});
backendService.getAllStatus().subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
} else {
this.status.push(...response.body);
}
});
backendService.getCategories().subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
} else {
this.categories.push(...response.body);
}
});
}
private applyFilterParameters(params: ParamMap) {
if (params.has('id')) {
this.highlightId = parseInt(params.get('id'));
}
}
public deleteUserstory(userstory: ScrumUserstory) {
this.backendService.deleteUserstory(userstory).subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
}
});
const index = this.items.indexOf(userstory);
if (index !== -1) {
this.items.splice(index, 1);
}
}
public openUserstoryForm(editUserstory?: ScrumUserstory) {
const modalRef = this.modalService.open(UserstoryFormComponent, {
backdrop: 'static',
keyboard: true,
});
if (editUserstory === null) {
modalRef.result.then((result) => {
this.items.push(result);
});
}
modalRef.componentInstance.userstory = editUserstory;
}
public getNumberOfTasks(userstory: ScrumUserstory) {
return this.tasks.filter((t) => t.userstoryid === userstory.id).length;
}
public sortById() {
this.doNumericSort('id', (us) => us.id);
}
public sortByTitle() {
this.doStringSort('title', (us) => us.title);
}
public sortByPrio() {
this.doNumericSort('priority', (us) => getNumberForPriority(us.priority));
}
public sortByTasks() {
this.doNumericSort('tasks', (us) => this.getNumberOfTasks(us));
}
sortByStatus() {
this.doNumericSort('statusid', (us) => us.statusid);
}
sortByCategory() {
this.doNumericSort('categoryid', (us) => us.categoryid);
}
getStatusTitleById(id) {
var status = this.status.find((x) => x.id === id);
if (!status) {
return 'N/A';
}
return status.title;
}
getCategoryTitleById(id) {
var category = this.categories.find((x) => x.id === id);
if (!category) {
return 'N/A';
}
return category.title;
}
}

View File

@ -1,95 +1,4 @@
<div class="mx-5 my-3">
<h3 class="my-1">Userstories</h3>
<button class="btn btn-secondary my-3" (click)="openUserstoryForm()">Neue Userstory</button>
<table class="table">
<thead>
<tr>
<th (click)="sortById()" class="sortable">
<span>ID</span>
<span *ngIf="sortBy === 'id'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByTitle()" class="sortable">
<span>Titel</span>
<span *ngIf="sortBy === 'title'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByTasks()" class="sortable">
<span>Tasks</span>
<span *ngIf="sortBy === 'tasks'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByStatus()" class="sortable">
<span>Status</span>
<span *ngIf="sortBy === 'statusid'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByPrio()" class="sortable">
<span>Priorität</span>
<label class="pl-3" (click)="$event.stopPropagation()">
<select [(ngModel)]="filterPriority">
<option [ngValue]="null" selected></option>
<option *ngFor="let p of getAllPriorities()" [ngValue]="p">{{p}}</option>
</select>
</label>
<span *ngIf="sortBy === 'priority'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th (click)="sortByCategory()" class="sortable">
<span>Category</span>
<span *ngIf="sortBy === 'categoryid'" class="pl-3">
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let userstory of filteredItems" [class.table-info]="userstory.id === highlightId">
<td>{{userstory.id}}</td>
<td>{{userstory.title}}</td>
<td>
<a [routerLink]="['/tasks', {userstoryId: userstory.id}]">
{{getNumberOfTasks(userstory)}} Tasks
</a>
</td>
<td>
<a [routerLink]="['/status', {id: userstory.statusid}]">
{{getStatusTitleById(userstory.statusid)}}
</a>
</td>
<td>{{userstory.priority}}</td>
<td>
<a [routerLink]="['/categories', {id: userstory.categoryid}]">
{{getCategoryTitleById(userstory.categoryid)}}
</a>
</td>
<td>
<button type="button" rel="tooltip" (click)="openUserstoryForm(userstory)" class="btn btn-success btn-sm btn-icon">
<i class="fa fa-pencil-alt"></i>
</button>
<button type="button" rel="tooltip" (click)="deleteUserstory(userstory)" class="btn btn-danger btn-sm btn-icon">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
<app-userstory-inner-table [items]="items"></app-userstory-inner-table>
</div>

View File

@ -1,49 +1,18 @@
import { Component } from '@angular/core';
import {
BackendService,
ScrumTask,
ScrumUserstory,
ScrumStatus,
ScrumCategory,
} from '../services/backend.service';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TableComponentBase } from '../services/table-component.base';
import { getNumberForPriority } from '../services/sorting.service';
import { UserstoryFormComponent } from '../userstory-form/userstory-form.component';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
@Component({
selector: 'app-userstory-table',
templateUrl: './userstory-table.component.html',
styleUrls: ['./userstory-table.component.css'],
})
export class UserstoryTableComponent extends TableComponentBase<
ScrumUserstory
> {
public tasks: ScrumTask[] = [];
public filterPriority: string | null = null;
public highlightId: number;
public status: ScrumStatus[] = [];
public categories: ScrumCategory[] = [];
public get filteredItems() {
return this.items.filter(
(task) =>
this.filterPriority === null || task.priority === this.filterPriority
);
}
constructor(
private backendService: BackendService,
private modalService: NgbModal,
private route: ActivatedRoute,
private router: Router
) {
super();
this.applyFilterParameters(this.route.snapshot.paramMap);
this.route.paramMap.subscribe((map) => this.applyFilterParameters(map));
export class UserstoryTableComponent {
public items: ScrumUserstory[] = [];
constructor(private backendService: BackendService) {
backendService.getUserstories().subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
@ -51,100 +20,5 @@ export class UserstoryTableComponent extends TableComponentBase<
this.items.push(...response.body);
}
});
backendService.getTasks().subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
} else {
this.tasks.push(...response.body);
}
});
backendService.getAllStatus().subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
} else {
this.status.push(...response.body);
}
});
backendService.getCategories().subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
} else {
this.categories.push(...response.body);
}
});
}
private applyFilterParameters(params: ParamMap) {
if (params.has('id')) {
this.highlightId = parseInt(params.get('id'));
}
}
public deleteUserstory(userstory: ScrumUserstory) {
this.backendService.deleteUserstory(userstory).subscribe((response) => {
if (response.status > 399) {
alert('Fehler');
}
});
const index = this.items.indexOf(userstory);
if (index !== -1) {
this.items.splice(index, 1);
}
}
public openUserstoryForm(editUserstory?: ScrumUserstory) {
const modalRef = this.modalService.open(UserstoryFormComponent, {
backdrop: 'static',
keyboard: true,
});
if (editUserstory === null) {
modalRef.result.then((result) => {
this.items.push(result);
});
}
modalRef.componentInstance.userstory = editUserstory;
}
public getNumberOfTasks(userstory: ScrumUserstory) {
return this.tasks.filter((t) => t.userstoryid === userstory.id).length;
}
public sortById() {
this.doNumericSort('id', (us) => us.id);
}
public sortByTitle() {
this.doStringSort('title', (us) => us.title);
}
public sortByPrio() {
this.doNumericSort('priority', (us) => getNumberForPriority(us.priority));
}
public sortByTasks() {
this.doNumericSort('tasks', (us) => this.getNumberOfTasks(us));
}
sortByStatus() {
this.doNumericSort('statusid', (us) => us.statusid);
}
sortByCategory() {
this.doNumericSort('categoryid', (us) => us.categoryid);
}
getStatusTitleById(id) {
var status = this.status.find((x) => x.id === id);
if (!status) {
return 'N/A';
}
return status.title;
}
getCategoryTitleById(id) {
var category = this.categories.find((x) => x.id === id);
if (!category) {
return 'N/A';
}
return category.title;
}
}

View File

@ -1 +1,9 @@
/* You can add global styles to this file, and also import other style files */
/* You can add global styles to this file, and also import other style files */
.custom-text-secondary {
color: white;
}
.white-content .custom-text-secondary {
color: black !important;
}