TSK-673 - Paging for Task list in workplace

This commit is contained in:
Jose Ignacio Recuerda Cambil 2018-10-22 15:22:32 +02:00 committed by Martin Rojas Miguel Angel
parent a9191e6bb0
commit 9c1fa6dfe2
33 changed files with 554 additions and 309 deletions

View File

@ -156,7 +156,7 @@ public class TaskControllerIntTest {
headers.add("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x");
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<PagedResources<TaskSummaryResource>> response = template.exchange(
"http://127.0.0.1:" + port + "/v1/tasks?por.type=VNR&por.value=22334455&sortBy=por.value&order=desc",
"http://127.0.0.1:" + port + "/v1/tasks?por.type=VNR&por.value=22334455&sort-by=por.value&order=desc",
HttpMethod.GET, request,
new ParameterizedTypeReference<PagedResources<TaskSummaryResource>>() {
});
@ -164,7 +164,7 @@ public class TaskControllerIntTest {
assertTrue(response.getBody()
.getLink(Link.REL_SELF)
.getHref()
.endsWith("/v1/tasks?por.type=VNR&por.value=22334455&sortBy=por.value&order=desc"));
.endsWith("/v1/tasks?por.type=VNR&por.value=22334455&sort-by=por.value&order=desc"));
}
@Test
@ -194,7 +194,7 @@ public class TaskControllerIntTest {
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<PagedResources<TaskSummaryResource>> response = template.exchange(
"http://127.0.0.1:" + port
+ "/v1/tasks?state=READY,CLAIMED&sortBy=por.value&order=desc&page=15&page-size=5",
+ "/v1/tasks?state=READY,CLAIMED&sort-by=por.value&order=desc&page=15&page-size=5",
HttpMethod.GET,
request,
new ParameterizedTypeReference<PagedResources<TaskSummaryResource>>() {
@ -207,7 +207,7 @@ public class TaskControllerIntTest {
assertTrue(response.getBody()
.getLink(Link.REL_SELF)
.getHref()
.endsWith("/v1/tasks?state=READY,CLAIMED&sortBy=por.value&order=desc&page=15&page-size=5"));
.endsWith("/v1/tasks?state=READY,CLAIMED&sort-by=por.value&order=desc&page=15&page-size=5"));
assertNotNull(response.getBody().getLink("allTasks"));
assertTrue(response.getBody()
.getLink("allTasks")
@ -228,14 +228,14 @@ public class TaskControllerIntTest {
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<PagedResources<TaskSummaryResource>> response = template.exchange(
"http://127.0.0.1:" + port + "/v1/tasks?sortBy=due&order=desc", HttpMethod.GET,
"http://127.0.0.1:" + port + "/v1/tasks?sort-by=due&order=desc", HttpMethod.GET,
request,
new ParameterizedTypeReference<PagedResources<TaskSummaryResource>>() {
});
assertEquals(25, response.getBody().getContent().size());
response = template.exchange(
"http://127.0.0.1:" + port + "/v1/tasks?sortBy=due&order=desc&page=5&page-size=5", HttpMethod.GET,
"http://127.0.0.1:" + port + "/v1/tasks?sort-by=due&order=desc&page=5&page-size=5", HttpMethod.GET,
request,
new ParameterizedTypeReference<PagedResources<TaskSummaryResource>>() {
});
@ -247,7 +247,7 @@ public class TaskControllerIntTest {
assertTrue(response.getBody()
.getLink(Link.REL_SELF)
.getHref()
.endsWith("/v1/tasks?sortBy=due&order=desc&page=5&page-size=5"));
.endsWith("/v1/tasks?sort-by=due&order=desc&page=5&page-size=5"));
assertNotNull(response.getBody().getLink("allTasks"));
assertTrue(response.getBody()
.getLink("allTasks")
@ -268,7 +268,7 @@ public class TaskControllerIntTest {
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<PagedResources<TaskSummaryResource>> response = template.exchange(
"http://127.0.0.1:" + port
+ "/v1/tasks?por.company=00&por.system=PASystem&por.instance=00&por.type=VNR&por.value=22334455&sortBy=por.type&order=asc&page=2&page-size=5",
+ "/v1/tasks?por.company=00&por.system=PASystem&por.instance=00&por.type=VNR&por.value=22334455&sort-by=por.type&order=asc&page=2&page-size=5",
HttpMethod.GET,
request,
new ParameterizedTypeReference<PagedResources<TaskSummaryResource>>() {
@ -281,7 +281,7 @@ public class TaskControllerIntTest {
.getLink(Link.REL_SELF)
.getHref()
.endsWith(
"/v1/tasks?por.company=00&por.system=PASystem&por.instance=00&por.type=VNR&por.value=22334455&sortBy=por.type&order=asc&page=2&page-size=5"));
"/v1/tasks?por.company=00&por.system=PASystem&por.instance=00&por.type=VNR&por.value=22334455&sort-by=por.type&order=asc&page=2&page-size=5"));
assertNotNull(response.getBody().getLink("allTasks"));
assertTrue(response.getBody()
.getLink("allTasks")

View File

@ -75,7 +75,7 @@ public class TaskController extends AbstractPagingController {
private static final String DUE = "due";
private static final String PLANNED = "planned";
private static final String SORT_BY = "sortBy";
private static final String SORT_BY = "sort-by";
private static final String SORT_DIRECTION = "order";
private static final String PAGING_PAGE = "page";

View File

@ -16,7 +16,6 @@ import {WorkbasketInformationComponent} from './workbasket/details/information/w
import {DistributionTargetsComponent} from './workbasket/details/distribution-targets/distribution-targets.component';
import {DualListComponent} from './workbasket/details/distribution-targets/dual-list/dual-list.component';
import {AccessItemsComponent} from './workbasket/details/access-items/access-items.component';
import {PaginationComponent} from './workbasket/master/list/pagination/pagination.component';
import {ClassificationListComponent} from './classification/master/list/classification-list.component';
import {ClassificationDetailsComponent} from './classification/details/classification-details.component';
import {ImportExportComponent} from './components/import-export/import-export.component';
@ -51,7 +50,6 @@ const DECLARATIONS = [
WorkbasketInformationComponent,
DistributionTargetsComponent,
DualListComponent,
PaginationComponent,
ClassificationListComponent,
ImportExportComponent,
ClassificationTypesSelectorComponent,

View File

@ -61,7 +61,6 @@ describe('ClassificationDetailsComponent', () => {
component = fixture.componentInstance;
classificationsService = TestBed.get(ClassificationsService);
classificationCategoriesService = TestBed.get(ClassificationCategoriesService);
classificationsService = TestBed.get(ClassificationsService);
removeConfirmationService = TestBed.get(RemoveConfirmationService);
spyOn(classificationsService, 'getClassifications').and.returnValue(of(treeNodes));
spyOn(classificationCategoriesService, 'getClassificationTypes').and.returnValue(of([]));

View File

@ -1,19 +0,0 @@
<ul id="wb-pagination" class="pagination vertical-center">
<li>
<a (click)="changeToPage(1)" aria-label="First">
First</a>
</li>
<li *ngFor="let pageNumber of workbasketsResource?.page?.totalPages |
spreadNumber: workbasketsResource?.page?.number: maxPagesAvailable: workbasketsResource?.page?.totalPages">
<a *ngIf="pageNumber + 1 !== workbasketsResource?.page?.number" (click)="changeToPage(pageNumber+1)">{{pageNumber + 1}}</a>
<a *ngIf="pageNumber + 1 === workbasketsResource?.page?.number" class="pagination">
<input [(ngModel)]="pageSelected" (keyup.enter)="changeToPage(pageSelected)" type="text" (blur)="changeToPage(pageSelected)">
</a>
</li>
<li>
<a (click)="changeToPage(workbasketsResource?.page.totalPages)" aria-label="Last">Last</a>
</li>
</ul>
<span class="footer pull-right">
<i [innerHTML]="getPagesTextToShow()"></i>
</span>

View File

@ -1,52 +0,0 @@
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
import { WorkbasketSummaryResource } from 'app/models/workbasket-summary-resource';
@Component({
selector: 'taskana-pagination',
templateUrl: './pagination.component.html',
styleUrls: ['./pagination.component.scss']
})
export class PaginationComponent implements OnInit, OnChanges {
@Input()
workbasketsResource: WorkbasketSummaryResource;
@Output()
workbasketsResourceChange = new EventEmitter<WorkbasketSummaryResource>();
@Output() changePage = new EventEmitter<number>();
previousPageSelected = 1;
pageSelected = 1;
maxPagesAvailable = 8;
constructor() { }
ngOnChanges(changes: SimpleChanges): void {
if (changes.workbasketsResource.currentValue && changes.workbasketsResource.currentValue.page) {
this.pageSelected = changes.workbasketsResource.currentValue.page.number;
}
}
ngOnInit() {
}
changeToPage(page) {
if (page < 1) { page = this.pageSelected = 1; }
if (page > this.workbasketsResource.page.totalPages) {
page = this.workbasketsResource.page.totalPages;
}
if (this.previousPageSelected !== page) {
this.changePage.emit(page);
this.previousPageSelected = page;
}
}
getPagesTextToShow(): string {
if (!this.workbasketsResource) {
return '';
}
let text = this.workbasketsResource.page.totalElements + '';
if (this.workbasketsResource.page && this.workbasketsResource.page.totalElements &&
this.workbasketsResource.page.totalElements >= this.workbasketsResource.page.size) {
text = this.workbasketsResource.page.size + '';
}
return `${text} of ${this.workbasketsResource.page.totalElements} workbaskets`;
}
}

View File

@ -1,41 +1,42 @@
<div class="workbasket-list-full-height">
<div class="footer-space">
<div #wbToolbar>
<taskana-workbasket-list-toolbar [workbaskets]="workbaskets" (performFilter)="performFilter($event)" (performSorting)="performSorting($event)"
(importSucessful)="refreshWorkbasketList()"></taskana-workbasket-list-toolbar>
</div>
<div *ngIf="(workbaskets && workbaskets.length > 0) else empty_workbaskets">
<ul #wbList id="wb-list-container" class="list-group">
<li class="list-group-item no-space">
<div class="row"></div>
</li>
<li class="list-group-item" *ngFor="let workbasket of workbaskets" [class.active]="workbasket.workbasketId == selectedId"
type="text" (click)="selectWorkbasket(workbasket.workbasketId)">
<div class="row">
<dl class="col-xs-1">
<taskana-icon-type class="vertical-align" [type]="workbasket.type" tooltip="true" [selected]="workbasket.workbasketId === selectedId"></taskana-icon-type>
</dl>
<dl class="col-xs-10">
<dt data-toggle="tooltip" title="{{workbasket.name}}">{{workbasket.name}},
<i data-toggle="tooltip" title="{{workbasket.key}}">{{workbasket.key}} </i>
</dt>
<dd data-toggle="tooltip" title="{{workbasket.description}}">{{workbasket.description}} &nbsp;</dd>
<dd data-toggle="tooltip" title="{{workbasket.owner}}">{{workbasket.owner}} &nbsp;</dd>
</dl>
<dl *ngIf="workbasket.markedForDeletion">
<span class="{{workbasket.workbasketId === selectedId ? 'white': 'red' }} glyphicon glyphicon-exclamation-sign" aria-hidden="true" data-toggle="tooltip" title="Marked for deletion"></span>
</dl>
</div>
</li>
</ul>
</div>
<taskana-spinner [isRunning]="requestInProgress"></taskana-spinner>
<ng-template #empty_workbaskets>
<div *ngIf="!requestInProgress" class="col-xs-12 container-no-items center-block">
<h3 class="grey">There are no workbaskets</h3>
<svg-icon class="img-responsive empty-icon" src="./assets/icons/wb-empty.svg"></svg-icon>
</div>
</ng-template>
<div class="footer-space-workbasket">
<div #wbToolbar>
<taskana-workbasket-list-toolbar [workbaskets]="workbaskets" (performFilter)="performFilter($event)"
(performSorting)="performSorting($event)" (importSucessful)="refreshWorkbasketList()"></taskana-workbasket-list-toolbar>
</div>
<taskana-pagination [(workbasketsResource)]="workbasketsResource" (changePage)="changePage($event)"></taskana-pagination>
<div *ngIf="(workbaskets && workbaskets.length > 0) else empty_workbaskets">
<ul #wbList id="wb-list-container" class="list-group">
<li class="list-group-item no-space">
<div class="row"></div>
</li>
<li class="list-group-item" *ngFor="let workbasket of workbaskets" [class.active]="workbasket.workbasketId == selectedId"
type="text" (click)="selectWorkbasket(workbasket.workbasketId)">
<div class="row">
<dl class="col-xs-1">
<taskana-icon-type class="vertical-align" [type]="workbasket.type" tooltip="true" [selected]="workbasket.workbasketId === selectedId"></taskana-icon-type>
</dl>
<dl class="col-xs-10">
<dt data-toggle="tooltip" title="{{workbasket.name}}">{{workbasket.name}},
<i data-toggle="tooltip" title="{{workbasket.key}}">{{workbasket.key}} </i>
</dt>
<dd data-toggle="tooltip" title="{{workbasket.description}}">{{workbasket.description}} &nbsp;</dd>
<dd data-toggle="tooltip" title="{{workbasket.owner}}">{{workbasket.owner}} &nbsp;</dd>
</dl>
<dl *ngIf="workbasket.markedForDeletion">
<span class="{{workbasket.workbasketId === selectedId ? 'white': 'red' }} glyphicon glyphicon-exclamation-sign"
aria-hidden="true" data-toggle="tooltip" title="Marked for deletion"></span>
</dl>
</div>
</li>
</ul>
</div>
<taskana-spinner [isRunning]="requestInProgress"></taskana-spinner>
<ng-template #empty_workbaskets>
<div *ngIf="!requestInProgress" class="col-xs-12 container-no-items center-block">
<h3 class="grey">There are no workbaskets</h3>
<svg-icon class="img-responsive empty-icon" src="./assets/icons/wb-empty.svg"></svg-icon>
</div>
</ng-template>
</div>
<taskana-pagination [(page)]="workbasketsResource !== undefined ? workbasketsResource.page : workbasketsResource" [type]="type" (changePage)="changePage($event)"></taskana-pagination>
</div>

View File

@ -1,7 +1,3 @@
.workbasket-list-full-height{
height: calc(100vh - 55px);
}
.row.list-group {
margin-left: 2px;
}

View File

@ -6,15 +6,12 @@ import { AngularSvgIconModule } from 'angular-svg-icon';
import { HttpClientModule } from '@angular/common/http';
import { Routes } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { SharedModule } from 'app/shared/shared.module';
import { AppModule } from 'app/app.module';
import { WorkbasketSummary } from 'app/models/workbasket-summary';
import { WorkbasketSummaryResource } from 'app/models/workbasket-summary-resource';
import { FilterModel } from 'app/models/filter';
import { LinksWorkbasketSummary } from 'app/models/links-workbasket-summary';
import { WorkbasketListComponent } from './workbasket-list.component';
import { WorkbasketListToolbarComponent } from './workbasket-list-toolbar/workbasket-list-toolbar.component';
import { ImportExportComponent } from 'app/administration/components/import-export/import-export.component';
@ -24,6 +21,7 @@ import { ClassificationDefinitionService } from 'app/administration/services/cla
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
import { OrientationService } from 'app/services/orientation/orientation.service';
import { configureTests } from 'app/app.test.configuration';
import { Page } from 'app/models/page';
@Component({
selector: 'taskana-dummy-detail',
@ -38,20 +36,12 @@ class DummyDetailComponent {
})
class PaginationComponent {
@Input()
workbasketsResource: any;
page: Page;
@Output()
workbasketsResourceChange = new EventEmitter<any>();
@Output() changePage = new EventEmitter<any>();
}
@Component({
selector: 'taskana-filter',
template: ''
})
class FilterComponent {
}
const workbasketSummaryResource: WorkbasketSummaryResource = new WorkbasketSummaryResource({
'workbaskets': new Array<WorkbasketSummary>(
new WorkbasketSummary('1', 'key1', 'NAME1', 'description 1', 'owner 1', '', '', 'PERSONAL', '', '', '', ''),
@ -75,14 +65,16 @@ describe('WorkbasketListComponent', () => {
beforeEach(done => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
declarations: [WorkbasketListComponent, DummyDetailComponent, WorkbasketListToolbarComponent,
PaginationComponent, ImportExportComponent],
declarations: [
WorkbasketListComponent,
DummyDetailComponent,
WorkbasketListToolbarComponent,
ImportExportComponent
],
imports: [
AngularSvgIconModule,
HttpClientModule,
RouterTestingModule.withRoutes(routes),
SharedModule,
AppModule
RouterTestingModule.withRoutes(routes)
],
providers: [
WorkbasketService,

View File

@ -1,4 +1,4 @@
import {ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Subscription} from 'rxjs';
@ -24,6 +24,10 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
workbaskets: Array<WorkbasketSummary> = [];
requestInProgress = false;
pageSelected = 1;
pageSize = 9;
type = 'workbaskets';
sort: SortingModel = new SortingModel();
filterBy: FilterModel = new FilterModel({name: '', owner: '', type: '', description: '', key: ''});
@ -38,8 +42,7 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
private workbasketService: WorkbasketService,
private router: Router,
private route: ActivatedRoute,
private orientationService: OrientationService,
private cd: ChangeDetectorRef) {
private orientationService: OrientationService) {
}
ngOnInit() {
@ -51,6 +54,9 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
}, 0);
});
TaskanaQueryParameters.page = this.pageSelected;
TaskanaQueryParameters.pageSize = this.pageSize;
this.workbasketServiceSavedSubscription = this.workbasketService.workbasketSavedTriggered().subscribe(value => {
this.performRequest();
});
@ -85,7 +91,7 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
const unusedHeight = 145;
const totalHeight = window.innerHeight;
const cards = Math.round((totalHeight - (unusedHeight + toolbarSize)) / cardHeight);
TaskanaQueryParameters.pageSize = cards;
cards > 0 ? TaskanaQueryParameters.pageSize = cards : TaskanaQueryParameters.pageSize = 1;
this.performRequest();
}

View File

@ -1,4 +1,4 @@
import { Injectable, HostListener } from '@angular/core';
import { Injectable } from '@angular/core';
import { Orientation } from 'app/models/orientation';
import { BehaviorSubject , Observable } from 'rxjs';

View File

@ -0,0 +1,19 @@
<ul id="wb-pagination" class="pagination vertical-center">
<li>
<a (click)="changeToPage(1)" aria-label="First">
First</a>
</li>
<li *ngFor="let pageNumber of page?.totalPages |
spreadNumber: page?.number: maxPagesAvailable: page?.totalPages">
<a *ngIf="pageNumber + 1 !== page?.number" (click)="changeToPage(pageNumber+1)">{{pageNumber + 1}}</a>
<a *ngIf="pageNumber + 1 === page?.number" class="pagination">
<input [(ngModel)]="pageSelected" (keyup.enter)="changeToPage(pageSelected)" type="text" (blur)="changeToPage(pageSelected)">
</a>
</li>
<li>
<a (click)="changeToPage(page?.totalPages)" aria-label="Last">Last</a>
</li>
</ul>
<span class="footer pull-right">
<i [innerHTML]="getPagesTextToShow()"></i>
</span>

View File

@ -1,10 +1,9 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SharedModule } from 'app/shared/shared.module';
import { PaginationComponent } from './pagination.component';
import { WorkbasketSummaryResource } from 'app/models/workbasket-summary-resource';
import { Page } from 'app/models/page';
import { configureTests } from 'app/app.test.configuration';
@ -17,9 +16,6 @@ describe('PaginationComponent', () => {
beforeEach(done => {
const configure = (testBed: TestBed) => {
testBed.configureTestingModule({
declarations: [
PaginationComponent
],
imports: [FormsModule, SharedModule]
})
};
@ -43,13 +39,13 @@ describe('PaginationComponent', () => {
});
it('should create 3 pages if total pages are 3', () => {
component.workbasketsResource = new WorkbasketSummaryResource(undefined, undefined, new Page(6, 3, 3, 1));
component.page = new Page(6, 3, 3, 1);
fixture.detectChanges();
expect(debugElement.querySelectorAll('#wb-pagination > li').length).toBe(5);
});
it('should emit change if previous page was different than current one', () => {
component.workbasketsResource = new WorkbasketSummaryResource(undefined, undefined, new Page(6, 3, 3, 1));
component.page = new Page(6, 3, 3, 1);
component.previousPageSelected = 2;
fixture.detectChanges();
component.changePage.subscribe(value => {
@ -59,7 +55,7 @@ describe('PaginationComponent', () => {
});
it('should not emit change if previous page was the same than current one', () => {
component.workbasketsResource = new WorkbasketSummaryResource(undefined, undefined, new Page(6, 3, 3, 1));
component.page = new Page(6, 3, 3, 1);
component.previousPageSelected = 2;
fixture.detectChanges();
component.changePage.subscribe(value => {
@ -69,7 +65,7 @@ describe('PaginationComponent', () => {
});
it('should emit totalPages if page is more than page.totalPages', () => {
component.workbasketsResource = new WorkbasketSummaryResource(undefined, undefined, new Page(6, 3, 3, 1));
component.page = new Page(6, 3, 3, 1);
component.previousPageSelected = 2;
fixture.detectChanges();
component.changePage.subscribe(value => {
@ -79,7 +75,7 @@ describe('PaginationComponent', () => {
});
it('should emit 1 if page is less than 1', () => {
component.workbasketsResource = new WorkbasketSummaryResource(undefined, undefined, new Page(6, 3, 3, 1));
component.page = new Page(6, 3, 3, 1);
component.previousPageSelected = 2;
fixture.detectChanges();
component.changePage.subscribe(value => {
@ -91,7 +87,7 @@ describe('PaginationComponent', () => {
it('should change pageSelected onChanges', () => {
expect(component.pageSelected).toBe(1);
component.ngOnChanges({
workbasketsResource: new SimpleChange(null, new WorkbasketSummaryResource(undefined, undefined, new Page(6, 3, 3, 2)), true)
page: new SimpleChange(null, new Page(6, 3, 3, 2), true)
});
fixture.detectChanges();
expect(component.pageSelected).toBe(2);
@ -99,17 +95,20 @@ describe('PaginationComponent', () => {
});
it('should getPagesTextToShow return 7 of 13 with size < totalElements', () => {
component.workbasketsResource = new WorkbasketSummaryResource(undefined, undefined, new Page(7, 13, 3, 2));
component.page = new Page(7, 13, 3, 2);
component.type = 'workbaskets';
expect(component.getPagesTextToShow()).toBe('7 of 13 workbaskets');
});
it('should getPagesTextToShow return 6 of 6 with size > totalElements', () => {
component.workbasketsResource = new WorkbasketSummaryResource(undefined, undefined, new Page(7, 6, 3, 2));
expect(component.getPagesTextToShow()).toBe('6 of 6 workbaskets');
component.page = new Page(7, 6, 3, 2);
component.type = 'tasks';
expect(component.getPagesTextToShow()).toBe('6 of 6 tasks');
});
it('should getPagesTextToShow return of with totalElements = 0', () => {
component.workbasketsResource = new WorkbasketSummaryResource(undefined, undefined, new Page(7, 0, 0, 0));
component.page = new Page(7, 0, 0, 0);
component.type = 'workbaskets';
expect(component.getPagesTextToShow()).toBe('0 of 0 workbaskets');
});

View File

@ -0,0 +1,67 @@
import {
Component,
OnInit,
Input,
Output,
EventEmitter,
OnChanges,
SimpleChanges
} from '@angular/core';
import { Page } from 'app/models/page';
@Component({
selector: 'taskana-pagination',
templateUrl: './pagination.component.html',
styleUrls: ['./pagination.component.scss']
})
export class PaginationComponent implements OnInit, OnChanges {
@Input()
page: Page;
@Input()
type: String;
@Output()
workbasketsResourceChange = new EventEmitter<Page>();
@Output()
changePage = new EventEmitter<number>();
previousPageSelected = 1;
pageSelected = 1;
maxPagesAvailable = 8;
constructor() {}
ngOnChanges(changes: SimpleChanges): void {
if (changes.page.currentValue !== undefined) {
this.pageSelected = changes.page.currentValue.number;
}
}
ngOnInit() {}
changeToPage(page) {
if (page < 1) {
page = this.pageSelected = 1;
}
if (page > this.page.totalPages) {
page = this.page.totalPages;
}
if (this.previousPageSelected !== page) {
this.changePage.emit(page);
this.previousPageSelected = page;
}
}
getPagesTextToShow(): string {
if (!this.page) {
return '';
}
let text = this.page.totalElements + '';
if (
this.page &&
this.page.totalElements &&
this.page.totalElements >= this.page.size
) {
text = this.page.size + '';
}
return `${text} of ${this.page.totalElements} ${this.type}`;
}
}

View File

@ -22,6 +22,7 @@ import { RemoveConfirmationComponent } from 'app/shared/remove-confirmation/remo
import { FilterComponent } from 'app/shared/filter/filter.component';
import { IconTypeComponent } from 'app/administration/components/type-icon/icon-type.component';
import { FieldErrorDisplayComponent } from 'app/shared/field-error-display/field-error-display.component';
import { PaginationComponent } from './pagination/pagination.component';
/**
* Pipes
@ -67,7 +68,8 @@ const DECLARATIONS = [
FilterComponent,
IconTypeComponent,
RemoveConfirmationComponent,
FieldErrorDisplayComponent
FieldErrorDisplayComponent,
PaginationComponent
];
@NgModule({

View File

@ -1,74 +1,90 @@
import { Direction } from 'app/models/sorting';
export class TaskanaQueryParameters {
// Sorting
static SORTBY = 'sort-by';
static ORDER = 'order';
// Sorting
static SORTBY = 'sort-by';
static ORDER = 'order';
// Filtering
static NAME = 'name';
static NAMELIKE = 'name-like';
static DESCLIKE = 'description-like';
static OWNER = 'owner';
static OWNERLIKE = 'owner-like';
static TYPE = 'type';
static KEY = 'key';
static WORKBASKET_KEY = 'workbasket-key';
static KEYLIKE = 'key-like';
// Filtering
static NAME = 'name';
static NAMELIKE = 'name-like';
static DESCLIKE = 'description-like';
static OWNER = 'owner';
static OWNERLIKE = 'owner-like';
static TYPE = 'type';
static KEY = 'key';
static WORKBASKET_KEY = 'workbasket-key';
static KEYLIKE = 'key-like';
static PRIORITY = 'priority';
static STATE = 'state';
static WORKBASKET_ID = 'workbasket-id';
// Access
static REQUIREDPERMISSION = 'required-permission';
static ACCESSIDS = 'access-ids';
static ACCESSIDLIKE = 'access-id-like';
static WORKBASKETKEYLIKE = 'workbasket-key-like';
// Access
static REQUIREDPERMISSION = 'required-permission';
static ACCESSIDS = 'access-ids';
static ACCESSIDLIKE = 'access-id-like';
static WORKBASKETKEYLIKE = 'workbasket-key-like';
// Pagination
static PAGE = 'page';
static PAGESIZE = 'page-size';
static page = 1;
static pageSize = 9;
// Pagination
static PAGE = 'page';
static PAGESIZE = 'page-size';
static page = 1;
static pageSize = 9;
// Domain
static DOMAIN = 'domain';
// Domain
static DOMAIN = 'domain';
public static getQueryParameters(sortBy: string = undefined,
order: string = undefined,
name: string = undefined,
nameLike: string = undefined,
descLike: string = undefined,
owner: string = undefined,
ownerLike: string = undefined,
type: string = undefined,
key: string = undefined,
keyLike: string = undefined,
requiredPermission: string = undefined,
page: number = undefined,
pageSize: number = undefined,
domain: string = undefined,
accessIds: string = undefined,
accessIdLike: string = undefined,
workbasketKeyLike: string = undefined): string {
let query = '?';
query += sortBy ? `${this.SORTBY}=${sortBy}&` : '';
query += order ? `${this.ORDER}=${order}&` : '';
query += name ? `${this.NAME}=${name}&` : '';
query += nameLike ? `${this.NAMELIKE}=${nameLike}&` : '';
query += descLike ? `${this.DESCLIKE}=${descLike}&` : '';
query += owner ? `${this.OWNER}=${owner}&` : '';
query += ownerLike ? `${this.OWNERLIKE}=${ownerLike}&` : '';
query += type ? `${this.TYPE}=${type}&` : '';
query += key ? `${this.KEY}=${key}&` : '';
query += keyLike ? `${this.KEYLIKE}=${keyLike}&` : '';
query += requiredPermission ? `${this.REQUIREDPERMISSION}=${requiredPermission}&` : '';
query += page ? `${this.PAGE}=${page}&` : '';
query += pageSize ? `${this.PAGESIZE}=${pageSize}&` : '';
query += domain ? `${this.DOMAIN}=${domain}&` : '';
query += accessIds ? `${this.ACCESSIDS}=${accessIds}&` : '';
query += accessIdLike ? `${this.ACCESSIDLIKE}=${accessIdLike}&` : '';
query += workbasketKeyLike ? `${this.WORKBASKETKEYLIKE}=${workbasketKeyLike}&` : '';
public static getQueryParameters(
sortBy: string = undefined,
order: string = undefined,
name: string = undefined,
nameLike: string = undefined,
descLike: string = undefined,
owner: string = undefined,
ownerLike: string = undefined,
type: string = undefined,
key: string = undefined,
keyLike: string = undefined,
requiredPermission: string = undefined,
page: number = undefined,
pageSize: number = undefined,
domain: string = undefined,
accessIds: string = undefined,
accessIdLike: string = undefined,
workbasketKeyLike: string = undefined,
basketId: string = undefined,
priority: string = undefined,
state: string = undefined,
): string {
let query = '?';
query += sortBy ? `${this.SORTBY}=${sortBy}&` : '';
query += order ? `${this.ORDER}=${order}&` : '';
query += name ? `${this.NAME}=${name}&` : '';
query += nameLike ? `${this.NAMELIKE}=${nameLike}&` : '';
query += descLike ? `${this.DESCLIKE}=${descLike}&` : '';
query += owner ? `${this.OWNER}=${owner}&` : '';
query += ownerLike ? `${this.OWNERLIKE}=${ownerLike}&` : '';
query += basketId ? `${this.WORKBASKET_ID}=${basketId}&` : '';
query += priority ? `${this.PRIORITY}=${priority}&` : '';
query += state ? `${this.STATE}=${state}&` : '';
query += type ? `${this.TYPE}=${type}&` : '';
query += key ? `${this.KEY}=${key}&` : '';
query += keyLike ? `${this.KEYLIKE}=${keyLike}&` : '';
query += requiredPermission
? `${this.REQUIREDPERMISSION}=${requiredPermission}&`
: '';
query += page ? `${this.PAGE}=${page}&` : '';
query += pageSize ? `${this.PAGESIZE}=${pageSize}&` : '';
query += domain ? `${this.DOMAIN}=${domain}&` : '';
query += accessIds ? `${this.ACCESSIDS}=${accessIds}&` : '';
query += accessIdLike ? `${this.ACCESSIDLIKE}=${accessIdLike}&` : '';
query += workbasketKeyLike
? `${this.WORKBASKETKEYLIKE}=${workbasketKeyLike}&`
: '';
if (query.lastIndexOf('&') === query.length - 1) {
query = query.slice(0, query.lastIndexOf('&'))
}
return query === '?' ? '' : query;
if (query.lastIndexOf('&') === query.length - 1) {
query = query.slice(0, query.lastIndexOf('&'));
}
return query === '?' ? '' : query;
}
}

View File

@ -1,7 +1,9 @@
import {Links} from '../../models/links';
import {Task} from './task';
import { Page } from 'app/models/page';
export class TaskResource {
constructor(public _embedded: { 'tasks': Array<Task> } = { 'tasks': [] },
public _links: Links = undefined) {}
public _links: Links = undefined,
public page: Page = new Page()) {}
}

View File

@ -5,16 +5,10 @@ import {Injectable} from '@angular/core';
import {environment} from 'environments/environment';
import {TaskResource} from 'app/workplace/models/task-resource';
import {Direction} from 'app/models/sorting';
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
@Injectable()
export class TaskService {
WORKBASKET_ID = 'workbasket-id';
SORT_BY = 'sortBy';
SORT_DIRECTION = 'order';
NAME = 'name';
OWNER = 'owner';
PRIORITY = 'priority';
STATE = 'state';
url = `${environment.taskanaRestUrl}/v1/tasks`;
@ -55,14 +49,27 @@ export class TaskService {
* @param {string} sortBy name of field, that the tasks should be sorted by, default is priority
* @returns {Observable<TaskResource>}
*/
/**
* @param {string} basketId the id of workbasket
* @param {string} sortBy name of field, that the tasks should be sorted by, default is priority
* @param {string} sortDirection ASC or DESC
* @param {string} name the name of the task
* @param {string} owner the owner of the task
* @param {string} priority the priority of the task
* @param {string} statethe state of the task
*/
findTasksWithWorkbasket(basketId: string,
sortBy = 'priority',
sortDirection: string = Direction.ASC,
name: string,
owner: string,
priority: string,
state: string): Observable<TaskResource> {
const url = `${this.url}${this.getTaskQueryParameters(basketId, sortBy, sortDirection, name, owner, priority, state)}`;
state: string,
allPages: boolean = false): Observable<TaskResource> {
const url = `${this.url}${TaskanaQueryParameters.getQueryParameters(
sortBy, sortDirection, name, undefined, undefined, owner, undefined, undefined, undefined, undefined, undefined,
!allPages ? TaskanaQueryParameters.page : undefined, !allPages ? TaskanaQueryParameters.pageSize : undefined,
undefined, undefined, undefined, undefined, basketId, priority, state)}`;
return this.httpClient.get<TaskResource>(url);
}
@ -93,26 +100,4 @@ export class TaskService {
createTask(task: Task): Observable<Task> {
return this.httpClient.post<Task>(this.url, task);
}
private getTaskQueryParameters(basketId: string,
sortBy: string,
sortDirection: string,
name: string,
owner: string,
priority: string,
state: string): string {
let query = '?';
query += basketId ? `${this.WORKBASKET_ID}=${basketId}&` : '';
query += `${this.SORT_BY}=${sortBy}&`;
query += `${this.SORT_DIRECTION}=${sortDirection}&`;
query += name ? `${this.NAME}=${name}&` : '';
query += owner ? `${this.OWNER}=${owner}&` : '';
query += priority ? `${this.PRIORITY}=${priority}&` : '';
query += state ? `${this.STATE}=${state}&` : '';
if (query.lastIndexOf('&') === query.length - 1) {
query = query.slice(0, query.lastIndexOf('&'))
}
return query;
}
}

View File

@ -1,16 +1,42 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Routes } from '@angular/router';
import { TaskComponent } from './task.component';
import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
import { FormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { TaskService } from '../services/task.service';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
import { Component } from '@angular/core';
import { DomainService } from 'app/services/domain/domain.service';
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
import { SelectedRouteService } from 'app/services/selected-route/selected-route';
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
describe('TaskComponent', () => {
@Component({
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
class DummyDetailComponent {
}
const routes: Routes = [
{ path: 'workplace/tasks', component: DummyDetailComponent }
];
// TODO: test pending to test. Failing random
xdescribe('TaskComponent', () => {
let component: TaskComponent;
let fixture: ComponentFixture<TaskComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TaskComponent ]
})
.compileComponents();
imports: [FormsModule, HttpClientModule, RouterTestingModule.withRoutes(routes)],
declarations: [TaskComponent, SpinnerComponent, DummyDetailComponent],
providers: [TaskService, HttpClient, WorkbasketService, DomainService, RequestInProgressService,
SelectedRouteService, ErrorModalService]
}).compileComponents();
}));
beforeEach(() => {

View File

@ -1,13 +1,16 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskdetailsAttributeComponent } from './attribute.component';
import { FormsModule } from '@angular/forms';
describe('AttributeComponent', () => {
// TODO: test pending to test. Failing random
xdescribe('AttributeComponent', () => {
let component: TaskdetailsAttributeComponent;
let fixture: ComponentFixture<TaskdetailsAttributeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule],
declarations: [ TaskdetailsAttributeComponent ]
})
.compileComponents();

View File

@ -1,13 +1,16 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskdetailsCustomFieldsComponent } from './custom-fields.component';
import { FormsModule } from '@angular/forms';
describe('CustomComponent', () => {
// TODO: test pending to test. Failing random
xdescribe('CustomComponent', () => {
let component: TaskdetailsCustomFieldsComponent;
let fixture: ComponentFixture<TaskdetailsCustomFieldsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule],
declarations: [ TaskdetailsCustomFieldsComponent ]
})
.compileComponents();

View File

@ -1,14 +1,40 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskdetailsGeneralFieldsComponent } from './general-fields.component';
import { FormsModule } from '@angular/forms';
import { ClassificationsService } from 'app/services/classifications/classifications.service';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { ClassificationCategoriesService } from 'app/services/classifications/classification-categories.service';
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
import { DomainService } from 'app/services/domain/domain.service';
import { RouterTestingModule } from '@angular/router/testing';
import { Routes } from '@angular/router';
import { Component } from '@angular/core';
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
import { SelectedRouteService } from 'app/services/selected-route/selected-route';
describe('GeneralComponent', () => {
@Component({
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
// TODO: test pending to test. Failing random
xdescribe('GeneralComponent', () => {
let component: TaskdetailsGeneralFieldsComponent;
let fixture: ComponentFixture<TaskdetailsGeneralFieldsComponent>;
const routes: Routes = [
{ path: '*', component: DummyDetailComponent }
];
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TaskdetailsGeneralFieldsComponent ]
imports: [FormsModule, HttpClientModule, RouterTestingModule.withRoutes(routes)],
declarations: [TaskdetailsGeneralFieldsComponent, DummyDetailComponent],
providers: [ClassificationsService, HttpClient, ClassificationCategoriesService, CustomFieldsService,
DomainService, RequestInProgressService, SelectedRouteService]
})
.compileComponents();
}));

View File

@ -1,14 +1,46 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskdetailsComponent } from './taskdetails.component';
import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
import { FormsModule } from '@angular/forms';
import { TaskdetailsGeneralFieldsComponent } from './general/general-fields.component';
import { TaskdetailsCustomFieldsComponent } from './custom/custom-fields.component';
import { TaskdetailsAttributeComponent } from './attribute/attribute.component';
import { Routes } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { Component } from '@angular/core';
import { TaskService } from '../services/task.service';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { WorkplaceService } from '../services/workplace.service';
import { RemoveConfirmationService } from 'app/services/remove-confirmation/remove-confirmation.service';
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
import { AlertService } from 'app/services/alert/alert.service';
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
describe('TaskdetailsComponent', () => {
@Component({
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
class DummyDetailComponent {
}
const routes: Routes = [
{ path: 'workplace/taskdetail/:id', component: DummyDetailComponent }
];
// TODO: test pending to test. Failing random
xdescribe('TaskdetailsComponent', () => {
let component: TaskdetailsComponent;
let fixture: ComponentFixture<TaskdetailsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TaskdetailsComponent ]
declarations: [TaskdetailsComponent, SpinnerComponent,
TaskdetailsGeneralFieldsComponent, TaskdetailsCustomFieldsComponent,
TaskdetailsAttributeComponent, DummyDetailComponent],
imports: [FormsModule, RouterTestingModule.withRoutes(routes), HttpClientModule],
providers: [TaskService, HttpClient, WorkplaceService, RemoveConfirmationService,
RequestInProgressService, AlertService, ErrorModalService]
})
.compileComponents();
}));

View File

@ -1,22 +1,16 @@
<li id="tasklist-action-toolbar" class="list-group-item tab-align">
<div class="row">
<div *ngIf="currentBasket">
<div *ngIf="currentBasket" class="col-xs-2">
<button (click)="createTask()" type="button"
class="btn btn-default pull-left green-blue"
title="Add Task to current Workbasket">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
<div class="col-xs-8">
<div class="input-group">
<input [(ngModel)]="resultName" [typeahead]="workbasketNames" class="form-control"
(typeaheadOnSelect)="workbasketSelected = true" (typeaheadNoResults)="workbasketSelected = false"
placeholder="Search for Workbasket ..."/>
<span class="input-group-btn">
<button class="btn btn-primary" type="button" (click)="searchBasket()"
[disabled]="!workbasketSelected">Go!</button>
</span>
</div>
<div class="col-xs-7">
<input [(ngModel)]="resultName" [typeahead]="workbasketNames" class="form-control"
(typeaheadOnSelect)="searchBasket()" (typeaheadNoResults)="workbasketSelected = false"
placeholder="Search for Workbasket ..."/>
</div>
<div class="pull-right margin-right">

View File

@ -0,0 +1,59 @@
import { TaskListToolbarComponent } from './tasklist-toolbar.component';
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { TypeaheadModule, ComponentLoaderFactory, PositioningService } from 'ngx-bootstrap';
import { SortComponent } from 'app/shared/sort/sort.component';
import { FilterComponent } from 'app/shared/filter/filter.component';
import { MapValuesPipe } from 'app/shared/pipes/mapValues/map-values.pipe';
import { IconTypeComponent } from 'app/administration/components/type-icon/icon-type.component';
import { SvgIconComponent } from 'angular-svg-icon';
import { TaskService } from 'app/workplace/services/task.service';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
import { DomainService } from 'app/services/domain/domain.service';
import { RouterTestingModule } from '@angular/router/testing';
import { Routes } from '@angular/router';
import { Component } from '@angular/core';
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
import { SelectedRouteService } from 'app/services/selected-route/selected-route';
import { WorkplaceService } from 'app/workplace/services/workplace.service';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@Component({
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
// TODO: test pending to test. Failing random
xdescribe('TasklistToolbarComponent', () => {
let component: TaskListToolbarComponent;
let fixture: ComponentFixture<TaskListToolbarComponent>;
const routes: Routes = [
{ path: '*', component: DummyDetailComponent }
];
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TaskListToolbarComponent, SortComponent, FilterComponent, MapValuesPipe,
IconTypeComponent, SvgIconComponent, DummyDetailComponent],
imports: [FormsModule, TypeaheadModule, HttpClientModule, RouterTestingModule.withRoutes(routes),
BrowserAnimationsModule],
providers: [TaskService, HttpClient, WorkbasketService, DomainService, RequestInProgressService,
SelectedRouteService, WorkplaceService, ComponentLoaderFactory, PositioningService]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TaskListToolbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -62,6 +62,7 @@ export class TaskListToolbarComponent implements OnInit {
searchBasket() {
this.toolbarState = false;
this.workbasketSelected = true;
if (this.workbaskets) {
this.workbaskets.forEach(workbasket => {
if (workbasket.name === this.resultName) {

View File

@ -1,13 +1,13 @@
<div class="task-list-full-height taskana-task-list">
<taskana-tasklist-toolbar (performSorting)="performSorting($event)"
(performFilter)="performFilter($event)">
</taskana-tasklist-toolbar>
<div *ngIf="!requestInProgress">
<div class="taskana-task-list">
<div #wbToolbar>
<taskana-tasklist-toolbar (performSorting)="performSorting($event)" (performFilter)="performFilter($event)" (importSucessful)="refreshWorkbasketList()">
</taskana-tasklist-toolbar>
</div>
<div *ngIf="!requestInProgress" class="footer-space-task">
<div *ngIf="(tasks && tasks.length > 0); else empty_list">
<ul #taskList id="task-list-container" class="list-group">
<li class="list-group-item"
*ngFor="let task of tasks" [class.active]="task.taskId == selectedId"
type="text" (click)="selectTask(task.taskId)">
<li class="list-group-item" *ngFor="let task of tasks" [class.active]="task.taskId == selectedId" type="text"
(click)="selectTask(task.taskId)">
<div class="row">
<dl class="col-xs-10">
<dt data-toggle="tooltip" title="{{task.name}}">{{task.name}}</dt>
@ -26,5 +26,6 @@
</div>
</ng-template>
</div>
<taskana-pagination *ngIf="tasks && tasks.length > 0" [(page)]="page" [type]="type" (changePage)="changePage($event)"></taskana-pagination>
</div>
<taskana-code></taskana-code>

View File

@ -2,10 +2,6 @@
cursor: pointer;
}
.task-list-full-height {
// height: calc(100vh - 55px);
}
.row.list-group {
margin-left: 2px;
}
@ -43,10 +39,6 @@ li > div.row > dl {
margin-bottom: 0px;
}
li > div.row > dl:first-child {
margin-left: 10px;
}
.no-space {
border-top: none;
padding: 0px

View File

@ -1,14 +1,57 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TasklistComponent } from './tasklist.component';
import { TaskListToolbarComponent } from './tasklist-toolbar/tasklist-toolbar.component';
import { SvgIconComponent, SvgIconRegistryService } from 'angular-svg-icon';
import { PaginationComponent } from 'app/shared/pagination/pagination.component';
import { CodeComponent } from '../components/code/code.component';
import { FormsModule } from '@angular/forms';
import { TypeaheadModule, ComponentLoaderFactory, PositioningService } from 'ngx-bootstrap';
import { SortComponent } from 'app/shared/sort/sort.component';
import { FilterComponent } from 'app/shared/filter/filter.component';
import { SpreadNumberPipe } from 'app/shared/pipes/spreadNumber/spread-number';
import { MapValuesPipe } from 'app/shared/pipes/mapValues/map-values.pipe';
import { IconTypeComponent } from 'app/administration/components/type-icon/icon-type.component';
import { RouterTestingModule } from '@angular/router/testing';
import { Routes } from '@angular/router';
import { Component } from '@angular/core';
import { TaskService } from '../services/task.service';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { WorkplaceService } from '../services/workplace.service';
import { AlertService } from 'app/services/alert/alert.service';
import { OrientationService } from 'app/services/orientation/orientation.service';
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
import { DomainService } from 'app/services/domain/domain.service';
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
import { SelectedRouteService } from 'app/services/selected-route/selected-route';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
describe('TasklistComponent', () => {
@Component({
selector: 'taskana-dummy-detail',
template: 'dummydetail'
})
export class DummyDetailComponent {
}
// TODO: test pending to test. Failing random
xdescribe('TasklistComponent', () => {
let component: TasklistComponent;
let fixture: ComponentFixture<TasklistComponent>;
const routes: Routes = [
{ path: '*', component: DummyDetailComponent }
];
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TasklistComponent ]
imports: [FormsModule, TypeaheadModule, RouterTestingModule.withRoutes(routes),
HttpClientModule, BrowserAnimationsModule],
declarations: [TasklistComponent, TaskListToolbarComponent, SvgIconComponent,
PaginationComponent, CodeComponent, SortComponent, FilterComponent,
SpreadNumberPipe, MapValuesPipe, IconTypeComponent, DummyDetailComponent],
providers: [TaskService, HttpClient, WorkplaceService, AlertService, OrientationService,
WorkbasketService, DomainService, RequestInProgressService, SelectedRouteService,
ComponentLoaderFactory, PositioningService, SvgIconRegistryService]
})
.compileComponents();
}));

View File

@ -1,4 +1,4 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import {Component, OnDestroy, OnInit, ViewChild, ElementRef} from '@angular/core';
import {Task} from 'app/workplace/models/task';
import {ActivatedRoute, Router} from '@angular/router';
import {TaskService} from 'app/workplace/services/task.service';
@ -9,6 +9,10 @@ import {FilterModel} from 'app/models/filter';
import {AlertService} from 'app/services/alert/alert.service';
import {AlertModel, AlertType} from 'app/models/alert';
import {WorkplaceService} from 'app/workplace/services/workplace.service';
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
import { Page } from 'app/models/page';
import { OrientationService } from 'app/services/orientation/orientation.service';
import { Orientation } from 'app/models/orientation';
@Component({
selector: 'taskana-task-list',
@ -19,6 +23,10 @@ export class TasklistComponent implements OnInit, OnDestroy {
tasks: Task[];
page: Page;
pageSelected = 1;
pageSize = 6;
type = 'tasks';
currentBasket: Workbasket;
selectedId = '';
sort: SortingModel = new SortingModel('priority');
@ -33,16 +41,20 @@ export class TasklistComponent implements OnInit, OnDestroy {
});
requestInProgress = false;
@ViewChild('wbToolbar')
private toolbarElement: ElementRef;
private taskChangeSubscription: Subscription;
private taskDeletedSubscription: Subscription;
private taskAddedSubscription: Subscription;
private workbasketChangeSubscription: Subscription;
private orientationSubscription: Subscription;
constructor(private router: Router,
private route: ActivatedRoute,
private taskService: TaskService,
private workplaceService: WorkplaceService,
private alertService: AlertService) {
private alertService: AlertService,
private orientationService: OrientationService) {
this.taskChangeSubscription = this.taskService.taskChangedStream.subscribe(task => {
for (let i = 0; i < this.tasks.length; i++) {
if (this.tasks[i].taskId === task.taskId) {
@ -78,7 +90,12 @@ export class TasklistComponent implements OnInit, OnDestroy {
if (!task) {
this.selectedId = undefined;
}
});
});
TaskanaQueryParameters.page = this.pageSelected;
TaskanaQueryParameters.pageSize = this.pageSize;
this.orientationSubscription = this.orientationService.getOrientation().subscribe((orientation: Orientation) => {
this.refreshWorkbasketList();
})
}
selectTask(taskId: string) {
@ -96,12 +113,34 @@ export class TasklistComponent implements OnInit, OnDestroy {
this.getTasks();
}
changePage(page) {
TaskanaQueryParameters.page = page;
this.getTasks();
}
refreshWorkbasketList() {
this.calculateHeightCard();
this.getTasks();
}
calculateHeightCard() {
if (this.toolbarElement && this.currentBasket) {
const toolbarSize = this.toolbarElement.nativeElement.offsetHeight;
const cardHeight = 95;
const unusedHeight = 140;
const totalHeight = window.innerHeight;
const cards = Math.round((totalHeight - (unusedHeight + toolbarSize)) / cardHeight);
cards > 0 ? TaskanaQueryParameters.pageSize = cards : TaskanaQueryParameters.pageSize = 1;
}
}
getTasks(): void {
this.requestInProgress = true;
if (this.currentBasket === undefined) {
this.requestInProgress = false;
this.tasks = [];
} else {
this.calculateHeightCard();
this.taskService.findTasksWithWorkbasket(this.currentBasket.workbasketId, this.sort.sortBy, this.sort.sortDirection,
this.filterBy.filterParams.name, this.filterBy.filterParams.owner, this.filterBy.filterParams.priority,
this.filterBy.filterParams.state)
@ -113,6 +152,9 @@ export class TasklistComponent implements OnInit, OnDestroy {
this.tasks = [];
this.alertService.triggerAlert(new AlertModel(AlertType.INFO, 'The selected Workbasket is empty!'));
}
if (tasks.page) {
this.page = tasks.page;
}
});
}
}
@ -121,5 +163,7 @@ export class TasklistComponent implements OnInit, OnDestroy {
this.taskChangeSubscription.unsubscribe();
this.taskDeletedSubscription.unsubscribe();
this.workbasketChangeSubscription.unsubscribe();
this.taskAddedSubscription.unsubscribe();
this.orientationSubscription.unsubscribe();
}
}

View File

@ -154,9 +154,16 @@ svg-icon.fa-fw > svg {
min-width: 0px;
}
.footer-space {
max-height: calc(100vh - 130px);
height: calc(100vh - 130px);
.footer-space-workbasket {
max-height: calc(100vh - 140px);
height: calc(100vh - 140px);
overflow-y: auto;
overflow-x: hidden;
}
.footer-space-task {
max-height: calc(100vh - 182px);
height: calc(100vh - 150px);
overflow-y: auto;
overflow-x: hidden;
}
@ -280,8 +287,7 @@ body{
}
taskana-workbasket-information,taskana-task-details, taskana-workbasket-access-items, taskana-workbaskets-distribution-targets, taskana-monitor-tasks,
taskana-monitor-workbaskets, taskana-monitor-classification-tasks, taskana-access-items-management, taskana-classification-details,taskana-workbasket-details,
taskana-task-list{
taskana-monitor-workbaskets, taskana-monitor-classification-tasks, taskana-access-items-management, taskana-classification-details,taskana-workbasket-details {
& .panel{
border: none;
box-shadow: none;
@ -294,6 +300,10 @@ taskana-task-list{
}
}
.taskana-workbasket-list, .taskana-task-list {
height: calc(100vh - 55px);
}
taskana-monitor-tasks, taskana-monitor-workbaskets, taskana-monitor-classification-tasks, taskana-access-items-management {
& .panel {
&> .panel-heading {

View File

@ -27,14 +27,14 @@ getTestBed().initTestEnvironment(
);
// Then we find all the tests.
const contextAdministration = require.context('./app/administration', true, /\.spec\.ts$/);
// const contextWorplace = require.context('./app/workplace', true, /\.spec\.ts$/);
const contextWorplace = require.context('./app/workplace', true, /\.spec\.ts$/);
// const contextMonitor = require.context('./app/monitor', true, /\.spec\.ts$/);
const contextShared = require.context('./app/shared', true, /\.spec\.ts$/);
const contextAppComponents = require.context('./app/components', true, /\.spec\.ts$/);
const contextAppServices = require.context('./app/services', true, /\.spec\.ts$/);
// And load the modules.
contextAdministration.keys().map(contextAdministration);
// contextWorplace.keys().map(contextWorplace);
contextWorplace.keys().map(contextWorplace);
// contextMonitor.keys().map(contextMonitor);
contextShared.keys().map(contextShared);
contextAppComponents.keys().map(contextAppComponents);