TSK-199 Create distribution targets view

This commit is contained in:
Martin Rojas Miguel Angel 2018-03-08 16:36:55 +01:00 committed by Holger Hagen
parent 5e648b7eb7
commit 2bd7acd656
22 changed files with 454 additions and 292 deletions

View File

@ -21,9 +21,9 @@ import { CategorieslistComponent } from './categorieslist/categorieslist.compone
import { CategoriestreeComponent } from './categoriestree/categoriestree.component'; import { CategoriestreeComponent } from './categoriestree/categoriestree.component';
import { CategoryeditorComponent } from './categoryeditor/categoryeditor.component'; import { CategoryeditorComponent } from './categoryeditor/categoryeditor.component';
import { CategoriesadministrationComponent } from './categoriesadministration/categoriesadministration.component'; import { CategoriesadministrationComponent } from './categoriesadministration/categoriesadministration.component';
import { WorkbasketDistributiontargetsComponent } from './workbasket-distributiontargets/workbasket-distributiontargets.component';
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 { 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';
@ -51,6 +51,7 @@ import { AlertService } from './services/alert.service';
*/ */
import { MapValuesPipe } from './pipes/map-values.pipe'; import { MapValuesPipe } from './pipes/map-values.pipe';
import { RemoveNoneTypePipe } from './pipes/remove-none-type'; import { RemoveNoneTypePipe } from './pipes/remove-none-type';
import { SelectWorkBasketPipe } from './pipes/seleted-workbasket.pipe';
const MODULES = [ const MODULES = [
BrowserModule, BrowserModule,
@ -74,7 +75,6 @@ const DECLARATIONS = [
CategoriesadministrationComponent, CategoriesadministrationComponent,
AccessItemsComponent, AccessItemsComponent,
WorkbasketDetailsComponent, WorkbasketDetailsComponent,
WorkbasketDistributiontargetsComponent,
MasterAndDetailComponent, MasterAndDetailComponent,
WorkbasketInformationComponent, WorkbasketInformationComponent,
NoAccessComponent, NoAccessComponent,
@ -83,9 +83,11 @@ const DECLARATIONS = [
IconTypeComponent, IconTypeComponent,
AlertComponent, AlertComponent,
GeneralMessageModalComponent, GeneralMessageModalComponent,
DistributionTargetsComponent,
SortComponent, SortComponent,
MapValuesPipe, MapValuesPipe,
RemoveNoneTypePipe RemoveNoneTypePipe,
SelectWorkBasketPipe
]; ];
@NgModule({ @NgModule({

View File

@ -0,0 +1,21 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'selectWorkbaskets' })
export class SelectWorkBasketPipe implements PipeTransform {
transform(originArray: any, arg0: any, arg1: any): Object[] {
let returnArray = [];
if (!originArray) {
return returnArray;
}
for (let index = originArray.length - 1; index >= 0; index--) {
if ((arg1 && !arg0.some(elementToRemove => { return originArray[index].workbasketId === elementToRemove.workbasketId})) ||
!arg1 && arg0.some(elementToRemove => { return originArray[index].workbasketId === elementToRemove.workbasketId})) {
originArray.splice(index, 1);
}
}
returnArray = originArray;
return returnArray;
}
}

View File

@ -93,73 +93,79 @@ export class WorkbasketService {
// PUT // PUT
updateWorkBasketAccessItem(url: string, workbasketAccessItem: Array<WorkbasketAccessItems>): Observable<string> { updateWorkBasketAccessItem(url: string, workbasketAccessItem: Array<WorkbasketAccessItems>): Observable<string> {
return this.httpClient.put<string>(url, return this.httpClient.put<string>(url,
workbasketAccessItem, workbasketAccessItem,
this.httpOptions); this.httpOptions);
} }
//#endregion // GET
getWorkBasketsDistributionTargets(id: String): Observable<WorkbasketSummary[]> {
//#region "Service extras" return this.httpClient.get<WorkbasketSummary[]>(environment.taskanaRestUrl + '/v1/workbaskets/' + id + '/distributiontargets', this.httpOptions);
selectWorkBasket(id: string) {
this.workBasketSelected.next(id);
} }
getSelectedWorkBasket(): Observable<string> {
return this.workBasketSelected.asObservable();
}
triggerWorkBasketSaved() { //#endregion
this.workBasketSaved.next(Date.now());
}
workbasketSavedTriggered(): Observable<number> { //#region "Service extras"
return this.workBasketSaved.asObservable(); selectWorkBasket(id: string) {
} this.workBasketSelected.next(id);
}
getSelectedWorkBasket(): Observable < string > {
return this.workBasketSelected.asObservable();
}
triggerWorkBasketSaved() {
this.workBasketSaved.next(Date.now());
}
workbasketSavedTriggered(): Observable < number > {
return this.workBasketSaved.asObservable();
}
//#endregion //#endregion
//#region private //#region private
private getWorkbasketSummaryQueryParameters(sortBy: string, private getWorkbasketSummaryQueryParameters(sortBy: string,
order: string, order: string,
name: string, name: string,
nameLike: string, nameLike: string,
descLike: string, descLike: string,
owner: string, owner: string,
ownerLike: string, ownerLike: string,
type: string, type: string,
key: string, key: string,
keyLike: string, keyLike: string,
requiredPermission: string): string { requiredPermission: string): string {
let query: string = '?'; let query: string = '?';
query += sortBy ? `${this.SORTBY}=${sortBy}&` : ''; query += sortBy ? `${this.SORTBY}=${sortBy}&` : '';
query += order ? `${this.ORDER}=${order}&` : ''; query += order ? `${this.ORDER}=${order}&` : '';
query += name ? `${this.NAME}=${name}&` : ''; query += name ? `${this.NAME}=${name}&` : '';
query += nameLike ? `${this.NAMELIKE}=${nameLike}&` : ''; query += nameLike ? `${this.NAMELIKE}=${nameLike}&` : '';
query += descLike ? `${this.DESCLIKE}=${descLike}&` : ''; query += descLike ? `${this.DESCLIKE}=${descLike}&` : '';
query += owner ? `${this.OWNER}=${owner}&` : ''; query += owner ? `${this.OWNER}=${owner}&` : '';
query += ownerLike ? `${this.OWNERLIKE}=${ownerLike}&` : ''; query += ownerLike ? `${this.OWNERLIKE}=${ownerLike}&` : '';
query += type ? `${this.TYPE}=${type}&` : ''; query += type ? `${this.TYPE}=${type}&` : '';
query += key ? `${this.KEY}=${key}&` : ''; query += key ? `${this.KEY}=${key}&` : '';
query += keyLike ? `${this.KEYLIKE}=${keyLike}&` : ''; query += keyLike ? `${this.KEYLIKE}=${keyLike}&` : '';
query += requiredPermission ? `${this.REQUIREDPERMISSION}=${requiredPermission}&` : ''; query += requiredPermission ? `${this.REQUIREDPERMISSION}=${requiredPermission}&` : '';
if (query.lastIndexOf('&') === query.length - 1) { if (query.lastIndexOf('&') === query.length - 1) {
query = query.slice(0, query.lastIndexOf('&')) query = query.slice(0, query.lastIndexOf('&'))
}
return query;
} }
return query;
}
private handleError(error: Response | any) { private handleError(error: Response | any) {
// In a real world app, you might use a remote logging infrastructure // In a real world app, you might use a remote logging infrastructure
let errMsg: string; let errMsg: string;
if (error instanceof Response) { if (error instanceof Response) {
const body = error.json() || ''; const body = error.json() || '';
const err = JSON.stringify(body); const err = JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`; errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else { } else {
errMsg = error.message ? error.message : error.toString(); errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
} }
console.error(errMsg);
return Observable.throw(errMsg);
}
//#endregion //#endregion
} }

View File

@ -1,43 +1,47 @@
<div type="text" id="{{target}}" data-toogle="collapse" class="list-group-seach collapse"> <div id="{{target}}" data-toogle="collapse" class="list-group-search collapse">
<div class="row"> <div class="row">
<div class="dropdown col-xs-2"> <div class="dropdown col-xs-2">
<button class="btn btn-default" type="button" id="dropdownMenufilter" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <button class="btn btn-default" (click)="toggleDropDown = !toggleDropDown" type="button" id="dropdownMenufilter" data-toggle="dropdown"
<taskana-icon-type [type] ="filter.type" class="vertical-align"></taskana-icon-type> aria-haspopup="true" aria-expanded="true">
<taskana-icon-type [type]="filter.type" class="vertical-align"></taskana-icon-type>
</button> </button>
<ul class="dropdown-menu dropdown-menu-users" role="menu"> <ul class="dropdown-menu dropdown-menu-users" [ngClass]="{'hidden': !toggleDropDown}" role="menu">
<li *ngFor ="let type of allTypes | mapValues"> <li *ngFor="let type of allTypes | mapValues">
<button type="button" (click)="selectType(type.key); search()" class="btn btn-default btn-users-list" data-toggle="tooltip" [title]="type.value"> <button type="button" (click)="selectType(type.key); toggleDropDown = !toggleDropDown; search()" class="btn btn-default btn-users-list" data-toggle="tooltip"
<taskana-icon-type class="vertical-align" [type] ='type.key'></taskana-icon-type> [title]="type.value">
<taskana-icon-type class="vertical-align" [type]='type.key'></taskana-icon-type>
</button> </button>
</li> </li>
</ul> </ul>
</div> </div>
<div class="col-xs-4"> <div class="col-xs-4">
<input type="text" [(ngModel)]= "filter.name" (keyup.enter)="search()" class="form-control" id="wb-display-name-filter" placeholder="Filter name"> <input type="text" [(ngModel)]="filter.name" (keyup.enter)="search()" class="form-control" id="wb-display-name-filter" placeholder="Filter name">
</div> </div>
<div class="col-xs-4"> <div class="col-xs-4">
<input type="text" [(ngModel)]= "filter.key" (keyup.enter)="search()" class="form-control" id="wb-display-key-filter" placeholder="Filter key"> <input type="text" [(ngModel)]="filter.key" (keyup.enter)="search()" class="form-control" id="wb-display-key-filter" placeholder="Filter key">
</div> </div>
<button (click)="clear(); search()" type="button" <button (click)="clear(); search()" type="button" class="btn btn-default glyphicon glyphicon-ban-circle blue pull-right margin-right"
class="btn btn-default glyphicon glyphicon-ban-circle blue pull-right margin-right" data-toggle="tooltip" title="Clear"> data-toggle="tooltip" title="Clear">
</button> </button>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-2"> <div class="col-xs-2">
</div> </div>
<div class="col-xs-8"> <div class="col-xs-8">
<input type="text" [(ngModel)]= "filter.description" (keyup.enter)="search()" class="form-control" id="wb-display-description-filter" placeholder="Filter by description"> <input type="text" [(ngModel)]="filter.description" (keyup.enter)="search()" class="form-control" id="wb-display-description-filter"
placeholder="Filter by description">
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-2"> <div class="col-xs-2">
</div> </div>
<div class="col-xs-8"> <div class="col-xs-8">
<input type="text" [(ngModel)]= "filter.owner" (keyup.enter)="search()" class="form-control" id="wb-display-task-owner-filter" placeholder="Filter by Task owner"> <input type="text" [(ngModel)]="filter.owner" (keyup.enter)="search()" class="form-control" id="wb-display-task-owner-filter"
placeholder="Filter by Task owner">
</div> </div>
<button (click)="search()" type="button" <button (click)="search()" type="button" class="btn btn-default glyphicon glyphicon-search blue pull-right margin-right"
class="btn btn-default glyphicon glyphicon-search blue pull-right margin-right" data-toggle="tooltip" title="Search"> data-toggle="tooltip" title="Search">
</button> </button>
</div> </div>
</div> </div>

View File

@ -4,3 +4,12 @@
} }
margin-left: 15px; margin-left: 15px;
} }
.list-group-search {
padding: 10px 15px;
}
.btn-users-list {
border: 0px solid transparent;
/* this was 1px earlier */
}

View File

@ -1,5 +1,5 @@
<div [hidden]="!isDelayedRunning"> <div [hidden]="!isDelayedRunning">
<div class="sk-circle spinner-centered" [hidden]="this.isModal"> <div class="sk-circle {{positionClass? positionClass: 'spinner-centered'}}" [hidden]="this.isModal">
<div class="sk-circle1 sk-child"></div> <div class="sk-circle1 sk-child"></div>
<div class="sk-circle2 sk-child"></div> <div class="sk-circle2 sk-child"></div>
<div class="sk-circle3 sk-child"></div> <div class="sk-circle3 sk-child"></div>
@ -15,7 +15,7 @@
</div> </div>
<div #spinnerModal class="modal fade" id="spinner-modal" data-backdrop="static" data-keyboard="false" role="dialog"> <div #spinnerModal class="modal fade" id="spinner-modal" data-backdrop="static" data-keyboard="false" role="dialog">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-dialog spinner-centered"> <div class="modal-dialog {{positionClass? positionClass: 'spinner-centered'}}">
<div class="sk-circle"> <div class="sk-circle">
<div class="sk-circle1 sk-child"></div> <div class="sk-circle1 sk-child"></div>
<div class="sk-circle2 sk-child"></div> <div class="sk-circle2 sk-child"></div>

View File

@ -34,6 +34,10 @@ export class SpinnerComponent {
@Input() @Input()
isModal: boolean = false; isModal: boolean = false;
@Input()
positionClass: string = undefined;
@ViewChild('spinnerModal') @ViewChild('spinnerModal')
private modal; private modal;

View File

@ -1,34 +0,0 @@
<!-- <h2>Distribution Targets</h2>
<div *ngFor="let alert of alerts">
<alert [type]="alert.type" dismissible="true" [dismissOnTimeout]="5000">{{ alert.msg }}</alert>
</div>
<div class="col-xs-6 col-md-6">
<table class="table">
<thead>
<th>All Workbaskets</th>
<th>Actions</th>
</thead>
<tr *ngFor="let workbasket of workbaskets">
<td>{{ workbasket.name }}</td>
<td>
<button type="button" class="btn btn-default" aria-label="Left Align" (click)="onAdd(workbasket)">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
</td>
</tr>
</table>
</div>
<div class="col-xs-6 col-md-6">
<table class="table">
<thead>
<th>Selected Workbaskets</th>
<th>Actions</th>
</thead>
<tr *ngFor="let workbasket of workbasket.distributionTargets">
<td>{{ resolveName(workbasket) }}</td>
<td>
<button type="button" class="btn btn-default" aria-label="Left Align" (click)="onDelete(workbasket)">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
</td>
</tr>
</table>
</div> -->

View File

@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { WorkbasketDistributiontargetsComponent } from './workbasket-distributiontargets.component';
describe('WorkbasketDistributiontargetsComponent', () => {
let component: WorkbasketDistributiontargetsComponent;
let fixture: ComponentFixture<WorkbasketDistributiontargetsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ WorkbasketDistributiontargetsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(WorkbasketDistributiontargetsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
xit('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,106 +0,0 @@
import { Component, OnInit, Input } from '@angular/core';
import { WorkbasketSummary } from '../model/workbasketSummary';
import { WorkbasketService } from '../services/workbasket.service'
import { Observable } from 'rxjs/Observable';
@Component({
selector: 'app-workbasket-distributiontargets',
templateUrl: './workbasket-distributiontargets.component.html',
styleUrls: ['./workbasket-distributiontargets.component.css']
})
export class WorkbasketDistributiontargetsComponent implements OnInit {
@Input()
workbasket: WorkbasketSummary;
workbaskets: WorkbasketSummary[];
public alerts: any = [];
constructor(private service: WorkbasketService) { }
ngOnInit() {
//this.prepareData();
}
// ngOnChange() {
// this.prepareData();
// }
// prepareData() {
// this.service.getWorkBasketsSummary().subscribe(resultList => {
// this.workbaskets = resultList;
// });
// }
// onAdd(w: WorkbasketSummary) {
// if (this.workbasket.distributionTargets.length > 0) {
// let found: boolean = false;
// for (var i = 0, len = this.workbasket.distributionTargets.length; i < len; i++) {
// if (this.workbasket.distributionTargets[i] === w.id) {
// found = true;
// break;
// }
// }
// if (!found) {
// this.onSaved(w, true);
// } else {
// this.alerts = [{
// type: "danger",
// msg: "This workbasket is already mapped!"
// }];
// }
// } else {
// this.onSaved(w, true);
// }
// }
// onDelete(id: string) {
// this.onSaved(this.resolveObject(id), false);
// }
// // get workbasket name
// resolveName(id: string): any {
// if (this.workbaskets != null) {
// return this.workbaskets.filter(item => item.id === id)[0].name;
// }
// }
// // create an WorkbasketSummary
// resolveObject(id: string): any {
// if (this.workbaskets != null) {
// return this.workbaskets.filter(item => item.id === id)[0];
// }
// }
// onSaved(w: WorkbasketSummary, isUpdate: boolean) {
// if (w != null) {
// // add changes
// if (isUpdate) {
// this.workbasket.distributionTargets.push(w.id);
// } else {
// let index = this.workbasket.distributionTargets.indexOf(w.id);
// this.workbasket.distributionTargets.splice(index, 1);
// }
// // try to save changes
// this.service.updateWorkbasket(this.workbasket).subscribe(result => {
// this.workbasket.id = result.id;
// this.workbasket.name = result.name;
// this.workbasket.description = result.description;
// this.workbasket.owner = result.owner;
// this.workbasket.distributionTargets = result.distributionTargets;
// }, (err) => {
// this.alerts = [{
// type: "danger",
// msg: "You are not authorized."
// }];
// // reset changes
// if (isUpdate) {
// this.workbasket.distributionTargets.pop();
// } else {
// this.workbasket.distributionTargets.push(w.id);
// }
// });
// }
// }
}

View File

@ -1,6 +1,6 @@
<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 && 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">
<button type="button" (click)="onSave()" [disabled]="!AccessItemsForm.form.valid" class="btn btn-default btn-primary">Save</button> <button type="button" (click)="onSave()" [disabled]="!AccessItemsForm.form.valid" class="btn btn-default btn-primary">Save</button>

View File

@ -58,6 +58,7 @@ export class AccessItemsComponent implements OnInit {
} }
onSave(): boolean { onSave(): boolean {
this.requestInProgress = true;
if(!this.accessItems[0].links){ if(!this.accessItems[0].links){
return; return;
} }
@ -66,6 +67,7 @@ export class AccessItemsComponent implements OnInit {
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`));
this.requestInProgress = false;
return true; return true;
}, },
error => { error => {

View File

@ -0,0 +1,101 @@
<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>
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
<div class="panel-heading">
<div class="btn-group pull-right">
<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>
</div>
<h4 class="panel-header">{{workbasket.name}}</h4>
</div>
<div class="panel-body">
<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(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}} &nbsp;</dd>
</dl>
</div>
</li>
</ul>
</div>
<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">
<span class="glyphicon glyphicon-chevron-right blue"></span>
</button>
<button (click)="moveDistributionTargets(1)" [disabled] = "requestInProgressLeft || requestInProgressRight" class="btn btn-default move-left">
<span class="glyphicon glyphicon-chevron-left blue"></span>
</button>
</div>
<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">
<span class="glyphicon glyphicon-chevron-down blue"></span>
</button>
<button (click)="moveDistributionTargets(1)" [disabled] = "requestInProgressLeft || requestInProgressRight" class="btn btn-default move-up">
<span class="glyphicon glyphicon-chevron-up blue"></span>
</button>
</div>
<div id="dual-list-right" class="dual-list list-right col-xs-12 col-md-5-6 container">
<div class="row">
<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.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}} &nbsp;</dd>
</dl>
</div>
</li>
</ul>
</ul>
</div>
</div>
</div>

View File

@ -0,0 +1,43 @@
.list-group {
margin-top: 8px;
}
.list-left li,
.list-right li {
cursor: pointer;
}
.button-margin-top {
margin-top: 100px
}
.list-arrows > button{
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 {
@media (min-width: 992px){
width: 45.82%;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DistributionTargetsComponent } from './distribution-targets.component';
describe('DistributionTargetsComponent', () => {
let component: DistributionTargetsComponent;
let fixture: ComponentFixture<DistributionTargetsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ DistributionTargetsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DistributionTargetsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
xit('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,123 @@
import { Component, OnInit, Input } from '@angular/core';
import { Workbasket } from '../../../model/workbasket';
import { WorkbasketSummary } from '../../../model/workbasketSummary';
import { WorkbasketAccessItems } from '../../../model/workbasket-access-items';
import { FilterModel } from '../../../shared/filter/filter.component'
import { WorkbasketService } from '../../../services/workbasket.service';
import { AlertService, AlertModel, AlertType } from '../../../services/alert.service';
import { Subscription } from 'rxjs';
import { element } from 'protractor';
@Component({
selector: 'taskana-workbaskets-distribution-targets',
templateUrl: './distribution-targets.component.html',
styleUrls: ['./distribution-targets.component.scss']
})
export class DistributionTargetsComponent implements OnInit {
@Input()
workbasket: Workbasket;
distributionTargetsSubscription: Subscription;
workbasketSubscription: Subscription;
workbasketFilterSubscription: Subscription;
distributionTargetsLeft: Array<WorkbasketSummary>;
distributionTargetsRight: Array<WorkbasketSummary>;
distributionTargetsSelected: Array<WorkbasketSummary>;
filterBy: FilterModel = new FilterModel();
requestInProgress: boolean = false;
requestInProgressLeft: boolean = false;
requestInProgressRight: boolean = false;
constructor(private workbasketService: WorkbasketService) { }
ngOnInit() {
this.requestInProgressLeft = true;
this.requestInProgressRight = true;
this.distributionTargetsSubscription = this.workbasketService.getWorkBasketsDistributionTargets(this.workbasket.workbasketId).subscribe((distributionTargetsSelected: Array<WorkbasketSummary>) => {
this.distributionTargetsSelected = distributionTargetsSelected;
this.workbasketSubscription = this.workbasketService.getWorkBasketsSummary().subscribe((distributionTargetsAvailable: Array<WorkbasketSummary>) => {
this.distributionTargetsLeft = distributionTargetsAvailable;
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable);
this.requestInProgressLeft = false;
this.requestInProgressRight = false;
});
})
}
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) {
if (side === 0) {
let itemsSelected = this.getSelectedItems(this.distributionTargetsLeft, this.distributionTargetsRight)
this.distributionTargetsSelected = this.distributionTargetsSelected.concat(itemsSelected);
this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected);
}
else if (side === 1) {
let itemsSelected = this.getSelectedItems(this.distributionTargetsRight, this.distributionTargetsLeft);
this.distributionTargetsSelected = this.removeSeletedItems(this.distributionTargetsSelected, itemsSelected);
this.distributionTargetsRight = this.removeSeletedItems(this.distributionTargetsRight, itemsSelected);
this.distributionTargetsLeft = this.distributionTargetsLeft.concat(itemsSelected);
}
}
performAvailableFilter(filterBy: FilterModel) {
this.filterBy = filterBy;
this.performFilter(0);
}
performSelectedFilter(filterBy: FilterModel) {
this.filterBy = filterBy;
this.performFilter(1);
}
private performFilter(listType: number) {
listType ? this.distributionTargetsRight = undefined : this.distributionTargetsLeft = undefined;
listType ? this.requestInProgressRight = true : this.requestInProgressLeft = true;
this.workbasketFilterSubscription = this.workbasketService.getWorkBasketsSummary(true, undefined, undefined, undefined,
this.filterBy.name, this.filterBy.description, undefined, this.filterBy.owner,
this.filterBy.type, undefined, this.filterBy.key).subscribe(resultList => {
listType ? this.distributionTargetsRight = resultList : this.distributionTargetsLeft = resultList;
listType ? this.requestInProgressRight = false : this.requestInProgressLeft = false;
});
}
private getSelectedItems(originList: any, destinationList: any): Array<any> {
return originList.filter((element: any) => { return (element.selected === true) });
}
private removeSeletedItems(originList: any, selectedItemList) {
for (let index = originList.length - 1; index >= 0; index--) {
if (selectedItemList.some(elementToRemove => { return originList[index].workbasketId === elementToRemove.workbasketId })) {
originList.splice(index, 1);
}
}
return originList;
}
private ngOnDestroy(): void {
if (this.distributionTargetsSubscription) { this.distributionTargetsSubscription.unsubscribe(); }
if (this.workbasketSubscription) { this.workbasketSubscription.unsubscribe(); }
if (this.workbasketFilterSubscription) { this.workbasketFilterSubscription.unsubscribe(); }
}
}

View File

@ -1,6 +1,6 @@
<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 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" [disabled]="!WorkbasketForm.form.valid" (click)="onSave()" class="btn btn-default btn-primary">Save</button> <button type="button" [disabled]="!WorkbasketForm.form.valid" (click)="onSave()" class="btn btn-default btn-primary">Save</button>

View File

@ -24,7 +24,7 @@
<taskana-workbasket-access-items [workbasket]="workbasket"></taskana-workbasket-access-items> <taskana-workbasket-access-items [workbasket]="workbasket"></taskana-workbasket-access-items>
</div> </div>
<div role="tabpanel" class="tab-pane inactive" id="distribution-targets"> <div role="tabpanel" class="tab-pane inactive" id="distribution-targets">
<!-- <taskana-workbasket-distributiontargets [workbasket]="workbasket"></taskana-workbasket-distributiontargets>--> <taskana-workbaskets-distribution-targets [workbasket]="workbasket"></taskana-workbaskets-distribution-targets>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,14 +1,17 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Component } from '@angular/core';
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 { 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';
import { ICONTYPES, IconTypeComponent } from '../../shared/type-icon/icon-type.component'; import { ICONTYPES, IconTypeComponent } from '../../shared/type-icon/icon-type.component';
import { MapValuesPipe } from '../../pipes/map-values.pipe'; import { MapValuesPipe } from '../../pipes/map-values.pipe';
import { RemoveNoneTypePipe } from '../../pipes/remove-none-type'; import { RemoveNoneTypePipe } from '../../pipes/remove-none-type';
import { SelectWorkBasketPipe } from '../../pipes/seleted-workbasket.pipe';
import { AlertComponent } from '../../shared/alert/alert.component'; import { AlertComponent } from '../../shared/alert/alert.component';
import { GeneralMessageModalComponent } from '../../shared/general-message-modal/general-message-modal.component'; import { GeneralMessageModalComponent } from '../../shared/general-message-modal/general-message-modal.component';
import { Links } from 'app/model/links'; import { Links } from 'app/model/links';
@ -26,6 +29,15 @@ import { HttpClientModule } from '@angular/common/http';
import { HttpModule } from '@angular/http'; import { HttpModule } from '@angular/http';
import { WorkbasketSummary } from '../../model/workbasketSummary'; import { WorkbasketSummary } from '../../model/workbasketSummary';
@Component({
selector: 'taskana-filter',
template: ''
})
export class FilterComponent {
}
describe('WorkbasketDetailsComponent', () => { describe('WorkbasketDetailsComponent', () => {
let component: WorkbasketDetailsComponent; let component: WorkbasketDetailsComponent;
let fixture: ComponentFixture<WorkbasketDetailsComponent>; let fixture: ComponentFixture<WorkbasketDetailsComponent>;
@ -33,11 +45,12 @@ describe('WorkbasketDetailsComponent', () => {
let masterAndDetailService; let masterAndDetailService;
let workbasketService; let workbasketService;
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 ], declarations: [WorkbasketDetailsComponent, NoAccessComponent, WorkbasketInformationComponent, SpinnerComponent, IconTypeComponent, MapValuesPipe, RemoveNoneTypePipe, AlertComponent, GeneralMessageModalComponent, AccessItemsComponent, DistributionTargetsComponent, FilterComponent, SelectWorkBasketPipe],
providers:[WorkbasketService, MasterAndDetailService, PermissionService, AlertService] providers: [WorkbasketService, MasterAndDetailService, PermissionService, AlertService]
}) })
.compileComponents(); .compileComponents();
})); }));
@ -49,11 +62,12 @@ describe('WorkbasketDetailsComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
masterAndDetailService = TestBed.get(MasterAndDetailService); masterAndDetailService = TestBed.get(MasterAndDetailService);
workbasketService = TestBed.get(WorkbasketService); workbasketService = TestBed.get(WorkbasketService);
spyOn(masterAndDetailService, 'getShowDetail').and.callFake(()=>{return Observable.of(true)}) spyOn(masterAndDetailService, 'getShowDetail').and.callFake(() => { return Observable.of(true) })
spyOn(workbasketService, 'getSelectedWorkBasket').and.callFake(()=>{return Observable.of('id1')}) spyOn(workbasketService, 'getSelectedWorkBasket').and.callFake(() => { return Observable.of('id1') })
spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(()=>{return Observable.of(new Array<WorkbasketSummary>(new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', new Array<Links>(new Links('self', 'someurl')))))}) spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => { return Observable.of(new Array<WorkbasketSummary>(new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', new Array<Links>(new Links('self', 'someurl'))))) })
spyOn(workbasketService, 'getWorkBasket').and.callFake(()=>{return Observable.of(new Workbasket('id1'))}) spyOn(workbasketService, 'getWorkBasket').and.callFake(() => { return Observable.of(new Workbasket('id1')) })
spyOn(workbasketService, 'getWorkBasketAccessItems').and.callFake(()=>{return Observable.of(new Array<WorkbasketAccessItems>())}) spyOn(workbasketService, 'getWorkBasketAccessItems').and.callFake(() => { return Observable.of(new Array<WorkbasketAccessItems>()) })
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => { return Observable.of(new Array<WorkbasketSummary>()) })
}); });

