From 9c1fa6dfe2755d249453583efe38185da5838394 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Recuerda Cambil Date: Mon, 22 Oct 2018 15:22:32 +0200 Subject: [PATCH] TSK-673 - Paging for Task list in workplace --- .../taskana/rest/TaskControllerIntTest.java | 18 +-- .../java/pro/taskana/rest/TaskController.java | 2 +- .../administration/administration.module.ts | 2 - .../classification-details.component.spec.ts | 1 - .../list/pagination/pagination.component.html | 19 --- .../list/pagination/pagination.component.ts | 52 ------- .../list/workbasket-list.component.html | 77 +++++----- .../list/workbasket-list.component.scss | 4 - .../list/workbasket-list.component.spec.ts | 26 ++-- .../master/list/workbasket-list.component.ts | 14 +- .../orientation/orientation.service.ts | 2 +- .../pagination/pagination.component.html | 19 +++ .../pagination/pagination.component.scss | 0 .../pagination/pagination.component.spec.ts | 29 ++-- .../shared/pagination/pagination.component.ts | 67 ++++++++ web/src/app/shared/shared.module.ts | 4 +- web/src/app/shared/util/query-parameters.ts | 144 ++++++++++-------- web/src/app/workplace/models/task-resource.ts | 4 +- .../app/workplace/services/task.service.ts | 47 ++---- .../app/workplace/task/task.component.spec.ts | 34 ++++- .../attribute/attribute.component.spec.ts | 5 +- .../custom/custom-fields.component.spec.ts | 5 +- .../general/general-fields.component.spec.ts | 30 +++- .../taskdetails/taskdetails.component.spec.ts | 36 ++++- .../tasklist-toolbar.component.html | 16 +- .../tasklist-toolbar.component.spec.ts | 59 +++++++ .../tasklist-toolbar.component.ts | 1 + .../tasklist/tasklist.component.html | 17 ++- .../tasklist/tasklist.component.scss | 8 - .../tasklist/tasklist.component.spec.ts | 47 +++++- .../workplace/tasklist/tasklist.component.ts | 50 +++++- web/src/assets/_site.scss | 20 ++- web/src/test.ts | 4 +- 33 files changed, 554 insertions(+), 309 deletions(-) delete mode 100644 web/src/app/administration/workbasket/master/list/pagination/pagination.component.html delete mode 100644 web/src/app/administration/workbasket/master/list/pagination/pagination.component.ts create mode 100644 web/src/app/shared/pagination/pagination.component.html rename web/src/app/{administration/workbasket/master/list => shared}/pagination/pagination.component.scss (100%) rename web/src/app/{administration/workbasket/master/list => shared}/pagination/pagination.component.spec.ts (69%) create mode 100644 web/src/app/shared/pagination/pagination.component.ts create mode 100644 web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.spec.ts diff --git a/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/TaskControllerIntTest.java b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/TaskControllerIntTest.java index b0be01a85..7d4549638 100644 --- a/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/TaskControllerIntTest.java +++ b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/TaskControllerIntTest.java @@ -156,7 +156,7 @@ public class TaskControllerIntTest { headers.add("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"); HttpEntity request = new HttpEntity(headers); ResponseEntity> 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>() { }); @@ -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 request = new HttpEntity(headers); ResponseEntity> 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>() { @@ -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 request = new HttpEntity(headers); ResponseEntity> 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>() { }); 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>() { }); @@ -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 request = new HttpEntity(headers); ResponseEntity> 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>() { @@ -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") diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java index 91dd0bf14..e54e50971 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java @@ -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"; diff --git a/web/src/app/administration/administration.module.ts b/web/src/app/administration/administration.module.ts index 6d32f576d..6a90c3e59 100644 --- a/web/src/app/administration/administration.module.ts +++ b/web/src/app/administration/administration.module.ts @@ -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, diff --git a/web/src/app/administration/classification/details/classification-details.component.spec.ts b/web/src/app/administration/classification/details/classification-details.component.spec.ts index 5db40d514..37a445e6f 100644 --- a/web/src/app/administration/classification/details/classification-details.component.spec.ts +++ b/web/src/app/administration/classification/details/classification-details.component.spec.ts @@ -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([])); diff --git a/web/src/app/administration/workbasket/master/list/pagination/pagination.component.html b/web/src/app/administration/workbasket/master/list/pagination/pagination.component.html deleted file mode 100644 index 68e07e1b4..000000000 --- a/web/src/app/administration/workbasket/master/list/pagination/pagination.component.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - \ No newline at end of file diff --git a/web/src/app/administration/workbasket/master/list/pagination/pagination.component.ts b/web/src/app/administration/workbasket/master/list/pagination/pagination.component.ts deleted file mode 100644 index 7caa76bba..000000000 --- a/web/src/app/administration/workbasket/master/list/pagination/pagination.component.ts +++ /dev/null @@ -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(); - @Output() changePage = new EventEmitter(); - 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`; - } -} diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list.component.html b/web/src/app/administration/workbasket/master/list/workbasket-list.component.html index 9bf07beed..8d8589716 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list.component.html +++ b/web/src/app/administration/workbasket/master/list/workbasket-list.component.html @@ -1,41 +1,42 @@
- diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list.component.scss b/web/src/app/administration/workbasket/master/list/workbasket-list.component.scss index f3bee29b8..94559136d 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list.component.scss +++ b/web/src/app/administration/workbasket/master/list/workbasket-list.component.scss @@ -1,7 +1,3 @@ -.workbasket-list-full-height{ - height: calc(100vh - 55px); -} - .row.list-group { margin-left: 2px; } diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list.component.spec.ts b/web/src/app/administration/workbasket/master/list/workbasket-list.component.spec.ts index 9081a1fc9..5cdd417f1 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list.component.spec.ts +++ b/web/src/app/administration/workbasket/master/list/workbasket-list.component.spec.ts @@ -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(); @Output() changePage = new EventEmitter(); } -@Component({ - selector: 'taskana-filter', - template: '' -}) -class FilterComponent { - -} - const workbasketSummaryResource: WorkbasketSummaryResource = new WorkbasketSummaryResource({ 'workbaskets': new Array( 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, diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list.component.ts b/web/src/app/administration/workbasket/master/list/workbasket-list.component.ts index 74f0fc96a..91aafd3ce 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list.component.ts +++ b/web/src/app/administration/workbasket/master/list/workbasket-list.component.ts @@ -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 = []; 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(); } diff --git a/web/src/app/services/orientation/orientation.service.ts b/web/src/app/services/orientation/orientation.service.ts index ae0ff90bf..0a2c5dacf 100644 --- a/web/src/app/services/orientation/orientation.service.ts +++ b/web/src/app/services/orientation/orientation.service.ts @@ -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'; diff --git a/web/src/app/shared/pagination/pagination.component.html b/web/src/app/shared/pagination/pagination.component.html new file mode 100644 index 000000000..ea4bc1c7c --- /dev/null +++ b/web/src/app/shared/pagination/pagination.component.html @@ -0,0 +1,19 @@ + + + + diff --git a/web/src/app/administration/workbasket/master/list/pagination/pagination.component.scss b/web/src/app/shared/pagination/pagination.component.scss similarity index 100% rename from web/src/app/administration/workbasket/master/list/pagination/pagination.component.scss rename to web/src/app/shared/pagination/pagination.component.scss diff --git a/web/src/app/administration/workbasket/master/list/pagination/pagination.component.spec.ts b/web/src/app/shared/pagination/pagination.component.spec.ts similarity index 69% rename from web/src/app/administration/workbasket/master/list/pagination/pagination.component.spec.ts rename to web/src/app/shared/pagination/pagination.component.spec.ts index 55d8b55d0..325414b6e 100644 --- a/web/src/app/administration/workbasket/master/list/pagination/pagination.component.spec.ts +++ b/web/src/app/shared/pagination/pagination.component.spec.ts @@ -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'); }); diff --git a/web/src/app/shared/pagination/pagination.component.ts b/web/src/app/shared/pagination/pagination.component.ts new file mode 100644 index 000000000..2925e45de --- /dev/null +++ b/web/src/app/shared/pagination/pagination.component.ts @@ -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(); + @Output() + changePage = new EventEmitter(); + 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}`; + } +} diff --git a/web/src/app/shared/shared.module.ts b/web/src/app/shared/shared.module.ts index 4e5a9c3f7..57c7cca80 100644 --- a/web/src/app/shared/shared.module.ts +++ b/web/src/app/shared/shared.module.ts @@ -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({ diff --git a/web/src/app/shared/util/query-parameters.ts b/web/src/app/shared/util/query-parameters.ts index e58074f17..f3eb8d7b4 100644 --- a/web/src/app/shared/util/query-parameters.ts +++ b/web/src/app/shared/util/query-parameters.ts @@ -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; + } } diff --git a/web/src/app/workplace/models/task-resource.ts b/web/src/app/workplace/models/task-resource.ts index 917f27575..a99f1b3d3 100644 --- a/web/src/app/workplace/models/task-resource.ts +++ b/web/src/app/workplace/models/task-resource.ts @@ -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 } = { 'tasks': [] }, - public _links: Links = undefined) {} + public _links: Links = undefined, + public page: Page = new Page()) {} } diff --git a/web/src/app/workplace/services/task.service.ts b/web/src/app/workplace/services/task.service.ts index e1216225c..1cd47c938 100644 --- a/web/src/app/workplace/services/task.service.ts +++ b/web/src/app/workplace/services/task.service.ts @@ -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} */ + /** + * @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 { - const url = `${this.url}${this.getTaskQueryParameters(basketId, sortBy, sortDirection, name, owner, priority, state)}`; + state: string, + allPages: boolean = false): Observable { + 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(url); } @@ -93,26 +100,4 @@ export class TaskService { createTask(task: Task): Observable { return this.httpClient.post(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; - } } diff --git a/web/src/app/workplace/task/task.component.spec.ts b/web/src/app/workplace/task/task.component.spec.ts index 992af5761..b7d22da3c 100644 --- a/web/src/app/workplace/task/task.component.spec.ts +++ b/web/src/app/workplace/task/task.component.spec.ts @@ -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; 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(() => { diff --git a/web/src/app/workplace/taskdetails/attribute/attribute.component.spec.ts b/web/src/app/workplace/taskdetails/attribute/attribute.component.spec.ts index 7a5cd5186..51e2ffc49 100644 --- a/web/src/app/workplace/taskdetails/attribute/attribute.component.spec.ts +++ b/web/src/app/workplace/taskdetails/attribute/attribute.component.spec.ts @@ -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; beforeEach(async(() => { TestBed.configureTestingModule({ + imports: [FormsModule], declarations: [ TaskdetailsAttributeComponent ] }) .compileComponents(); diff --git a/web/src/app/workplace/taskdetails/custom/custom-fields.component.spec.ts b/web/src/app/workplace/taskdetails/custom/custom-fields.component.spec.ts index 0f517bf14..7ecee3ea0 100644 --- a/web/src/app/workplace/taskdetails/custom/custom-fields.component.spec.ts +++ b/web/src/app/workplace/taskdetails/custom/custom-fields.component.spec.ts @@ -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; beforeEach(async(() => { TestBed.configureTestingModule({ + imports: [FormsModule], declarations: [ TaskdetailsCustomFieldsComponent ] }) .compileComponents(); diff --git a/web/src/app/workplace/taskdetails/general/general-fields.component.spec.ts b/web/src/app/workplace/taskdetails/general/general-fields.component.spec.ts index 53b7b1ebd..6ffbd0375 100644 --- a/web/src/app/workplace/taskdetails/general/general-fields.component.spec.ts +++ b/web/src/app/workplace/taskdetails/general/general-fields.component.spec.ts @@ -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; + 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(); })); diff --git a/web/src/app/workplace/taskdetails/taskdetails.component.spec.ts b/web/src/app/workplace/taskdetails/taskdetails.component.spec.ts index b70f0a3a3..037f0b162 100644 --- a/web/src/app/workplace/taskdetails/taskdetails.component.spec.ts +++ b/web/src/app/workplace/taskdetails/taskdetails.component.spec.ts @@ -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; 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(); })); diff --git a/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.html b/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.html index ec1a9e0b6..3b82f2ae0 100644 --- a/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.html +++ b/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.html @@ -1,22 +1,16 @@
  • -
    +
    -
    -
    - - - - -
    +
    +
    diff --git a/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.spec.ts b/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.spec.ts new file mode 100644 index 000000000..5463360bb --- /dev/null +++ b/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.spec.ts @@ -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; + + 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(); + }); +}); diff --git a/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.ts b/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.ts index 69d8abc63..7c22c9590 100644 --- a/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.ts +++ b/web/src/app/workplace/tasklist/tasklist-toolbar/tasklist-toolbar.component.ts @@ -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) { diff --git a/web/src/app/workplace/tasklist/tasklist.component.html b/web/src/app/workplace/tasklist/tasklist.component.html index 3ed0d4202..5cb22176c 100644 --- a/web/src/app/workplace/tasklist/tasklist.component.html +++ b/web/src/app/workplace/tasklist/tasklist.component.html @@ -1,13 +1,13 @@ -
    - - -
    +
    +
    + + +
    + diff --git a/web/src/app/workplace/tasklist/tasklist.component.scss b/web/src/app/workplace/tasklist/tasklist.component.scss index c15425bfa..9c8abb5e3 100644 --- a/web/src/app/workplace/tasklist/tasklist.component.scss +++ b/web/src/app/workplace/tasklist/tasklist.component.scss @@ -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 diff --git a/web/src/app/workplace/tasklist/tasklist.component.spec.ts b/web/src/app/workplace/tasklist/tasklist.component.spec.ts index d73c8d2fe..607bf95ae 100644 --- a/web/src/app/workplace/tasklist/tasklist.component.spec.ts +++ b/web/src/app/workplace/tasklist/tasklist.component.spec.ts @@ -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; + 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(); })); diff --git a/web/src/app/workplace/tasklist/tasklist.component.ts b/web/src/app/workplace/tasklist/tasklist.component.ts index e3c477f8b..e44207805 100644 --- a/web/src/app/workplace/tasklist/tasklist.component.ts +++ b/web/src/app/workplace/tasklist/tasklist.component.ts @@ -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(); } } diff --git a/web/src/assets/_site.scss b/web/src/assets/_site.scss index b7ef5b67f..d3f39c545 100644 --- a/web/src/assets/_site.scss +++ b/web/src/assets/_site.scss @@ -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 { diff --git a/web/src/test.ts b/web/src/test.ts index 253869cbc..a7d433565 100644 --- a/web/src/test.ts +++ b/web/src/test.ts @@ -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);