Add filters by priority + highlighting

This commit is contained in:
Jakob Fahr 2020-06-22 11:21:25 +02:00
parent 03e8c96a1e
commit a81a26d989
No known key found for this signature in database
GPG Key ID: 8873416D8E4CEF6B
7 changed files with 110 additions and 87 deletions

View File

@ -1,33 +1,38 @@
import {sortByNumberAscending, sortByStringAscending} from './sorting.service'; import {sortByNumberAscending, sortByStringAscending} from './sorting.service';
import {Priority} from './backend.service';
export abstract class TableComponentBase<T> { export abstract class TableComponentBase<T> {
public sortBy: string; public sortBy: string;
public sortDescending = false; public sortDescending = false;
public items: T[] = []; public items: T[] = [];
protected doNumericSort(by: string, key: (ScrumTask) => number) { protected doNumericSort(by: string, key: (item: T) => number) {
if (this.sortBy === by) { if (this.sortBy === by) {
this.sortDescending = !this.sortDescending; this.sortDescending = !this.sortDescending;
} else { } else {
this.sortBy = by; this.sortBy = by;
}
this.items = sortByNumberAscending(this.items, key);
if (this.sortDescending) {
this.items = this.items.reverse();
}
} }
protected doStringSort(by: string, key: (ScrumTask) => string) { this.items = sortByNumberAscending(this.items, key);
if (this.sortBy === by) { if (this.sortDescending) {
this.sortDescending = !this.sortDescending; this.items = this.items.reverse();
} else {
this.sortBy = by;
}
this.items = sortByStringAscending(this.items, key);
if (this.sortDescending) {
this.items = this.items.reverse();
}
} }
}
protected doStringSort(by: string, key: (item: T) => string) {
if (this.sortBy === by) {
this.sortDescending = !this.sortDescending;
} else {
this.sortBy = by;
}
this.items = sortByStringAscending(this.items, key);
if (this.sortDescending) {
this.items = this.items.reverse();
}
}
public getAllPriorities(): string[] {
return Object.values(Priority);
}
} }

View File

@ -2,3 +2,6 @@ table {
table-layout: fixed; table-layout: fixed;
} }
th.sortable:hover {
text-decoration: underline;
}

View File

@ -9,6 +9,9 @@
</a> </a>
Tasks Tasks
</h3> </h3>
<div *ngIf="filterUserstoryId">
<a [routerLink]="'/tasks'">Alle Tasks anzeigen</a>
</div>
<button class="btn btn-secondary my-3" (click)="openTaskForm()">Neuer Task</button> <button class="btn btn-secondary my-3" (click)="openTaskForm()">Neuer Task</button>
@ -16,37 +19,43 @@
<thead> <thead>
<tr> <tr>
<th (click)="sortById()"> <th (click)="sortById()" class="sortable">
<span>ID</span> <span>ID</span>
<span *ngIf="sortBy === 'id'" class="pl-3"> <span *ngIf="sortBy === 'id'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
</th> </th>
<th (click)="sortByTitle()"> <th (click)="sortByTitle()" class="sortable">
<span>Titel</span> <span>Titel</span>
<span *ngIf="sortBy === 'title'" class="pl-3"> <span *ngIf="sortBy === 'title'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
</th> </th>
<th (click)="sortByContent()"> <th (click)="sortByContent()" class="sortable">
<span>Inhalt</span> <span>Inhalt</span>
<span *ngIf="sortBy === 'content'" class="pl-3"> <span *ngIf="sortBy === 'content'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
</th> </th>
<th (click)="sortByTasks()"> <th (click)="sortByTasks()" class="sortable">
<span>Tasks</span> <span>Userstory</span>
<span *ngIf="sortBy === 'tasks'" class="pl-3"> <span *ngIf="sortBy === 'userstory'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
</th> </th>
<th (click)="sortByPrio()"> <th (click)="sortByPrio()" class="sortable">
<span>Priorität</span> <span>Priorität</span>
<span *ngIf="sortBy === 'priority'" class="pl-3"> <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'" (click)="sortByPrio()" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
@ -56,7 +65,7 @@
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let task of filteredItems"> <tr *ngFor="let task of filteredItems" [class.table-info]="task.id === highlightId">
<td>{{task.id}}</td> <td>{{task.id}}</td>
<td>{{task.title}}</td> <td>{{task.title}}</td>
<td>{{task.content}}</td> <td>{{task.content}}</td>

View File

@ -1,9 +1,10 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {BackendService, Priority, ScrumTask, ScrumUserstory} from '../services/backend.service'; import {BackendService, ScrumTask} from '../services/backend.service';
import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {TaskFormComponent} from '../task-form/task-form.component'; import {TaskFormComponent} from '../task-form/task-form.component';
import {TableComponentBase} from '../services/table-component.base'; import {TableComponentBase} from '../services/table-component.base';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, ParamMap, Router} from '@angular/router';
import {getNumberForPriority} from '../services/sorting.service';
@Component({ @Component({
selector: 'app-userstory-table', selector: 'app-userstory-table',
@ -12,11 +13,14 @@ import {ActivatedRoute, Router} from '@angular/router';
}) })
export class TaskTableComponent extends TableComponentBase<ScrumTask> { export class TaskTableComponent extends TableComponentBase<ScrumTask> {
public filterUserstoryId: number | null = null; public filterUserstoryId: number | null = null;
public filterPriority: string | null = null;
public highlightId: number;
public get filteredItems() { public get filteredItems() {
if (this.filterUserstoryId === null || isNaN(this.filterUserstoryId)) { return this.items.filter(task =>
return this.items; (this.filterUserstoryId === null || task.userstoryid === this.filterUserstoryId)
} && (this.filterPriority === null || task.priority === this.filterPriority)
return this.items.filter(task => task.userstoryid === this.filterUserstoryId); );
} }
constructor( constructor(
@ -25,14 +29,8 @@ export class TaskTableComponent extends TableComponentBase<ScrumTask> {
) { ) {
super(); super();
const params = route.snapshot.paramMap; this.applyFilterParameters(route.snapshot.paramMap);
if (params.has('userstoryId')) { route.paramMap.subscribe(map => this.applyFilterParameters(map));
this.filterUserstoryId = parseInt(params.get('userstoryId'));
}
if (params.has('id')) {
const id = parseInt(params.get('id'));
this.openTaskForm(this.items.find(t => t.id === id));
}
backendService.getTasks().subscribe(response => { backendService.getTasks().subscribe(response => {
if (response.status > 399) { if (response.status > 399) {
@ -43,6 +41,17 @@ export class TaskTableComponent extends TableComponentBase<ScrumTask> {
}); });
} }
private applyFilterParameters(params: ParamMap) {
if (params.has('userstoryId')) {
this.filterUserstoryId = parseInt(params.get('userstoryId'));
} else {
this.filterUserstoryId = null;
}
if (params.has('id')) {
this.highlightId = parseInt(params.get('id'));
}
}
public deleteTask(task: ScrumTask) { public deleteTask(task: ScrumTask) {
this.backendService.deleteTask(task).subscribe(response => { this.backendService.deleteTask(task).subscribe(response => {
if (response.status > 399) { if (response.status > 399) {
@ -69,37 +78,26 @@ export class TaskTableComponent extends TableComponentBase<ScrumTask> {
} }
sortById() { sortById() {
this.doNumericSort('id', us => us.id); this.doNumericSort('id', task => task.id);
} }
sortByTitle() { sortByTitle() {
this.doStringSort('title', us => us.title); this.doStringSort('title', task => task.title);
} }
sortByContent() { sortByContent() {
this.doStringSort('content', us => us.content); this.doStringSort('content', task => task.content);
}
private getNumberForPriority(priority: Priority): number {
switch (priority) {
case Priority.High:
return 2;
case Priority.Medium:
return 1;
case Priority.Low:
return 0;
}
} }
sortByPrio() { sortByPrio() {
this.doNumericSort('priority', us => this.getNumberForPriority(us.priority)); this.doNumericSort('priority', task => getNumberForPriority(task.priority));
}
getNumberOfTasks(userstory: ScrumUserstory) {
return this.items.filter(t => t.userstoryid === userstory.id).length;
} }
sortByTasks() { sortByTasks() {
this.doNumericSort('tasks', us => this.getNumberOfTasks(us)); this.doNumericSort('userstory', task => task.userstoryid);
}
private findTaskById(id: number): ScrumTask {
return this.items.find(t => t.id === id);
} }
} }

View File

@ -2,3 +2,6 @@ table {
table-layout: fixed; table-layout: fixed;
} }
th.sortable:hover {
text-decoration: underline;
}

View File

@ -8,36 +8,42 @@
<thead> <thead>
<tr> <tr>
<th (click)="sortById()"> <th (click)="sortById()" class="sortable">
<span>ID</span> <span>ID</span>
<span *ngIf="sortBy === 'id'" class="pl-3"> <span *ngIf="sortBy === 'id'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
</th> </th>
<th (click)="sortByTitle()"> <th (click)="sortByTitle()" class="sortable">
<span>Titel</span> <span>Titel</span>
<span *ngIf="sortBy === 'title'" class="pl-3"> <span *ngIf="sortBy === 'title'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
</th> </th>
<th (click)="sortByContent()"> <th (click)="sortByContent()" class="sortable">
<span>Inhalt</span> <span>Inhalt</span>
<span *ngIf="sortBy === 'content'" class="pl-3"> <span *ngIf="sortBy === 'content'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
</th> </th>
<th (click)="sortByTasks()"> <th (click)="sortByTasks()" class="sortable">
<span>Tasks</span> <span>Tasks</span>
<span *ngIf="sortBy === 'tasks'" class="pl-3"> <span *ngIf="sortBy === 'tasks'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
</span> </span>
</th> </th>
<th (click)="sortByPrio()"> <th (click)="sortByPrio()" class="sortable">
<span>Priorität</span> <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="sortBy === 'priority'" class="pl-3">
<span *ngIf="sortDescending"></span> <span *ngIf="sortDescending"></span>
<span *ngIf="sortDescending === false"></span> <span *ngIf="sortDescending === false"></span>
@ -48,7 +54,7 @@
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let userstory of items"> <tr *ngFor="let userstory of filteredItems" [class.table-info]="userstory.id === highlightId">
<td>{{userstory.id}}</td> <td>{{userstory.id}}</td>
<td>{{userstory.title}}</td> <td>{{userstory.title}}</td>
<td>{{userstory.content}}</td> <td>{{userstory.content}}</td>

View File

@ -4,7 +4,7 @@ import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {TableComponentBase} from '../services/table-component.base'; import {TableComponentBase} from '../services/table-component.base';
import {getNumberForPriority} from '../services/sorting.service'; import {getNumberForPriority} from '../services/sorting.service';
import {UserstoryFormComponent} from '../userstory-form/userstory-form.component'; import {UserstoryFormComponent} from '../userstory-form/userstory-form.component';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, ParamMap, Router} from '@angular/router';
@Component({ @Component({
selector: 'app-userstory-table', selector: 'app-userstory-table',
@ -13,6 +13,12 @@ import {ActivatedRoute, Router} from '@angular/router';
}) })
export class UserstoryTableComponent extends TableComponentBase<ScrumUserstory> { export class UserstoryTableComponent extends TableComponentBase<ScrumUserstory> {
public tasks: ScrumTask[] = []; public tasks: ScrumTask[] = [];
public filterPriority: string | null = null;
public highlightId: number;
public get filteredItems() {
return this.items.filter(task => this.filterPriority === null || task.priority === this.filterPriority);
}
constructor( constructor(
private backendService: BackendService, private modalService: NgbModal, private backendService: BackendService, private modalService: NgbModal,
@ -20,19 +26,14 @@ export class UserstoryTableComponent extends TableComponentBase<ScrumUserstory>
) { ) {
super(); super();
const params = route.snapshot.paramMap; this.applyFilterParameters(this.route.snapshot.paramMap);
this.route.paramMap.subscribe(map => this.applyFilterParameters(map));
backendService.getUserstories().subscribe(response => { backendService.getUserstories().subscribe(response => {
if (response.status > 399) { if (response.status > 399) {
alert('Fehler'); alert('Fehler');
} else { } else {
this.items.push(...response.body); this.items.push(...response.body);
// Wenn /userstories;id=3 aufgerufen wird, öffne die Userstory mit ID 3
const userstory = this.findUserstoryById(parseInt(params.get('id')));
if (userstory !== null) {
this.openUserstoryForm(userstory);
}
} }
}); });
backendService.getTasks().subscribe(response => { backendService.getTasks().subscribe(response => {
@ -44,12 +45,10 @@ export class UserstoryTableComponent extends TableComponentBase<ScrumUserstory>
}); });
} }
private findUserstoryById(id: number): ScrumUserstory { private applyFilterParameters(params: ParamMap) {
const userstoriesWithId = this.items.filter(us => us.id === id); if (params.has('id')) {
if (userstoriesWithId.length === 0) { this.highlightId = parseInt(params.get('id'));
return null;
} }
return userstoriesWithId[0];
} }
public deleteUserstory(userstory: ScrumUserstory) { public deleteUserstory(userstory: ScrumUserstory) {