TSK-647 - [UI] Add pagination or infite scrolling to distribution targets lists in order to avoid loading the whole workbasket list.
This commit is contained in:
parent
f31964d771
commit
aa250c926d
File diff suppressed because it is too large
Load Diff
|
@ -35,6 +35,7 @@
|
||||||
"material-design-icons": "3.0.1",
|
"material-design-icons": "3.0.1",
|
||||||
"ng2-charts": "1.6.0",
|
"ng2-charts": "1.6.0",
|
||||||
"ngx-bootstrap": "3.0.1",
|
"ngx-bootstrap": "3.0.1",
|
||||||
|
"ngx-infinite-scroll": "6.0.1",
|
||||||
"node-sass": "4.9.2",
|
"node-sass": "4.9.2",
|
||||||
"popper.js": "1.14.3",
|
"popper.js": "1.14.3",
|
||||||
"rxjs": "6.2.2",
|
"rxjs": "6.2.2",
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {AngularSvgIconModule} from 'angular-svg-icon';
|
||||||
import {AlertModule, TypeaheadModule} from 'ngx-bootstrap';
|
import {AlertModule, TypeaheadModule} from 'ngx-bootstrap';
|
||||||
import {SharedModule} from 'app/shared/shared.module';
|
import {SharedModule} from 'app/shared/shared.module';
|
||||||
import {AdministrationRoutingModule} from './administration-routing.module';
|
import {AdministrationRoutingModule} from './administration-routing.module';
|
||||||
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
*/
|
*/
|
||||||
|
@ -39,7 +40,8 @@ const MODULES = [
|
||||||
AlertModule,
|
AlertModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
AdministrationRoutingModule,
|
AdministrationRoutingModule,
|
||||||
TypeaheadModule
|
TypeaheadModule,
|
||||||
|
InfiniteScrollModule
|
||||||
];
|
];
|
||||||
|
|
||||||
const DECLARATIONS = [
|
const DECLARATIONS = [
|
||||||
|
|
|
@ -12,10 +12,13 @@
|
||||||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div #panelBody class="panel-body">
|
||||||
<taskana-dual-list id="dual-list-Left" header="Available distribution targets" [(distributionTargets)]="distributionTargetsLeft"
|
<div class="dual-list list-left col-xs-12 col-md-5-6 container">
|
||||||
[distributionTargetsSelected]="distributionTargetsSelected" (performDualListFilter)="performFilter($event)" [side]="side.LEFT"
|
<taskana-dual-list #dualListLeft id="dual-list-Left" header="Available distribution targets" [(distributionTargets)]="distributionTargetsLeft"
|
||||||
[requestInProgress]="requestInProgressLeft" class="dual-list list-left col-xs-12 col-md-5-6 container"></taskana-dual-list>
|
[distributionTargetsSelected]="distributionTargetsSelected" (performDualListFilter)="performFilter($event)" (scrolling)="onScroll($event)" [side]="side.LEFT"
|
||||||
|
[requestInProgress]="requestInProgressLeft" [loadingItems]="loadingItems" [(allSelected)]="selectAllLeft"></taskana-dual-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="hidden-xs hidden-sm col-md-1 list-arrows text-center button-margin-top">
|
<div class="hidden-xs hidden-sm col-md-1 list-arrows text-center button-margin-top">
|
||||||
<button (click)="moveDistributionTargets(side.LEFT)" [disabled]="requestInProgressLeft || requestInProgressRight"
|
<button (click)="moveDistributionTargets(side.LEFT)" [disabled]="requestInProgressLeft || requestInProgressRight"
|
||||||
class="btn btn-default move-right" data-toggle="tooltip" title="Move to selected distribution targets">
|
class="btn btn-default move-right" data-toggle="tooltip" title="Move to selected distribution targets">
|
||||||
|
@ -35,9 +38,11 @@
|
||||||
class="btn btn-default move-up" data-toggle="tooltip" title="Move to available distribution targets">
|
class="btn btn-default move-up" data-toggle="tooltip" title="Move to available distribution targets">
|
||||||
<span class="material-icons md-20 blue">expand_less</span>
|
<span class="material-icons md-20 blue">expand_less</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<taskana-dual-list id="dual-list-right" header="Selected distribution targets" [(distributionTargets)]="distributionTargetsRight"
|
<div class="dual-list list-right col-xs-12 col-md-5-6 container">
|
||||||
[distributionTargetsSelected]="distributionTargetsSelected" (performDualListFilter)="performFilter($event)"
|
<taskana-dual-list #dualListRight id="dual-list-right" header="Selected distribution targets" [(distributionTargets)]="distributionTargetsRight"
|
||||||
[requestInProgress]="requestInProgressRight" [side]="side.RIGHT" class="dual-list list-right col-xs-12 col-md-5-6 container"></taskana-dual-list>
|
[distributionTargetsSelected]="distributionTargetsSelected" (performDualListFilter)="performFilter($event)" [requestInProgress]="requestInProgressRight"
|
||||||
|
[side]="side.RIGHT" [(allSelected)]="selectAllRight"></taskana-dual-list>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { DualListComponent } from './dual-list/dual-list.component';
|
||||||
import { DistributionTargetsComponent, Side } from './distribution-targets.component';
|
import { DistributionTargetsComponent, Side } from './distribution-targets.component';
|
||||||
import { LinksWorkbasketSummary } from 'app/models/links-workbasket-summary';
|
import { LinksWorkbasketSummary } from 'app/models/links-workbasket-summary';
|
||||||
import { configureTests } from 'app/app.test.configuration';
|
import { configureTests } from 'app/app.test.configuration';
|
||||||
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
|
|
||||||
describe('DistributionTargetsComponent', () => {
|
describe('DistributionTargetsComponent', () => {
|
||||||
let component: DistributionTargetsComponent;
|
let component: DistributionTargetsComponent;
|
||||||
|
@ -33,7 +34,7 @@ describe('DistributionTargetsComponent', () => {
|
||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
const configure = (testBed: TestBed) => {
|
const configure = (testBed: TestBed) => {
|
||||||
testBed.configureTestingModule({
|
testBed.configureTestingModule({
|
||||||
imports: [AngularSvgIconModule, HttpClientModule],
|
imports: [AngularSvgIconModule, HttpClientModule, InfiniteScrollModule],
|
||||||
declarations: [DistributionTargetsComponent, DualListComponent],
|
declarations: [DistributionTargetsComponent, DualListComponent],
|
||||||
providers: [WorkbasketService, AlertService, SavingWorkbasketService, ErrorModalService, RequestInProgressService,
|
providers: [WorkbasketService, AlertService, SavingWorkbasketService, ErrorModalService, RequestInProgressService,
|
||||||
]
|
]
|
||||||
|
@ -72,10 +73,10 @@ describe('DistributionTargetsComponent', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should clone distribution target selected on init', () => {
|
it('should clone distribution target selected on init', () => {
|
||||||
expect(component.distributionTargetsClone).toBeDefined();
|
expect(component.distributionTargetsClone).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clone distribution target left and distribution target right lists on init', () => {
|
it('should clone distribution target left and distribution target right lists on init', () => {
|
||||||
expect(component.distributionTargetsLeft).toBeDefined();
|
expect(component.distributionTargetsLeft).toBeDefined();
|
||||||
expect(component.distributionTargetsRight).toBeDefined();
|
expect(component.distributionTargetsRight).toBeDefined();
|
||||||
|
@ -91,7 +92,8 @@ describe('DistributionTargetsComponent', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
expect(repeteadElemens).toBeFalsy();
|
expect(repeteadElemens).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter left list and keep selected elements as selected', () => {
|
it('should filter left list and keep selected elements as selected', () => {
|
||||||
component.performFilter({ filterBy: new FilterModel({
|
component.performFilter({ filterBy: new FilterModel({
|
||||||
name: 'someName', owner: 'someOwner', description: 'someDescription', key: 'someKey'}), side: Side.LEFT });
|
name: 'someName', owner: 'someOwner', description: 'someDescription', key: 'someKey'}), side: Side.LEFT });
|
||||||
|
@ -102,7 +104,8 @@ describe('DistributionTargetsComponent', () => {
|
||||||
expect(component.distributionTargetsLeft[0].workbasketId).toBe('id1');
|
expect(component.distributionTargetsLeft[0].workbasketId).toBe('id1');
|
||||||
expect(component.distributionTargetsRight.length).toBe(1);
|
expect(component.distributionTargetsRight.length).toBe(1);
|
||||||
expect(component.distributionTargetsRight[0].workbasketId).toBe('id2');
|
expect(component.distributionTargetsRight[0].workbasketId).toBe('id2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset distribution target and distribution target selected on reset', () => {
|
it('should reset distribution target and distribution target selected on reset', () => {
|
||||||
component.distributionTargetsLeft.push(
|
component.distributionTargetsLeft.push(
|
||||||
new WorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ 'href': 'someurl' })));
|
new WorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ 'href': 'someurl' })));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, Input, OnDestroy, SimpleChanges, OnChanges } from '@angular/core';
|
import { Component, Input, OnDestroy, SimpleChanges, OnChanges, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { Workbasket } from 'app/models/workbasket';
|
import { Workbasket } from 'app/models/workbasket';
|
||||||
|
@ -14,6 +14,10 @@ import { AlertService } from 'app/services/alert/alert.service';
|
||||||
import { SavingWorkbasketService, SavingInformation } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
import { SavingWorkbasketService, SavingInformation } from 'app/administration/services/saving-workbaskets/saving-workbaskets.service';
|
||||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.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';
|
||||||
|
|
||||||
export enum Side {
|
export enum Side {
|
||||||
LEFT,
|
LEFT,
|
||||||
|
@ -32,12 +36,13 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
||||||
action: string;
|
action: string;
|
||||||
@Input()
|
@Input()
|
||||||
active: string;
|
active: string;
|
||||||
badgeMessage = '';
|
badgeMessage = '';
|
||||||
|
|
||||||
distributionTargetsSubscription: Subscription;
|
distributionTargetsSubscription: Subscription;
|
||||||
workbasketSubscription: Subscription;
|
workbasketSubscription: Subscription;
|
||||||
workbasketFilterSubscription: Subscription;
|
workbasketFilterSubscription: Subscription;
|
||||||
savingDistributionTargetsSubscription: Subscription;
|
savingDistributionTargetsSubscription: Subscription;
|
||||||
|
orientationSubscription: Subscription;
|
||||||
|
|
||||||
distributionTargetsSelectedResource: WorkbasketDistributionTargetsResource;
|
distributionTargetsSelectedResource: WorkbasketDistributionTargetsResource;
|
||||||
distributionTargetsLeft: Array<WorkbasketSummary>;
|
distributionTargetsLeft: Array<WorkbasketSummary>;
|
||||||
|
@ -47,17 +52,26 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
||||||
distributionTargetsSelectedClone: Array<WorkbasketSummary>;
|
distributionTargetsSelectedClone: Array<WorkbasketSummary>;
|
||||||
|
|
||||||
requestInProgressLeft = false;
|
requestInProgressLeft = false;
|
||||||
requestInProgressRight = false;
|
requestInProgressRight = false;
|
||||||
|
loadingItems = false;
|
||||||
modalErrorMessage: string;
|
modalErrorMessage: string;
|
||||||
side = Side;
|
side = Side;
|
||||||
private initialized = false;
|
private initialized = false;
|
||||||
|
page: Page;
|
||||||
|
cards: number;
|
||||||
|
selectAllLeft = false;
|
||||||
|
selectAllRight = false;
|
||||||
|
|
||||||
|
@ViewChild('panelBody')
|
||||||
|
private panelBody: ElementRef;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private workbasketService: WorkbasketService,
|
private workbasketService: WorkbasketService,
|
||||||
private alertService: AlertService,
|
private alertService: AlertService,
|
||||||
private savingWorkbaskets: SavingWorkbasketService,
|
private savingWorkbaskets: SavingWorkbasketService,
|
||||||
private errorModalService: ErrorModalService,
|
private errorModalService: ErrorModalService,
|
||||||
private requestInProgressService: RequestInProgressService) { }
|
private requestInProgressService: RequestInProgressService,
|
||||||
|
private orientationService: OrientationService) { }
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (!this.initialized && changes.active && changes.active.currentValue === 'distributionTargets') {
|
if (!this.initialized && changes.active && changes.active.currentValue === 'distributionTargets') {
|
||||||
|
@ -67,52 +81,31 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
||||||
this.setBadge();
|
this.setBadge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onScroll(side: Side) {
|
||||||
private init() {
|
if (side === this.side.LEFT && this.page.totalPages > TaskanaQueryParameters.page) {
|
||||||
this.initialized = true;
|
this.loadingItems = true;
|
||||||
this.onRequest(undefined);
|
this.getNextPage(side);
|
||||||
if (!this.workbasket._links.distributionTargets) {
|
}
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
this.distributionTargetsSubscription = this.workbasketService.getWorkBasketsDistributionTargets(
|
|
||||||
this.workbasket._links.distributionTargets.href).subscribe(
|
|
||||||
(distributionTargetsSelectedResource: WorkbasketDistributionTargetsResource) => {
|
|
||||||
this.distributionTargetsSelectedResource = distributionTargetsSelectedResource;
|
|
||||||
this.distributionTargetsSelected = distributionTargetsSelectedResource._embedded ?
|
|
||||||
distributionTargetsSelectedResource._embedded.distributionTargets : [];
|
|
||||||
this.distributionTargetsSelectedClone = Object.assign([], this.distributionTargetsSelected);
|
|
||||||
this.workbasketSubscription = this.workbasketService.getWorkBasketsSummary(true,
|
|
||||||
undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined,
|
|
||||||
undefined, undefined, undefined, true).subscribe(
|
|
||||||
(distributionTargetsAvailable: WorkbasketSummaryResource) => {
|
|
||||||
this.distributionTargetsLeft = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
|
||||||
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
|
||||||
this.distributionTargetsClone = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
|
||||||
this.onRequest(undefined, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.savingDistributionTargetsSubscription = this.savingWorkbaskets.triggeredDistributionTargetsSaving()
|
|
||||||
.subscribe((savingInformation: SavingInformation) => {
|
|
||||||
if (this.action === ACTION.COPY) {
|
|
||||||
this.distributionTargetsSelectedResource._links.self.href = savingInformation.url;
|
|
||||||
this.onSave();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
moveDistributionTargets(side: number) {
|
moveDistributionTargets(side: number) {
|
||||||
if (side === Side.LEFT) {
|
if (side === Side.LEFT) {
|
||||||
|
const itemsLeft = this.distributionTargetsLeft.length;
|
||||||
|
const itemsRight = this.distributionTargetsRight.length;
|
||||||
const itemsSelected = this.getSelectedItems(this.distributionTargetsLeft, this.distributionTargetsRight)
|
const itemsSelected = this.getSelectedItems(this.distributionTargetsLeft, this.distributionTargetsRight)
|
||||||
this.distributionTargetsSelected = this.distributionTargetsSelected.concat(itemsSelected);
|
this.distributionTargetsSelected = this.distributionTargetsSelected.concat(itemsSelected);
|
||||||
this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected);
|
this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected);
|
||||||
|
if (((itemsLeft - itemsSelected.length) <= TaskanaQueryParameters.pageSize) && ((itemsLeft + itemsRight) < this.page.totalElements)) {
|
||||||
|
this.getNextPage(side);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const itemsSelected = this.getSelectedItems(this.distributionTargetsRight, this.distributionTargetsLeft);
|
const itemsSelected = this.getSelectedItems(this.distributionTargetsRight, this.distributionTargetsLeft);
|
||||||
this.distributionTargetsSelected = this.removeSeletedItems(this.distributionTargetsSelected, itemsSelected);
|
this.distributionTargetsSelected = this.removeSeletedItems(this.distributionTargetsSelected, itemsSelected);
|
||||||
this.distributionTargetsRight = this.removeSeletedItems(this.distributionTargetsRight, itemsSelected);
|
this.distributionTargetsRight = this.removeSeletedItems(this.distributionTargetsRight, itemsSelected);
|
||||||
this.distributionTargetsLeft = this.distributionTargetsLeft.concat(itemsSelected);
|
this.distributionTargetsLeft = this.distributionTargetsLeft.concat(itemsSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.uncheckSelectAll(side);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave() {
|
onSave() {
|
||||||
|
@ -156,8 +149,92 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
||||||
this.distributionTargetsLeft = (resultList._embedded ? resultList._embedded.workbaskets : []);
|
this.distributionTargetsLeft = (resultList._embedded ? resultList._embedded.workbaskets : []);
|
||||||
this.onRequest(dualListFilter.side, true);
|
this.onRequest(dualListFilter.side, true);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.distributionTargetsSubscription) { this.distributionTargetsSubscription.unsubscribe(); }
|
||||||
|
if (this.workbasketSubscription) { this.workbasketSubscription.unsubscribe(); }
|
||||||
|
if (this.workbasketFilterSubscription) { this.workbasketFilterSubscription.unsubscribe(); }
|
||||||
|
if (this.savingDistributionTargetsSubscription) { this.savingDistributionTargetsSubscription.unsubscribe(); }
|
||||||
|
if (this.orientationSubscription) { this.orientationSubscription.unsubscribe(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
this.onRequest(undefined);
|
||||||
|
if (!this.workbasket._links.distributionTargets) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.distributionTargetsSubscription = this.workbasketService.getWorkBasketsDistributionTargets(
|
||||||
|
this.workbasket._links.distributionTargets.href).subscribe(
|
||||||
|
(distributionTargetsSelectedResource: WorkbasketDistributionTargetsResource) => {
|
||||||
|
this.distributionTargetsSelectedResource = distributionTargetsSelectedResource;
|
||||||
|
this.distributionTargetsSelected = distributionTargetsSelectedResource._embedded ?
|
||||||
|
distributionTargetsSelectedResource._embedded.distributionTargets : [];
|
||||||
|
this.distributionTargetsSelectedClone = Object.assign([], this.distributionTargetsSelected);
|
||||||
|
TaskanaQueryParameters.page = 1;
|
||||||
|
this.calculateNumberItemsList();
|
||||||
|
this.getWorkbaskets();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.savingDistributionTargetsSubscription = this.savingWorkbaskets.triggeredDistributionTargetsSaving()
|
||||||
|
.subscribe((savingInformation: SavingInformation) => {
|
||||||
|
if (this.action === ACTION.COPY) {
|
||||||
|
this.distributionTargetsSelectedResource._links.self.href = savingInformation.url;
|
||||||
|
this.onSave();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.orientationSubscription = this.orientationService.getOrientation().subscribe((orientation: Orientation) => {
|
||||||
|
this.calculateNumberItemsList();
|
||||||
|
this.getWorkbaskets();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateNumberItemsList() {
|
||||||
|
if (this.panelBody) {
|
||||||
|
const cardHeight = 72;
|
||||||
|
this.cards = this.orientationService.calculateNumberItemsList(this.panelBody.nativeElement.offsetHeight, cardHeight, 100, true) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNextPage(side: Side) {
|
||||||
|
TaskanaQueryParameters.page = TaskanaQueryParameters.page + 1;
|
||||||
|
this.getWorkbaskets(side);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getWorkbaskets(side?: Side) {
|
||||||
|
if (!this.distributionTargetsLeft) {
|
||||||
|
this.distributionTargetsLeft = [];
|
||||||
|
}
|
||||||
|
if (!this.distributionTargetsRight) {
|
||||||
|
this.distributionTargetsRight = [];
|
||||||
|
}
|
||||||
|
if (this.distributionTargetsSelected && !this.initialized) {
|
||||||
|
this.initialized = true;
|
||||||
|
TaskanaQueryParameters.pageSize = this.cards + this.distributionTargetsSelected.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.workbasketSubscription = this.workbasketService.getWorkBasketsSummary(true,
|
||||||
|
undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined,
|
||||||
|
undefined, undefined, undefined, false).subscribe(
|
||||||
|
(distributionTargetsAvailable: WorkbasketSummaryResource) => {
|
||||||
|
if (TaskanaQueryParameters.page === 1) {
|
||||||
|
this.distributionTargetsLeft = [];
|
||||||
|
this.page = distributionTargetsAvailable.page;
|
||||||
|
}
|
||||||
|
if (side === this.side.LEFT) {
|
||||||
|
this.distributionTargetsLeft.push(...distributionTargetsAvailable._embedded.workbaskets);
|
||||||
|
} else if (side === this.side.RIGHT) {
|
||||||
|
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
||||||
|
} else {
|
||||||
|
this.distributionTargetsLeft.push(...distributionTargetsAvailable._embedded.workbaskets);
|
||||||
|
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
||||||
|
this.distributionTargetsClone = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
||||||
|
}
|
||||||
|
this.onRequest(undefined, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private setBadge() {
|
private setBadge() {
|
||||||
if (this.action === ACTION.COPY) {
|
if (this.action === ACTION.COPY) {
|
||||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||||
|
@ -178,13 +255,16 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRequest(side: Side = undefined, finished: boolean = false) {
|
private onRequest(side: Side = undefined, finished: boolean = false) {
|
||||||
|
if (this.loadingItems) {
|
||||||
|
this.loadingItems = false;
|
||||||
|
}
|
||||||
if (finished) {
|
if (finished) {
|
||||||
side === undefined ? (this.requestInProgressLeft = false, this.requestInProgressRight = false) :
|
side === undefined ? (this.requestInProgressLeft = false, this.requestInProgressRight = false) :
|
||||||
side === Side.LEFT ? this.requestInProgressLeft = false : this.requestInProgressRight = false;
|
side === Side.LEFT ? this.requestInProgressLeft = false : this.requestInProgressRight = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
side === undefined ? (this.requestInProgressLeft = true, this.requestInProgressRight = true) :
|
side === undefined ? (this.requestInProgressLeft = true, this.requestInProgressRight = true) :
|
||||||
side === Side.LEFT ? this.requestInProgressLeft = true : this.requestInProgressRight = true;
|
side === Side.LEFT ? this.requestInProgressLeft = true : this.requestInProgressRight = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSeletedIds(): Array<string> {
|
private getSeletedIds(): Array<string> {
|
||||||
|
@ -193,15 +273,10 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
||||||
distributionTargetsSelelected.push(item.workbasketId);
|
distributionTargetsSelelected.push(item.workbasketId);
|
||||||
})
|
})
|
||||||
return distributionTargetsSelelected;
|
return distributionTargetsSelelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
if (this.distributionTargetsSubscription) { this.distributionTargetsSubscription.unsubscribe(); }
|
|
||||||
if (this.workbasketSubscription) { this.workbasketSubscription.unsubscribe(); }
|
|
||||||
if (this.workbasketFilterSubscription) { this.workbasketFilterSubscription.unsubscribe(); }
|
|
||||||
if (this.savingDistributionTargetsSubscription) { this.savingDistributionTargetsSubscription.unsubscribe(); }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private uncheckSelectAll(side: number) {
|
||||||
|
if (side === Side.LEFT && this.selectAllLeft) { this.selectAllLeft = false; }
|
||||||
|
if (side === Side.RIGHT && this.selectAllRight) { this.selectAllRight = false; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
<div id="dual-list-Left" class="dual-list list-left col-xs-12 col-md-5-6 container">
|
<div id="dual-list-Left" class="dual-list list-left col-xs-12 col-md-5-6 container">
|
||||||
<div class="row">
|
<div class="row header">
|
||||||
<div class="col-xs-2">
|
<div class="col-xs-2">
|
||||||
<button (click)="toggleDtl = !toggleDtl; selectAll(toggleDtl);" class="btn btn-default no-style" title="Toggle select all">
|
<button (click)="allSelected = !allSelected; selectAll(allSelected);" class="btn btn-default btn-sm no-style" title="Select all">
|
||||||
<span class="material-icons md-20 blue ">{{toggleDtl ? 'check_box': 'check_box_outline_blank'}}</span>
|
<span class="material-icons md-20 blue ">{{allSelected ? 'check_box': 'check_box_outline_blank'}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-7">
|
<div class="col-xs-7">
|
||||||
<h5>{{header}}</h5>
|
<h5>{{header}}</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button class="btn btn-default" type="button" id="collapsedMenufilterWb" aria-expanded="false" (click)="toolbarState=!toolbarState"
|
<button class="btn btn-default btn-sm" type="button" id="collapsedMenufilterWb" aria-expanded="false" (click)="changeToolbarState(!toolbarState)"
|
||||||
data-toggle="tooltip" title="Filter">
|
data-toggle="tooltip" title="Filter">
|
||||||
<span class="material-icons md-20 blue ">{{!toolbarState? 'search' : 'expand_less'}}</span>
|
<span class="material-icons md-20 blue ">{{!toolbarState? 'search' : 'expand_less'}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div [@toggleDown]="toolbarState" class="row">
|
<div [@toggleDown]="toolbarState">
|
||||||
<taskana-filter class="col-xs-12" (performFilter)="performAvailableFilter($event)"></taskana-filter>
|
<taskana-filter (performFilter)="performAvailableFilter($event)"></taskana-filter>
|
||||||
</div>
|
</div>
|
||||||
<taskana-spinner [isRunning]="requestInProgress" positionClass="centered-spinner" class="floating"></taskana-spinner>
|
<taskana-spinner [isRunning]="requestInProgress" positionClass="centered-spinner" class="floating"></taskana-spinner>
|
||||||
<div>
|
<div infiniteScroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="50" (scrolled)="onScroll()" [scrollWindow]="false" class="infinite-scroll">
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item" *ngFor="let distributionTarget of distributionTargets | selectWorkbaskets: distributionTargetsSelected: side"
|
<li class="list-group-item" *ngFor="let distributionTarget of distributionTargets | selectWorkbaskets: distributionTargetsSelected: side"
|
||||||
[class.selected]="distributionTarget.selected" type="text" (click)="distributionTarget.selected = !distributionTarget.selected">
|
[class.selected]="distributionTarget.selected" type="text" (click)="distributionTarget.selected = !distributionTarget.selected">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<dl class="col-xs-1">
|
<dl class="col-xs-1">
|
||||||
|
@ -35,7 +35,10 @@
|
||||||
<dd>{{distributionTarget.owner}} </dd>
|
<dd>{{distributionTarget.owner}} </dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="list-group-item" *ngIf="loadingItems">
|
||||||
|
<taskana-spinner [isRunning]="loadingItems" positionClass="centered-spinner" class="floating"></taskana-spinner>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,20 +8,20 @@ $selected-item: #e3f3f5;
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
& .row {
|
& .row {
|
||||||
padding: 3px 0px 0px 3px;
|
padding: 0px 0px 0px 3px;
|
||||||
}
|
}
|
||||||
& .row:first {
|
& .row:first {
|
||||||
border-top: 1px solid #ddd;
|
border-top: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
& div.pull-right {
|
& div.pull-right {
|
||||||
margin-right: 18px;
|
margin-right: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& >.list-group {
|
& >.list-group {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
|
||||||
}
|
}
|
||||||
& > .list-group > li {
|
& > .list-group > li {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
|
@ -29,13 +29,28 @@ $selected-item: #e3f3f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
|
|
||||||
@media screen and (max-width: 991px){
|
@media screen and (max-width: 991px){
|
||||||
max-height: 33vh;
|
height: calc((100vh - 241px) / 2);
|
||||||
|
min-height: 120px;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
max-height: 75vh;
|
max-height: calc(100vh - 194px);
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infinite-scroll {
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: calc(100vh - 233px);
|
||||||
|
@media screen and (max-width: 991px){
|
||||||
|
height: calc((100vh - 315px) / 2);
|
||||||
|
min-height: 83px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin: 2px -15px 1px -15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-group {
|
.list-group {
|
||||||
|
@ -96,4 +111,3 @@ li.list-group-item:hover, {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,30 +13,40 @@ import { expandDown } from 'app/shared/animations/expand.animation';
|
||||||
export class DualListComponent implements OnInit {
|
export class DualListComponent implements OnInit {
|
||||||
|
|
||||||
@Input() distributionTargets: Array<WorkbasketSummary>;
|
@Input() distributionTargets: Array<WorkbasketSummary>;
|
||||||
@Output() distributionTargetsChange = new EventEmitter<Array<WorkbasketSummary>>();
|
|
||||||
@Input() distributionTargetsSelected: Array<WorkbasketSummary>;
|
@Input() distributionTargetsSelected: Array<WorkbasketSummary>;
|
||||||
@Output() performDualListFilter = new EventEmitter<{ filterBy: FilterModel, side: Side }>();
|
@Output() performDualListFilter = new EventEmitter<{ filterBy: FilterModel, side: Side }>();
|
||||||
@Input() requestInProgress = false;
|
@Input() requestInProgress = false;
|
||||||
|
@Input() loadingItems ? = false;
|
||||||
@Input() side: Side;
|
@Input() side: Side;
|
||||||
@Input() header: string;
|
@Input() header: string;
|
||||||
|
@Output() scrolling = new EventEmitter<Side>();
|
||||||
|
@Input() allSelected;
|
||||||
|
@Output() allSelectedChange = new EventEmitter<boolean>();
|
||||||
|
|
||||||
sideNumber = 0;
|
sideNumber = 0;
|
||||||
toggleDtl = false;
|
toolbarState = false;
|
||||||
toolbarState = false;
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.sideNumber = this.side === Side.LEFT ? 0 : 1;
|
this.sideNumber = this.side === Side.LEFT ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAll(selected: boolean) {
|
selectAll(selected: boolean) {
|
||||||
this.distributionTargets.forEach((element: any) => {
|
this.distributionTargets.forEach((element: any) => {
|
||||||
element.selected = selected;
|
element.selected = selected;
|
||||||
});
|
});
|
||||||
}
|
this.allSelectedChange.emit(this.allSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
onScroll() {
|
||||||
|
this.scrolling.emit(this.side);
|
||||||
|
}
|
||||||
|
|
||||||
performAvailableFilter(filterModel: FilterModel) {
|
performAvailableFilter(filterModel: FilterModel) {
|
||||||
this.performDualListFilter.emit({ filterBy: filterModel, side: this.side });
|
this.performDualListFilter.emit({ filterBy: filterModel, side: this.side });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeToolbarState(state: boolean) {
|
||||||
|
this.toolbarState = state;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { WorkbasketInformationComponent } from './information/workbasket-informa
|
||||||
import { AccessItemsComponent } from './access-items/access-items.component';
|
import { AccessItemsComponent } from './access-items/access-items.component';
|
||||||
import { DistributionTargetsComponent } from './distribution-targets/distribution-targets.component';
|
import { DistributionTargetsComponent } from './distribution-targets/distribution-targets.component';
|
||||||
import { DualListComponent } from './distribution-targets//dual-list/dual-list.component';
|
import { DualListComponent } from './distribution-targets//dual-list/dual-list.component';
|
||||||
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-dummy-detail',
|
selector: 'taskana-dummy-detail',
|
||||||
|
@ -55,7 +56,8 @@ describe('WorkbasketDetailsComponent', () => {
|
||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
const configure = (testBed: TestBed) => {
|
const configure = (testBed: TestBed) => {
|
||||||
testBed.configureTestingModule({
|
testBed.configureTestingModule({
|
||||||
imports: [RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, HttpClientModule, ReactiveFormsModule],
|
imports: [RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, HttpClientModule, ReactiveFormsModule,
|
||||||
|
InfiniteScrollModule],
|
||||||
declarations: [WorkbasketDetailsComponent, WorkbasketInformationComponent,
|
declarations: [WorkbasketDetailsComponent, WorkbasketInformationComponent,
|
||||||
AccessItemsComponent,
|
AccessItemsComponent,
|
||||||
DistributionTargetsComponent, DualListComponent, DummyDetailComponent],
|
DistributionTargetsComponent, DualListComponent, DummyDetailComponent],
|
||||||
|
|
|
@ -37,4 +37,4 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
<taskana-pagination [(page)]="workbasketsResource !== undefined ? workbasketsResource.page : workbasketsResource"
|
<taskana-pagination [(page)]="workbasketsResource !== undefined ? workbasketsResource.page : workbasketsResource"
|
||||||
[type]="type" (changePage)="changePage($event)"></taskana-pagination>
|
[type]="type" [numberOfItems]="workbaskets.length" (changePage)="changePage($event)"></taskana-pagination>
|
||||||
|
|
|
@ -27,6 +27,7 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
||||||
pageSelected = 1;
|
pageSelected = 1;
|
||||||
pageSize = 9;
|
pageSize = 9;
|
||||||
type = 'workbaskets';
|
type = 'workbaskets';
|
||||||
|
cards: number = this.pageSize;
|
||||||
|
|
||||||
sort: SortingModel = new SortingModel();
|
sort: SortingModel = new SortingModel();
|
||||||
filterBy: FilterModel = new FilterModel({name: '', owner: '', type: '', description: '', key: ''});
|
filterBy: FilterModel = new FilterModel({name: '', owner: '', type: '', description: '', key: ''});
|
||||||
|
@ -62,7 +63,7 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
this.orientationSubscription = this.orientationService.getOrientation().subscribe((orientation: Orientation) => {
|
this.orientationSubscription = this.orientationService.getOrientation().subscribe((orientation: Orientation) => {
|
||||||
this.refreshWorkbasketList();
|
this.refreshWorkbasketList();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
selectWorkbasket(id: string) {
|
selectWorkbasket(id: string) {
|
||||||
|
@ -86,16 +87,13 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshWorkbasketList() {
|
refreshWorkbasketList() {
|
||||||
const toolbarSize = this.toolbarElement.nativeElement.offsetHeight;
|
this.cards = this.orientationService.calculateNumberItemsList(
|
||||||
const cardHeight = 72;
|
window.innerHeight, 72, 170 + this.toolbarElement.nativeElement.offsetHeight, false);
|
||||||
const unusedHeight = 145;
|
|
||||||
const totalHeight = window.innerHeight;
|
|
||||||
const cards = Math.round((totalHeight - (unusedHeight + toolbarSize)) / cardHeight);
|
|
||||||
cards > 0 ? TaskanaQueryParameters.pageSize = cards : TaskanaQueryParameters.pageSize = 1;
|
|
||||||
this.performRequest();
|
this.performRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
private performRequest(): void {
|
private performRequest(): void {
|
||||||
|
TaskanaQueryParameters.pageSize = this.cards;
|
||||||
this.requestInProgress = true;
|
this.requestInProgress = true;
|
||||||
this.workbaskets = [];
|
this.workbaskets = [];
|
||||||
this.workbasketServiceSubscription = this.workbasketService.getWorkBasketsSummary(
|
this.workbasketServiceSubscription = this.workbasketService.getWorkBasketsSummary(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Orientation } from 'app/models/orientation';
|
import { Orientation } from 'app/models/orientation';
|
||||||
import { BehaviorSubject , Observable } from 'rxjs';
|
import { BehaviorSubject , Observable } from 'rxjs';
|
||||||
|
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrientationService {
|
export class OrientationService {
|
||||||
|
@ -33,6 +34,13 @@ export class OrientationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateNumberItemsList(heightContainer: number, cardHeight: number, unusedHeight: number, doubleList = false): number {
|
||||||
|
let cards = Math.round((heightContainer - unusedHeight) / cardHeight);
|
||||||
|
if (doubleList && window.innerWidth < 992) { cards = Math.floor(cards / 2); }
|
||||||
|
cards > 0 ? TaskanaQueryParameters.pageSize = cards : TaskanaQueryParameters.pageSize = 1;
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
|
||||||
private detectOrientation(): Orientation {
|
private detectOrientation(): Orientation {
|
||||||
if (window.innerHeight > window.innerWidth) {
|
if (window.innerHeight > window.innerWidth) {
|
||||||
return Orientation.portrait;
|
return Orientation.portrait;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div *ngIf="filterTypeIsWorkbasket(); else tasktype">
|
<div *ngIf="filterTypeIsWorkbasket(); else tasktype">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="dropdown col-xs-2">
|
<div class="dropdown col-xs-2">
|
||||||
<button class="btn btn-default" data-toggle="dropdown" type="button" id="dropdownMenufilter" data-toggle="dropdown"
|
<button class="btn btn-default btn-sm" data-toggle="dropdown" type="button" id="dropdownMenufilter" data-toggle="dropdown"
|
||||||
aria-haspopup="true" aria-expanded="true">
|
aria-haspopup="true" aria-expanded="true">
|
||||||
<taskana-icon-type [type]="filter.filterParams?.type"></taskana-icon-type>
|
<taskana-icon-type [type]="filter.filterParams?.type"></taskana-icon-type>
|
||||||
</button>
|
</button>
|
||||||
|
@ -16,30 +16,30 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-4">
|
<div class="col-xs-4">
|
||||||
<input type="text" [(ngModel)]="filter.filterParams.name" (keyup.enter)="search()" class="form-control" id="display-name-filter"
|
<input type="text" [(ngModel)]="filter.filterParams.name" (keyup.enter)="search()" class="form-control input-sm" id="display-name-filter"
|
||||||
placeholder="Filter name">
|
placeholder="Filter name">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-4">
|
<div class="col-xs-4">
|
||||||
<input type="text" [(ngModel)]="filter.filterParams.key" (keyup.enter)="search()" class="form-control" id="display-key-filter"
|
<input type="text" [(ngModel)]="filter.filterParams.key" (keyup.enter)="search()" class="form-control input-sm" id="display-key-filter"
|
||||||
placeholder="Filter key">
|
placeholder="Filter key">
|
||||||
</div>
|
</div>
|
||||||
<button (click)="clear(); search()" type="button" class="btn btn-default pull-right margin-right"
|
<button (click)="clear(); search()" type="button" class="btn btn-default btn-sm pull-right margin-right"
|
||||||
data-toggle="tooltip" title="Clear">
|
data-toggle="tooltip" title="Clear">
|
||||||
<span class="material-icons md-20 blue">clear</span>
|
<span class="material-icons md-20 blue">clear</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-8 col-xs-offset-2">
|
<div class="col-xs-8 col-xs-offset-2">
|
||||||
<input type="text" [(ngModel)]="filter.filterParams.description" (keyup.enter)="search()" class="form-control"
|
<input type="text" [(ngModel)]="filter.filterParams.description" (keyup.enter)="search()" class="form-control input-sm"
|
||||||
id="display-name-description" placeholder="Filter description">
|
id="display-name-description" placeholder="Filter description">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-8 col-xs-offset-2">
|
<div class="col-xs-8 col-xs-offset-2">
|
||||||
<input type="text" [(ngModel)]="filter.filterParams.owner" (keyup.enter)="search()" class="form-control" id="display-name-owner"
|
<input type="text" [(ngModel)]="filter.filterParams.owner" (keyup.enter)="search()" class="form-control input-sm" id="display-name-owner"
|
||||||
placeholder="Filter owner">
|
placeholder="Filter owner">
|
||||||
</div>
|
</div>
|
||||||
<button (click)="search()" type="button" class="btn btn-default pull-right margin-right" data-toggle="tooltip"
|
<button (click)="search()" type="button" class="btn btn-default btn-sm pull-right margin-right" data-toggle="tooltip"
|
||||||
title="Search">
|
title="Search">
|
||||||
<span class="material-icons md-20 blue ">search</span>
|
<span class="material-icons md-20 blue ">search</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
<ul id="wb-pagination" class="pagination vertical-center">
|
<ul id="wb-pagination" class="pagination vertical-center">
|
||||||
<li>
|
<li>
|
||||||
<a (click)="changeToPage(1)" aria-label="First">
|
<a (click)="changeToPage(1)" aria-label="First">
|
||||||
First</a>
|
First</a>
|
||||||
</li>
|
</li>
|
||||||
<li *ngFor="let pageNumber of page?.totalPages |
|
<li *ngFor="let pageNumber of page?.totalPages | spreadNumber: page?.number: maxPagesAvailable">
|
||||||
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" (click)="changeToPage(pageNumber+1)">{{pageNumber + 1}}</a>
|
<a *ngIf="pageNumber + 1 === page?.number" class="pagination">
|
||||||
<a *ngIf="pageNumber + 1 === page?.number" class="pagination">
|
<input [(ngModel)]="pageSelected" (keyup.enter)="changeToPage(pageSelected)" type="text" (blur)="changeToPage(pageSelected)">
|
||||||
<input [(ngModel)]="pageSelected" (keyup.enter)="changeToPage(pageSelected)" type="text" (blur)="changeToPage(pageSelected)">
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
<a (click)="changeToPage(page?.totalPages)" aria-label="Last">Last</a>
|
||||||
<a (click)="changeToPage(page?.totalPages)" aria-label="Last">Last</a>
|
</li>
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<span class="footer pull-right">
|
<span class="footer pull-right">
|
||||||
<i [innerHTML]="getPagesTextToShow()"></i>
|
<i [innerHTML]="getPagesTextToShow()"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -31,6 +31,6 @@ ul.pagination{
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer{
|
.footer{
|
||||||
margin: 5px 5px 0 0;
|
margin: 0 5px 0 0;
|
||||||
color: $blue;
|
color: $blue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,19 +97,22 @@ describe('PaginationComponent', () => {
|
||||||
it('should getPagesTextToShow return 7 of 13 with size < totalElements', () => {
|
it('should getPagesTextToShow return 7 of 13 with size < totalElements', () => {
|
||||||
component.page = new Page(7, 13, 3, 2);
|
component.page = new Page(7, 13, 3, 2);
|
||||||
component.type = 'workbaskets';
|
component.type = 'workbaskets';
|
||||||
expect(component.getPagesTextToShow()).toBe('7 of 13 workbaskets');
|
component.numberOfItems = 5;
|
||||||
|
expect(component.getPagesTextToShow()).toBe(component.numberOfItems.toString().concat(' of 13 workbaskets'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should getPagesTextToShow return 6 of 6 with size > totalElements', () => {
|
it('should getPagesTextToShow return 6 of 6 with size > totalElements', () => {
|
||||||
component.page = new Page(7, 6, 3, 2);
|
component.page = new Page(7, 6, 3, 2);
|
||||||
component.type = 'tasks';
|
component.type = 'tasks';
|
||||||
expect(component.getPagesTextToShow()).toBe('6 of 6 tasks');
|
component.numberOfItems = 6;
|
||||||
|
expect(component.getPagesTextToShow()).toBe(component.numberOfItems.toString().concat(' of 6 tasks'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should getPagesTextToShow return of with totalElements = 0', () => {
|
it('should getPagesTextToShow return of with totalElements = 0', () => {
|
||||||
component.page = new Page(7, 0, 0, 0);
|
component.page = new Page(7, 0, 0, 0);
|
||||||
component.type = 'workbaskets';
|
component.type = 'workbaskets';
|
||||||
expect(component.getPagesTextToShow()).toBe('0 of 0 workbaskets');
|
component.numberOfItems = 0;
|
||||||
|
expect(component.getPagesTextToShow()).toBe(component.numberOfItems.toString().concat(' of 0 workbaskets'));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { Page } from 'app/models/page';
|
||||||
templateUrl: './pagination.component.html',
|
templateUrl: './pagination.component.html',
|
||||||
styleUrls: ['./pagination.component.scss']
|
styleUrls: ['./pagination.component.scss']
|
||||||
})
|
})
|
||||||
export class PaginationComponent implements OnInit, OnChanges {
|
export class PaginationComponent implements OnChanges {
|
||||||
@Input()
|
@Input()
|
||||||
page: Page;
|
page: Page;
|
||||||
@Input()
|
@Input()
|
||||||
|
@ -23,6 +23,8 @@ export class PaginationComponent implements OnInit, OnChanges {
|
||||||
workbasketsResourceChange = new EventEmitter<Page>();
|
workbasketsResourceChange = new EventEmitter<Page>();
|
||||||
@Output()
|
@Output()
|
||||||
changePage = new EventEmitter<number>();
|
changePage = new EventEmitter<number>();
|
||||||
|
@Input()
|
||||||
|
numberOfItems: number;
|
||||||
previousPageSelected = 1;
|
previousPageSelected = 1;
|
||||||
pageSelected = 1;
|
pageSelected = 1;
|
||||||
maxPagesAvailable = 8;
|
maxPagesAvailable = 8;
|
||||||
|
@ -30,13 +32,11 @@ export class PaginationComponent implements OnInit, OnChanges {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (changes.page.currentValue !== undefined) {
|
if (changes.page && changes.page.currentValue !== undefined) {
|
||||||
this.pageSelected = changes.page.currentValue.number;
|
this.pageSelected = changes.page.currentValue.number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {}
|
|
||||||
|
|
||||||
changeToPage(page) {
|
changeToPage(page) {
|
||||||
if (page < 1) {
|
if (page < 1) {
|
||||||
page = this.pageSelected = 1;
|
page = this.pageSelected = 1;
|
||||||
|
@ -47,6 +47,8 @@ export class PaginationComponent implements OnInit, OnChanges {
|
||||||
if (this.previousPageSelected !== page) {
|
if (this.previousPageSelected !== page) {
|
||||||
this.changePage.emit(page);
|
this.changePage.emit(page);
|
||||||
this.previousPageSelected = page;
|
this.previousPageSelected = page;
|
||||||
|
this.page.number = page;
|
||||||
|
this.pageSelected = page;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,14 +56,7 @@ export class PaginationComponent implements OnInit, OnChanges {
|
||||||
if (!this.page) {
|
if (!this.page) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
let text = this.page.totalElements + '';
|
const text = this.numberOfItems + '';
|
||||||
if (
|
|
||||||
this.page &&
|
|
||||||
this.page.totalElements &&
|
|
||||||
this.page.totalElements >= this.page.size
|
|
||||||
) {
|
|
||||||
text = this.page.size + '';
|
|
||||||
}
|
|
||||||
return `${text} of ${this.page.totalElements} ${this.type}`;
|
return `${text} of ${this.page.totalElements} ${this.type}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
|
||||||
|
|
||||||
@Pipe({ name: 'selectWorkbaskets' })
|
@Pipe({ name: 'selectWorkbaskets' })
|
||||||
export class SelectWorkBasketPipe implements PipeTransform {
|
export class SelectWorkBasketPipe implements PipeTransform {
|
||||||
|
@ -18,6 +19,9 @@ export class SelectWorkBasketPipe implements PipeTransform {
|
||||||
originArray.splice(index, 1);
|
originArray.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (originArray.length > TaskanaQueryParameters.pageSize) {
|
||||||
|
originArray.slice(0, TaskanaQueryParameters.pageSize);
|
||||||
|
}
|
||||||
returnArray = originArray;
|
returnArray = originArray;
|
||||||
return returnArray;
|
return returnArray;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
@Pipe({ name: 'spreadNumber' })
|
@Pipe({ name: 'spreadNumber' })
|
||||||
export class SpreadNumberPipe implements PipeTransform {
|
export class SpreadNumberPipe implements PipeTransform {
|
||||||
transform(value: number, currentIndex: number, maxArrayElements: number, maxPageNumber: number): number[] {
|
transform(maxPageNumber: number, currentIndex: number, maxArrayElements: number): number[] {
|
||||||
const returnArray = new Array();
|
const returnArray = new Array();
|
||||||
if (maxPageNumber <= 5) {
|
if (maxPageNumber <= 5) {
|
||||||
for (let i = 0; i < maxPageNumber; i++) {
|
for (let i = 0; i < maxPageNumber; i++) {
|
||||||
|
|
|
@ -44,5 +44,5 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<taskana-pagination *ngIf="tasks && tasks.length > 0" [(page)]="tasksPageInformation" [type]="type" (changePage)="changePage($event)"></taskana-pagination>
|
<taskana-pagination [numberOfItems]="tasks.length" *ngIf="tasks && tasks.length > 0" [(page)]="tasksPageInformation" [type]="type" (changePage)="changePage($event)"></taskana-pagination>
|
||||||
<taskana-code></taskana-code>
|
<taskana-code></taskana-code>
|
||||||
|
|
Loading…
Reference in New Issue