View File

@ -24,7 +24,7 @@
<div class="clearfix btn-group"> <div class="clearfix btn-group">
<button class="btn btn-default collapsed" type="button" id="collapsedMenufilterWb" data-toggle="collapse" data-target="#wb-filter-bar" <button class="btn btn-default collapsed" type="button" id="collapsedMenufilterWb" data-toggle="collapse" data-target="#wb-filter-bar"
aria-expanded="false"> aria-expanded="false">
<span class="glyphicon glyphicon-filter blue wb-filter-toggle"></span> <span class="glyphicon glyphicon-filter blue"></span>
</button> </button>
</div> </div>
</div> </div>

View File

@ -155,10 +155,6 @@ li > div.row > dl {
padding-right: 0px; padding-right: 0px;
} }
.list-group-seach {
padding: 10px 15px;
}
.user-select { .user-select {
margin-left: 2px; margin-left: 2px;
} }
@ -198,36 +194,6 @@ li > div.row > dl {
margin-bottom: 5px; margin-bottom: 5px;
} }
.btn-users-list {
border: 0px solid transparent;
/* this was 1px earlier */
}
.dual-list .list-group {
margin-top: 8px;
}
.list-left li,
.list-right li {
cursor: pointer;
}
.list-arrows {
padding-top: 100px;
}
.list-arrows button {
margin-bottom: 20px;
}
.well-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);
}
.toolbar { .toolbar {
padding: 13px 15px; padding: 13px 15px;
@ -254,3 +220,10 @@ li > div.row > dl {
.panel-heading{ .panel-heading{
min-height: 60px; min-height: 60px;
} }
.panel {
border-radius: 0px;
}
.centered-spinner {
margin-top: 100px;
}