TSK-207 Add classification details JSON+HAL support for classifications.
This commit is contained in:
parent
b62563f463
commit
441c276946
|
@ -0,0 +1,133 @@
|
|||
<div class="container-scrollable">
|
||||
<div id="classification-details" *ngIf="classification && !requestInProgress">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li *ngIf="showDetail" class="visible-xs visible-sm hidden">
|
||||
<a (click)="backClicked()">
|
||||
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>Back</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<taskana-spinner [isRunning]="requestInProgress" [isModal]="modalSpinner" class="centered-horizontally floating"></taskana-spinner>
|
||||
<div *ngIf="classification" id="classification" class="panel panel-default classification">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
<button type="button" [disabled]="!ClassificationForm.form.valid" (click)="onSave()" class="btn btn-default btn-primary">Save</button>
|
||||
<button type="button" (click)="onClear()" class="btn btn-default">Undo</button>
|
||||
<button type="button" (click)="removeClassification()" data-toggle="tooltip" title="Remove" class="btn btn-default remove">
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<h4 class="panel-header">{{classification.name}}
|
||||
<span *ngIf="!classification.classificationId" class="badge warning"> {{badgeMessage}}</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form #ClassificationForm="ngForm">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group required">
|
||||
<label for="classification-key" class="control-label">Key</label>
|
||||
<input type="text" required #key="ngModel" class="form-control" id="classification-key" placeholder="Key" [(ngModel)]="classification.key"
|
||||
name="classification.key">
|
||||
<div [hidden]="key.valid" class="required-text">
|
||||
* Key is required
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group required">
|
||||
<label for="classification-name" class="control-label">Name</label>
|
||||
<input type="text" required #name="ngModel" class="form-control" id="classification-name" placeholder="Name" [(ngModel)]="classification.name"
|
||||
name="classification.name">
|
||||
<div [hidden]="name.valid" class="required-text">
|
||||
* Name is required
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group required">
|
||||
<label for="classification-domain" class="control-label">Domain</label>
|
||||
<input type="text" required #domain="ngModel" class="form-control" id="classification-domain" placeholder="Domain" [(ngModel)]="classification.domain"
|
||||
name="classification.domain">
|
||||
<div [hidden]="domain.valid" class="required-text">
|
||||
* Domain is required
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-type" class="control-label">Type</label>
|
||||
<div class="dropdown clearfix btn-group">
|
||||
<button class="btn btn-default" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||
{{classification.type}}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu" aria-labelledby="dropdownMenu">
|
||||
<li *ngFor="let type of classificationTypes" (click)="selectType(type)">
|
||||
<a>
|
||||
<label>
|
||||
{{type}}
|
||||
</label>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-description" class="control-label">Description</label>
|
||||
<textarea class="form-control" rows="5" id="classification-description" placeholder="Description" [(ngModel)]="classification.description"
|
||||
name="classification.description"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-service-level" class="control-label">Service Level</label>
|
||||
<input type="text" class="form-control" id="classification-service-level" placeholder="Service Level" [(ngModel)]="classification.serviceLevel"
|
||||
name="classification.serviceLevel">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="classification-application-entry-point" class="control-label">Application entry point</label>
|
||||
<input type="text" class="form-control" id="classification-application-entry-point" placeholder="Application entry point"
|
||||
[(ngModel)]="classification.applicationEntryPoint" name="classification.applicationEntryPoint">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="classification-custom-1" class="control-label">Custom 1</label>
|
||||
<input type="text" class="form-control" id="classification-custom-1" placeholder="Custom 1" [(ngModel)]="classification.custom1"
|
||||
name="classification.custom1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-custom-2" class="control-label">Custom 2</label>
|
||||
<input type="text" class="form-control" id="classification-custom-2" placeholder="Custom 2" [(ngModel)]="classification.custom2"
|
||||
name="classification.custom2">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-custom-3" class="control-label">Custom 3</label>
|
||||
<input type="text" class="form-control" id="classification-custom-3" placeholder="Custom 3" [(ngModel)]="classification.custom3"
|
||||
name="classification.custom3">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-custom-4" class="control-label">Custom 4</label>
|
||||
<input type="text" class="form-control" id="classification-custom-4" placeholder="Custom 4" [(ngModel)]="classification.custom4"
|
||||
name="classification.custom4">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-custom-5" class="control-label">Custom 5</label>
|
||||
<input type="text" class="form-control" id="classification-custom-5" placeholder="Custom 5" [(ngModel)]="classification.custom5"
|
||||
name="classification.custom5">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-custom-6" class="control-label">Custom 6</label>
|
||||
<input type="text" class="form-control" id="classification-custom-6" placeholder="Custom 6" [(ngModel)]="classification.custom6"
|
||||
name="classification.custom6">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-custom-7" class="control-label">Custom 7</label>
|
||||
<input type="text" class="form-control" id="classification-custom-7" placeholder="Custom 7" [(ngModel)]="classification.custom7"
|
||||
name="classification.custom7">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="classification-custom-8" class="control-label">Custom 8</label>
|
||||
<input type="text" class="form-control" id="classification-custom-8" placeholder="Custom 8" [(ngModel)]="classification.custom8"
|
||||
name="classification.custom8">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
.classification.panel{
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
margin-bottom: 0px;
|
||||
&> .panel-body {
|
||||
height: 80vh;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClient, HttpClientModule } from '@angular/common/http';
|
||||
import { Routes } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { ClassificationDetailsComponent } from './classification-details.component';
|
||||
import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
|
||||
|
||||
import { MasterAndDetailService } from 'app/services/masterAndDetail/master-and-detail.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { ClassificationsService } from 'app/services/classifications/classifications.service';
|
||||
import { TreeNodeModel } from 'app/models/tree-node';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-dummy-detail',
|
||||
template: 'dummydetail'
|
||||
})
|
||||
class DummyDetailComponent {
|
||||
}
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: ':id', component: DummyDetailComponent, outlet: 'detail' },
|
||||
{ path: 'classifications', component: DummyDetailComponent }
|
||||
];
|
||||
|
||||
describe('ClassificationDetailsComponent', () => {
|
||||
let component: ClassificationDetailsComponent;
|
||||
let fixture: ComponentFixture<ClassificationDetailsComponent>;
|
||||
const treeNodes: Array<TreeNodeModel> = new Array(new TreeNodeModel());
|
||||
const classificationTypes: Array<string> = new Array<string>('type1', 'type2');
|
||||
let classificationsSpy, classificationsTypesSpy;
|
||||
let classificationsService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule, HttpClientModule, RouterTestingModule.withRoutes(routes)],
|
||||
declarations: [ClassificationDetailsComponent, SpinnerComponent, DummyDetailComponent],
|
||||
providers: [MasterAndDetailService, RequestInProgressService, ClassificationsService, HttpClient]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ClassificationDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
classificationsService = TestBed.get(ClassificationsService);
|
||||
classificationsSpy = spyOn(classificationsService, 'getClassifications').and.returnValue(Observable.of(treeNodes));
|
||||
classificationsTypesSpy = spyOn(classificationsService, 'getClassificationTypes').and.returnValue(Observable.of(classificationTypes));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,110 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import { ClassificationDefinition } from 'app/models/classification-definition';
|
||||
import { ACTION } from 'app/models/action';
|
||||
|
||||
import { ClassificationsService } from 'app/services/classifications/classifications.service';
|
||||
import { MasterAndDetailService } from 'app/services/masterAndDetail/master-and-detail.service';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-classification-details',
|
||||
templateUrl: './classification-details.component.html',
|
||||
styleUrls: ['./classification-details.component.scss']
|
||||
})
|
||||
export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||
|
||||
classification: ClassificationDefinition;
|
||||
selectedId: string = undefined;
|
||||
showDetail = false;
|
||||
requestInProgress = false;
|
||||
classificationTypes: Array<string> = [];
|
||||
badgeMessage = '';
|
||||
private action: any;
|
||||
private classificationServiceSubscription: Subscription;
|
||||
private classificationSelectedSubscription: Subscription;
|
||||
private routeSubscription: Subscription;
|
||||
private masterAndDetailSubscription: Subscription;
|
||||
|
||||
|
||||
constructor(private classificationsService: ClassificationsService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private masterAndDetailService: MasterAndDetailService) { }
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
this.classificationsService.getClassificationTypes().subscribe((classificationTypes: Array<string>) => {
|
||||
this.classificationTypes = classificationTypes;
|
||||
})
|
||||
this.classificationSelectedSubscription = this.classificationsService.getSelectedClassification()
|
||||
.subscribe(classificationIdSelected => {
|
||||
this.classification = undefined;
|
||||
if (classificationIdSelected) {
|
||||
this.getClassificationInformation(classificationIdSelected);
|
||||
}
|
||||
});
|
||||
|
||||
this.routeSubscription = this.route.params.subscribe(params => {
|
||||
let id = params['id'];
|
||||
this.action = undefined;
|
||||
if (id && id.indexOf('new-classification') !== -1) {
|
||||
this.action = ACTION.CREATE;
|
||||
id = undefined;
|
||||
this.badgeMessage = 'Creating new workbasket';
|
||||
this.getClassificationInformation(id);
|
||||
}
|
||||
|
||||
if (id && id !== '') {
|
||||
this.selectClassification(id);
|
||||
}
|
||||
});
|
||||
|
||||
this.masterAndDetailSubscription = this.masterAndDetailService.getShowDetail().subscribe(showDetail => {
|
||||
this.showDetail = showDetail;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
backClicked(): void {
|
||||
this.classificationsService.selectClassification(undefined);
|
||||
this.router.navigate(['./'], { relativeTo: this.route.parent });
|
||||
}
|
||||
|
||||
selectType(type: string) {
|
||||
this.classification.type = type;
|
||||
}
|
||||
removeClassification() { }
|
||||
|
||||
onSave() { }
|
||||
|
||||
onClear() { }
|
||||
|
||||
|
||||
private selectClassification(id: string) {
|
||||
this.selectedId = id;
|
||||
this.classificationsService.selectClassification(id);
|
||||
}
|
||||
private getClassificationInformation(classificationIdSelected: string) {
|
||||
if (this.action === ACTION.CREATE) { // CREATE
|
||||
this.classification = new ClassificationDefinition();
|
||||
} else {
|
||||
this.requestInProgress = true;
|
||||
this.classificationServiceSubscription = this.classificationsService.getClassification(classificationIdSelected)
|
||||
.subscribe((classification: ClassificationDefinition) => {
|
||||
this.classification = classification;
|
||||
this.requestInProgress = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.masterAndDetailSubscription) { this.masterAndDetailSubscription.unsubscribe(); }
|
||||
if (this.routeSubscription) { this.routeSubscription.unsubscribe(); }
|
||||
if (this.classificationSelectedSubscription) { this.classificationSelectedSubscription.unsubscribe(); }
|
||||
if (this.classificationServiceSubscription) { this.classificationServiceSubscription.unsubscribe(); }
|
||||
|
||||
}
|
||||
}
|
|
@ -5,9 +5,6 @@
|
|||
<button type="button" (click)="addClassification()" data-toggle="tooltip" title="Add" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-plus green" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" (click)="removeClassification()" data-toggle="tooltip" title="Remove" class="btn btn-default remove">
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</button>
|
||||
<taskana-import-export-component [currentSelection]="selectionToImport"></taskana-import-export-component>
|
||||
<taskana-classification-types-selector class="pull-right" [classificationTypes]="classificationsTypes" [(classificationTypeSelected)]="classificationTypeSelected"
|
||||
(classificationTypeChanged)=selectClassificationType($event)></taskana-classification-types-selector>
|
||||
|
@ -15,6 +12,6 @@
|
|||
</div>
|
||||
</li>
|
||||
<taskana-spinner [isRunning]="requestInProgress" class="centered-horizontally"></taskana-spinner>
|
||||
<taskana-tree [treeNodes]="classifications"></taskana-tree>
|
||||
<taskana-tree [treeNodes]="classifications" [selectNodeId] ="selectedId" (selectNodeIdChanged) ="selectClassification($event)"></taskana-tree>
|
||||
|
||||
</div>
|
|
@ -3,7 +3,7 @@
|
|||
}
|
||||
|
||||
.list-group-item {
|
||||
padding: 5px 0px;
|
||||
padding: 5px 0px 2px 1px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,15 @@ import { Component, Input } from '@angular/core';
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { HttpClient, HttpClientModule } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Routes } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
import { TreeNode } from 'app/models/tree-node';
|
||||
import { TreeNodeModel } from 'app/models/tree-node';
|
||||
|
||||
import { ClassificationListComponent } from './classification-list.component';
|
||||
import { ImportExportComponent } from 'app/shared/import-export/import-export.component';
|
||||
import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
|
||||
import { ClassificationTypesSelectorComponent } from 'app/shared/classification-types-selector/classification-types-selector.component';
|
||||
import { MapValuesPipe } from 'app/pipes/mapValues/map-values.pipe';
|
||||
|
||||
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||
import { WorkbasketDefinitionService } from 'app/services/workbasket-definition/workbasket-definition.service';
|
||||
|
@ -22,25 +23,40 @@ import { DomainService } from 'app/services/domains/domain.service';
|
|||
selector: 'taskana-tree',
|
||||
template: ''
|
||||
})
|
||||
class TreeComponent {
|
||||
class TaskanaTreeComponent {
|
||||
@Input() treeNodes;
|
||||
@Input() selectNodeId;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-dummy-detail',
|
||||
template: 'dummydetail'
|
||||
})
|
||||
class DummyDetailComponent {
|
||||
}
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: ':id', component: DummyDetailComponent, outlet: 'detail' },
|
||||
{ path: 'classifications', component: DummyDetailComponent }
|
||||
];
|
||||
|
||||
|
||||
describe('ClassificationListComponent', () => {
|
||||
let component: ClassificationListComponent;
|
||||
let fixture: ComponentFixture<ClassificationListComponent>;
|
||||
const treeNodes: Array<TreeNode> = new Array(new TreeNode());
|
||||
const classificationTypes: Map<string, string> = new Map<string, string>([['type1', 'type1'], ['type2', 'type2']])
|
||||
const treeNodes: Array<TreeNodeModel> = new Array(new TreeNodeModel());
|
||||
const classificationTypes: Array<string> = new Array<string>('type1', 'type2');
|
||||
let classificationsSpy, classificationsTypesSpy;
|
||||
let classificationsService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ClassificationListComponent, ImportExportComponent, SpinnerComponent, ClassificationTypesSelectorComponent,
|
||||
TreeComponent, MapValuesPipe],
|
||||
imports: [HttpClientModule],
|
||||
TaskanaTreeComponent, DummyDetailComponent],
|
||||
imports: [HttpClientModule, RouterTestingModule.withRoutes(routes)],
|
||||
providers: [
|
||||
HttpClient, WorkbasketDefinitionService, AlertService, ClassificationsService, DomainService, ClassificationDefinitionService
|
||||
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { ImportType } from 'app/models/import-type';
|
||||
import { Classification } from 'app/models/classification';
|
||||
import { TreeNode } from 'app/models/tree-node';
|
||||
import { TreeNodeModel } from 'app/models/tree-node';
|
||||
|
||||
import { ClassificationsService } from 'app/services/classifications/classifications.service';
|
||||
|
||||
|
@ -15,41 +16,61 @@ import { ClassificationsService } from 'app/services/classifications/classificat
|
|||
export class ClassificationListComponent implements OnInit, OnDestroy {
|
||||
|
||||
|
||||
selectedId: string;
|
||||
selectionToImport = ImportType.CLASSIFICATIONS;
|
||||
requestInProgress = false;
|
||||
|
||||
classifications: Array<Classification> = [];
|
||||
classificationsTypes: Map<string, string> = new Map();
|
||||
classificationsTypes: Array<string> = [];
|
||||
classificationTypeSelected: string;
|
||||
classificationServiceSubscription: Subscription;
|
||||
classificationTypeServiceSubscription: Subscription;
|
||||
constructor(private classificationService: ClassificationsService) {
|
||||
classificationSelectedSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private classificationService: ClassificationsService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute, ) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.classificationServiceSubscription = this.classificationService.getClassifications()
|
||||
.subscribe((classifications: Array<TreeNode>) => {
|
||||
.subscribe((classifications: Array<TreeNodeModel>) => {
|
||||
this.classifications = classifications;
|
||||
this.classificationTypeServiceSubscription = this.classificationService.getClassificationTypes()
|
||||
.subscribe((classificationsTypes: Map<string, string>) => {
|
||||
.subscribe((classificationsTypes: Array<string>) => {
|
||||
this.classificationsTypes = classificationsTypes;
|
||||
this.classificationTypeSelected = this.classifications[0].type;
|
||||
});
|
||||
});
|
||||
this.classificationSelectedSubscription = this.classificationService.getSelectedClassification()
|
||||
.subscribe((classificationSelected: string) => {
|
||||
// TODO should be done in a different way.
|
||||
setTimeout(() => { this.selectedId = classificationSelected; }, 0);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
selectClassificationType(classificationTypeSelected: string) {
|
||||
this.classificationService.getClassifications(true, classificationTypeSelected)
|
||||
.subscribe((classifications: Array<TreeNode>) => {
|
||||
.subscribe((classifications: Array<TreeNodeModel>) => {
|
||||
this.classifications = classifications;
|
||||
});
|
||||
}
|
||||
selectClassification(id: string) {
|
||||
this.selectedId = id;
|
||||
this.router.navigate([{ outlets: { detail: [this.selectedId] } }], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
addClassification() { }
|
||||
removeClassification() { }
|
||||
addClassification() {
|
||||
this.selectedId = undefined;
|
||||
this.router.navigate([{ outlets: { detail: ['new-classification'] } }], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.classificationServiceSubscription) { this.classificationServiceSubscription.unsubscribe(); }
|
||||
if (this.classificationTypeServiceSubscription) { this.classificationTypeServiceSubscription.unsubscribe(); }
|
||||
if (this.classificationSelectedSubscription) { this.classificationSelectedSubscription.unsubscribe(); }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<taskana-spinner [isRunning]="requestInProgress" [isModal]="modalSpinner" class="centered-horizontally floating"></taskana-spinner>
|
||||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
<div class="pull-right">
|
||||
<button type="button" (click)="onSave()" [disabled]="!AccessItemsForm.form.valid || action === 'COPY'" class="btn btn-default btn-primary">Save</button>
|
||||
<button type="button" (click)="clear()" class="btn btn-default">Undo changes</button>
|
||||
<button type="button" (click)="clear()" class="btn btn-default">Undo</button>
|
||||
</div>
|
||||
<h4 class="panel-header">{{workbasket.name}}
|
||||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||
|
|
|
@ -54,6 +54,9 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
if (!this.initialized && changes.active && changes.active.currentValue === 'accessItems') {
|
||||
this.init();
|
||||
}
|
||||
if (changes.action) {
|
||||
this.setBadge();
|
||||
}
|
||||
}
|
||||
private init() {
|
||||
this.initialized = true;
|
||||
|
@ -75,9 +78,7 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
this.onSave();
|
||||
}
|
||||
})
|
||||
if (this.action === ACTION.COPY) {
|
||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
addAccessItem() {
|
||||
|
@ -113,6 +114,11 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
})
|
||||
return false;
|
||||
}
|
||||
private setBadge() {
|
||||
if (this.action === ACTION.COPY) {
|
||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||
}
|
||||
}
|
||||
|
||||
private cloneAccessItems(inputaccessItem): Array<WorkbasketAccessItems> {
|
||||
const accessItemClone = new Array<WorkbasketAccessItems>();
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
class="centered-horizontally floating"></taskana-spinner>
|
||||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
<div class="pull-right">
|
||||
<button type="button" (click)="onSave()" [disabled]="action === 'COPY'" class="btn btn-default btn-primary">Save</button>
|
||||
<button type="button" (click)="onClear()" class="btn btn-default">Undo changes</button>
|
||||
<button type="button" (click)="onClear()" class="btn btn-default">Undo</button>
|
||||
</div>
|
||||
<h4 class="panel-header">{{workbasket.name}}
|
||||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||
|
|
|
@ -60,9 +60,12 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
|||
private errorModalService: ErrorModalService) { }
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (!this.initialized && changes.active && changes.active.currentValue === 'distributionTargets' ) {
|
||||
if (!this.initialized && changes.active && changes.active.currentValue === 'distributionTargets') {
|
||||
this.init();
|
||||
}
|
||||
if (changes.action) {
|
||||
this.setBadge();
|
||||
}
|
||||
}
|
||||
|
||||
private init() {
|
||||
|
@ -97,9 +100,6 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
|||
}
|
||||
});
|
||||
|
||||
if (this.action === ACTION.COPY) {
|
||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||
}
|
||||
}
|
||||
|
||||
moveDistributionTargets(side: number) {
|
||||
|
@ -162,6 +162,12 @@ export class DistributionTargetsComponent implements OnChanges, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
private setBadge() {
|
||||
if (this.action === ACTION.COPY) {
|
||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||
}
|
||||
}
|
||||
|
||||
private getSelectedItems(originList: any, destinationList: any): Array<any> {
|
||||
return originList.filter((item: any) => { return (item.selected === true) });
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
<taskana-spinner [isRunning]="requestInProgress" [isModal]="modalSpinner" class="centered-horizontally floating"></taskana-spinner>
|
||||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
<div class="pull-right">
|
||||
<button type="button" [disabled]="!WorkbasketForm.form.valid" (click)="onSave()" class="btn btn-default btn-primary">Save</button>
|
||||
<button type="button" (click)="onClear()" class="btn btn-default">Undo changes</button>
|
||||
<button type="button" (click)="onClear()" class="btn btn-default">Undo</button>
|
||||
<button type="button" (click)="copyWorkbasket()" data-toggle="tooltip" title="copy" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-copy green" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" (click)="removeWorkbasket()" data-toggle="tooltip" title="Remove" class="btn btn-default remove">
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<h4 class="panel-header">{{workbasket.name}}
|
||||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||
|
|
|
@ -24,6 +24,7 @@ import { RemoveNoneTypePipe } from 'app/pipes/removeNoneType/remove-none-type.pi
|
|||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { SavingWorkbasketService, SavingInformation } from 'app/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-dummy-detail',
|
||||
|
@ -47,7 +48,7 @@ describe('InformationComponent', () => {
|
|||
declarations: [WorkbasketInformationComponent, IconTypeComponent, MapValuesPipe,
|
||||
RemoveNoneTypePipe, SpinnerComponent, GeneralMessageModalComponent, DummyDetailComponent],
|
||||
imports: [FormsModule, AngularSvgIconModule, HttpClientModule, HttpModule, RouterTestingModule.withRoutes(routes)],
|
||||
providers: [WorkbasketService, AlertService, SavingWorkbasketService, ErrorModalService]
|
||||
providers: [WorkbasketService, AlertService, SavingWorkbasketService, ErrorModalService, RequestInProgressService]
|
||||
|
||||
})
|
||||
.compileComponents();
|
||||
|
@ -162,4 +163,15 @@ describe('InformationComponent', () => {
|
|||
expect(savingWorkbasketService.triggerAccessItemsSaving).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
// it('should call to workbasket service to remove workbasket after click on remove workbasket', () => {
|
||||
// const spy = spyOn(router, 'navigate');
|
||||
// component.removeWorkbasket();
|
||||
// expect(requestInProgressService.setRequestInProgress).toHaveBeenCalledWith(true);
|
||||
// expect(workbasketService.deleteWorkbasket).toHaveBeenCalledWith('selfLink');
|
||||
// expect(requestInProgressService.setRequestInProgress).toHaveBeenCalledWith(false);
|
||||
// expect(workbasketService.triggerWorkBasketSaved).toHaveBeenCalled();
|
||||
// expect(spy.calls.first().args[0][0]).toBe('/workbaskets');
|
||||
// });
|
||||
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, OnInit, Input, Output, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ActivatedRoute, Params, Router, NavigationStart } from '@angular/router';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
@ -8,13 +9,14 @@ import { ICONTYPES } from 'app/models/type';
|
|||
import { ErrorModel } from 'app/models/modal-error';
|
||||
import { ACTION } from 'app/models/action';
|
||||
import { Workbasket } from 'app/models/workbasket';
|
||||
import { AlertModel, AlertType } from 'app/models/alert';
|
||||
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { SavingWorkbasketService, SavingInformation } from 'app/services/saving-workbaskets/saving-workbaskets.service';
|
||||
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||
import { AlertModel, AlertType } from 'app/models/alert';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
|
||||
|
||||
const dateFormat = 'yyyy-MM-ddTHH:mm:ss';
|
||||
const dateLocale = 'en-US';
|
||||
|
@ -46,7 +48,8 @@ export class WorkbasketInformationComponent implements OnChanges, OnDestroy {
|
|||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private errorModalService: ErrorModalService,
|
||||
private savingWorkbasket: SavingWorkbasketService) {
|
||||
private savingWorkbasket: SavingWorkbasketService,
|
||||
private requestInProgressService: RequestInProgressService) {
|
||||
this.allTypes = IconTypeComponent.allTypes;
|
||||
}
|
||||
|
||||
|
@ -91,13 +94,33 @@ export class WorkbasketInformationComponent implements OnChanges, OnDestroy {
|
|||
this.hasChanges = false;
|
||||
}
|
||||
|
||||
removeWorkbasket() {
|
||||
this.requestInProgressService.setRequestInProgress(true);
|
||||
this.workbasketService.deleteWorkbasket(this.workbasket._links.self.href).subscribe(response => {
|
||||
this.requestInProgressService.setRequestInProgress(false);
|
||||
this.workbasketService.triggerWorkBasketSaved();
|
||||
this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS,
|
||||
`Workbasket ${this.workbasket.workbasketId} was removed successfully`))
|
||||
this.router.navigate(['administration/workbaskets']);
|
||||
}, error => {
|
||||
this.requestInProgressService.setRequestInProgress(false);
|
||||
this.errorModalService.triggerError(new ErrorModel(
|
||||
`There was an error deleting workbasket ${this.workbasket.workbasketId}`, error.error.message))
|
||||
});
|
||||
}
|
||||
|
||||
copyWorkbasket() {
|
||||
this.router.navigate([{ outlets: { detail: ['copy-workbasket'] } }], { relativeTo: this.route.parent });
|
||||
}
|
||||
|
||||
|
||||
private beforeRequest() {
|
||||
this.requestInProgress = true;
|
||||
this.requestInProgressService.setRequestInProgress(true);
|
||||
this.modalSpinner = true;
|
||||
}
|
||||
|
||||
private afterRequest() {
|
||||
this.requestInProgress = false;
|
||||
this.requestInProgressService.setRequestInProgress(false);
|
||||
this.workbasketService.triggerWorkBasketSaved();
|
||||
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@ import { WorkbasketAccessItemsResource } from 'app/models/workbasket-access-item
|
|||
import { ICONTYPES } from 'app/models/type';
|
||||
import { Links } from 'app/models/links';
|
||||
import { WorkbasketAccessItems } from 'app/models/workbasket-access-items';
|
||||
import { LinksWorkbasketSummary } from 'app/models/links-workbasket-summary';
|
||||
|
||||
import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||
import { MasterAndDetailService } from 'app/services/masterAndDetail/master-and-detail.service';
|
||||
import { PermissionService } from 'app/services/permission/permission.service';
|
||||
import { AlertService } from 'app/services/alert/alert.service';
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { SavingWorkbasketService } from 'app/services/saving-workbaskets/saving-workbaskets.service';
|
||||
|
||||
import { WorkbasketDetailsComponent } from './workbasket-details.component';
|
||||
|
@ -37,7 +37,9 @@ import { GeneralMessageModalComponent } from 'app/shared/general-message-modal/g
|
|||
import { MapValuesPipe } from 'app/pipes/mapValues/map-values.pipe';
|
||||
import { RemoveNoneTypePipe } from 'app/pipes/removeNoneType/remove-none-type.pipe';
|
||||
import { SelectWorkBasketPipe } from 'app/pipes/selectedWorkbasket/seleted-workbasket.pipe';
|
||||
import { LinksWorkbasketSummary } from '../../../models/links-workbasket-summary';
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-filter',
|
||||
|
@ -76,8 +78,8 @@ describe('WorkbasketDetailsComponent', () => {
|
|||
declarations: [WorkbasketDetailsComponent, NoAccessComponent, WorkbasketInformationComponent, SpinnerComponent,
|
||||
IconTypeComponent, MapValuesPipe, RemoveNoneTypePipe, AlertComponent, GeneralMessageModalComponent, AccessItemsComponent,
|
||||
DistributionTargetsComponent, FilterComponent, DualListComponent, DummyDetailComponent, SelectWorkBasketPipe],
|
||||
providers: [WorkbasketService, MasterAndDetailService, PermissionService,
|
||||
AlertService, ErrorModalService, SavingWorkbasketService]
|
||||
providers: [WorkbasketService, MasterAndDetailService, PermissionService, ErrorModalService, RequestInProgressService,
|
||||
AlertService, SavingWorkbasketService]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
|
|
@ -41,8 +41,7 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
|||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private masterAndDetailService: MasterAndDetailService,
|
||||
private permissionService: PermissionService,
|
||||
private errorModalService: ErrorModalService) { }
|
||||
private permissionService: PermissionService) { }
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
<button type="button" (click)="addWorkbasket()" data-toggle="tooltip" title="Add" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-plus green" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button *ngIf="workbasketIdSelected" type="button" (click)="copyWorkbasket()" data-toggle="tooltip" title="copy" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-copy" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button *ngIf="workbasketIdSelected" type="button" (click)="removeWorkbasket()" data-toggle="tooltip" title="Remove" class="btn btn-default remove">
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</button>
|
||||
<taskana-import-export-component [currentSelection]="'workbaskets'"></taskana-import-export-component>
|
||||
</div>
|
||||
<div class="pull-right margin-right">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.list-group-item {
|
||||
padding: 5px 0px;
|
||||
padding: 5px 0px 2px 1px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,6 @@ describe('WorkbasketListToolbarComponent', () => {
|
|||
component.workbaskets = new Array<WorkbasketSummary>(
|
||||
new WorkbasketSummary('1', 'key1', 'NAME1', 'description 1', 'owner 1',
|
||||
undefined, undefined, undefined, undefined, undefined, undefined, undefined, new Links({ 'href': 'selfLink' })));
|
||||
component.workbasketIdSelected = '1';
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
@ -93,23 +92,6 @@ describe('WorkbasketListToolbarComponent', () => {
|
|||
});
|
||||
|
||||
|
||||
it('should navigate to copy-workbasket when click on add copy workbasket', () => {
|
||||
const spy = spyOn(router, 'navigate');
|
||||
component.copyWorkbasket();
|
||||
expect(spy.calls.first().args[0][0].outlets.detail[0]).toBe('copy-workbasket');
|
||||
});
|
||||
|
||||
|
||||
it('should call to workbasket service to remove workbasket after click on remove workbasket', () => {
|
||||
const spy = spyOn(router, 'navigate');
|
||||
component.removeWorkbasket();
|
||||
expect(requestInProgressService.setRequestInProgress).toHaveBeenCalledWith(true);
|
||||
expect(workbasketService.deleteWorkbasket).toHaveBeenCalledWith('selfLink');
|
||||
expect(requestInProgressService.setRequestInProgress).toHaveBeenCalledWith(false);
|
||||
expect(workbasketService.triggerWorkBasketSaved).toHaveBeenCalled();
|
||||
expect(spy.calls.first().args[0][0]).toBe('/workbaskets');
|
||||
});
|
||||
|
||||
it('should emit performSorting when sorting is triggered', () => {
|
||||
let sort: SortingModel;
|
||||
const compareSort = new SortingModel();
|
||||
|
|
|
@ -38,8 +38,6 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
|||
|
||||
|
||||
@Input() workbaskets: Array<WorkbasketSummary>;
|
||||
@Input() workbasketIdSelected: string;
|
||||
@Output() workbasketIdSelectedChanged: string;
|
||||
@Output() performSorting = new EventEmitter<SortingModel>();
|
||||
@Output() performFilter = new EventEmitter<FilterModel>();
|
||||
workbasketServiceSubscription: Subscription;
|
||||
|
@ -66,31 +64,7 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
|||
}
|
||||
|
||||
addWorkbasket() {
|
||||
this.workbasketIdSelected = undefined;
|
||||
this.workbasketService.selectWorkBasket(undefined);
|
||||
this.router.navigate([{ outlets: { detail: ['new-workbasket'] } }], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
removeWorkbasket() {
|
||||
this.requestInProgressService.setRequestInProgress(true);
|
||||
this.workbasketService.deleteWorkbasket(this.findWorkbasketSelectedObject()._links.self.href).subscribe(response => {
|
||||
this.requestInProgressService.setRequestInProgress(false);
|
||||
this.workbasketService.triggerWorkBasketSaved();
|
||||
this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS,
|
||||
`Workbasket ${this.workbasketIdSelected} was removed successfully`))
|
||||
this.router.navigate(['/workbaskets']);
|
||||
}, error => {
|
||||
this.requestInProgressService.setRequestInProgress(false);
|
||||
this.errorModalService.triggerError(new ErrorModel(
|
||||
`There was an error deleting workbasket ${this.workbasketIdSelected}`, error.error.message))
|
||||
});
|
||||
}
|
||||
|
||||
copyWorkbasket() {
|
||||
this.workbasketIdSelected = undefined;
|
||||
this.router.navigate([{ outlets: { detail: ['copy-workbasket'] } }], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
private findWorkbasketSelectedObject() {
|
||||
return this.workbaskets.find(element => element.workbasketId === this.workbasketIdSelected);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<div class="workbasket-list-full-height">
|
||||
<div class="footer-space">
|
||||
<div #wbToolbar>
|
||||
<taskana-workbasket-list-toolbar [workbaskets]="workbaskets" (performFilter)="performFilter($event)" (performSorting)="performSorting ($event)"
|
||||
[(workbasketIdSelected)]="selectedId"></taskana-workbasket-list-toolbar>
|
||||
<taskana-workbasket-list-toolbar [workbaskets]="workbaskets" (performFilter)="performFilter($event)" (performSorting)="performSorting ($event)"></taskana-workbasket-list-toolbar>
|
||||
</div>
|
||||
<taskana-spinner [isRunning]="requestInProgress" class="centered-horizontally"></taskana-spinner>
|
||||
<div>
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { WorkbasketListComponent } from './administration/workbasket/master/list/workbasket-list.component';
|
||||
import { WorkbasketDetailsComponent } from './administration/workbasket/details/workbasket-details.component';
|
||||
import { MasterAndDetailComponent } from './shared/master-and-detail/master-and-detail.component';
|
||||
import { NoAccessComponent } from './administration/workbasket/details/noAccess/no-access.component';
|
||||
import {ClassificationListComponent} from './administration/classification/master/list/classification-list.component';
|
||||
import { ClassificationListComponent } from './administration/classification/master/list/classification-list.component';
|
||||
import { ClassificationDetailsComponent } from 'app/administration/classification/details/classification-details.component';
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{
|
||||
|
@ -30,15 +32,20 @@ const appRoutes: Routes = [
|
|||
]
|
||||
},
|
||||
{
|
||||
path: 'administration/classifications',
|
||||
component: MasterAndDetailComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: ClassificationListComponent,
|
||||
outlet: 'master'
|
||||
}
|
||||
]
|
||||
path: 'administration/classifications',
|
||||
component: MasterAndDetailComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: ClassificationListComponent,
|
||||
outlet: 'master'
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: ClassificationDetailsComponent,
|
||||
outlet: 'detail'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
|
|
|
@ -32,10 +32,11 @@ import { SortComponent } from './shared/sort/sort.component';
|
|||
import { GeneralMessageModalComponent } from './shared/general-message-modal/general-message-modal.component';
|
||||
import { PaginationComponent } from './administration/workbasket/master/list/pagination/pagination.component';
|
||||
import { ClassificationListComponent } from './administration/classification/master/list/classification-list.component';
|
||||
import { ClassificationDetailsComponent } from './administration/classification/details/classification-details.component';
|
||||
import { ImportExportComponent } from './shared/import-export/import-export.component';
|
||||
import { MasterAndDetailComponent } from './shared/master-and-detail/master-and-detail.component';
|
||||
import { ClassificationTypesSelectorComponent } from './shared/classification-types-selector/classification-types-selector.component';
|
||||
import { TreeComponent } from './shared/tree/tree.component';
|
||||
import { TaskanaTreeComponent } from './shared/tree/tree.component';
|
||||
|
||||
/**
|
||||
* Services
|
||||
|
@ -96,8 +97,9 @@ const DECLARATIONS = [
|
|||
PaginationComponent,
|
||||
ClassificationListComponent,
|
||||
ImportExportComponent,
|
||||
TreeComponent,
|
||||
TaskanaTreeComponent,
|
||||
ClassificationTypesSelectorComponent,
|
||||
ClassificationDetailsComponent,
|
||||
MapValuesPipe,
|
||||
RemoveNoneTypePipe,
|
||||
SelectWorkBasketPipe,
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
import { LinksClassification } from 'app/models/links-classfication';
|
||||
|
||||
export class ClassificationDefinition {
|
||||
constructor(public classificationId: string,
|
||||
public key: string,
|
||||
public parentId: string,
|
||||
public category: string,
|
||||
public domain: string,
|
||||
public isValidInDomain: boolean,
|
||||
public created: string,
|
||||
public modifies: string,
|
||||
public name: string,
|
||||
public description: string,
|
||||
public priority: number,
|
||||
public serviceLevel: string,
|
||||
public applicationEntryPoint: string,
|
||||
public custom1: string,
|
||||
public custom2: string,
|
||||
public custom3: string,
|
||||
public custom4: string,
|
||||
public custom5: string,
|
||||
public custom6: string,
|
||||
public custom7: string,
|
||||
public custom8: string) {
|
||||
constructor(public classificationId: string = undefined,
|
||||
public key: string = undefined,
|
||||
public parentId: string = undefined,
|
||||
public category: string = undefined,
|
||||
public domain: string = undefined,
|
||||
public type: string = undefined,
|
||||
public isValidInDomain: boolean = undefined,
|
||||
public created: string = undefined,
|
||||
public modifies: string = undefined,
|
||||
public name: string = undefined,
|
||||
public description: string = undefined,
|
||||
public priority: number = undefined,
|
||||
public serviceLevel: string = undefined,
|
||||
public applicationEntryPoint: string = undefined,
|
||||
public custom1: string = undefined,
|
||||
public custom2: string = undefined,
|
||||
public custom3: string = undefined,
|
||||
public custom4: string = undefined,
|
||||
public custom5: string = undefined,
|
||||
public custom6: string = undefined,
|
||||
public custom7: string = undefined,
|
||||
public custom8: string = undefined,
|
||||
public _links: LinksClassification = new LinksClassification()) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { Classification } from './classification';
|
||||
import { Links } from './links';
|
||||
|
||||
|
||||
export class ClassificationResource {
|
||||
constructor(
|
||||
public _embedded: {
|
||||
'classificationSummaryResourceList': Array<Classification>
|
||||
} = { 'classificationSummaryResourceList': [] },
|
||||
public _links: Links = new Links(),
|
||||
) {
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import { Links } from 'app/models/links';
|
||||
|
||||
export class Classification {
|
||||
constructor(public id: string,
|
||||
constructor(public classificationId: string,
|
||||
public key: string,
|
||||
public category: string,
|
||||
public type: string,
|
||||
|
@ -7,6 +9,7 @@ export class Classification {
|
|||
public name: string,
|
||||
public parentId: string,
|
||||
public priority: number,
|
||||
public serviceLevel: string) {
|
||||
public serviceLevel: string,
|
||||
public _links: Links = new Links()) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { Links } from './links';
|
||||
|
||||
export class LinksClassification extends Links {
|
||||
constructor(
|
||||
self = undefined,
|
||||
distributionTargets = undefined,
|
||||
accessItems = undefined,
|
||||
public getAllClassifications: { 'href': string } = undefined,
|
||||
public createClassification: { 'href': string } = undefined,
|
||||
public updateClassification: { 'href': string } = undefined,
|
||||
) { super(self, distributionTargets, accessItems) }
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { Classification } from 'app/models/classification';
|
||||
|
||||
export class TreeNode extends Classification {
|
||||
export class TreeNodeModel extends Classification {
|
||||
constructor(public id: string = '',
|
||||
public key: string = '',
|
||||
public category: string = '',
|
||||
|
@ -10,7 +10,7 @@ export class TreeNode extends Classification {
|
|||
public parentId: string = '',
|
||||
public priority: number = 0,
|
||||
public serviceLevel: string = '',
|
||||
public children: Array<TreeNode> = undefined) {
|
||||
public children: Array<TreeNodeModel> = undefined) {
|
||||
super(id, key, category, type, domain, name, parentId, priority, serviceLevel);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,18 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|||
import { environment } from '../../../environments/environment';
|
||||
|
||||
import { Classification } from 'app/models/classification';
|
||||
import { TreeNode } from 'app/models/tree-node';
|
||||
import { TreeNodeModel } from 'app/models/tree-node';
|
||||
import { ClassificationDefinition } from 'app/models/classification-definition';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { ClassificationResource } from '../../models/classification-resource';
|
||||
|
||||
@Injectable()
|
||||
export class ClassificationsService {
|
||||
|
||||
url = environment.taskanaRestUrl + '/v1/classifications';
|
||||
classificationSelected = new Subject<string>();
|
||||
|
||||
httpOptions = {
|
||||
headers: new HttpHeaders({
|
||||
|
@ -19,39 +23,65 @@ export class ClassificationsService {
|
|||
})
|
||||
};
|
||||
|
||||
private classificationRef: Observable<Array<Classification>>;
|
||||
private classificationRef: Observable<ClassificationResource>;
|
||||
private classificationTypes: Array<string>;
|
||||
|
||||
constructor(private httpClient: HttpClient) {
|
||||
}
|
||||
|
||||
// GET
|
||||
getClassifications(forceRequest = false, type = 'TASK', domain = ''): Observable<Array<TreeNode>> {
|
||||
getClassifications(forceRequest = false, type = 'TASK', domain = ''): Observable<Array<TreeNodeModel>> {
|
||||
if (!forceRequest && this.classificationRef) {
|
||||
return this.classificationRef.map((response: Array<Classification>) => {
|
||||
return this.buildHierarchy(response, type, domain);
|
||||
return this.classificationRef.map((response: ClassificationResource) => {
|
||||
if (!response._embedded) {
|
||||
return [];
|
||||
}
|
||||
return this.buildHierarchy(response._embedded.classificationSummaryResourceList, type, domain);
|
||||
});
|
||||
}
|
||||
this.classificationRef = this.httpClient.get<Array<Classification>>(`${environment.taskanaRestUrl}/v1/classifications`,
|
||||
this.classificationRef = this.httpClient.get<ClassificationResource>(`${environment.taskanaRestUrl}/v1/classifications`,
|
||||
this.httpOptions);
|
||||
|
||||
return this.classificationRef.map((response: Array<Classification>) => {
|
||||
return this.buildHierarchy(response, type, domain);
|
||||
return this.classificationRef.map((response: ClassificationResource) => {
|
||||
if (!response._embedded) {
|
||||
return [];
|
||||
}
|
||||
return this.buildHierarchy(response._embedded.classificationSummaryResourceList, type, domain);
|
||||
});
|
||||
}
|
||||
|
||||
getClassificationTypes(): Observable<Map<string, string>> {
|
||||
const typesSubject = new Subject<Map<string, string>>();
|
||||
this.classificationRef.subscribe((classifications: Array<Classification>) => {
|
||||
// GET
|
||||
getClassification(id: string): Observable<ClassificationDefinition> {
|
||||
return this.httpClient.get<ClassificationDefinition>(`${environment.taskanaRestUrl}/v1/classifications/${id}`, this.httpOptions);
|
||||
}
|
||||
|
||||
getClassificationTypes(): Observable<Array<string>> {
|
||||
const typesSubject = new Subject<Array<string>>();
|
||||
this.classificationRef.subscribe((classifications: ClassificationResource) => {
|
||||
if (!classifications._embedded) {
|
||||
return typesSubject;
|
||||
}
|
||||
const types = new Map<string, string>();
|
||||
classifications.forEach(element => {
|
||||
classifications._embedded.classificationSummaryResourceList.forEach(element => {
|
||||
types.set(element.type, element.type);
|
||||
});
|
||||
typesSubject.next(types);
|
||||
|
||||
typesSubject.next(this.map2Array(types));
|
||||
});
|
||||
return typesSubject.asObservable();
|
||||
}
|
||||
|
||||
// #region "Service extras"
|
||||
selectClassification(id: string) {
|
||||
this.classificationSelected.next(id);
|
||||
}
|
||||
|
||||
getSelectedClassification(): Observable<string> {
|
||||
return this.classificationSelected.asObservable();
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
private buildHierarchy(classifications: Array<Classification>, type: string, domain: string) {
|
||||
const roots = []
|
||||
const children = new Array<any>();
|
||||
|
@ -73,12 +103,22 @@ export class ClassificationsService {
|
|||
|
||||
|
||||
private findChildren(parent: any, children: Array<any>) {
|
||||
if (children[parent.id]) {
|
||||
parent.children = children[parent.id];
|
||||
if (children[parent.classificationId]) {
|
||||
parent.children = children[parent.classificationId];
|
||||
for (let index = 0, len = parent.children.length; index < len; ++index) {
|
||||
this.findChildren(parent.children[index], children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private map2Array(map: Map<string, string>): Array<string> {
|
||||
const returnArray = [];
|
||||
|
||||
map.forEach((entryVal, entryKey) => {
|
||||
returnArray.push(entryKey);
|
||||
});
|
||||
|
||||
return returnArray;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<div class="dropdown clearfix btn-group">
|
||||
|
||||
<button type="button" class="btn btn-default"> {{classificationTypeSelected}}</button>
|
||||
<button type="button" class="btn btn-default"> {{classificationTypeSelected}}</button>
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right sortby-dropdown popup" aria-labelledby="sortingDropdown">
|
||||
<li *ngFor="let classificationType of classificationTypes | mapValues">
|
||||
<a (click)="select(classificationType.key)">
|
||||
<li *ngFor="let classificationType of classificationTypes">
|
||||
<a (click)="select(classificationType)">
|
||||
<label>
|
||||
<span class="glyphicon {{classificationTypeSelected === classificationType.key? 'glyphicon-check': 'glyphicon-unchecked'}} blue"
|
||||
<span class="glyphicon {{classificationTypeSelected === classificationType? 'glyphicon-check': 'glyphicon-unchecked'}} blue"
|
||||
aria-hidden="true"></span>
|
||||
{{classificationType.key}}
|
||||
{{classificationType}}
|
||||
</label>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ClassificationTypesSelectorComponent } from './classification-types-selector.component';
|
||||
import { MapValuesPipe } from 'app/pipes/mapValues/map-values.pipe';
|
||||
|
||||
describe('ClassificationTypesSelectorComponent', () => {
|
||||
let component: ClassificationTypesSelectorComponent;
|
||||
|
@ -9,9 +8,9 @@ describe('ClassificationTypesSelectorComponent', () => {
|
|||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ClassificationTypesSelectorComponent, MapValuesPipe ]
|
||||
declarations: [ClassificationTypesSelectorComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
|||
})
|
||||
export class ClassificationTypesSelectorComponent implements OnInit {
|
||||
|
||||
@Input() classificationTypes: Map<string, string> = new Map<string, string>();
|
||||
@Input() classificationTypes: Array<string> = [];
|
||||
@Input()
|
||||
classificationTypeSelected: string = undefined;
|
||||
@Output()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<tree-root [nodes]="treeNodes" [state]="state" [options]="options">
|
||||
<tree-root #tree [nodes]="treeNodes" [options]="options" (activate)="onActivate($event)">
|
||||
<ng-template #treeNodeTemplate let-node let-index="index">
|
||||
<span class="text-top">
|
||||
<svg-icon class="blue small fa-fw" src="./assets/icons/{{node.data.category === 'EXTERN'? 'external':
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Input, Component } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TreeComponent } from './tree.component';
|
||||
import { TaskanaTreeComponent } from './tree.component';
|
||||
|
||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
@ -16,23 +16,26 @@ class TreeVendorComponent {
|
|||
@Input() options;
|
||||
@Input() state;
|
||||
@Input() nodes;
|
||||
treeModel = {
|
||||
getActiveNode() { }
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:enable:component-selector
|
||||
fdescribe('TreeComponent', () => {
|
||||
let component: TreeComponent;
|
||||
let fixture: ComponentFixture<TreeComponent>;
|
||||
|
||||
describe('TaskanaTreeComponent', () => {
|
||||
let component: TaskanaTreeComponent;
|
||||
let fixture: ComponentFixture<TaskanaTreeComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [AngularSvgIconModule, HttpClientModule, HttpModule],
|
||||
declarations: [TreeComponent, TreeVendorComponent]
|
||||
declarations: [TaskanaTreeComponent, TreeVendorComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TreeComponent);
|
||||
fixture = TestBed.createComponent(TaskanaTreeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { TreeNode } from 'app/models/tree-node';
|
||||
import { TREE_ACTIONS, KEYS, IActionMapping, ITreeOptions, ITreeState } from 'angular-tree-component';
|
||||
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, AfterViewChecked } from '@angular/core';
|
||||
import { TreeNodeModel } from 'app/models/tree-node';
|
||||
|
||||
import { TREE_ACTIONS, KEYS, IActionMapping, ITreeOptions, ITreeState, TreeComponent, TreeNode } from 'angular-tree-component';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-tree',
|
||||
templateUrl: './tree.component.html',
|
||||
styleUrls: ['./tree.component.scss']
|
||||
})
|
||||
export class TreeComponent implements OnInit {
|
||||
export class TaskanaTreeComponent implements OnInit, AfterViewChecked {
|
||||
|
||||
@Input() treeNodes: TreeNode;
|
||||
@Output() treeNodesChange = new EventEmitter<Array<TreeNode>>();
|
||||
|
||||
@ViewChild('tree')
|
||||
private tree: TreeComponent;
|
||||
|
||||
@Input() treeNodes: TreeNodeModel;
|
||||
@Output() treeNodesChange = new EventEmitter<Array<TreeNodeModel>>();
|
||||
@Input() selectNodeId: string;
|
||||
@Output() selectNodeIdChanged = new EventEmitter<string>();
|
||||
|
||||
options: ITreeOptions = {
|
||||
displayField: 'name',
|
||||
idField: 'id',
|
||||
idField: 'classificationId',
|
||||
actionMapping: {
|
||||
keys: {
|
||||
[KEYS.ENTER]: (tree, node, $event) => {
|
||||
|
@ -27,14 +34,41 @@ export class TreeComponent implements OnInit {
|
|||
levelPadding: 20
|
||||
}
|
||||
|
||||
state: ITreeState = {
|
||||
activeNodeIds: { ['']: true },
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
this.selectNode(this.selectNodeId);
|
||||
}
|
||||
|
||||
ngAfterViewChecked(): void {
|
||||
if (this.selectNodeId && !this.tree.treeModel.getActiveNode()) {
|
||||
this.selectNode(this.selectNodeId);
|
||||
} else if (!this.selectNodeId && this.tree.treeModel.getActiveNode()) {
|
||||
this.unSelectActiveNode();
|
||||
}
|
||||
}
|
||||
|
||||
onActivate(treeNode: any) {
|
||||
this.selectNodeIdChanged.emit(treeNode.node.data.classificationId + '');
|
||||
}
|
||||
|
||||
private selectNode(nodeId: string) {
|
||||
if (nodeId) {
|
||||
const selectedNode = this.getSelectedNode(nodeId)
|
||||
if (selectedNode) {
|
||||
selectedNode.setIsActive(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unSelectActiveNode() {
|
||||
const activeNode = this.tree.treeModel.getActiveNode();
|
||||
activeNode.setIsActive(false);
|
||||
activeNode.blur();
|
||||
}
|
||||
|
||||
private getSelectedNode(nodeId: string) {
|
||||
return this.tree.treeModel.getNodeById(nodeId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
.ng-invalid:not(form) {
|
||||
border-color: $invalid;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
|
@ -8,4 +7,8 @@
|
|||
.required-text {
|
||||
padding-left: 15px;
|
||||
color: $invalid;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: none;
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ svg-icon.fa-fw > svg {
|
|||
}
|
||||
|
||||
.panel-heading{
|
||||
min-height: 60px;
|
||||
padding: 8px 15px 4px 15px;
|
||||
}
|
||||
.panel {
|
||||
border-radius: 0px;
|
||||
|
|
Loading…
Reference in New Issue