TSK-201 Add Distribution targets saving feature.
This commit is contained in:
parent
5de0cd5e09
commit
5778ac75c0
|
@ -20,6 +20,7 @@ import { WorkbasketListComponent } from './workbasket/list/workbasket-list.compo
|
||||||
import { WorkbasketDetailsComponent } from './workbasket/details/workbasket-details.component';
|
import { WorkbasketDetailsComponent } from './workbasket/details/workbasket-details.component';
|
||||||
import { WorkbasketInformationComponent } from './workbasket/details/information/workbasket-information.component';
|
import { WorkbasketInformationComponent } from './workbasket/details/information/workbasket-information.component';
|
||||||
import { DistributionTargetsComponent } from './workbasket/details/distribution-targets/distribution-targets.component';
|
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 { AccessItemsComponent } from './workbasket/details/access-items/access-items.component';
|
||||||
import { NoAccessComponent } from './workbasket/noAccess/no-access.component';
|
import { NoAccessComponent } from './workbasket/noAccess/no-access.component';
|
||||||
import { SpinnerComponent } from './shared/spinner/spinner.component';
|
import { SpinnerComponent } from './shared/spinner/spinner.component';
|
||||||
|
@ -77,6 +78,7 @@ const DECLARATIONS = [
|
||||||
GeneralMessageModalComponent,
|
GeneralMessageModalComponent,
|
||||||
DistributionTargetsComponent,
|
DistributionTargetsComponent,
|
||||||
SortComponent,
|
SortComponent,
|
||||||
|
DualListComponent,
|
||||||
MapValuesPipe,
|
MapValuesPipe,
|
||||||
RemoveNoneTypePipe,
|
RemoveNoneTypePipe,
|
||||||
SelectWorkBasketPipe
|
SelectWorkBasketPipe
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { WorkbasketSummary } from './workbasket-summary';
|
||||||
|
import { Links } from './links';
|
||||||
|
|
||||||
|
export class WorkbasketDistributionTargetsResource {
|
||||||
|
constructor(public _embedded: {'distributionTargets': Array<WorkbasketSummary> } = {'distributionTargets': []}, public _links: Links = null) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import {Links} from './links';
|
||||||
|
|
||||||
|
export class WorkbasketSummary {
|
||||||
|
constructor(
|
||||||
|
public workbasketId: string,
|
||||||
|
public key: string,
|
||||||
|
public name: string,
|
||||||
|
public description: string,
|
||||||
|
public owner: string,
|
||||||
|
public modified: string,
|
||||||
|
public domain: string,
|
||||||
|
public type: string,
|
||||||
|
public orgLevel1: string,
|
||||||
|
public orgLevel2: string,
|
||||||
|
public orgLevel3: string,
|
||||||
|
public orgLevel4: string,
|
||||||
|
public links: Array<Links> = undefined){}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import { Pipe, PipeTransform } from '@angular/core';
|
||||||
export class SelectWorkBasketPipe implements PipeTransform {
|
export class SelectWorkBasketPipe implements PipeTransform {
|
||||||
transform(originArray: any, selectionArray: any, arg1: any): Object[] {
|
transform(originArray: any, selectionArray: any, arg1: any): Object[] {
|
||||||
let returnArray = [];
|
let returnArray = [];
|
||||||
if (!originArray) {
|
if (!originArray || !selectionArray) {
|
||||||
return returnArray;
|
return returnArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,5 +17,4 @@ export class SelectWorkBasketPipe implements PipeTransform {
|
||||||
returnArray = originArray;
|
returnArray = originArray;
|
||||||
return returnArray;
|
return returnArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -10,6 +10,7 @@ import { Subject } from 'rxjs/Subject';
|
||||||
import { map } from 'rxjs/operator/map';
|
import { map } from 'rxjs/operator/map';
|
||||||
import { WorkbasketSummaryResource } from '../model/workbasket-summary-resource';
|
import { WorkbasketSummaryResource } from '../model/workbasket-summary-resource';
|
||||||
import { WorkbasketAccessItemsResource } from '../model/workbasket-access-items-resource';
|
import { WorkbasketAccessItemsResource } from '../model/workbasket-access-items-resource';
|
||||||
|
import { WorkbasketDistributionTargetsResource } from '../model/workbasket-distribution-targets-resource';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkbasketService {
|
export class WorkbasketService {
|
||||||
|
@ -100,8 +101,13 @@ export class WorkbasketService {
|
||||||
this.httpOptions);
|
this.httpOptions);
|
||||||
}
|
}
|
||||||
// GET
|
// GET
|
||||||
getWorkBasketsDistributionTargets(url: string): Observable<WorkbasketSummaryResource> {
|
getWorkBasketsDistributionTargets(url: string): Observable<WorkbasketDistributionTargetsResource> {
|
||||||
return this.httpClient.get<WorkbasketSummaryResource>(url, this.httpOptions);
|
return this.httpClient.get<WorkbasketDistributionTargetsResource>(url, this.httpOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT
|
||||||
|
updateWorkBasketsDistributionTargets(url: string, distributionTargetsIds :Array<string>): Observable<WorkbasketDistributionTargetsResource> {
|
||||||
|
return this.httpClient.put<WorkbasketDistributionTargetsResource>(url, distributionTargetsIds, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,12 @@
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-group-search {
|
|
||||||
padding: 10px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-users-list {
|
.btn-users-list {
|
||||||
border: 0px solid transparent;
|
border: 0px solid transparent;
|
||||||
/* this was 1px earlier */
|
}
|
||||||
|
|
||||||
|
.list-group-search {
|
||||||
|
padding: 10px 15px;
|
||||||
|
margin-top: 12px;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
.word-break{word-break: break-all; }
|
.word-break{word-break: break-word; }
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, OnInit, Input, ViewChild, OnChanges, SimpleChanges } from '@angular/core';
|
import { Component, OnInit, Input, ViewChild, OnChanges, SimpleChanges, Output, EventEmitter, DoCheck } from '@angular/core';
|
||||||
declare var $: any;
|
declare var $: any;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -8,8 +8,8 @@ declare var $: any;
|
||||||
})
|
})
|
||||||
export class GeneralMessageModalComponent implements OnChanges {
|
export class GeneralMessageModalComponent implements OnChanges {
|
||||||
|
|
||||||
@Input()
|
@Input() message: string;
|
||||||
message: string = '';
|
@Output() messageChange = new EventEmitter<string>();
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
title: string = '';
|
title: string = '';
|
||||||
|
@ -29,7 +29,8 @@ export class GeneralMessageModalComponent implements OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeMessage() {
|
removeMessage() {
|
||||||
this.message = undefined;
|
this.message = '';
|
||||||
|
this.messageChange.emit(this.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, Input, ElementRef } from '@angular/core';
|
import { Component, Input, ElementRef, Output, EventEmitter } from '@angular/core';
|
||||||
import { ViewChild } from '@angular/core';
|
import { ViewChild } from '@angular/core';
|
||||||
declare var $: any;
|
declare var $: any;
|
||||||
|
|
||||||
|
@ -9,11 +9,13 @@ declare var $: any;
|
||||||
})
|
})
|
||||||
export class SpinnerComponent {
|
export class SpinnerComponent {
|
||||||
private currentTimeout: any;
|
private currentTimeout: any;
|
||||||
|
private requestTimeout: any;
|
||||||
|
private maxRequestTimeout: number = 10000;
|
||||||
|
|
||||||
isDelayedRunning: boolean = false;
|
isDelayedRunning: boolean = false;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
delay: number = 100;
|
delay: number = 200;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set isRunning(value: boolean) {
|
set isRunning(value: boolean) {
|
||||||
|
@ -37,6 +39,8 @@ export class SpinnerComponent {
|
||||||
@Input()
|
@Input()
|
||||||
positionClass: string = undefined;
|
positionClass: string = undefined;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
requestTimeoutExceeded = new EventEmitter<string>()
|
||||||
|
|
||||||
@ViewChild('spinnerModal')
|
@ViewChild('spinnerModal')
|
||||||
private modal;
|
private modal;
|
||||||
|
@ -46,6 +50,11 @@ export class SpinnerComponent {
|
||||||
if (this.isModal) { $(this.modal.nativeElement).modal('toggle'); }
|
if (this.isModal) { $(this.modal.nativeElement).modal('toggle'); }
|
||||||
this.isDelayedRunning = value;
|
this.isDelayedRunning = value;
|
||||||
this.cancelTimeout();
|
this.cancelTimeout();
|
||||||
|
this.requestTimeout = setTimeout(() => {
|
||||||
|
this.requestTimeoutExceeded.emit('There was an error with your request, please make sure you have internet connection');
|
||||||
|
this.cancelTimeout();
|
||||||
|
this.isRunning = false;
|
||||||
|
},this.maxRequestTimeout);
|
||||||
}, this.delay);
|
}, this.delay);
|
||||||
}
|
}
|
||||||
private closeModal() {
|
private closeModal() {
|
||||||
|
@ -57,7 +66,9 @@ export class SpinnerComponent {
|
||||||
|
|
||||||
private cancelTimeout(): void {
|
private cancelTimeout(): void {
|
||||||
clearTimeout(this.currentTimeout);
|
clearTimeout(this.currentTimeout);
|
||||||
|
clearTimeout(this.requestTimeout);
|
||||||
this.currentTimeout = undefined;
|
this.currentTimeout = undefined;
|
||||||
|
this.requestTimeout = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): any {
|
ngOnDestroy(): any {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<taskana-spinner [isRunning]="requestInProgress" [isModal]="modalSpinner" class="centered-horizontally floating"></taskana-spinner>
|
<taskana-spinner [isRunning]="requestInProgress" [isModal]="modalSpinner" class="centered-horizontally floating"></taskana-spinner>
|
||||||
<taskana-general-message-modal *ngIf="modalErrorMessage" [message]="modalErrorMessage" [title]="modalTitle" error="true"></taskana-general-message-modal>
|
<taskana-general-message-modal *ngIf="modalErrorMessage" [(message)]="modalErrorMessage" [title]="modalTitle" error="true"></taskana-general-message-modal>
|
||||||
<div *ngIf="workbasket && accessItems" id="wb-information" class="panel panel-default">
|
<div *ngIf="workbasket && accessItems" id="wb-information" class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
<td [ngClass]="{'has-changes': (accessItemsClone[index].permAppend !== accessItem.permAppend)}">
|
<td [ngClass]="{'has-changes': (accessItemsClone[index].permAppend !== accessItem.permAppend)}">
|
||||||
<input type="checkbox" name="accessItem.permAppend-{{index}}" [(ngModel)]="accessItem.permAppend">
|
<input type="checkbox" name="accessItem.permAppend-{{index}}" [(ngModel)]="accessItem.permAppend">
|
||||||
</td>
|
</td>
|
||||||
<td ngClass="{{(accessItemsClone[index].permTransfer != accessItem.permTransfer)? 'has-changes': 'pepe'}}">
|
<td [ngClass]="{'has-changes': (accessItemsClone[index].permTransfer !== accessItem.permTransfer)}">
|
||||||
<input type="checkbox" name="accessItem.permTransfer-{{index}}" [(ngModel)]="accessItem.permTransfer">
|
<input type="checkbox" name="accessItem.permTransfer-{{index}}" [(ngModel)]="accessItem.permTransfer">
|
||||||
</td>
|
</td>
|
||||||
<td [ngClass]="{'has-changes': (accessItemsClone[index].permDistribute !== accessItem.permDistribute)}">
|
<td [ngClass]="{'has-changes': (accessItemsClone[index].permDistribute !== accessItem.permDistribute)}">
|
||||||
|
|
|
@ -36,7 +36,7 @@ export class AccessItemsComponent implements OnInit {
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.accessItemsubscription = this.workbasketService.getWorkBasketAccessItems(this.workbasket._links.accessItems.href).subscribe( (accessItemsResource: WorkbasketAccessItemsResource) =>{
|
this.accessItemsubscription = this.workbasketService.getWorkBasketAccessItems(this.workbasket._links.accessItems.href).subscribe( (accessItemsResource: WorkbasketAccessItemsResource) =>{
|
||||||
this.accessItemsResource = accessItemsResource;
|
this.accessItemsResource = accessItemsResource;
|
||||||
this.accessItems = accessItemsResource._embedded.accessItems;
|
this.accessItems = accessItemsResource._embedded?accessItemsResource._embedded.accessItems: [];
|
||||||
this.accessItemsClone = this.cloneAccessItems(this.accessItems);
|
this.accessItemsClone = this.cloneAccessItems(this.accessItems);
|
||||||
this.accessItemsResetClone = this.cloneAccessItems(this.accessItems);
|
this.accessItemsResetClone = this.cloneAccessItems(this.accessItems);
|
||||||
})
|
})
|
||||||
|
@ -61,7 +61,7 @@ export class AccessItemsComponent implements OnInit {
|
||||||
|
|
||||||
onSave(): boolean {
|
onSave(): boolean {
|
||||||
this.requestInProgress = true;
|
this.requestInProgress = true;
|
||||||
this.workbasketService.updateWorkBasketAccessItem(this.accessItemsResource._links.self.href + '/', this.accessItems).subscribe(response =>{
|
this.workbasketService.updateWorkBasketAccessItem(this.accessItemsResource._links.self.href , this.accessItems).subscribe(response =>{
|
||||||
this.accessItemsClone = this.cloneAccessItems(this.accessItems);
|
this.accessItemsClone = this.cloneAccessItems(this.accessItems);
|
||||||
this.accessItemsResetClone = this.cloneAccessItems(this.accessItems);
|
this.accessItemsResetClone = this.cloneAccessItems(this.accessItems);
|
||||||
this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS, `Workbasket ${this.workbasket.name} Access items were saved successfully`));
|
this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS, `Workbasket ${this.workbasket.name} Access items were saved successfully`));
|
||||||
|
|
|
@ -1,101 +1,34 @@
|
||||||
<taskana-spinner [isRunning]="requestInProgress" [isModal]="modalSpinner" class="centered-horizontally floating"></taskana-spinner>
|
<taskana-spinner [isRunning]="requestInProgress" isModal="true" (requestTimeoutExceeded)="requestTimeoutExceeded($event)"
|
||||||
<taskana-general-message-modal *ngIf="modalErrorMessage" [message]="modalErrorMessage" [title]="modalTitle" error="true"></taskana-general-message-modal>
|
class="centered-horizontally floating"></taskana-spinner>
|
||||||
|
<taskana-general-message-modal *ngIf="modalErrorMessage" [(message)]="modalErrorMessage" [title]="modalTitle" error="true"></taskana-general-message-modal>
|
||||||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<button type="button" (click)="onSave()" class="btn btn-default btn-primary">Save</button>
|
<button type="button" (click)="onSave()" class="btn btn-default btn-primary">Save</button>
|
||||||
<button type="button" (click)="clear()" class="btn btn-default">Undo changes</button>
|
<button type="button" (click)="onClear()" class="btn btn-default">Undo changes</button>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="panel-header">{{workbasket.name}}</h4>
|
<h4 class="panel-header">{{workbasket.name}}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div id="dual-list-Left" class="dual-list list-left col-xs-12 col-md-5-6 container">
|
<taskana-dual-list id="dual-list-Left" [(distributionTargets)]="distributionTargetsLeft" [distributionTargetsSelected]="distributionTargetsSelected"
|
||||||
<div class="row">
|
(performDualListFilter)="performFilter($event)" [side]="side.LEFT" [requestInProgress]="requestInProgressLeft" class="dual-list list-left col-xs-12 col-md-5-6 container"></taskana-dual-list>
|
||||||
<div class="col-xs-2">
|
|
||||||
<button (click)="toggleDtl = !toggleDtl; selectAll(0, toggleDtl);" class="btn btn-default selector" title="select all">
|
|
||||||
<span aria-hidden="true" class="glyphicon blue {{toggleDtl? 'glyphicon-check': 'glyphicon-unchecked'}}"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-8">
|
|
||||||
<h5>Available distribution targets</h5>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-default pull-right collapsed" type="button" id="collapsedMenufilterWbDta" data-toggle="collapse" data-target="#wb-dta-filter-bar"
|
|
||||||
aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-filter blue"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<taskana-filter target="wb-dta-filter-bar" (performFilter)="performAvailableFilter($event)"></taskana-filter>
|
|
||||||
<ul class="list-group dual-list-group">
|
|
||||||
<taskana-spinner [isRunning]="requestInProgressLeft" [isModal]="modalSpinner" positionClass="centered-spinner" class="centered-horizontally floating"></taskana-spinner>
|
|
||||||
<li class="list-group-item" *ngFor="let distributionTarget of distributionTargetsLeft | selectWorkbaskets: distributionTargetsSelected: 0"
|
|
||||||
[class.selected]="distributionTarget.selected" type="text" (click)="distributionTarget.selected = !distributionTarget.selected">
|
|
||||||
<div class="row">
|
|
||||||
<dl class="col-xs-1">
|
|
||||||
<dt>
|
|
||||||
<taskana-icon-type class="vertical-align" [type]="distributionTarget.type"></taskana-icon-type>
|
|
||||||
</dt>
|
|
||||||
</dl>
|
|
||||||
<dl class="col-xs-10">
|
|
||||||
<dt>{{distributionTarget.name}} ({{distributionTarget.key}}) </dt>
|
|
||||||
<dd>{{distributionTarget.description}}</dd>
|
|
||||||
<dd>{{distributionTarget.owner}} </dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</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(0)" [disabled] = "requestInProgressLeft || requestInProgressRight" class="btn btn-default move-right">
|
<button (click)="moveDistributionTargets(side.LEFT)" [disabled]="requestInProgressLeft || requestInProgressRight" class="btn btn-default move-right">
|
||||||
<span class="glyphicon glyphicon-chevron-right blue"></span>
|
<span class="glyphicon glyphicon-chevron-right blue"></span>
|
||||||
</button>
|
</button>
|
||||||
<button (click)="moveDistributionTargets(1)" [disabled] = "requestInProgressLeft || requestInProgressRight" class="btn btn-default move-left">
|
<button (click)="moveDistributionTargets(side.RIGHT)" [disabled]="requestInProgressLeft || requestInProgressRight" class="btn btn-default move-left">
|
||||||
<span class="glyphicon glyphicon-chevron-left blue"></span>
|
<span class="glyphicon glyphicon-chevron-left blue"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden visible-xs visible-sm col-xs-12 list-arrows text-center">
|
<div class="hidden visible-xs visible-sm col-xs-12 list-arrows text-center">
|
||||||
<button (click)="moveDistributionTargets(0)" [disabled] = "requestInProgressLeft || requestInProgressRight" class="btn btn-default move-down">
|
<button (click)="moveDistributionTargets(side.LEFT)" [disabled]="requestInProgressLeft || requestInProgressRight" class="btn btn-default move-down">
|
||||||
<span class="glyphicon glyphicon-chevron-down blue"></span>
|
<span class="glyphicon glyphicon-chevron-down blue"></span>
|
||||||
</button>
|
</button>
|
||||||
<button (click)="moveDistributionTargets(1)" [disabled] = "requestInProgressLeft || requestInProgressRight" class="btn btn-default move-up">
|
<button (click)="moveDistributionTargets(side.RIGHT)" [disabled]="requestInProgressLeft || requestInProgressRight" class="btn btn-default move-up">
|
||||||
<span class="glyphicon glyphicon-chevron-up blue"></span>
|
<span class="glyphicon glyphicon-chevron-up blue"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="dual-list-right" class="dual-list list-right col-xs-12 col-md-5-6 container">
|
<taskana-dual-list id="dual-list-right" [(distributionTargets)]="distributionTargetsRight" [distributionTargetsSelected]="distributionTargetsSelected"
|
||||||
<div class="row">
|
(performDualListFilter)="performFilter($event)" [requestInProgress]="requestInProgressRight" [side]="side.RIGHT" class="dual-list list-right col-xs-12 col-md-5-6 container"></taskana-dual-list>
|
||||||
<div class="col-xs-2">
|
|
||||||
<button (click)="toggleDtr = !toggleDtr; selectAll(1, toggleDtr);" class="btn btn-default selector" title="select all">
|
|
||||||
<span aria-hidden="true" class="glyphicon blue {{toggleDtr? 'glyphicon-check': 'glyphicon-unchecked'}}"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-8">
|
|
||||||
<h5>Selected distribution targets</h5>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-default pull-right collapsed" type="button" id="collapsedMenufilterWbDta" data-toggle="collapse" data-target="#wb-dts-filter-bar"
|
|
||||||
aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-filter blue"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<taskana-filter target="wb-dts-filter-bar" (performFilter)="performSelectedFilter($event)"></taskana-filter>
|
|
||||||
<ul class="list-group dual-list-group">
|
|
||||||
<taskana-spinner [isRunning]="requestInProgressRight" [isModal]="modalSpinner" positionClass="centered-spinner" class="centered-horizontally floating"></taskana-spinner>
|
|
||||||
<ul class="list-group dual-list-group">
|
|
||||||
<li class="list-group-item" *ngFor="let distributionTarget of distributionTargetsRight | selectWorkbaskets: distributionTargetsSelected: 1"
|
|
||||||
[class.selected]="distributionTarget.select" type="text" (click)="distributionTarget.select = !distributionTarget.select">
|
|
||||||
<div class="row">
|
|
||||||
<dl class="col-xs-1">
|
|
||||||
<dt>
|
|
||||||
<taskana-icon-type class="vertical-align" [type]="distributionTarget.type"></taskana-icon-type>
|
|
||||||
</dt>
|
|
||||||
</dl>
|
|
||||||
<dl class="col-xs-10">
|
|
||||||
<dt>{{distributionTarget.name}} ({{distributionTarget.key}}) </dt>
|
|
||||||
<dd>{{distributionTarget.description}}</dd>
|
|
||||||
<dd>{{distributionTarget.owner}} </dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,13 +1,4 @@
|
||||||
|
|
||||||
.list-group {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-left li,
|
|
||||||
.list-right li {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-margin-top {
|
.button-margin-top {
|
||||||
margin-top: 100px
|
margin-top: 100px
|
||||||
}
|
}
|
||||||
|
@ -16,25 +7,6 @@
|
||||||
margin: 10px 0px;
|
margin: 10px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dual-list {
|
|
||||||
min-height: 20px;
|
|
||||||
padding: 5px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border: 1px solid #e3e3e3;
|
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
|
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
|
|
||||||
& .row {
|
|
||||||
padding: 10px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 991px){
|
|
||||||
max-height: calc(50vh - 150px);
|
|
||||||
}
|
|
||||||
max-height: calc(100vh - 250px);
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-y: scroll;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-md-5-6 {
|
.col-md-5-6 {
|
||||||
@media (min-width: 992px){
|
@media (min-width: 992px){
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import { Input } from '@angular/core';
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { HttpModule, JsonpModule } from '@angular/http';
|
import { HttpModule, JsonpModule } from '@angular/http';
|
||||||
|
|
||||||
import { DistributionTargetsComponent } from './distribution-targets.component';
|
import { DistributionTargetsComponent, Side } from './distribution-targets.component';
|
||||||
import { SpinnerComponent } from '../../../shared/spinner/spinner.component';
|
import { SpinnerComponent } from '../../../shared/spinner/spinner.component';
|
||||||
import { GeneralMessageModalComponent } from '../../../shared/general-message-modal/general-message-modal.component';
|
import { GeneralMessageModalComponent } from '../../../shared/general-message-modal/general-message-modal.component';
|
||||||
import { IconTypeComponent } from '../../../shared/type-icon/icon-type.component';
|
import { IconTypeComponent } from '../../../shared/type-icon/icon-type.component';
|
||||||
|
@ -16,6 +17,9 @@ import { WorkbasketService } from '../../../services/workbasket.service';
|
||||||
import { AlertService } from '../../../services/alert.service';
|
import { AlertService } from '../../../services/alert.service';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { Workbasket } from '../../../model/workbasket';
|
import { Workbasket } from '../../../model/workbasket';
|
||||||
|
import { WorkbasketDistributionTargetsResource } from '../../../model/workbasket-distribution-targets-resource';
|
||||||
|
import { FilterModel } from '../../../shared/filter/filter.component';
|
||||||
|
import { DualListComponent } from './dual-list/dual-list.component';
|
||||||
|
|
||||||
const workbasketSummaryResource: WorkbasketSummaryResource = new WorkbasketSummaryResource({
|
const workbasketSummaryResource: WorkbasketSummaryResource = new WorkbasketSummaryResource({
|
||||||
'workbaskets': new Array<WorkbasketSummary>(
|
'workbaskets': new Array<WorkbasketSummary>(
|
||||||
|
@ -26,9 +30,12 @@ const workbasketSummaryResource: WorkbasketSummaryResource = new WorkbasketSumma
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-filter',
|
selector: 'taskana-filter',
|
||||||
template: ''
|
template: ''
|
||||||
|
|
||||||
})
|
})
|
||||||
export class FilterComponent {
|
export class FilterComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
target: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +48,7 @@ describe('DistributionTargetsComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [AngularSvgIconModule, HttpClientModule, HttpModule, JsonpModule],
|
imports: [AngularSvgIconModule, HttpClientModule, HttpModule, JsonpModule],
|
||||||
declarations: [DistributionTargetsComponent, SpinnerComponent, GeneralMessageModalComponent, FilterComponent, SelectWorkBasketPipe, IconTypeComponent],
|
declarations: [DistributionTargetsComponent, SpinnerComponent, GeneralMessageModalComponent, FilterComponent, SelectWorkBasketPipe, IconTypeComponent, DualListComponent],
|
||||||
providers: [WorkbasketService, AlertService]
|
providers: [WorkbasketService, AlertService]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
@ -54,11 +61,16 @@ describe('DistributionTargetsComponent', () => {
|
||||||
workbasketService = TestBed.get(WorkbasketService);
|
workbasketService = TestBed.get(WorkbasketService);
|
||||||
spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => {
|
spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => {
|
||||||
return Observable.of(new WorkbasketSummaryResource(
|
return Observable.of(new WorkbasketSummaryResource(
|
||||||
{ 'workbaskets': new Array<WorkbasketSummary>(new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' }))) }, new Links({ 'href': 'someurl' })))
|
{
|
||||||
|
'workbaskets': new Array<WorkbasketSummary>(
|
||||||
|
new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' })),
|
||||||
|
new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' })),
|
||||||
|
new WorkbasketSummary('id3', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' })))
|
||||||
|
}, new Links({ 'href': 'someurl' })))
|
||||||
})
|
})
|
||||||
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => {
|
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => {
|
||||||
return Observable.of(new WorkbasketSummaryResource(
|
return Observable.of(new WorkbasketDistributionTargetsResource(
|
||||||
{ 'workbaskets': new Array<WorkbasketSummary>(new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' }))) }, new Links({ 'href': 'someurl' })))
|
{ 'distributionTargets': new Array<WorkbasketSummary>(new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' }))) }, new Links({ 'href': 'someurl' })))
|
||||||
})
|
})
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -67,4 +79,66 @@ describe('DistributionTargetsComponent', () => {
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should clone distribution target selected on init', () => {
|
||||||
|
expect(component.distributionTargetsClone).toBeDefined();
|
||||||
|
});
|
||||||
|
it('should clone distribution target left and distribution target right lists on init', () => {
|
||||||
|
expect(component.distributionTargetsLeft).toBeDefined();
|
||||||
|
expect(component.distributionTargetsRight).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have two list with differents elements onInit', () => {
|
||||||
|
let repeteadElemens = false;
|
||||||
|
expect(component.distributionTargetsLeft.length).toBe(2);
|
||||||
|
expect(component.distributionTargetsRight.length).toBe(1);
|
||||||
|
component.distributionTargetsLeft.forEach(leftElement => {
|
||||||
|
component.distributionTargetsRight.forEach(rightElement => {
|
||||||
|
if (leftElement.workbasketId === rightElement.workbasketId) repeteadElemens = true;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
expect(repeteadElemens).toBeFalsy();
|
||||||
|
});
|
||||||
|
it('should filter left list and keep selected elements as selected', () => {
|
||||||
|
component.performFilter({filterBy:new FilterModel(), side: Side.LEFT});
|
||||||
|
component.distributionTargetsLeft = new Array<WorkbasketSummary>(
|
||||||
|
new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' }))
|
||||||
|
)
|
||||||
|
expect(component.distributionTargetsLeft.length).toBe(1);
|
||||||
|
expect(component.distributionTargetsLeft[0].workbasketId).toBe('id1');
|
||||||
|
expect(component.distributionTargetsRight.length).toBe(1);
|
||||||
|
expect(component.distributionTargetsRight[0].workbasketId).toBe('id2');
|
||||||
|
});
|
||||||
|
it('should reset distribution target and distribution target selected on reset', () => {
|
||||||
|
component.distributionTargetsLeft.push(new WorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' })));
|
||||||
|
component.distributionTargetsRight.push(new WorkbasketSummary('id5', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' })));
|
||||||
|
|
||||||
|
expect(component.distributionTargetsLeft.length).toBe(3);
|
||||||
|
expect(component.distributionTargetsRight.length).toBe(2);
|
||||||
|
|
||||||
|
component.onClear();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.distributionTargetsLeft.length).toBe(2);
|
||||||
|
expect(component.distributionTargetsRight.length).toBe(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save distribution targets selected and update Clone objects.', () => {
|
||||||
|
expect(component.distributionTargetsSelected.length).toBe(1);
|
||||||
|
expect(component.distributionTargetsSelectedClone.length).toBe(1);
|
||||||
|
spyOn(workbasketService, 'updateWorkBasketsDistributionTargets').and.callFake(() => {
|
||||||
|
return Observable.of(new WorkbasketDistributionTargetsResource(
|
||||||
|
{
|
||||||
|
'distributionTargets': new Array<WorkbasketSummary>(
|
||||||
|
new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' })),
|
||||||
|
new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', new Links({ 'href': 'someurl' })))
|
||||||
|
}, new Links({ 'href': 'someurl' })))
|
||||||
|
})
|
||||||
|
component.onSave();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.distributionTargetsSelected.length).toBe(2);
|
||||||
|
expect(component.distributionTargetsSelectedClone.length).toBe(2);
|
||||||
|
expect(component.distributionTargetsLeft.length).toBe(1);
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Workbasket } from '../../../model/workbasket';
|
||||||
import { WorkbasketSummary } from '../../../model/workbasket-summary';
|
import { WorkbasketSummary } from '../../../model/workbasket-summary';
|
||||||
import { WorkbasketAccessItems } from '../../../model/workbasket-access-items';
|
import { WorkbasketAccessItems } from '../../../model/workbasket-access-items';
|
||||||
import { FilterModel } from '../../../shared/filter/filter.component'
|
import { FilterModel } from '../../../shared/filter/filter.component'
|
||||||
|
import { TREE_ACTIONS, KEYS, IActionMapping, ITreeOptions } from 'angular-tree-component';
|
||||||
|
|
||||||
import { WorkbasketService } from '../../../services/workbasket.service';
|
import { WorkbasketService } from '../../../services/workbasket.service';
|
||||||
import { AlertService, AlertModel, AlertType } from '../../../services/alert.service';
|
import { AlertService, AlertModel, AlertType } from '../../../services/alert.service';
|
||||||
|
@ -10,7 +11,12 @@ import { AlertService, AlertModel, AlertType } from '../../../services/alert.ser
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { element } from 'protractor';
|
import { element } from 'protractor';
|
||||||
import { WorkbasketSummaryResource } from '../../../model/workbasket-summary-resource';
|
import { WorkbasketSummaryResource } from '../../../model/workbasket-summary-resource';
|
||||||
|
import { WorkbasketDistributionTargetsResource } from '../../../model/workbasket-distribution-targets-resource';
|
||||||
|
|
||||||
|
export enum Side {
|
||||||
|
LEFT,
|
||||||
|
RIGHT
|
||||||
|
}
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-workbaskets-distribution-targets',
|
selector: 'taskana-workbaskets-distribution-targets',
|
||||||
templateUrl: './distribution-targets.component.html',
|
templateUrl: './distribution-targets.component.html',
|
||||||
|
@ -24,54 +30,45 @@ export class DistributionTargetsComponent implements OnInit {
|
||||||
distributionTargetsSubscription: Subscription;
|
distributionTargetsSubscription: Subscription;
|
||||||
workbasketSubscription: Subscription;
|
workbasketSubscription: Subscription;
|
||||||
workbasketFilterSubscription: Subscription;
|
workbasketFilterSubscription: Subscription;
|
||||||
distributionTargetsResource: WorkbasketSummaryResource;
|
|
||||||
distributionTargetsLeft: Array<WorkbasketSummary> = [];
|
distributionTargetsSelectedResource: WorkbasketDistributionTargetsResource;
|
||||||
distributionTargetsRight: Array<WorkbasketSummary> = [];
|
distributionTargetsLeft: Array<WorkbasketSummary>;
|
||||||
distributionTargetsSelected: Array<WorkbasketSummary> = [];
|
distributionTargetsRight: Array<WorkbasketSummary>;
|
||||||
|
distributionTargetsSelected: Array<WorkbasketSummary>;
|
||||||
|
distributionTargetsClone: Array<WorkbasketSummary>;
|
||||||
|
distributionTargetsSelectedClone: Array<WorkbasketSummary>;
|
||||||
|
|
||||||
|
|
||||||
filterBy: FilterModel = new FilterModel();
|
|
||||||
requestInProgress: boolean = false;
|
requestInProgress: boolean = false;
|
||||||
requestInProgressLeft: boolean = false;
|
requestInProgressLeft: boolean = false;
|
||||||
requestInProgressRight: boolean = false;
|
requestInProgressRight: boolean = false;
|
||||||
|
modalErrorMessage: string;
|
||||||
|
side = Side;
|
||||||
|
|
||||||
constructor(private workbasketService: WorkbasketService) { }
|
constructor(private workbasketService: WorkbasketService, private alertService: AlertService) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.requestInProgressLeft = true;
|
this.onRequest(undefined);
|
||||||
this.requestInProgressRight = true;
|
this.distributionTargetsSubscription = this.workbasketService.getWorkBasketsDistributionTargets(this.workbasket._links.distributionTargets.href).subscribe((distributionTargetsSelectedResource: WorkbasketDistributionTargetsResource) => {
|
||||||
this.distributionTargetsSubscription = this.workbasketService.getWorkBasketsDistributionTargets(this.workbasket._links.distributionTargets.href).subscribe((distributionTargetsSelectedResource: WorkbasketSummaryResource) => {
|
this.distributionTargetsSelectedResource = distributionTargetsSelectedResource;
|
||||||
this.distributionTargetsSelected = distributionTargetsSelectedResource._embedded ? distributionTargetsSelectedResource._embedded.workbaskets :[];
|
this.distributionTargetsSelected = distributionTargetsSelectedResource._embedded ? distributionTargetsSelectedResource._embedded.distributionTargets : [];
|
||||||
|
this.distributionTargetsSelectedClone = Object.assign([], this.distributionTargetsSelected);
|
||||||
this.workbasketSubscription = this.workbasketService.getWorkBasketsSummary().subscribe((distributionTargetsAvailable: WorkbasketSummaryResource) => {
|
this.workbasketSubscription = this.workbasketService.getWorkBasketsSummary().subscribe((distributionTargetsAvailable: WorkbasketSummaryResource) => {
|
||||||
this.distributionTargetsResource = distributionTargetsAvailable;
|
|
||||||
this.distributionTargetsLeft = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
this.distributionTargetsLeft = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
||||||
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
||||||
this.requestInProgressLeft = false;
|
this.distributionTargetsClone = Object.assign([], distributionTargetsAvailable._embedded.workbaskets);
|
||||||
this.requestInProgressRight = false;
|
this.onRequest(undefined, true);
|
||||||
});
|
});
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
selectAll(side: number, selected: boolean) {
|
|
||||||
if (side === 0) {
|
|
||||||
this.distributionTargetsLeft.forEach((element: any) => {
|
|
||||||
element.selected = selected;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (side === 1) {
|
|
||||||
this.distributionTargetsRight.forEach((element: any) => {
|
|
||||||
element.selected = selected;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
moveDistributionTargets(side: number) {
|
moveDistributionTargets(side: number) {
|
||||||
if (side === 0) {
|
if (side === Side.LEFT) {
|
||||||
let itemsSelected = this.getSelectedItems(this.distributionTargetsLeft, this.distributionTargetsRight)
|
let 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);
|
||||||
}
|
}
|
||||||
else if (side === 1) {
|
else {
|
||||||
let itemsSelected = this.getSelectedItems(this.distributionTargetsRight, this.distributionTargetsLeft);
|
let 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);
|
||||||
|
@ -79,29 +76,51 @@ export class DistributionTargetsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
performAvailableFilter(filterBy: FilterModel) {
|
onSave() {
|
||||||
this.filterBy = filterBy;
|
this.requestInProgress = true;
|
||||||
this.performFilter(0);
|
this.workbasketService.updateWorkBasketsDistributionTargets(this.distributionTargetsSelectedResource._links.self.href, this.getSeletedIds()).subscribe(response => {
|
||||||
|
this.requestInProgress = false;
|
||||||
|
this.distributionTargetsSelected = response._embedded ? response._embedded.distributionTargets : [];
|
||||||
|
this.distributionTargetsSelectedClone = Object.assign([], this.distributionTargetsSelected);
|
||||||
|
this.distributionTargetsClone = Object.assign([], this.distributionTargetsLeft);
|
||||||
|
this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS, `Workbasket ${this.workbasket.name} Access items were saved successfully`));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.modalErrorMessage = error.message;
|
||||||
|
this.requestInProgress = false;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
performSelectedFilter(filterBy: FilterModel) {
|
)
|
||||||
this.filterBy = filterBy;
|
return false;
|
||||||
this.performFilter(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private performFilter(listType: number) {
|
onClear() {
|
||||||
|
this.alertService.triggerAlert(new AlertModel(AlertType.INFO, 'Reset edited fields'))
|
||||||
|
this.distributionTargetsLeft = Object.assign([], this.distributionTargetsClone);
|
||||||
|
this.distributionTargetsRight = Object.assign([], this.distributionTargetsSelectedClone);
|
||||||
|
this.distributionTargetsSelected = Object.assign([], this.distributionTargetsSelectedClone);
|
||||||
|
}
|
||||||
|
|
||||||
listType ? this.distributionTargetsRight = undefined : this.distributionTargetsLeft = undefined;
|
requestTimeoutExceeded(message: string) {
|
||||||
listType ? this.requestInProgressRight = true : this.requestInProgressLeft = true;
|
this.modalErrorMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
performFilter(dualListFilter: any) {
|
||||||
|
|
||||||
|
dualListFilter.side === Side.RIGHT ? this.distributionTargetsRight = undefined : this.distributionTargetsLeft = undefined;
|
||||||
|
this.onRequest(dualListFilter.side, false);
|
||||||
this.workbasketFilterSubscription = this.workbasketService.getWorkBasketsSummary(true, undefined, undefined, undefined,
|
this.workbasketFilterSubscription = this.workbasketService.getWorkBasketsSummary(true, undefined, undefined, undefined,
|
||||||
this.filterBy.name, this.filterBy.description, undefined, this.filterBy.owner,
|
dualListFilter.filterBy.name, dualListFilter.filterBy.description, undefined, dualListFilter.filterBy.owner,
|
||||||
this.filterBy.type, undefined, this.filterBy.key).subscribe((resultList: WorkbasketSummaryResource) => {
|
dualListFilter.filterBy.type, undefined, dualListFilter.filterBy.key).subscribe(resultList => {
|
||||||
listType ? this.distributionTargetsRight = resultList._embedded.workbaskets : this.distributionTargetsLeft = resultList._embedded.workbaskets;
|
(dualListFilter.side === Side.RIGHT) ?
|
||||||
listType ? this.requestInProgressRight = false : this.requestInProgressLeft = false;
|
this.distributionTargetsRight = (resultList._embedded ? resultList._embedded.workbaskets : []) :
|
||||||
|
this.distributionTargetsLeft = (resultList._embedded ? resultList._embedded.workbaskets : []);
|
||||||
|
this.onRequest(dualListFilter.side, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private getSelectedItems(originList: any, destinationList: any): Array<any> {
|
private getSelectedItems(originList: any, destinationList: any): Array<any> {
|
||||||
return originList.filter((element: any) => { return (element.selected === true) });
|
return originList.filter((element: any) => { return (element.selected === true) });
|
||||||
}
|
}
|
||||||
|
@ -113,7 +132,24 @@ export class DistributionTargetsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return originList;
|
return originList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onRequest(side: Side = undefined, finished: boolean = false) {
|
||||||
|
if (finished) {
|
||||||
|
side === undefined ? (this.requestInProgressLeft = false, this.requestInProgressRight = false) :
|
||||||
|
side === Side.LEFT ? this.requestInProgressLeft = false : this.requestInProgressRight = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
side === undefined ? (this.requestInProgressLeft = true, this.requestInProgressRight = true) :
|
||||||
|
side === Side.LEFT ? this.requestInProgressLeft = true : this.requestInProgressRight = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSeletedIds(): Array<string> {
|
||||||
|
let distributionTargetsSelelected: Array<string> = [];
|
||||||
|
this.distributionTargetsSelected.forEach(element => {
|
||||||
|
distributionTargetsSelelected.push(element.workbasketId);
|
||||||
|
})
|
||||||
|
return distributionTargetsSelelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ngOnDestroy(): void {
|
private ngOnDestroy(): void {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<div id="dual-list-Left" class="dual-list list-left col-xs-12 col-md-5-6 container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-2">
|
||||||
|
<button (click)="toggleDtl = !toggleDtl; selectAll(toggleDtl);" class="btn btn-default no-style" title="select all">
|
||||||
|
<span aria-hidden="true" class="glyphicon blue {{toggleDtl? 'glyphicon-check': 'glyphicon-unchecked'}}"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-7">
|
||||||
|
<h5>Available distribution targets</h5>
|
||||||
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
<button class="btn btn-default collapsed" type="button" id="collapsedMenufilterWbDta" data-toggle="collapse"
|
||||||
|
[attr.data-target]="'#wb-dta-filter-bar-' + sideNumber"
|
||||||
|
aria-expanded="false">
|
||||||
|
<span class="glyphicon glyphicon-filter blue"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<taskana-filter target="wb-dta-filter-bar-{{sideNumber}}" (performFilter)="performAvailableFilter($event)"></taskana-filter>
|
||||||
|
<taskana-spinner [isRunning]="requestInProgress" positionClass="centered-spinner" class="centered-horizontally floating"></taskana-spinner>
|
||||||
|
<ul class="list-group ">
|
||||||
|
<li class="list-group-item" *ngFor="let distributionTarget of distributionTargets | selectWorkbaskets: distributionTargetsSelected: side"
|
||||||
|
[class.selected]="distributionTarget.selected" type="text" (click)="distributionTarget.selected = !distributionTarget.selected">
|
||||||
|
<div class="row">
|
||||||
|
<dl class="col-xs-1">
|
||||||
|
<dt>
|
||||||
|
<taskana-icon-type class="vertical-align" [type]="distributionTarget.type"></taskana-icon-type>
|
||||||
|
</dt>
|
||||||
|
</dl>
|
||||||
|
<dl class="col-xs-10">
|
||||||
|
<dt>{{distributionTarget.name}} ({{distributionTarget.key}}) </dt>
|
||||||
|
<dd>{{distributionTarget.description}}</dd>
|
||||||
|
<dd>{{distributionTarget.owner}} </dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
|
@ -0,0 +1,60 @@
|
||||||
|
.dual-list {
|
||||||
|
min-height: 300px;
|
||||||
|
padding: 0px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
|
& .row {
|
||||||
|
padding: 3px 0px 0px 3px;
|
||||||
|
}
|
||||||
|
& .row:first {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
& div.pull-right {
|
||||||
|
margin-right: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& >.list-group {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-top: 0px;
|
||||||
|
|
||||||
|
}
|
||||||
|
& > .list-group > li {
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
@media screen and (max-width: 991px){
|
||||||
|
max-height: 38vh;
|
||||||
|
}
|
||||||
|
max-height: 78vh;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ul>li {
|
||||||
|
&:first-child.list-group-item.selected {
|
||||||
|
border-color: #ddd;
|
||||||
|
}
|
||||||
|
&.list-group-item.selected {
|
||||||
|
background-color: #e9f2fc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-left li {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.no-style{
|
||||||
|
background: none;
|
||||||
|
border:none;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
import { WorkbasketSummary } from '../../../../model/workbasket-summary';
|
||||||
|
import { FilterModel } from '../../../../shared/filter/filter.component';
|
||||||
|
import { filter } from 'rxjs/operators';
|
||||||
|
import { Side } from '../distribution-targets.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'taskana-dual-list',
|
||||||
|
templateUrl: './dual-list.component.html',
|
||||||
|
styleUrls: ['./dual-list.component.scss']
|
||||||
|
})
|
||||||
|
export class DualListComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.sideNumber = this.side === Side.LEFT ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() distributionTargets: Array<WorkbasketSummary>;
|
||||||
|
@Output() distributionTargetsChange = new EventEmitter<Array<WorkbasketSummary>>();
|
||||||
|
@Input() distributionTargetsSelected: Array<WorkbasketSummary>;
|
||||||
|
@Output() performDualListFilter = new EventEmitter<{ filterBy: FilterModel, side: Side }>();
|
||||||
|
@Input() requestInProgress: boolean = false;
|
||||||
|
|
||||||
|
@Input() side: Side;
|
||||||
|
sideNumber: number = 0;
|
||||||
|
|
||||||
|
selectAll(selected: boolean) {
|
||||||
|
this.distributionTargets.forEach((element: any) => {
|
||||||
|
element.selected = selected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
performAvailableFilter(filterModel: FilterModel) {
|
||||||
|
this.performDualListFilter.emit({ filterBy: filterModel, side: this.side });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<taskana-spinner [isRunning]="requestInProgress" [isModal]="modalSpinner" class="centered-horizontally floating"></taskana-spinner>
|
<taskana-spinner [isRunning]="requestInProgress" [isModal]="modalSpinner" class="centered-horizontally floating"></taskana-spinner>
|
||||||
<taskana-general-message-modal *ngIf="modalErrorMessage" [message]="modalErrorMessage" [title]="modalTitle" error="true"></taskana-general-message-modal>
|
<taskana-general-message-modal *ngIf="modalErrorMessage" [(message)]="modalErrorMessage" [title]="modalTitle" error="true"></taskana-general-message-modal>
|
||||||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="container-scrollable" >
|
<div class="container-scrollable" >
|
||||||
<taskana-spinner [isRunning]="requestInProgress" class = "centered-horizontally"></taskana-spinner>
|
<taskana-spinner [isRunning]="requestInProgress" class = "centered-horizontally"></taskana-spinner>
|
||||||
<app-no-access *ngIf="!requestInProgress && (!hasPermission || !workbasket && selectedId)" ></app-no-access>
|
<app-no-access *ngIf="!requestInProgress && (!hasPermission || !workbasket && selectedId)" ></app-no-access>
|
||||||
<div id ="workbasket-details" class="workbasket-details" *ngIf="workbasket && !requestInProgress">
|
<div id ="workbasket-details" *ngIf="workbasket && !requestInProgress">
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li *ngIf="showDetail" class="visible-xs visible-sm hidden">
|
<li *ngIf="showDetail" class="visible-xs visible-sm hidden">
|
||||||
<a (click) = "backClicked()"><span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>Back</a>
|
<a (click) = "backClicked()"><span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>Back</a>
|
||||||
|
@ -9,14 +9,14 @@
|
||||||
<li role="presentation" class="active">
|
<li role="presentation" class="active">
|
||||||
<a href="#work-baskets" aria-controls="work baskets" role="tab" data-toggle="tab" aria-expanded="true">Information</a>
|
<a href="#work-baskets" aria-controls="work baskets" role="tab" data-toggle="tab" aria-expanded="true">Information</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation" class="inactive">
|
<li role="presentation" class="">
|
||||||
<a href="#access-items" aria-controls="Acccess" role="tab" data-toggle="tab" aria-expanded="true">Access</a>
|
<a href="#access-items" aria-controls="Acccess" role="tab" data-toggle="tab" aria-expanded="true">Access</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation" class="inactive">
|
<li role="presentation" class="">
|
||||||
<a href="#distribution-targets" aria-controls="distribution targets" role="tab" data-toggle="tab" aria-expanded="true">Distribution targets</a>
|
<a href="#distribution-targets" aria-controls="distribution targets" role="tab" data-toggle="tab" aria-expanded="true">Distribution targets</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content detail-tab-content">
|
<div class="tab-content">
|
||||||
<div role="tabpanel" class="tab-pane active" id="work-baskets">
|
<div role="tabpanel" class="tab-pane active" id="work-baskets">
|
||||||
<workbasket-information [workbasket]="workbasket"></workbasket-information>
|
<workbasket-information [workbasket]="workbasket"></workbasket-information>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
.nav.nav-tabs {
|
.nav.nav-tabs {
|
||||||
& > li {
|
& > li {
|
||||||
& > a {
|
& > a {
|
||||||
min-height: 56px;
|
min-height: 52px;
|
||||||
padding-top: 17px;
|
padding-top: 15px;
|
||||||
|
border-radius: 0px;
|
||||||
&.has-changes{
|
&.has-changes{
|
||||||
border-bottom: solid #f0ad4e;
|
border-bottom: solid #f0ad4e;
|
||||||
}
|
}
|
||||||
|
@ -12,11 +13,12 @@
|
||||||
border-left: none;
|
border-left: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
& > li.active > a {
|
||||||
|
border-top: 3px solid #479ea9;
|
||||||
|
padding-top: 13px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
& > p{
|
& > p{
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.workbasket-details{
|
|
||||||
margin-top:1px;
|
|
||||||
}
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { async, ComponentFixture, TestBed, } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed, } from '@angular/core/testing';
|
||||||
import { WorkbasketDetailsComponent } from './workbasket-details.component';
|
import { WorkbasketDetailsComponent } from './workbasket-details.component';
|
||||||
import { NoAccessComponent } from '../noAccess/no-access.component';
|
import { NoAccessComponent } from '../noAccess/no-access.component';
|
||||||
import { WorkbasketInformationComponent } from './information/workbasket-information.component';
|
import { WorkbasketInformationComponent } from './information/workbasket-information.component';
|
||||||
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 { Workbasket } from 'app/model/workbasket';
|
import { Workbasket } from 'app/model/workbasket';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { SpinnerComponent } from '../../shared/spinner/spinner.component';
|
import { SpinnerComponent } from '../../shared/spinner/spinner.component';
|
||||||
|
@ -37,6 +38,8 @@ import { WorkbasketAccessItemsResource } from '../../model/workbasket-access-ite
|
||||||
})
|
})
|
||||||
export class FilterComponent {
|
export class FilterComponent {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
target: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +55,7 @@ describe('WorkbasketDetailsComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [RouterTestingModule, FormsModule, AngularSvgIconModule, HttpClientModule, HttpModule],
|
imports: [RouterTestingModule, FormsModule, AngularSvgIconModule, HttpClientModule, HttpModule],
|
||||||
declarations: [WorkbasketDetailsComponent, NoAccessComponent, WorkbasketInformationComponent, SpinnerComponent, IconTypeComponent, MapValuesPipe, RemoveNoneTypePipe, AlertComponent, GeneralMessageModalComponent, AccessItemsComponent, DistributionTargetsComponent, FilterComponent, SelectWorkBasketPipe],
|
declarations: [WorkbasketDetailsComponent, NoAccessComponent, WorkbasketInformationComponent, SpinnerComponent, IconTypeComponent, MapValuesPipe, RemoveNoneTypePipe, AlertComponent, GeneralMessageModalComponent, AccessItemsComponent, DistributionTargetsComponent, FilterComponent, DualListComponent, SelectWorkBasketPipe],
|
||||||
providers: [WorkbasketService, MasterAndDetailService, PermissionService, AlertService]
|
providers: [WorkbasketService, MasterAndDetailService, PermissionService, AlertService]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
@ -73,8 +76,8 @@ describe('WorkbasketDetailsComponent', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
spyOn(workbasketService, 'getWorkBasket').and.callFake(() => { return Observable.of(workbasket) })
|
spyOn(workbasketService, 'getWorkBasket').and.callFake(() => { return Observable.of(workbasket) })
|
||||||
spyOn(workbasketService, 'getWorkBasketAccessItems').and.callFake(() => { return Observable.of(new WorkbasketAccessItemsResource( {'accessItems': new Array<WorkbasketAccessItems>()}, new Links({'href': 'url'})) )})
|
spyOn(workbasketService, 'getWorkBasketAccessItems').and.callFake(() => { return Observable.of(new WorkbasketAccessItemsResource({ 'accessItems': new Array<WorkbasketAccessItems>() }, new Links({ 'href': 'url' }))) })
|
||||||
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => { return Observable.of(new WorkbasketSummaryResource( {'workbaskets': new Array<WorkbasketSummary>()}, new Links({'href': 'url'})) ) })
|
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => { return Observable.of(new WorkbasketSummaryResource({ 'workbaskets': new Array<WorkbasketSummary>() }, new Links({ 'href': 'url' }))) })
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,5 +17,6 @@ a > label{
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-align{
|
.tab-align{
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,14 +146,6 @@ li > div.row > dl {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-tab-content {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-xs-9.mod-col-9 {
|
|
||||||
width: 74%;
|
|
||||||
padding-right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-select {
|
.user-select {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
|
@ -225,5 +217,27 @@ li > div.row > dl {
|
||||||
}
|
}
|
||||||
|
|
||||||
.centered-spinner {
|
.centered-spinner {
|
||||||
margin-top: 100px;
|
margin-top: 30px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item {
|
||||||
|
padding: 5px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-list > taskana-filter >.list-group-search {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
workbasket-information, taskana-workbasket-access-items, taskana-workbaskets-distribution-targets {
|
||||||
|
&> .panel{
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
&> .panel-body {
|
||||||
|
height: 84vh;
|
||||||
|
max-height: 84vh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue