TSK-1298: TSK-1292: fixed several bugs in taskana-admin > classifications

This commit is contained in:
Mustapha Zorgati 2020-06-30 07:26:52 +02:00
parent db9175a3f6
commit 135d78ff54
89 changed files with 1265 additions and 3468 deletions

View File

@ -5,12 +5,10 @@ import java.lang.annotation.Inherited;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/** Use this annotation to test with a spring context and a standardized configuration. */ /** Use this annotation to test with a spring context and a standardized configuration. */
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@ -20,7 +18,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
@DirtiesContext(classMode = ClassMode.AFTER_CLASS) @DirtiesContext(classMode = ClassMode.AFTER_CLASS)
@Inherited @Inherited
@ActiveProfiles({"test"}) @ActiveProfiles({"test"})
@ExtendWith(SpringExtension.class)
@SpringBootTest( @SpringBootTest(
classes = CommonTestConfiguration.class, classes = CommonTestConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

View File

@ -5,12 +5,10 @@ import java.lang.annotation.Inherited;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import pro.taskana.TestConfiguration; import pro.taskana.TestConfiguration;
@ -22,7 +20,6 @@ import pro.taskana.TestConfiguration;
@DirtiesContext(classMode = ClassMode.AFTER_CLASS) @DirtiesContext(classMode = ClassMode.AFTER_CLASS)
@Inherited @Inherited
@ActiveProfiles({"test"}) @ActiveProfiles({"test"})
@ExtendWith(SpringExtension.class)
@SpringBootTest( @SpringBootTest(
classes = TestConfiguration.class, classes = TestConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

3700
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,8 @@
"build:prod-silent": "ng build --prod=true --no-progress", "build:prod-silent": "ng build --prod=true --no-progress",
"test": "ng test --karma-config karma.conf.js --watch=false --browsers Firefox", "test": "ng test --karma-config karma.conf.js --watch=false --browsers Firefox",
"test:watch": "ng test --karma-config karma.conf.js --browsers Chrome", "test:watch": "ng test --karma-config karma.conf.js --browsers Chrome",
"lint": "eslint --ext .ts src" "lint": "eslint --ext .ts src",
"lint:fix": "eslint --ext .ts src --fix"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {

View File

@ -9,7 +9,6 @@ import { of } from 'rxjs';
import { NgxsModule } from '@ngxs/store'; import { NgxsModule } from '@ngxs/store';
import { AccessItemsManagementComponent } from './access-items-management.component'; import { AccessItemsManagementComponent } from './access-items-management.component';
describe('AccessItemsManagementComponent', () => { describe('AccessItemsManagementComponent', () => {
let component: AccessItemsManagementComponent; let component: AccessItemsManagementComponent;
let fixture: ComponentFixture<AccessItemsManagementComponent>; let fixture: ComponentFixture<AccessItemsManagementComponent>;
@ -23,7 +22,6 @@ describe('AccessItemsManagementComponent', () => {
}); });
}; };
beforeEach(done => { beforeEach(done => {
configureTests(configure).then(testBed => { configureTests(configure).then(testBed => {
fixture = testBed.createComponent(AccessItemsManagementComponent); fixture = testBed.createComponent(AccessItemsManagementComponent);

View File

@ -87,7 +87,7 @@
</button> </button>
<ul class="dropdown-menu dropdown-menu" aria-labelledby="dropdownMenu"> <ul class="dropdown-menu dropdown-menu" aria-labelledby="dropdownMenu">
<li> <li>
<a *ngFor="let category of getAvailableCategories(classification.type)" (click)="selectCategory(category)"> <a *ngFor="let category of getAvailableCategories(classification.type) | async" (click)="selectCategory(category)">
<span class="text-top"> <span class="text-top">
<svg-icon class="blue fa-fw" src="{{(getCategoryIcon(category) | async)?.name}}" data-toggle="tooltip" <svg-icon class="blue fa-fw" src="{{(getCategoryIcon(category) | async)?.name}}" data-toggle="tooltip"
[title]="(getCategoryIcon(category) | async)?.text"></svg-icon> [title]="(getCategoryIcon(category) | async)?.text"></svg-icon>

View File

@ -7,7 +7,6 @@ import { AngularSvgIconModule } from 'angular-svg-icon';
import { configureTests } from 'app/app.test.configuration'; import { configureTests } from 'app/app.test.configuration';
import { NgxsModule, Store } from '@ngxs/store'; import { NgxsModule, Store } from '@ngxs/store';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { LinksClassification } from 'app/shared/models/links-classfication';
import { MasterAndDetailService } from 'app/shared/services/master-and-detail/master-and-detail.service'; import { MasterAndDetailService } from 'app/shared/services/master-and-detail/master-and-detail.service';
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service'; import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
@ -20,7 +19,6 @@ import { ClassificationDetailsComponent } from './classification-details.compone
import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { ACTION } from '../../../shared/models/action'; import { ACTION } from '../../../shared/models/action';
@Component({ @Component({
selector: 'taskana-dummy-detail', selector: 'taskana-dummy-detail',
template: 'dummydetail' template: 'dummydetail'
@ -31,9 +29,10 @@ class DummyDetailComponent {
describe('ClassificationDetailsComponent', () => { describe('ClassificationDetailsComponent', () => {
let component: ClassificationDetailsComponent; let component: ClassificationDetailsComponent;
let fixture: ComponentFixture<ClassificationDetailsComponent>; let fixture: ComponentFixture<ClassificationDetailsComponent>;
const treeNodes: Array<TreeNodeModel> = new Array(new TreeNodeModel()); const treeNodes: TreeNodeModel[] = [];
let classificationsService; let classificationsService;
const locationSpy: jasmine.SpyObj<Location> = jasmine.createSpyObj('Location', ['go']); const locationSpy: jasmine.SpyObj<Location> = jasmine.createSpyObj('Location', ['go']);
const storeSpy: jasmine.SpyObj<Store> = jasmine.createSpyObj('Store', ['select', 'dispatch']); const storeSpy: jasmine.SpyObj<Store> = jasmine.createSpyObj('Store', ['select', 'dispatch']);
const configure = (testBed: TestBed) => { const configure = (testBed: TestBed) => {
@ -61,7 +60,6 @@ describe('ClassificationDetailsComponent', () => {
} }
}); });
fixture = testBed.createComponent(ClassificationDetailsComponent); fixture = testBed.createComponent(ClassificationDetailsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
@ -93,13 +91,11 @@ describe('ClassificationDetailsComponent', () => {
modified: '2020-06-22T12:51:31.164Z', modified: '2020-06-22T12:51:31.164Z',
description: 'Beratungsprotokoll' description: 'Beratungsprotokoll'
}; };
component.classification._links = new LinksClassification({ self: '' });
fixture.detectChanges(); fixture.detectChanges();
done(); done();
}); });
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });

View File

@ -1,8 +1,7 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Select, Store } from '@ngxs/store'; import { Select, Store } from '@ngxs/store';
import { Observable, Subject, zip, combineLatest } from 'rxjs'; import { combineLatest, Observable, Subject } from 'rxjs';
import { ClassificationDefinition, customFieldCount } from 'app/shared/models/classification-definition';
import { ACTION } from 'app/shared/models/action'; import { ACTION } from 'app/shared/models/action';
import { highlight } from 'theme/animations/validation.animation'; import { highlight } from 'theme/animations/validation.animation';
@ -24,11 +23,15 @@ import { NotificationService } from '../../../shared/services/notifications/noti
import { ClassificationCategoryImages, import { ClassificationCategoryImages,
CustomField, CustomField,
getCustomFields } from '../../../shared/models/customisation'; getCustomFields } from '../../../shared/models/customisation';
import { Classification } from '../../../shared/models/classification';
import { customFieldCount } from '../../../shared/models/classification-summary';
import { CategoriesResponse } from '../../../shared/services/classification-categories/classification-categories.service';
import { CreateClassification, import { CreateClassification,
RemoveSelectedClassification, RemoveSelectedClassification,
RestoreSelectedClassification, RestoreSelectedClassification,
SaveClassification, SelectClassification, SaveClassification,
SelectClassification,
SetActiveAction } from '../../../shared/store/classification-store/classification.actions'; SetActiveAction } from '../../../shared/store/classification-store/classification.actions';
@Component({ @Component({
@ -38,14 +41,14 @@ import { CreateClassification,
styleUrls: ['./classification-details.component.scss'] styleUrls: ['./classification-details.component.scss']
}) })
export class ClassificationDetailsComponent implements OnInit, OnDestroy { export class ClassificationDetailsComponent implements OnInit, OnDestroy {
classification: ClassificationDefinition; classification: Classification;
badgeMessage = ''; badgeMessage = '';
requestInProgress = false; requestInProgress = false;
@Select(ClassificationSelectors.selectCategories) categories$: Observable<string[]>; @Select(ClassificationSelectors.selectCategories) categories$: Observable<string[]>;
@Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable<ClassificationCategoryImages>; @Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable<ClassificationCategoryImages>;
@Select(ClassificationSelectors.selectedClassificationType) selectedClassificationType$: Observable<string>; @Select(ClassificationSelectors.selectedClassificationType) selectedClassificationType$: Observable<string>;
@Select(ClassificationSelectors.selectClassificationTypesObject) classificationTypes$: Observable<Object>; @Select(ClassificationSelectors.selectClassificationTypesObject) classificationTypes$: Observable<CategoriesResponse>;
@Select(ClassificationSelectors.selectedClassification) selectedClassification$: Observable<ClassificationDefinition>; @Select(ClassificationSelectors.selectedClassification) selectedClassification$: Observable<Classification>;
@Select(ClassificationSelectors.activeAction) action$: Observable<ACTION>; @Select(ClassificationSelectors.activeAction) action$: Observable<ACTION>;
spinnerIsRunning = false; spinnerIsRunning = false;
@ -74,7 +77,7 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
getCustomFields(customFieldCount) getCustomFields(customFieldCount)
); );
combineLatest(this.selectedClassification$, this.action$).pipe(takeUntil(this.destroy$)) combineLatest([this.selectedClassification$, this.action$]).pipe(takeUntil(this.destroy$))
.subscribe(([classification, action]) => { .subscribe(([classification, action]) => {
this.action = action; this.action = action;
if (this.action === ACTION.CREATE) { if (this.action === ACTION.CREATE) {
@ -92,30 +95,11 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
} }
}); });
this.action$.pipe(takeUntil(this.destroy$)).subscribe(data => {
this.action = data;
if (this.action === ACTION.CREATE) {
this.selectedClassification$.pipe(take(1)).subscribe(initialClassification => {
this.classification = { ...initialClassification };
});
this.badgeMessage = 'Creating new classification';
} else if (this.action === ACTION.COPY) {
this.badgeMessage = `Copying Classification: ${this.classification.name}`;
} else {
this.badgeMessage = '';
}
});
this.importExportService.getImportingFinished().pipe(takeUntil(this.destroy$)).subscribe(() => { this.importExportService.getImportingFinished().pipe(takeUntil(this.destroy$)).subscribe(() => {
this.store.dispatch(new SelectClassification(this.classification.classificationId)); this.store.dispatch(new SelectClassification(this.classification.classificationId));
}); });
} }
backClicked(): void {
this.location.go(this.location.path().replace(/(classifications).*/g, 'classifications'));
}
removeClassification() { removeClassification() {
this.notificationsService.showDialog(`You are going to delete classification: ${this.classification.key}. Can you confirm this action?`, this.notificationsService.showDialog(`You are going to delete classification: ${this.classification.key}. Can you confirm this action?`,
this.removeClassificationConfirmation.bind(this)); this.removeClassificationConfirmation.bind(this));
@ -137,8 +121,12 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
onRestore() { onRestore() {
this.formsValidatorService.formSubmitAttempt = false; this.formsValidatorService.formSubmitAttempt = false;
if (this.action === ACTION.CREATE) { if (this.action === ACTION.CREATE) {
this.classification = new ClassificationDefinition(); this.categories$.pipe(take(1)).subscribe(categories => {
this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT); const category = categories[0];
const { type, created, modified, domain, parentId, parentKey } = this.classification;
this.classification = { type, created, category, modified, domain, parentId, parentKey };
this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT);
});
} else { } else {
this.store.dispatch( this.store.dispatch(
new RestoreSelectedClassification(this.classification.classificationId) new RestoreSelectedClassification(this.classification.classificationId)
@ -149,8 +137,12 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
} }
onCopy() { onCopy() {
this.store.dispatch(new SetActiveAction(ACTION.COPY)); if (this.action !== ACTION.CREATE) {
this.classification.key = null; this.store.dispatch(new SetActiveAction(ACTION.COPY));
this.classification.key = null;
} else {
this.notificationsService.showToast(NOTIFICATION_TYPES.WARNING_CANT_COPY);
}
} }
selectCategory(category: string) { selectCategory(category: string) {
@ -177,6 +169,19 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
return this.domainService.getSelectedDomainValue() === ''; return this.domainService.getSelectedDomainValue() === '';
} }
getClassificationCustom(customNumber: number): string {
return `custom${customNumber}`;
}
getAvailableCategories(type: string): Observable<string[]> {
return this.classificationTypes$.pipe(take(1), map(classTypes => classTypes[type]));
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
private async onSave() { private async onSave() {
this.requestInProgressService.setRequestInProgress(true); this.requestInProgressService.setRequestInProgress(true);
if (this.action) { if (this.action) {
@ -188,6 +193,10 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
NOTIFICATION_TYPES.SUCCESS_ALERT_2, NOTIFICATION_TYPES.SUCCESS_ALERT_2,
new Map<string, string>([['classificationKey', store.classification.selectedClassification.key]]) new Map<string, string>([['classificationKey', store.classification.selectedClassification.key]])
); );
this.location.go(this.location.path().replace(
/(classifications).*/g,
`classifications/(detail:${store.classification.selectedClassification.classificationId})`
));
this.afterRequest(); this.afterRequest();
}, error => { }, error => {
this.notificationsService.triggerError(NOTIFICATION_TYPES.CREATE_ERR, error); this.notificationsService.triggerError(NOTIFICATION_TYPES.CREATE_ERR, error);
@ -195,12 +204,14 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
}); });
} else { } else {
try { try {
this.store.dispatch(new SaveClassification(this.classification)); this.store.dispatch(new SaveClassification(this.classification))
this.afterRequest(); .pipe(take(1)).subscribe(() => {
this.notificationsService.showToast( this.afterRequest();
NOTIFICATION_TYPES.SUCCESS_ALERT_3, this.notificationsService.showToast(
new Map<string, string>([['classificationKey', this.classification.key]]) NOTIFICATION_TYPES.SUCCESS_ALERT_3,
); new Map<string, string>([['classificationKey', this.classification.key]])
);
});
} catch (error) { } catch (error) {
this.notificationsService.triggerError(NOTIFICATION_TYPES.SAVE_ERR, error); this.notificationsService.triggerError(NOTIFICATION_TYPES.SAVE_ERR, error);
this.afterRequest(); this.afterRequest();
@ -210,7 +221,6 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
private afterRequest() { private afterRequest() {
this.requestInProgressService.setRequestInProgress(false); this.requestInProgressService.setRequestInProgress(false);
this.classificationsService.triggerClassificationSaved();
} }
private removeClassificationConfirmation() { private removeClassificationConfirmation() {
@ -227,22 +237,4 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
}); });
this.location.go(this.location.path().replace(/(classifications).*/g, 'classifications')); this.location.go(this.location.path().replace(/(classifications).*/g, 'classifications'));
} }
getClassificationCustom(customNumber: number): string {
return `custom${customNumber}`;
}
getAvailableCategories(type: string) {
let returnCategories: string[] = [];
this.classificationTypes$.pipe(take(1)).subscribe(classTypes => {
returnCategories = classTypes[type];
});
return returnCategories;
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
} }

View File

@ -53,7 +53,7 @@
<!-- CLASSIFICATION TREE --> <!-- CLASSIFICATION TREE -->
<taskana-spinner class="col-xs-12" [isRunning]="requestInProgress" positionClass="centered-spinner-whole-screen"></taskana-spinner> <taskana-spinner class="col-xs-12" [isRunning]="requestInProgress" positionClass="centered-spinner-whole-screen"></taskana-spinner>
<taskana-tree class="col-xs-12" *ngIf="(classifications && classifications.length) else empty_classifications" <taskana-tree class="col-xs-12" *ngIf="(classifications && classifications.length) else empty_classifications"
[filterText]="inputValue" [filterIcon]="selectedCategory"></taskana-tree> [filterText]="inputValue" [filterIcon]="selectedCategory" (switchTaskanaSpinnerEmit)="requestInProgress=$event"></taskana-tree>
<ng-template #empty_classifications> <ng-template #empty_classifications>
<div *ngIf="!requestInProgress" class="col-xs-12 container-no-items center-block"> <div *ngIf="!requestInProgress" class="col-xs-12 container-no-items center-block">
<h3 class="grey">There are no classifications</h3> <h3 class="grey">There are no classifications</h3>

View File

@ -24,7 +24,6 @@ import { MatRadioModule } from '@angular/material/radio';
import { ClassificationListComponent } from './classification-list.component'; import { ClassificationListComponent } from './classification-list.component';
import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { NotificationService } from '../../../shared/services/notifications/notification.service';
@Component({ @Component({
selector: 'taskana-dummy-detail', selector: 'taskana-dummy-detail',
template: 'dummydetail' template: 'dummydetail'
@ -39,7 +38,7 @@ const routes: Routes = [
describe('ClassificationListComponent', () => { describe('ClassificationListComponent', () => {
let component: ClassificationListComponent; let component: ClassificationListComponent;
let fixture: ComponentFixture<ClassificationListComponent>; let fixture: ComponentFixture<ClassificationListComponent>;
const treeNodes: Array<TreeNodeModel> = new Array(new TreeNodeModel()); const treeNodes: TreeNodeModel[] = [{ children: [] }];
let classificationsService; let classificationsService;
const configure = (testBed: TestBed) => { const configure = (testBed: TestBed) => {

View File

@ -13,10 +13,10 @@ import { ClassificationSelectors } from 'app/shared/store/classification-store/c
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { ClassificationCategoryImages } from '../../../shared/models/customisation'; import { ClassificationCategoryImages } from '../../../shared/models/customisation';
import { GetClassifications, SetActiveAction } from '../../../shared/store/classification-store/classification.actions'; import { GetClassifications,
SetActiveAction } from '../../../shared/store/classification-store/classification.actions';
import { ACTION } from '../../../shared/models/action'; import { ACTION } from '../../../shared/models/action';
import { TreeNodeModel } from '../../../shared/models/tree-node'; import { ClassificationSummary } from '../../../shared/models/classification-summary';
@Component({ @Component({
selector: 'taskana-classification-list', selector: 'taskana-classification-list',
@ -32,13 +32,13 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
@Select(ClassificationSelectors.classificationTypes) classificationTypes$: Observable<string[]>; @Select(ClassificationSelectors.classificationTypes) classificationTypes$: Observable<string[]>;
@Select(ClassificationSelectors.selectedClassificationType) classificationTypeSelected$: Observable<string>; @Select(ClassificationSelectors.selectedClassificationType) classificationTypeSelected$: Observable<string>;
@Select(ClassificationSelectors.selectCategories) categories$: Observable<string[]>; @Select(ClassificationSelectors.selectCategories) categories$: Observable<string[]>;
@Select(ClassificationSelectors.classifications) classifications$: Observable<TreeNodeModel[]>; @Select(ClassificationSelectors.classifications) classifications$: Observable<ClassificationSummary[]>;
@Select(ClassificationSelectors.activeAction) activeAction$: Observable<ACTION>; @Select(ClassificationSelectors.activeAction) activeAction$: Observable<ACTION>;
@Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable<ClassificationCategoryImages>; @Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable<ClassificationCategoryImages>;
action: ACTION; action: ACTION;
destroy$ = new Subject<void>(); destroy$ = new Subject<void>();
classifications: TreeNodeModel[]; classifications: ClassificationSummary[];
constructor( constructor(
private classificationService: ClassificationsService, private classificationService: ClassificationsService,
@ -61,18 +61,9 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.classifications$.pipe(takeUntil(this.destroy$)).subscribe(classifications => { this.classifications$.pipe(takeUntil(this.destroy$)).subscribe(classifications => {
if (classifications) { this.classifications = classifications;
this.classifications = [...classifications];
}
}); });
this.classificationService
.classificationSavedTriggered()
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.store.dispatch(new GetClassifications());
});
this.classificationTypeSelected$ this.classificationTypeSelected$
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe(() => { .subscribe(() => {

View File

@ -4,11 +4,11 @@ import { Observable, Subject } from 'rxjs';
import { Select, Store } from '@ngxs/store'; import { Select, Store } from '@ngxs/store';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { ClassificationSelectors } from '../../../shared/store/classification-store/classification.selectors'; import { ClassificationSelectors } from '../../../shared/store/classification-store/classification.selectors';
import { ClassificationDefinition } from '../../../shared/models/classification-definition';
import { ACTION } from '../../../shared/models/action'; import { ACTION } from '../../../shared/models/action';
import { GetClassifications, import { GetClassifications,
SelectClassification, SelectClassification,
SetActiveAction } from '../../../shared/store/classification-store/classification.actions'; SetActiveAction } from '../../../shared/store/classification-store/classification.actions';
import { Classification } from '../../../shared/models/classification';
@Component({ @Component({
selector: 'app-classification-overview', selector: 'app-classification-overview',
@ -17,7 +17,7 @@ import { GetClassifications,
}) })
export class ClassificationOverviewComponent implements OnInit, OnDestroy { export class ClassificationOverviewComponent implements OnInit, OnDestroy {
showDetail = false; showDetail = false;
@Select(ClassificationSelectors.selectedClassification) selectedClassification$: Observable<ClassificationDefinition>; @Select(ClassificationSelectors.selectedClassification) selectedClassification$: Observable<Classification>;
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
routerParams: any; routerParams: any;

View File

@ -2,17 +2,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NgxsModule } from '@ngxs/store'; import { NgxsModule } from '@ngxs/store';
import { MatRadioModule } from '@angular/material/radio'; import { MatRadioModule } from '@angular/material/radio';
import { Location } from '@angular/common';
import { ClassificationTypesSelectorComponent } from './classification-types-selector.component'; import { ClassificationTypesSelectorComponent } from './classification-types-selector.component';
describe('ClassificationTypesSelectorComponent', () => { describe('ClassificationTypesSelectorComponent', () => {
let component: ClassificationTypesSelectorComponent; let component: ClassificationTypesSelectorComponent;
let fixture: ComponentFixture<ClassificationTypesSelectorComponent>; let fixture: ComponentFixture<ClassificationTypesSelectorComponent>;
const locationSpy: jasmine.SpyObj<Location> = jasmine.createSpyObj('Location', ['go']);
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NgxsModule.forRoot(), MatRadioModule], imports: [NgxsModule.forRoot(), MatRadioModule],
declarations: [ClassificationTypesSelectorComponent], declarations: [ClassificationTypesSelectorComponent],
providers: [] providers: [
{ provide: Location, useValue: locationSpy },
]
}).compileComponents(); }).compileComponents();
})); }));

View File

@ -3,7 +3,7 @@ import { Observable } from 'rxjs';
import { Store, Select } from '@ngxs/store'; import { Store, Select } from '@ngxs/store';
import { ClassificationSelectors } from 'app/shared/store/classification-store/classification.selectors'; import { ClassificationSelectors } from 'app/shared/store/classification-store/classification.selectors';
import { SetSelectedClassificationType } from 'app/shared/store/classification-store/classification.actions'; import { SetSelectedClassificationType } from 'app/shared/store/classification-store/classification.actions';
import { Location } from '@angular/common';
@Component({ @Component({
selector: 'taskana-classification-types-selector', selector: 'taskana-classification-types-selector',
@ -14,9 +14,10 @@ export class ClassificationTypesSelectorComponent {
@Select(ClassificationSelectors.selectedClassificationType) classificationTypeSelected$: Observable<string>; @Select(ClassificationSelectors.selectedClassificationType) classificationTypeSelected$: Observable<string>;
@Select(ClassificationSelectors.classificationTypes) classificationTypes$: Observable<string[]>; @Select(ClassificationSelectors.classificationTypes) classificationTypes$: Observable<string[]>;
constructor(private store: Store) {} constructor(private store: Store, private location: Location) {}
select(value: string): void { select(value: string): void {
this.store.dispatch(new SetSelectedClassificationType(value)); this.store.dispatch(new SetSelectedClassificationType(value));
this.location.go(this.location.path().replace(/(classifications).*/g, 'classifications'));
} }
} }

View File

@ -41,7 +41,7 @@
[ngClass]="{ [ngClass]="{
'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId), 'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
'has-error': !accessItem.value.accessId }"> 'has-error': !accessItem.value.accessId }">
<taskana-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required" [validationValue]="toogleValidationAccessIdMap.get(index)" <taskana-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required" [validationValue]="toggleValidationAccessIdMap.get(index)"
[displayError]="!isFieldValid('accessItem.value.accessId', index)" (selectedItem)="accessItemSelected($event, index)" (inputField)="focusNewInput($event)"></taskana-type-ahead> [displayError]="!isFieldValid('accessItem.value.accessId', index)" (selectedItem)="accessItemSelected($event, index)" (inputField)="focusNewInput($event)"></taskana-type-ahead>
</td> </td>
<ng-template #accessIdInput> <ng-template #accessIdInput>
@ -50,7 +50,7 @@
!accessItem.value.accessId && formSubmitAttempt}"> !accessItem.value.accessId && formSubmitAttempt}">
<input type="text" class="form-control" formControlName="accessId" placeholder="{{accessItem.invalid? <input type="text" class="form-control" formControlName="accessId" placeholder="{{accessItem.invalid?
'* Access id is required': ''}}" '* Access id is required': ''}}"
[@validation]="toogleValidationAccessIdMap.get(index)" #htmlInputElement> [@validation]="toggleValidationAccessIdMap.get(index)" #htmlInputElement>
</div> </div>
</td> </td>
</ng-template> </ng-template>

View File

@ -1,5 +1,5 @@
import { SimpleChange } from '@angular/core'; import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { AngularSvgIconModule } from 'angular-svg-icon'; import { AngularSvgIconModule } from 'angular-svg-icon';
@ -7,7 +7,6 @@ import { of } from 'rxjs';
import { configureTests } from 'app/app.test.configuration'; import { configureTests } from 'app/app.test.configuration';
import { Workbasket } from 'app/shared/models/workbasket'; import { Workbasket } from 'app/shared/models/workbasket';
import { Links } from 'app/shared/models/links';
import { WorkbasketAccessItems } from 'app/shared/models/workbasket-access-items'; import { WorkbasketAccessItems } from 'app/shared/models/workbasket-access-items';
import { WorkbasketAccessItemsResource } from 'app/shared/models/workbasket-access-items-resource'; import { WorkbasketAccessItemsResource } from 'app/shared/models/workbasket-access-items-resource';
import { ICONTYPES } from 'app/shared/models/icon-types'; import { ICONTYPES } from 'app/shared/models/icon-types';
@ -64,8 +63,7 @@ describe('WorkbasketAccessItemsComponent', () => {
component = fixture.componentInstance; component = fixture.componentInstance;
component.workbasket = new Workbasket('1'); component.workbasket = new Workbasket('1');
component.workbasket.type = ICONTYPES.TOPIC; component.workbasket.type = ICONTYPES.TOPIC;
component.workbasket._links = new Links(); component.workbasket._links = { accessItems: { href: 'someurl' } };
component.workbasket._links.accessItems = { href: 'someurl' };
workbasketService = testBed.get(WorkbasketService); workbasketService = testBed.get(WorkbasketService);
notificationsService = testBed.get(NotificationService); notificationsService = testBed.get(NotificationService);
@ -74,8 +72,7 @@ describe('WorkbasketAccessItemsComponent', () => {
new WorkbasketAccessItems('id1', '1', 'accessID1', '', false, false, false, false, false, false, false, false, new WorkbasketAccessItems('id1', '1', 'accessID1', '', false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false), false, false, false, false, false, false, false, false, false),
new WorkbasketAccessItems('id2', '1', 'accessID2') new WorkbasketAccessItems('id2', '1', 'accessID2')
), ), { self: { href: 'someurl' } }
new Links({ href: 'someurl' })
))); )));
spyOn(workbasketService, 'updateWorkBasketAccessItem').and.returnValue(of(true)); spyOn(workbasketService, 'updateWorkBasketAccessItem').and.returnValue(of(true));
spyOn(notificationsService, 'showToast').and.returnValue(of(true)); spyOn(notificationsService, 'showToast').and.returnValue(of(true));
@ -97,7 +94,6 @@ describe('WorkbasketAccessItemsComponent', () => {
document.body.removeChild(debugElement); document.body.removeChild(debugElement);
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });

View File

@ -1,4 +1,4 @@
import { Component, import { AfterViewInit, Component,
ElementRef, ElementRef,
Input, Input,
OnChanges, OnChanges,
@ -7,7 +7,7 @@ import { Component,
ViewChildren } from '@angular/core'; ViewChildren } from '@angular/core';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { Select } from '@ngxs/store'; import { Select } from '@ngxs/store';
import { FormArray, FormBuilder, FormControlDirective, Validators } from '@angular/forms'; import { FormArray, FormBuilder, Validators } from '@angular/forms';
import { Workbasket } from 'app/shared/models/workbasket'; import { Workbasket } from 'app/shared/models/workbasket';
import { customFieldCount, WorkbasketAccessItems } from 'app/shared/models/workbasket-access-items'; import { customFieldCount, WorkbasketAccessItems } from 'app/shared/models/workbasket-access-items';
@ -34,7 +34,7 @@ import { AccessItemsCustomisation,
animations: [highlight], animations: [highlight],
styleUrls: ['./workbasket-access-items.component.scss'] styleUrls: ['./workbasket-access-items.component.scss']
}) })
export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy { export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy, AfterViewInit {
@Input() @Input()
workbasket: Workbasket; workbasket: Workbasket;
@ -46,7 +46,6 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
@ViewChildren('htmlInputElement') inputs: QueryList<ElementRef>; @ViewChildren('htmlInputElement') inputs: QueryList<ElementRef>;
badgeMessage = ''; badgeMessage = '';
@Select(EngineConfigurationSelectors.accessItemsCustomisation) accessItemsCustomization$: Observable<AccessItemsCustomisation>; @Select(EngineConfigurationSelectors.accessItemsCustomisation) accessItemsCustomization$: Observable<AccessItemsCustomisation>;
@ -56,13 +55,13 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
accessItemsClone: Array<WorkbasketAccessItems>; accessItemsClone: Array<WorkbasketAccessItems>;
accessItemsResetClone: Array<WorkbasketAccessItems>; accessItemsResetClone: Array<WorkbasketAccessItems>;
requestInProgress = false; requestInProgress = false;
accessItemsubscription: Subscription; accessItemSubscription: Subscription;
savingAccessItemsSubscription: Subscription; savingAccessItemsSubscription: Subscription;
AccessItemsForm = this.formBuilder.group({ AccessItemsForm = this.formBuilder.group({
accessItemsGroups: this.formBuilder.array([]) accessItemsGroups: this.formBuilder.array([])
}); });
toogleValidationAccessIdMap = new Map<number, boolean>(); toggleValidationAccessIdMap = new Map<number, boolean>();
private initialized = false; private initialized = false;
private added = false; private added = false;
@ -104,7 +103,7 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
return; return;
} }
this.requestInProgress = true; this.requestInProgress = true;
this.accessItemsubscription = this.workbasketService.getWorkBasketAccessItems(this.workbasket._links.accessItems.href) this.accessItemSubscription = this.workbasketService.getWorkBasketAccessItems(this.workbasket._links.accessItems.href)
.subscribe((accessItemsResource: WorkbasketAccessItemsResource) => { .subscribe((accessItemsResource: WorkbasketAccessItemsResource) => {
this.accessItemsResource = accessItemsResource; this.accessItemsResource = accessItemsResource;
this.setAccessItemsGroups(accessItemsResource.accessItems); this.setAccessItemsGroups(accessItemsResource.accessItems);
@ -162,7 +161,7 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
onSubmit() { onSubmit() {
this.formsValidatorService.formSubmitAttempt = true; this.formsValidatorService.formSubmitAttempt = true;
this.formsValidatorService.validateFormAccess(this.accessItemsGroups, this.toogleValidationAccessIdMap).then(value => { this.formsValidatorService.validateFormAccess(this.accessItemsGroups, this.toggleValidationAccessIdMap).then(value => {
if (value) { if (value) {
this.onSave(); this.onSave();
} }
@ -230,10 +229,9 @@ export class WorkbasketAccessItemsComponent implements OnChanges, OnDestroy {
} }
} }
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.accessItemsubscription) { if (this.accessItemSubscription) {
this.accessItemsubscription.unsubscribe(); this.accessItemSubscription.unsubscribe();
} }
if (this.savingAccessItemsSubscription) { if (this.savingAccessItemsSubscription) {
this.savingAccessItemsSubscription.unsubscribe(); this.savingAccessItemsSubscription.unsubscribe();

View File

@ -14,7 +14,6 @@ import { WorkbasketAccessItemsResource } from 'app/shared/models/workbasket-acce
import { ICONTYPES } from 'app/shared/models/icon-types'; import { ICONTYPES } from 'app/shared/models/icon-types';
import { Links } from 'app/shared/models/links'; import { Links } from 'app/shared/models/links';
import { WorkbasketAccessItems } from 'app/shared/models/workbasket-access-items'; import { WorkbasketAccessItems } from 'app/shared/models/workbasket-access-items';
import { LinksWorkbasketSummary } from 'app/shared/models/links-workbasket-summary';
import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service'; import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service';
import { MasterAndDetailService } from 'app/shared/services/master-and-detail/master-and-detail.service'; import { MasterAndDetailService } from 'app/shared/services/master-and-detail/master-and-detail.service';
@ -46,7 +45,7 @@ describe('WorkbasketDetailsComponent', () => {
let workbasketService; let workbasketService;
let router; let router;
const workbasket = new Workbasket('1', '', '', '', ICONTYPES.TOPIC, '', '', '', '', '', '', '', '', '', '', '', '', const workbasket = new Workbasket('1', '', '', '', ICONTYPES.TOPIC, '', '', '', '', '', '', '', '', '', '', '', '',
new Links({ href: 'someurl' }, { href: 'someurl' }, { href: 'someurl' })); {});
const routes: Routes = [ const routes: Routes = [
{ path: '*', component: DummyDetailComponent } { path: '*', component: DummyDetailComponent }
@ -77,17 +76,17 @@ describe('WorkbasketDetailsComponent', () => {
spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => of(new WorkbasketSummaryResource( spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => of(new WorkbasketSummaryResource(
new Array<WorkbasketSummary>( new Array<WorkbasketSummary>(
new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '',
false, new Links({ href: 'someurl' })) false, {})
), ),
new LinksWorkbasketSummary({ href: 'someurl' }) {}
))); )));
spyOn(workbasketService, 'getWorkBasket').and.callFake(() => of(workbasket)); spyOn(workbasketService, 'getWorkBasket').and.callFake(() => of(workbasket));
spyOn(workbasketService, 'getWorkBasketAccessItems').and.callFake(() => of(new WorkbasketAccessItemsResource( spyOn(workbasketService, 'getWorkBasketAccessItems').and.callFake(() => of(new WorkbasketAccessItemsResource(
new Array<WorkbasketAccessItems>(), new Links({ href: 'url' }) new Array<WorkbasketAccessItems>(), {}
))); )));
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => of(new WorkbasketSummaryResource( spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => of(new WorkbasketSummaryResource(
new Array<WorkbasketSummary>(), new LinksWorkbasketSummary({ href: 'url' }) new Array<WorkbasketSummary>(), {}
))); )));
done(); done();
}); });

View File

@ -41,7 +41,6 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
private errorsService: NotificationService, private errorsService: NotificationService,
private importExportService: ImportExportService) { } private importExportService: ImportExportService) { }
ngOnInit() { ngOnInit() {
this.workbasketSelectedSubscription = this.service.getSelectedWorkBasket().subscribe(workbasketIdSelected => { this.workbasketSelectedSubscription = this.service.getSelectedWorkBasket().subscribe(workbasketIdSelected => {
delete this.workbasket; delete this.workbasket;

View File

@ -16,10 +16,10 @@ import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.ser
import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets.service'; import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets.service';
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service'; import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
import { LinksWorkbasketSummary } from 'app/shared/models/links-workbasket-summary';
import { configureTests } from 'app/app.test.configuration'; import { configureTests } from 'app/app.test.configuration';
import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { WorkbasketDistributionTargetsComponent, Side } from './workbasket-distribution-targets.component'; import { Side,
WorkbasketDistributionTargetsComponent } from './workbasket-distribution-targets.component';
import { WorkbasketDualListComponent } from '../workbasket-dual-list/workbasket-dual-list.component'; import { WorkbasketDualListComponent } from '../workbasket-dual-list/workbasket-dual-list.component';
import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { NotificationService } from '../../../shared/services/notifications/notification.service';
@ -28,10 +28,10 @@ describe('WorkbasketDistributionTargetsComponent', () => {
let fixture: ComponentFixture<WorkbasketDistributionTargetsComponent>; let fixture: ComponentFixture<WorkbasketDistributionTargetsComponent>;
let workbasketService; let workbasketService;
const workbasket = new Workbasket('1', '', '', '', ICONTYPES.TOPIC, '', '', '', '', '', '', '', '', '', '', '', '', const workbasket = new Workbasket('1', '', '', '', ICONTYPES.TOPIC, '', '', '', '', '', '', '', '', '', '', '', '',
new Links({ href: 'someurl' }, { href: 'someurl' }, { href: 'someurl' })); { distributionTargets: { href: 'someurl' } });
beforeEach(done => { beforeEach(done => {
const configure = (testBed: TestBed) => { const configure = testBed => {
testBed.configureTestingModule({ testBed.configureTestingModule({
imports: [AngularSvgIconModule, HttpClientModule, InfiniteScrollModule], imports: [AngularSvgIconModule, HttpClientModule, InfiniteScrollModule],
declarations: [WorkbasketDistributionTargetsComponent, WorkbasketDualListComponent], declarations: [WorkbasketDistributionTargetsComponent, WorkbasketDualListComponent],
@ -40,23 +40,23 @@ describe('WorkbasketDistributionTargetsComponent', () => {
}); });
}; };
configureTests(configure).then(testBed => { configureTests(configure).then(testBed => {
fixture = TestBed.createComponent(WorkbasketDistributionTargetsComponent); fixture = testBed.createComponent(WorkbasketDistributionTargetsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.workbasket = workbasket; component.workbasket = workbasket;
workbasketService = TestBed.get(WorkbasketService); workbasketService = testBed.get(WorkbasketService);
spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => of(new WorkbasketSummaryResource( spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => of(new WorkbasketSummaryResource(
new Array<WorkbasketSummary>( new Array<WorkbasketSummary>(
new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })), new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, {}),
new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })), new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, {}),
new WorkbasketSummary('id3', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })) new WorkbasketSummary('id3', '', '', '', '', '', '', '', '', '', '', '', false, {})
), ),
new LinksWorkbasketSummary({ href: 'someurl' }) {}
))); )));
spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => of(new WorkbasketDistributionTargetsResource( spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => of(new WorkbasketDistributionTargetsResource(
new Array<WorkbasketSummary>( new Array<WorkbasketSummary>(
new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })) new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, {})
), ),
new LinksWorkbasketSummary({ href: 'someurl' }) { self: { href: 'someurl' } }
))); )));
component.ngOnChanges({ component.ngOnChanges({
active: new SimpleChange(undefined, 'distributionTargets', true) active: new SimpleChange(undefined, 'distributionTargets', true)
@ -85,7 +85,9 @@ describe('WorkbasketDistributionTargetsComponent', () => {
expect(component.distributionTargetsRight.length).toBe(1); expect(component.distributionTargetsRight.length).toBe(1);
component.distributionTargetsLeft.forEach(leftElement => { component.distributionTargetsLeft.forEach(leftElement => {
component.distributionTargetsRight.forEach(rightElement => { component.distributionTargetsRight.forEach(rightElement => {
if (leftElement.workbasketId === rightElement.workbasketId) { repeteadElemens = true; } if (leftElement.workbasketId === rightElement.workbasketId) {
repeteadElemens = true;
}
}); });
}); });
expect(repeteadElemens).toBeFalsy(); expect(repeteadElemens).toBeFalsy();
@ -99,7 +101,7 @@ describe('WorkbasketDistributionTargetsComponent', () => {
side: Side.LEFT side: Side.LEFT
}); });
component.distributionTargetsLeft = new Array<WorkbasketSummary>( component.distributionTargetsLeft = new Array<WorkbasketSummary>(
new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })) new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, {})
); );
expect(component.distributionTargetsLeft.length).toBe(1); expect(component.distributionTargetsLeft.length).toBe(1);
expect(component.distributionTargetsLeft[0].workbasketId).toBe('id1'); expect(component.distributionTargetsLeft[0].workbasketId).toBe('id1');
@ -109,10 +111,10 @@ describe('WorkbasketDistributionTargetsComponent', () => {
it('should reset distribution target and distribution target selected on reset', () => { it('should reset distribution target and distribution target selected on reset', () => {
component.distributionTargetsLeft.push( component.distributionTargetsLeft.push(
new WorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })) new WorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', '', '', false, {})
); );
component.distributionTargetsRight.push( component.distributionTargetsRight.push(
new WorkbasketSummary('id5', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })) new WorkbasketSummary('id5', '', '', '', '', '', '', '', '', '', '', '', false, {})
); );
expect(component.distributionTargetsLeft.length).toBe(3); expect(component.distributionTargetsLeft.length).toBe(3);
@ -129,10 +131,10 @@ describe('WorkbasketDistributionTargetsComponent', () => {
expect(component.distributionTargetsSelectedClone.length).toBe(1); expect(component.distributionTargetsSelectedClone.length).toBe(1);
spyOn(workbasketService, 'updateWorkBasketsDistributionTargets').and.callFake(() => of(new WorkbasketDistributionTargetsResource( spyOn(workbasketService, 'updateWorkBasketsDistributionTargets').and.callFake(() => of(new WorkbasketDistributionTargetsResource(
new Array<WorkbasketSummary>( new Array<WorkbasketSummary>(
new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })), new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, {}),
new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, new Links({ href: 'someurl' })) new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, {})
), ),
new LinksWorkbasketSummary({ href: 'someurl' }) {}
))); )));
component.onSave(); component.onSave();
fixture.detectChanges(); fixture.detectChanges();

View File

@ -11,7 +11,6 @@ import { Routes } from '@angular/router';
import { Workbasket } from 'app/shared/models/workbasket'; import { Workbasket } from 'app/shared/models/workbasket';
import { ICONTYPES } from 'app/shared/models/icon-types'; import { ICONTYPES } from 'app/shared/models/icon-types';
import { ACTION } from 'app/shared/models/action'; import { ACTION } from 'app/shared/models/action';
import { Links } from 'app/shared/models/links';
import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets.service'; import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets.service';
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service'; import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
@ -125,7 +124,7 @@ describe('WorkbasketInformationComponent', () => {
it('should reset requestInProgress after saving request is done', async(() => { it('should reset requestInProgress after saving request is done', async(() => {
component.workbasket = new Workbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', component.workbasket = new Workbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description',
'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2',
'orgLevel3', 'orgLevel4', new Links({ href: 'someUrl' })); 'orgLevel3', 'orgLevel4', { self: { href: 'someUrl' } });
fixture.detectChanges(); fixture.detectChanges();
spyOn(workbasketService, 'updateWorkbasket').and.returnValue(of(component.workbasket)); spyOn(workbasketService, 'updateWorkbasket').and.returnValue(of(component.workbasket));
spyOn(workbasketService, 'triggerWorkBasketSaved').and.returnValue(of(component.workbasket)); spyOn(workbasketService, 'triggerWorkBasketSaved').and.returnValue(of(component.workbasket));
@ -136,7 +135,7 @@ describe('WorkbasketInformationComponent', () => {
it('should trigger triggerWorkBasketSaved method after saving request is done', async(() => { it('should trigger triggerWorkBasketSaved method after saving request is done', async(() => {
component.workbasket = new Workbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', component.workbasket = new Workbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description',
'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2',
'orgLevel3', 'orgLevel4', new Links({ href: 'someUrl' })); 'orgLevel3', 'orgLevel4', { self: { href: 'someurl' } });
spyOn(workbasketService, 'updateWorkbasket').and.returnValue(of(component.workbasket)); spyOn(workbasketService, 'updateWorkbasket').and.returnValue(of(component.workbasket));
spyOn(workbasketService, 'triggerWorkBasketSaved').and.returnValue(of(component.workbasket)); spyOn(workbasketService, 'triggerWorkBasketSaved').and.returnValue(of(component.workbasket));
fixture.detectChanges(); fixture.detectChanges();
@ -150,14 +149,13 @@ describe('WorkbasketInformationComponent', () => {
})); }));
it('should post a new workbasket when no workbasketId is defined and update workbasket', async(() => { it('should post a new workbasket when no workbasketId is defined and update workbasket', async(() => {
const workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', component.workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description',
'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2',
'orgLevel3', 'orgLevel4', new Links({ href: 'someUrl' })); 'orgLevel3', 'orgLevel4', {});
component.workbasket = workbasket;
spyOn(workbasketService, 'createWorkbasket').and.returnValue(of( spyOn(workbasketService, 'createWorkbasket').and.returnValue(of(
new Workbasket('someNewId', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', new Workbasket('someNewId', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description',
'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2',
'orgLevel3', 'orgLevel4', new Links({ href: 'someUrl' })) 'orgLevel3', 'orgLevel4', {})
)); ));
fixture.detectChanges(); fixture.detectChanges();
spyOn(formsValidatorService, 'validateFormAccess').and.returnValue(Promise.resolve(true)); spyOn(formsValidatorService, 'validateFormAccess').and.returnValue(Promise.resolve(true));
@ -171,17 +169,16 @@ describe('WorkbasketInformationComponent', () => {
it('should post a new workbasket, new distribution targets and new access ' it('should post a new workbasket, new distribution targets and new access '
+ 'items when no workbasketId is defined and action is copy', async(() => { + 'items when no workbasketId is defined and action is copy', async(() => {
const workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, component.workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC,
'modified', 'name', 'description', 'owner', 'custom1', 'custom2', 'modified', 'name', 'description', 'owner', 'custom1', 'custom2',
'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2',
'orgLevel3', 'orgLevel4', new Links({ href: 'someUrl' })); 'orgLevel3', 'orgLevel4', {});
component.workbasket = workbasket;
component.action = ACTION.COPY; component.action = ACTION.COPY;
spyOn(workbasketService, 'createWorkbasket').and.returnValue(of( spyOn(workbasketService, 'createWorkbasket').and.returnValue(of(
new Workbasket('someNewId', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', new Workbasket('someNewId', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description',
'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2',
'orgLevel3', 'orgLevel4', new Links({ href: 'someUrl' }, { href: 'someUrl' }, { href: 'someUrl' })) 'orgLevel3', 'orgLevel4', { distributionTargets: { href: 'someurl' }, accessItems: { href: 'someurl' } })
)); ));
spyOn(savingWorkbasketService, 'triggerDistributionTargetSaving'); spyOn(savingWorkbasketService, 'triggerDistributionTargetSaving');
@ -199,13 +196,10 @@ describe('WorkbasketInformationComponent', () => {
})); }));
it('should trigger requestInProgress service true before and requestInProgress false after remove a workbasket', () => { it('should trigger requestInProgress service true before and requestInProgress false after remove a workbasket', () => {
const links = new Links({ href: 'someUrl' }); component.workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC,
links.removeDistributionTargets = { href: 'someUrl' };
const workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC,
'modified', 'name', 'description', 'owner', 'custom1', 'custom2', 'modified', 'name', 'description', 'owner', 'custom1', 'custom2',
'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2',
'orgLevel3', 'orgLevel4', links); 'orgLevel3', 'orgLevel4', { removeDistributionTargets: { href: 'someurl' } });
component.workbasket = workbasket;
spyOn(workbasketService, 'removeDistributionTarget').and.returnValue(of(undefined)); spyOn(workbasketService, 'removeDistributionTarget').and.returnValue(of(undefined));
const requestInProgressServiceSpy = spyOn(requestInProgressService, 'setRequestInProgress'); const requestInProgressServiceSpy = spyOn(requestInProgressService, 'setRequestInProgress');
@ -213,7 +207,7 @@ describe('WorkbasketInformationComponent', () => {
expect(requestInProgressServiceSpy).toHaveBeenCalledWith(true); expect(requestInProgressServiceSpy).toHaveBeenCalledWith(true);
workbasketService.removeDistributionTarget().subscribe(() => { workbasketService.removeDistributionTarget().subscribe(() => {
}, error => { }, complete => { }, () => { }, () => {
expect(requestInProgressServiceSpy).toHaveBeenCalledWith(false); expect(requestInProgressServiceSpy).toHaveBeenCalledWith(false);
}); });
}); });

View File

@ -58,19 +58,18 @@ describe('WorkbasketListToolbarComponent', () => {
}); });
}; };
configureTests(configure).then(testBed => { configureTests(configure).then(testBed => {
fixture = TestBed.createComponent(WorkbasketListToolbarComponent); fixture = testBed.createComponent(WorkbasketListToolbarComponent);
workbasketService = TestBed.get(WorkbasketService); workbasketService = testBed.get(WorkbasketService);
router = TestBed.get(Router); router = testBed.get(Router);
spyOn(workbasketService, 'markWorkbasketForDeletion').and.returnValue(of('')); spyOn(workbasketService, 'markWorkbasketForDeletion').and.returnValue(of(''));
spyOn(workbasketService, 'triggerWorkBasketSaved'); spyOn(workbasketService, 'triggerWorkBasketSaved');
debugElement = fixture.debugElement.nativeElement; debugElement = fixture.debugElement.nativeElement;
component = fixture.componentInstance; component = fixture.componentInstance;
component.workbaskets = new Array<WorkbasketSummary>( component.workbaskets = [
new WorkbasketSummary('1', 'key1', 'NAME1', 'description 1', 'owner 1') new WorkbasketSummary('1', 'key1', 'NAME1', 'description 1', 'owner 1')
); ];
component.workbaskets[0].markedForDeletion = false; component.workbaskets[0].markedForDeletion = false;
component.workbaskets[0]._links = new Links({ href: 'selfLink' });
fixture.detectChanges(); fixture.detectChanges();
done(); done();
@ -91,7 +90,6 @@ describe('WorkbasketListToolbarComponent', () => {
expect(spy.calls.first().args[0][0].outlets.detail[0]).toBe('new-workbasket'); expect(spy.calls.first().args[0][0].outlets.detail[0]).toBe('new-workbasket');
}); });
it('should emit performSorting when sorting is triggered', () => { it('should emit performSorting when sorting is triggered', () => {
let sort: Sorting; let sort: Sorting;
const compareSort = new Sorting(); const compareSort = new Sorting();

View File

@ -10,7 +10,6 @@ import { RouterTestingModule } from '@angular/router/testing';
import { WorkbasketSummary } from 'app/shared/models/workbasket-summary'; import { WorkbasketSummary } from 'app/shared/models/workbasket-summary';
import { WorkbasketSummaryResource } from 'app/shared/models/workbasket-summary-resource'; import { WorkbasketSummaryResource } from 'app/shared/models/workbasket-summary-resource';
import { Filter } from 'app/shared/models/filter'; import { Filter } from 'app/shared/models/filter';
import { LinksWorkbasketSummary } from 'app/shared/models/links-workbasket-summary';
import { ImportExportComponent } from 'app/administration/components/import-export/import-export.component'; import { ImportExportComponent } from 'app/administration/components/import-export/import-export.component';
@ -50,10 +49,9 @@ const workbasketSummaryResource: WorkbasketSummaryResource = new WorkbasketSumma
new WorkbasketSummary('1', 'key1', 'NAME1', 'description 1', 'owner 1', '', '', 'PERSONAL', '', '', '', ''), new WorkbasketSummary('1', 'key1', 'NAME1', 'description 1', 'owner 1', '', '', 'PERSONAL', '', '', '', ''),
new WorkbasketSummary('2', 'key2', 'NAME2', 'description 2', 'owner 2', '', '', 'GROUP', '', '', '', '') new WorkbasketSummary('2', 'key2', 'NAME2', 'description 2', 'owner 2', '', '', 'GROUP', '', '', '', '')
), ),
new LinksWorkbasketSummary({ href: 'url' }) {}
); );
describe('WorkbasketListComponent', () => { describe('WorkbasketListComponent', () => {
let component: WorkbasketListComponent; let component: WorkbasketListComponent;
let fixture: ComponentFixture<WorkbasketListComponent>; let fixture: ComponentFixture<WorkbasketListComponent>;
@ -66,7 +64,6 @@ describe('WorkbasketListComponent', () => {
{ path: 'workbaskets', component: DummyDetailComponent } { path: 'workbaskets', component: DummyDetailComponent }
]; ];
beforeEach(done => { beforeEach(done => {
const configure = (testBed: TestBed) => { const configure = (testBed: TestBed) => {
testBed.configureTestingModule({ testBed.configureTestingModule({

View File

@ -1,9 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { environment } from 'app/../environments/environment'; import { environment } from 'app/../environments/environment';
import { ClassificationDefinition } from 'app/shared/models/classification-definition';
import { TaskanaDate } from 'app/shared/util/taskana.date'; import { TaskanaDate } from 'app/shared/util/taskana.date';
import { BlobGenerator } from 'app/shared/util/blob-generator'; import { BlobGenerator } from 'app/shared/util/blob-generator';
import { Classification } from '../../shared/models/classification';
@Injectable() @Injectable()
export class ClassificationDefinitionService { export class ClassificationDefinitionService {
@ -13,7 +13,7 @@ export class ClassificationDefinitionService {
// GET // GET
async exportClassifications(domain: string) { async exportClassifications(domain: string) {
const domainRequest = (domain ? '' : `?domain=${domain}`); const domainRequest = (domain ? '' : `?domain=${domain}`);
const classificationDefinitions = await this.httpClient.get<ClassificationDefinition[]>(this.url + domainRequest).toPromise(); const classificationDefinitions = await this.httpClient.get<Classification[]>(this.url + domainRequest).toPromise();
BlobGenerator.saveFile(classificationDefinitions, `Classifications_${TaskanaDate.getDate()}.json`); BlobGenerator.saveFile(classificationDefinitions, `Classifications_${TaskanaDate.getDate()}.json`);
} }
} }

View File

@ -7,7 +7,6 @@ export class SavingInformation {
} }
} }
@Injectable() @Injectable()
export class SavingWorkbasketService { export class SavingWorkbasketService {
public distributionTargetsSavingInformation = new Subject<SavingInformation>(); public distributionTargetsSavingInformation = new Subject<SavingInformation>();

View File

@ -43,7 +43,6 @@ describe('AppComponent', () => {
expect(app).toBeTruthy(); expect(app).toBeTruthy();
})); }));
it('should render title in a <a> tag', (() => { it('should render title in a <a> tag', (() => {
fixture.detectChanges(); fixture.detectChanges();
expect(debugElement.querySelector('ul p a').textContent).toContain('Taskana administration'); expect(debugElement.querySelector('ul p a').textContent).toContain('Taskana administration');

View File

@ -77,7 +77,6 @@ export function startupServiceFactory(startupService: StartupService): () => Pro
return (): Promise<any> => startupService.load(); return (): Promise<any> => startupService.load();
} }
@NgModule({ @NgModule({
declarations: DECLARATIONS, declarations: DECLARATIONS,
imports: MODULES, imports: MODULES,

View File

@ -1,4 +1,3 @@
import { getTestBed, TestBed } from '@angular/core/testing'; import { getTestBed, TestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@ -41,7 +41,6 @@ export class TaskQueryComponent implements OnInit {
this.initTaskQueryForm(); this.initTaskQueryForm();
} }
getHeaderFieldDescription(property: string): string { getHeaderFieldDescription(property: string): string {
switch (property) { switch (property) {
case 'parentBusinessProcessId': case 'parentBusinessProcessId':

View File

@ -13,7 +13,6 @@ import { RequestInProgressService } from '../../../shared/services/request-in-pr
export class ClassificationReportComponent implements OnInit { export class ClassificationReportComponent implements OnInit {
reportData: ReportData; reportData: ReportData;
lineChartLabels: Array<any>; lineChartLabels: Array<any>;
lineChartLegend = true; lineChartLegend = true;
lineChartType = 'line'; lineChartType = 'line';

View File

@ -1,6 +1,5 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'taskana-monitor', selector: 'taskana-monitor',
templateUrl: './monitor.component.html', templateUrl: './monitor.component.html',

View File

@ -17,7 +17,6 @@ export class WorkbasketReportDueDateComponent implements OnInit {
reportData: ReportData; reportData: ReportData;
lineChartLabels: Array<any>; lineChartLabels: Array<any>;
lineChartLegend = true; lineChartLegend = true;
lineChartType = 'line'; lineChartType = 'line';
@ -34,7 +33,6 @@ export class WorkbasketReportDueDateComponent implements OnInit {
) { ) {
} }
async ngOnInit() { async ngOnInit() {
this.requestInProgressService.setRequestInProgress(true); this.requestInProgressService.setRequestInProgress(true);
this.reportData = await this.restConnectorService.getWorkbasketStatisticsQueryingByDueDate().toPromise(); this.reportData = await this.restConnectorService.getWorkbasketStatisticsQueryingByDueDate().toPromise();

View File

@ -17,7 +17,6 @@ export class WorkbasketReportPlannedDateComponent implements OnInit {
reportData: ReportData; reportData: ReportData;
lineChartLabels: Array<any>; lineChartLabels: Array<any>;
lineChartLegend = true; lineChartLegend = true;
lineChartType = 'line'; lineChartType = 'line';

View File

@ -23,7 +23,6 @@ import { WorkbasketReportPlannedDateComponent } from './components/workbasket-re
import { WorkbasketReportDueDateComponent } from './components/workbasket-report-due-date/workbasket-report-due-date.component'; import { WorkbasketReportDueDateComponent } from './components/workbasket-report-due-date/workbasket-report-due-date.component';
import { WorkbasketReportQuerySwitcherComponent } from './components/workbasket-report-query-switcher/workbasket-report-query-switcher.component'; import { WorkbasketReportQuerySwitcherComponent } from './components/workbasket-report-query-switcher/workbasket-report-query-switcher.component';
const MODULES = [ const MODULES = [
CommonModule, CommonModule,
MonitorRoutingModule, MonitorRoutingModule,

View File

@ -13,7 +13,6 @@ describe('FilterComponent', () => {
let fixture: ComponentFixture<FilterComponent>; let fixture: ComponentFixture<FilterComponent>;
let debugElement: any; let debugElement: any;
beforeEach(done => { beforeEach(done => {
const configure = (testBed: TestBed) => { const configure = (testBed: TestBed) => {
testBed.configureTestingModule({ testBed.configureTestingModule({

View File

@ -8,7 +8,6 @@ import { MasterAndDetailService } from '../../services/master-and-detail/master-
import { MasterAndDetailComponent } from './master-and-detail.component'; import { MasterAndDetailComponent } from './master-and-detail.component';
@Component({ @Component({
selector: 'taskana-dummy-master', selector: 'taskana-dummy-master',
template: 'dummymaster' template: 'dummymaster'

View File

@ -86,7 +86,6 @@ export class NavBarComponent implements OnInit, OnDestroy {
this.showNavbar = !this.showNavbar; this.showNavbar = !this.showNavbar;
} }
logout() { logout() {
this.taskanaEngineService.logout().subscribe(() => { this.taskanaEngineService.logout().subscribe(() => {
}); });

View File

@ -11,7 +11,6 @@ describe('NoAccessComponent', () => {
let fixture: ComponentFixture<NoAccessComponent>; let fixture: ComponentFixture<NoAccessComponent>;
let debugElement; let debugElement;
beforeEach(done => { beforeEach(done => {
const configure = (testBed: TestBed) => { const configure = (testBed: TestBed) => {
testBed.configureTestingModule({ testBed.configureTestingModule({

View File

@ -12,7 +12,6 @@ describe('PaginationComponent', () => {
let fixture: ComponentFixture<PaginationComponent>; let fixture: ComponentFixture<PaginationComponent>;
let debugElement; let debugElement;
beforeEach(done => { beforeEach(done => {
const configure = (testBed: TestBed) => { const configure = (testBed: TestBed) => {
testBed.configureTestingModule({ testBed.configureTestingModule({

View File

@ -1,6 +1,5 @@
import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { HttpErrorResponse } from '@angular/common/http';
import { notifications } from '../../models/notifications'; import { notifications } from '../../models/notifications';
@Component({ @Component({
@ -33,7 +32,8 @@ export class DialogPopUpComponent implements OnInit {
initError() { initError() {
this.title = notifications.get(this.data.key).name || ''; this.title = notifications.get(this.data.key).name || '';
this.message = notifications.get(this.data.key).text || this.data.passedError.error ? this.data.passedError.error.message : ''; this.message = notifications.get(this.data.key).text
|| (this.data && this.data.passedError && this.data.passedError.error) ? this.data.passedError.error.message : '';
if (this.data.additions) { if (this.data.additions) {
this.data.additions.forEach((value: string, replacementKey: string) => { this.data.additions.forEach((value: string, replacementKey: string) => {
this.message = this.message.replace(`{${replacementKey}}`, value); this.message = this.message.replace(`{${replacementKey}}`, value);

View File

@ -4,7 +4,6 @@ import { NotificationService } from '../../services/notifications/notification.s
declare let $: any; declare let $: any;
@Component({ @Component({
selector: 'taskana-spinner', selector: 'taskana-spinner',
templateUrl: './spinner.component.html', templateUrl: './spinner.component.html',

View File

@ -1,4 +1,4 @@
<tree-root #tree [nodes]="classifications" [options]="options" (activate)="onActivate($event)" (deactivate)="onDeactivate($event)" <tree-root #tree [nodes]="treeNodes" [options]="options" (activate)="onActivate($event)" (deactivate)="onDeactivate($event)"
(moveNode)="onMoveNode($event)" (treeDrop)="onDrop($event)"> (moveNode)="onMoveNode($event)" (treeDrop)="onDrop($event)">
<ng-template #treeNodeTemplate let-node let-index="index"> <ng-template #treeNodeTemplate let-node let-index="index">
<span class="text-top"> <span class="text-top">

View File

@ -1,5 +1,5 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed, tick } from '@angular/core/testing';
import { AngularSvgIconModule } from 'angular-svg-icon'; import { AngularSvgIconModule } from 'angular-svg-icon';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
@ -12,9 +12,8 @@ import { TreeNodeModel } from 'app/shared/models/tree-node';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { UpdateClassification } from 'app/shared/store/classification-store/classification.actions'; import { UpdateClassification } from 'app/shared/store/classification-store/classification.actions';
import { TaskanaTreeComponent } from './tree.component'; import { TaskanaTreeComponent } from './tree.component';
import { ClassificationDefinition } from '../../models/classification-definition';
import { LinksClassification } from '../../models/links-classfication';
import { ClassificationsService } from '../../services/classifications/classifications.service'; import { ClassificationsService } from '../../services/classifications/classifications.service';
import { Classification } from '../../models/classification';
@Component({ @Component({
selector: 'tree-root', selector: 'tree-root',
@ -56,11 +55,12 @@ describe('TaskanaTreeComponent', () => {
case ClassificationSelectors.activeAction: case ClassificationSelectors.activeAction:
return of(ACTION.CREATE); return of(ACTION.CREATE);
case ClassificationSelectors.classifications: case ClassificationSelectors.classifications:
return of([new TreeNodeModel('id4')]); return of([{ classificationId: 'id4' }]);
default: default:
return of(); return of();
} }
}); });
storeSpy.dispatch.and.callFake(() => of());
fixture = testBed.createComponent(TaskanaTreeComponent); fixture = testBed.createComponent(TaskanaTreeComponent);
classificationsService = testBed.get(ClassificationsService); classificationsService = testBed.get(ClassificationsService);
@ -86,10 +86,22 @@ describe('TaskanaTreeComponent', () => {
}); });
it('should be change the classification parent (onMoveNode)', async () => { it('should be change the classification parent (onMoveNode)', async () => {
const classification = new ClassificationDefinition('id4', const classification: Classification = {
'key4', '', '', 'MANUAL', 'DOMAIN_A', 'TASK', true, '019-04-10T10:23:34.985Z', '2019-04-10T10:23:34.985Z', classificationId: 'id4',
'classification4', 'description', 1, 'level', '', '', '', '', '', '', key: 'key4',
'', '', '', new LinksClassification({ href: '' }, '', '', { href: '' }, { href: '' }, { href: '' })); category: 'MANUAL',
domain: 'DOMAIN_A',
parentId: '',
parentKey: '',
type: 'TASK',
isValidInDomain: true,
created: '019-04-10T10:23:34.985Z',
modified: '2019-04-10T10:23:34.985Z',
name: 'classification4',
description: 'description',
priority: 1,
serviceLevel: 'level',
};
// using parameter 'any' since getClassification is a private method // using parameter 'any' since getClassification is a private method
spyOn<any>(component, 'getClassification').and.returnValue(classification); spyOn<any>(component, 'getClassification').and.returnValue(classification);
@ -103,15 +115,25 @@ describe('TaskanaTreeComponent', () => {
expect(classification.parentId).toEqual('id3'); expect(classification.parentId).toEqual('id3');
expect(classification.parentKey).toEqual('key3'); expect(classification.parentKey).toEqual('key3');
expect(storeSpy.dispatch).toHaveBeenCalledWith(new UpdateClassification(classification)); expect(storeSpy.dispatch).toHaveBeenCalledWith(new UpdateClassification(classification));
expect(component.switchTaskanaSpinner).toHaveBeenCalledWith(true);
expect(component.switchTaskanaSpinner).toHaveBeenCalledWith(false);
}); });
it('should be changed the parent classification to root node (onDrop)', async () => { it('should be changed the parent classification to root node (onDrop)', async () => {
const classification = new ClassificationDefinition('id3', const classification: Classification = {
'key3', 'id1', 'key1', 'MANUAL', 'DOMAIN_A', 'TASK', true, '019-04-10T10:23:34.985Z', '2019-04-10T10:23:34.985Z', classificationId: 'id3',
'classification3', 'description', 1, 'level', '', '', '', '', '', '', key: 'key3',
'', '', '', new LinksClassification({ href: '' }, '', '', { href: '' }, { href: '' }, { href: '' })); parentId: 'id1',
parentKey: 'key1',
category: 'MANUAL',
domain: 'DOMAIN_A',
type: 'TASK',
isValidInDomain: true,
created: '019-04-10T10:23:34.985Z',
modified: '2019-04-10T10:23:34.985Z',
name: 'classification3',
description: 'description',
priority: 1,
serviceLevel: 'level'
};
// using parameter 'any' since getClassification is a private method // using parameter 'any' since getClassification is a private method
spyOn<any>(component, 'getClassification').and.returnValue(classification); spyOn<any>(component, 'getClassification').and.returnValue(classification);
@ -124,7 +146,5 @@ describe('TaskanaTreeComponent', () => {
expect(classification.parentId).toEqual(''); expect(classification.parentId).toEqual('');
expect(classification.parentKey).toEqual(''); expect(classification.parentKey).toEqual('');
expect(component.switchTaskanaSpinner).toHaveBeenCalledWith(true);
expect(component.switchTaskanaSpinner).toHaveBeenCalledWith(false);
}); });
}); });

View File

@ -1,4 +1,10 @@
import { AfterViewChecked, Component, ElementRef, EventEmitter, HostListener, Input, OnDestroy, import { AfterViewChecked,
Component,
ElementRef,
EventEmitter,
HostListener,
Input,
OnDestroy,
OnInit, OnInit,
Output, Output,
ViewChild } from '@angular/core'; ViewChild } from '@angular/core';
@ -6,8 +12,8 @@ import { TreeNodeModel } from 'app/shared/models/tree-node';
import { ITreeOptions, KEYS, TREE_ACTIONS, TreeComponent } from 'angular-tree-component'; import { ITreeOptions, KEYS, TREE_ACTIONS, TreeComponent } from 'angular-tree-component';
import { Pair } from 'app/shared/models/pair'; import { Pair } from 'app/shared/models/pair';
import { Observable, Subject, combineLatest } from 'rxjs'; import { combineLatest, Observable, Subject } from 'rxjs';
import { map, takeUntil, filter, tap } from 'rxjs/operators'; import { filter, map, takeUntil } from 'rxjs/operators';
import { Select, Store } from '@ngxs/store'; import { Select, Store } from '@ngxs/store';
import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors'; import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors';
@ -15,7 +21,6 @@ import { Location } from '@angular/common';
import { NOTIFICATION_TYPES } from 'app/shared/models/notifications'; import { NOTIFICATION_TYPES } from 'app/shared/models/notifications';
import { NotificationService } from 'app/shared/services/notifications/notification.service'; import { NotificationService } from 'app/shared/services/notifications/notification.service';
import { Classification } from '../../models/classification'; import { Classification } from '../../models/classification';
import { ClassificationDefinition } from '../../models/classification-definition';
import { ClassificationsService } from '../../services/classifications/classifications.service'; import { ClassificationsService } from '../../services/classifications/classifications.service';
import { ClassificationCategoryImages } from '../../models/customisation'; import { ClassificationCategoryImages } from '../../models/customisation';
import { ClassificationSelectors } from '../../store/classification-store/classification.selectors'; import { ClassificationSelectors } from '../../store/classification-store/classification.selectors';
@ -23,6 +28,7 @@ import { DeselectClassification,
SelectClassification, SelectClassification,
UpdateClassification } from '../../store/classification-store/classification.actions'; UpdateClassification } from '../../store/classification-store/classification.actions';
import { ACTION } from '../../models/action'; import { ACTION } from '../../models/action';
import { ClassificationTreeService } from '../../services/classification-tree/classification-tree.service';
@Component({ @Component({
selector: 'taskana-tree', selector: 'taskana-tree',
@ -30,7 +36,8 @@ import { ACTION } from '../../models/action';
styleUrls: ['./tree.component.scss'], styleUrls: ['./tree.component.scss'],
}) })
export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy { export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy {
classifications: TreeNodeModel[]; treeNodes: TreeNodeModel[];
@Input() selectNodeId: string; @Input() selectNodeId: string;
@Input() filterText: string; @Input() filterText: string;
@Input() filterIcon = ''; @Input() filterIcon = '';
@ -38,7 +45,7 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
@Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable<ClassificationCategoryImages>; @Select(EngineConfigurationSelectors.selectCategoryIcons) categoryIcons$: Observable<ClassificationCategoryImages>;
@Select(ClassificationSelectors.selectedClassificationId) selectedClassificationId$: Observable<string>; @Select(ClassificationSelectors.selectedClassificationId) selectedClassificationId$: Observable<string>;
@Select(ClassificationSelectors.activeAction) activeAction$: Observable<ACTION>; @Select(ClassificationSelectors.activeAction) activeAction$: Observable<ACTION>;
@Select(ClassificationSelectors.classifications) classifications$: Observable<TreeNodeModel[]>; @Select(ClassificationSelectors.classifications) classifications$: Observable<Classification[]>;
@Select(ClassificationSelectors.selectedClassificationType) classificationTypeSelected$: Observable<string>; @Select(ClassificationSelectors.selectedClassificationType) classificationTypeSelected$: Observable<string>;
options: ITreeOptions = { options: ITreeOptions = {
@ -70,6 +77,7 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
private location: Location, private location: Location,
private store: Store, private store: Store,
private notificationsService: NotificationService, private notificationsService: NotificationService,
private classificationTreeService: ClassificationTreeService
) { ) {
} }
@ -85,34 +93,31 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
this.action = action; this.action = action;
}); });
const classificationCopy$: Observable<TreeNodeModel[]> = this.classifications$.pipe( const computedTreeNodes$: Observable<TreeNodeModel[]> = this.classifications$.pipe(
filter(classifications => typeof (classifications) !== 'undefined'), filter(classifications => typeof (classifications) !== 'undefined'),
map(classifications => classifications.map(c => this.classificationsDeepCopy(c))) map(classifications => this.classificationTreeService.transformToTreeNode(classifications))
); );
combineLatest(this.selectedClassificationId$, classificationCopy$).pipe(takeUntil(this.destroy$)) combineLatest([this.selectedClassificationId$, computedTreeNodes$]).pipe(takeUntil(this.destroy$))
.subscribe(([selectedClassificationId, classifications]) => { .subscribe(([selectedClassificationId, treeNodes]) => {
this.classifications = classifications; this.treeNodes = treeNodes;
this.selectNodeId = typeof selectedClassificationId !== 'undefined' ? selectedClassificationId : undefined; this.selectNodeId = typeof selectedClassificationId !== 'undefined' ? selectedClassificationId : undefined;
if (typeof this.tree.treeModel.getActiveNode() !== 'undefined') { if (typeof this.tree.treeModel.getActiveNode() !== 'undefined') {
if (this.tree.treeModel.getActiveNode().data.classificationId !== this.selectNodeId) { if (this.tree.treeModel.getActiveNode().data.classificationId !== this.selectNodeId) {
this.selectNode(this.selectNodeId); // wait for angular's two-way binding to convert the treeNodes to the internal tree structure.
// after that conversion the new treeNodes are available
setTimeout(() => this.selectNode(this.selectNodeId), 0);
} }
} }
}); });
this.classificationTypeSelected$.pipe(takeUntil(this.destroy$)).subscribe(() => { this.classificationTypeSelected$.pipe(takeUntil(this.destroy$)).subscribe(() => {
if (this.tree.treeModel.getActiveNode()) { this.deselectActiveNode(); } if (this.tree.treeModel.getActiveNode()) {
this.deselectActiveNode();
}
}); });
} }
classificationsDeepCopy(classification: TreeNodeModel) {
const ret: TreeNodeModel = { ...classification };
ret.children = ret.children ? [...ret.children] : [];
ret.children = ret.children.map(children => this.classificationsDeepCopy(children));
return ret;
}
ngAfterViewChecked(): void { ngAfterViewChecked(): void {
if (this.selectNodeId && !this.tree.treeModel.getActiveNode()) { if (this.selectNodeId && !this.tree.treeModel.getActiveNode()) {
this.selectNode(this.selectNodeId); this.selectNode(this.selectNodeId);
@ -179,6 +184,11 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
this.switchTaskanaSpinnerEmit.emit(active); this.switchTaskanaSpinnerEmit.emit(active);
} }
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
private selectNode(nodeId: string) { private selectNode(nodeId: string) {
if (nodeId) { if (nodeId) {
const selectedNode = this.getNode(nodeId); const selectedNode = this.getNode(nodeId);
@ -200,16 +210,16 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
} }
private filterNodes(text, iconText) { private filterNodes(text, iconText) {
this.tree.treeModel.filterNodes(node => this.checkNameAndKey(node, text) this.tree.treeModel.filterNodes(node => TaskanaTreeComponent.checkNameAndKey(node, text)
&& this.checkIcon(node, iconText)); && TaskanaTreeComponent.checkIcon(node, iconText));
} }
private checkNameAndKey(node: any, text: string): boolean { private static checkNameAndKey(node: any, text: string): boolean {
return (node.data.name.toUpperCase().includes(text.toUpperCase()) return (node.data.name.toUpperCase().includes(text.toUpperCase())
|| node.data.key.toUpperCase().includes(text.toUpperCase())); || node.data.key.toUpperCase().includes(text.toUpperCase()));
} }
private checkIcon(node: any, iconText: string): boolean { private static checkIcon(node: any, iconText: string): boolean {
return (node.data.category.toUpperCase() === iconText.toUpperCase() return (node.data.category.toUpperCase() === iconText.toUpperCase()
|| iconText === ''); || iconText === '');
} }
@ -228,17 +238,19 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
|| event.target.localName === 'taskana-tree'); || event.target.localName === 'taskana-tree');
} }
private getClassification(classificationId: string): Promise<ClassificationDefinition> { private getClassification(classificationId: string): Promise<Classification> {
return this.classificationsService.getClassification(classificationId).toPromise(); return this.classificationsService.getClassification(classificationId).toPromise();
} }
private updateClassification(classification: Classification) { private updateClassification(classification: Classification) {
this.store.dispatch(new UpdateClassification(classification)); this.store.dispatch(new UpdateClassification(classification))
this.notificationsService.showToast( .subscribe(() => {
NOTIFICATION_TYPES.SUCCESS_ALERT_5, this.notificationsService.showToast(
new Map<string, string>([['classificationKey', classification.key]]) NOTIFICATION_TYPES.SUCCESS_ALERT_5,
); new Map<string, string>([['classificationKey', classification.key]])
this.switchTaskanaSpinner(false); );
this.switchTaskanaSpinner(false);
});
} }
private collapseParentNodeIfItIsTheLastChild(node: any) { private collapseParentNodeIfItIsTheLastChild(node: any) {
@ -247,9 +259,4 @@ export class TaskanaTreeComponent implements OnInit, AfterViewChecked, OnDestroy
this.getNode(node.parentId).collapse(); this.getNode(node.parentId).collapse();
} }
} }
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
} }

View File

@ -7,13 +7,11 @@ import { MatSnackBar } from '@angular/material/snack-bar';
import { Overlay } from '@angular/cdk/overlay'; import { Overlay } from '@angular/cdk/overlay';
import { UserInformationComponent } from './user-information.component'; import { UserInformationComponent } from './user-information.component';
describe('UserInformationComponent', () => { describe('UserInformationComponent', () => {
let component: UserInformationComponent; let component: UserInformationComponent;
let fixture: ComponentFixture<UserInformationComponent>; let fixture: ComponentFixture<UserInformationComponent>;
let debugElement; let debugElement;
beforeEach(done => { beforeEach(done => {
const configure = (testBed: TestBed) => { const configure = (testBed: TestBed) => {
testBed.configureTestingModule({ testBed.configureTestingModule({

View File

@ -15,7 +15,6 @@ export class BusinessAdminGuard implements CanActivate {
return this.navigateToWorkplace(); return this.navigateToWorkplace();
} }
navigateToWorkplace(): boolean { navigateToWorkplace(): boolean {
this.router.navigate(['workplace']); this.router.navigate(['workplace']);
return false; return false;

View File

@ -1,5 +1,3 @@
import { LinksClassification } from 'app/shared/models/links-classfication';
export class AccessIdDefinition { export class AccessIdDefinition {
constructor( constructor(
public accessId?: string, public accessId?: string,

View File

@ -1,4 +1,3 @@
export enum ACTION { export enum ACTION {
DEFAULT, DEFAULT,
CREATE, CREATE,

View File

@ -1,33 +0,0 @@
import { LinksClassification } from 'app/shared/models/links-classfication';
export class ClassificationDefinition {
constructor(
public classificationId?: string,
public key?: string,
public parentId?: string,
public parentKey?: string,
public category?: string,
public domain?: string,
public type?: string,
public isValidInDomain?: boolean,
public created?: string,
public modified?: 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,
public _links?: LinksClassification
) {
}
}
export const customFieldCount: number = 8;

View File

@ -0,0 +1,9 @@
import { Links } from './links';
import { Page } from './page';
import { ClassificationSummary } from './classification-summary';
export interface ClassificationPagingList {
classifications: ClassificationSummary[];
_links?: Links;
page?: Page
}

View File

@ -1,10 +0,0 @@
import { Classification } from './classification';
import { Links } from './links';
export class ClassificationResource {
constructor(
public classifications: Classification[] = [],
public _links?: Links
) {
}
}

View File

@ -0,0 +1,23 @@
export interface ClassificationSummary {
classificationId?: string;
key?: string;
applicationEntryPoint?: string;
category?: string;
domain?: string;
name?: string;
parentId?: string;
parentKey?: string;
priority?: number;
serviceLevel?: string;
type?: string;
custom1?: string;
custom2?: string;
custom3?: string;
custom4?: string;
custom5?: string;
custom6?: string;
custom7?: string;
custom8?: string;
}
export const customFieldCount: number = 8;

View File

@ -1,17 +1,11 @@
import { Links } from 'app/shared/models/links'; import { ClassificationSummary } from './classification-summary';
import { Links } from './links';
export class Classification { export interface Classification extends ClassificationSummary {
constructor( isValidInDomain?: boolean;
public classificationId?: string, // newly created classifications don't have an id yet. created?: string; // TODO: make this a Date
public key?: string, modified?: string; // TODO: make this a Date
public category?: string, description?: string;
public type?: string,
public domain?: string, _links?: Links;
public name?: string,
public parentId?: string,
public priority?: number,
public serviceLevel?: string,
public _links?: Links
) {
}
} }

View File

@ -2,7 +2,7 @@ import { map } from 'rxjs/operators';
import { OperatorFunction } from 'rxjs'; import { OperatorFunction } from 'rxjs';
export interface Customisation { export interface Customisation {
[language: string]: CustomisationContent [language: string]: CustomisationContent;
} }
export interface CustomisationContent { export interface CustomisationContent {
@ -13,17 +13,17 @@ export interface CustomisationContent {
export interface TasksCustomisation { export interface TasksCustomisation {
information?: { information?: {
owner: LookupField owner: LookupField;
}; }
} }
export interface ClassificationsCustomisation { export interface ClassificationsCustomisation {
information?: CustomFields; information?: CustomFields;
categories?: ClassificationCategoryImages categories?: ClassificationCategoryImages;
} }
export interface ClassificationCategoryImages { export interface ClassificationCategoryImages {
[key: string]: string [key: string]: string;
} }
export interface WorkbasketsCustomisation { export interface WorkbasketsCustomisation {
@ -34,16 +34,16 @@ export interface WorkbasketsCustomisation {
export type AccessItemsCustomisation = { accessId?: LookupField } & CustomFields; export type AccessItemsCustomisation = { accessId?: LookupField } & CustomFields;
export interface CustomFields { export interface CustomFields {
[key: string]: CustomField [key: string]: CustomField;
} }
export interface CustomField { export interface CustomField {
visible: boolean visible: boolean;
field: string field: string;
} }
export interface LookupField { export interface LookupField {
lookupField: boolean lookupField: boolean;
} }
export function getCustomFields(amount: number): OperatorFunction<CustomFields, CustomField[]> { export function getCustomFields(amount: number): OperatorFunction<CustomFields, CustomField[]> {

View File

@ -1,4 +1,3 @@
export enum ICONTYPES { export enum ICONTYPES {
ALL = 'ALL', ALL = 'ALL',
PERSONAL = 'PERSONAL', PERSONAL = 'PERSONAL',

View File

@ -1,12 +0,0 @@
import { Links } from './links';
export class LinksClassification extends Links {
constructor(
self?,
distributionTargets?,
accessItems?,
public getAllClassifications?: { 'href': string },
public createClassification?: { 'href': string },
public updateClassification?: { 'href': string },
) { super(self, distributionTargets, accessItems); }
}

View File

@ -1,10 +0,0 @@
import { Links } from './links';
export class LinksWorkbasketSummary extends Links {
constructor(
self?,
distributionTargets?,
accessItems?,
public allWorkbaskets?: { 'href': string }
) { super(self, distributionTargets, accessItems); }
}

View File

@ -1,9 +1,5 @@
export class Links { export interface Links {
constructor( [description: string]: {
public self?: { 'href': string }, href: string
public distributionTargets?: { 'href': string }, }
public accessItems?: { 'href': string },
public allWorkbasketUrl?: { 'href': string },
public removeDistributionTargets?: {'href': string}
) { }
} }

View File

@ -1,6 +1,5 @@
import { Pair } from './pair'; import { Pair } from './pair';
export enum NOTIFICATION_TYPES { export enum NOTIFICATION_TYPES {
// ERRORS // ERRORS
@ -58,6 +57,7 @@ export enum NOTIFICATION_TYPES {
SUCCESS_ALERT_14, SUCCESS_ALERT_14,
WARNING_ALERT, WARNING_ALERT,
WARNING_ALERT_2, WARNING_ALERT_2,
WARNING_CANT_COPY
} }
export const notifications = new Map<NOTIFICATION_TYPES, Pair>([ export const notifications = new Map<NOTIFICATION_TYPES, Pair>([
@ -310,5 +310,9 @@ export const notifications = new Map<NOTIFICATION_TYPES, Pair>([
[NOTIFICATION_TYPES.INFO_ALERT_2, new Pair( [NOTIFICATION_TYPES.INFO_ALERT_2, new Pair(
'', '',
'The selected Workbasket is empty!' 'The selected Workbasket is empty!'
)],
[NOTIFICATION_TYPES.WARNING_CANT_COPY, new Pair(
'',
'Can\'t copy a not created classification'
)] )]
]); ]);

View File

@ -1,4 +1,3 @@
export class QueryParameters { export class QueryParameters {
SORTBY: string; SORTBY: string;
SORTDIRECTION: string; SORTDIRECTION: string;

View File

@ -3,7 +3,6 @@ export enum Direction {
DESC = 'desc' DESC = 'desc'
} }
export class Sorting { export class Sorting {
sortBy: string; sortBy: string;
sortDirection: string; sortDirection: string;

View File

@ -3,5 +3,5 @@ import { TaskHistoryEventData } from './task-history-event';
export class TaskHistoryEventResourceData { export class TaskHistoryEventResourceData {
public taskHistoryEvents: Array<TaskHistoryEventData>; public taskHistoryEvents: Array<TaskHistoryEventData>;
public _links: Links = new Links(); public _links: Links = {};
} }

View File

@ -1,18 +1,5 @@
import { Classification } from 'app/shared/models/classification'; import { Classification } from 'app/shared/models/classification';
export class TreeNodeModel extends Classification { export interface TreeNodeModel extends Classification {
constructor( children: TreeNodeModel[]
public id?: string,
public key?: string,
public category?: string,
public type?: string,
public domain?: string,
public name?: string,
public parentId?: string,
public priority?: number,
public serviceLevel?: string,
public children: TreeNodeModel[] = []
) {
super(id, key, category, type, domain, name, parentId, priority, serviceLevel);
}
} }

View File

@ -1,4 +1,3 @@
export class UserInfo { export class UserInfo {
constructor( constructor(
public userId: string = '', public userId: string = '',

View File

@ -1,4 +1,3 @@
export class Version { export class Version {
constructor( constructor(
public version: string = '' public version: string = ''

View File

@ -4,6 +4,6 @@ import { WorkbasketAccessItems } from './workbasket-access-items';
export class WorkbasketAccessItemsResource { export class WorkbasketAccessItemsResource {
constructor( constructor(
public accessItems: Array<WorkbasketAccessItems> = [], public accessItems: Array<WorkbasketAccessItems> = [],
public _links: Links = new Links() public _links: Links = {}
) { } ) { }
} }

View File

@ -23,7 +23,7 @@ export class WorkbasketAccessItems {
public permCustom10: boolean = false, public permCustom10: boolean = false,
public permCustom11: boolean = false, public permCustom11: boolean = false,
public permCustom12: boolean = false, public permCustom12: boolean = false,
public _links: Links = new Links() public _links: Links = {}
) { } ) { }
} }

View File

@ -4,6 +4,6 @@ import { Workbasket } from './workbasket';
export class WorkbasketResource { export class WorkbasketResource {
constructor( constructor(
public workbaskets: Array<Workbasket> = [], public workbaskets: Array<Workbasket> = [],
public _links: Links = new Links() public _links: Links = {}
) { } ) { }
} }

View File

@ -1,11 +1,11 @@
import { Page } from 'app/shared/models/page'; import { Page } from 'app/shared/models/page';
import { WorkbasketSummary } from './workbasket-summary'; import { WorkbasketSummary } from './workbasket-summary';
import { LinksWorkbasketSummary } from './links-workbasket-summary'; import { Links } from './links';
export class WorkbasketSummaryResource { export class WorkbasketSummaryResource {
constructor( constructor(
public workbaskets: Array<WorkbasketSummary> = [], public workbaskets: Array<WorkbasketSummary> = [],
public _links: LinksWorkbasketSummary = new LinksWorkbasketSummary(), public _links: Links = {},
public page: Page = new Page() public page: Page = new Page()
) { ) {
} }

View File

@ -2,28 +2,6 @@ import { Links } from './links';
import { ICONTYPES } from './icon-types'; import { ICONTYPES } from './icon-types';
export class Workbasket { export class Workbasket {
public static equals(org: Workbasket, comp: Workbasket): boolean {
if (org.workbasketId !== comp.workbasketId) { return false; }
if (org.created !== comp.created) { return false; }
if (org.key !== comp.key) { return false; }
if (org.domain !== comp.domain) { return false; }
if (org.type !== comp.type) { return false; }
if (org.modified !== comp.modified) { return false; }
if (org.name !== comp.name) { return false; }
if (org.description !== comp.description) { return false; }
if (org.owner !== comp.owner) { return false; }
if (org.custom1 !== comp.custom1) { return false; }
if (org.custom2 !== comp.custom2) { return false; }
if (org.custom3 !== comp.custom3) { return false; }
if (org.custom4 !== comp.custom4) { return false; }
if (org.orgLevel1 !== comp.orgLevel1) { return false; }
if (org.orgLevel2 !== comp.orgLevel2) { return false; }
if (org.orgLevel3 !== comp.orgLevel3) { return false; }
if (org.orgLevel4 !== comp.orgLevel4) { return false; }
return true;
}
constructor( constructor(
public workbasketId?: string, public workbasketId?: string,
public created?: string, public created?: string,
@ -42,7 +20,7 @@ export class Workbasket {
public orgLevel2?: string, public orgLevel2?: string,
public orgLevel3?: string, public orgLevel3?: string,
public orgLevel4?: string, public orgLevel4?: string,
public _links: Links = new Links() public _links: Links = {}
) { ) {
} }
} }

View File

@ -16,7 +16,6 @@ export class SpreadNumberPipe implements PipeTransform {
let leftDifference = 0; let leftDifference = 0;
let rightDifference = 0; let rightDifference = 0;
if (minArrayValue < 0) { leftDifference = Math.abs(minArrayValue); minArrayValue = 0; } if (minArrayValue < 0) { leftDifference = Math.abs(minArrayValue); minArrayValue = 0; }
if (maxArrayValue > maxPageNumber) { if (maxArrayValue > maxPageNumber) {
rightDifference = maxArrayValue - maxPageNumber; rightDifference = maxArrayValue - maxPageNumber;

View File

@ -0,0 +1,31 @@
import { Injectable } from '@angular/core';
import { TreeNodeModel } from '../../models/tree-node';
import { Classification } from '../../models/classification';
@Injectable({
providedIn: 'root'
})
export class ClassificationTreeService {
transformToTreeNode(classifications: Classification[]): TreeNodeModel[] {
const classificationsAsTree: TreeNodeModel[] = classifications.map(c => ({
...c,
children: []
})).sort((a: TreeNodeModel, b: TreeNodeModel) => a.key.localeCompare(b.key));
const roots: TreeNodeModel[] = [];
const children: TreeNodeModel[] = [];
classificationsAsTree.forEach(item => {
const parent = item.parentId;
const target = !parent ? roots : (children[parent] || (children[parent] = []));
target.push(item);
});
roots.forEach(parent => this.findChildren(parent, children));
return roots;
}
private findChildren(parent: TreeNodeModel, children: TreeNodeModel[]) {
if (children[parent.classificationId]) {
parent.children = children[parent.classificationId];
parent.children.forEach(child => this.findChildren(child, children));
}
}
}

View File

@ -5,9 +5,8 @@ import { Observable, Subject } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators'; import { map, mergeMap, tap } from 'rxjs/operators';
import { Classification } from 'app/shared/models/classification'; import { Classification } from 'app/shared/models/classification';
import { ClassificationDefinition } from 'app/shared/models/classification-definition';
import { ClassificationResource } from 'app/shared/models/classification-resource'; import { ClassificationPagingList } from 'app/shared/models/classification-paging-list';
import { DomainService } from 'app/shared/services/domain/domain.service'; import { DomainService } from 'app/shared/services/domain/domain.service';
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters'; import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
import { Direction } from 'app/shared/models/sorting'; import { Direction } from 'app/shared/models/sorting';
@ -16,20 +15,21 @@ import { QueryParameters } from 'app/shared/models/query-parameters';
@Injectable() @Injectable()
export class ClassificationsService { export class ClassificationsService {
private url = `${environment.taskanaRestUrl}/v1/classifications/`; private url = `${environment.taskanaRestUrl}/v1/classifications/`;
private classificationSaved = new Subject<number>(); private classificationResourcePromise: Promise<ClassificationPagingList>;
private classificationResourcePromise: Promise<ClassificationResource>;
private lastDomain: string; private lastDomain: string;
constructor( constructor(
private httpClient: HttpClient, private httpClient: HttpClient,
private domainService: DomainService, private domainService: DomainService,
) {} ) {
}
private static classificationParameters(domain: string): QueryParameters { private static classificationParameters(domain: string, type?: string): QueryParameters {
const parameters = new QueryParameters(); const parameters = new QueryParameters();
parameters.SORTBY = TaskanaQueryParameters.parameters.KEY; parameters.SORTBY = TaskanaQueryParameters.parameters.KEY;
parameters.SORTDIRECTION = Direction.ASC; parameters.SORTDIRECTION = Direction.ASC;
parameters.DOMAIN = domain; parameters.DOMAIN = domain;
parameters.TYPE = type;
delete TaskanaQueryParameters.page; delete TaskanaQueryParameters.page;
delete TaskanaQueryParameters.pageSize; delete TaskanaQueryParameters.pageSize;
@ -37,22 +37,22 @@ export class ClassificationsService {
} }
// GET // GET
getClassifications(classificationType?: string): Observable<Array<Classification>> { getClassifications(classificationType?: string): Observable<ClassificationPagingList> {
return this.domainService.getSelectedDomain().pipe( return this.domainService.getSelectedDomain().pipe(
mergeMap(domain => this.getClassificationObservable(this.httpClient.get<ClassificationResource>( mergeMap(domain => this.httpClient.get<ClassificationPagingList>(
`${this.url}${TaskanaQueryParameters.getQueryParameters(ClassificationsService.classificationParameters(domain))}` `${this.url}${TaskanaQueryParameters.getQueryParameters(
), classificationType)), ClassificationsService.classificationParameters(domain, classificationType)
tap(() => { )}`
this.domainService.domainChangedComplete(); )),
}) tap(() => this.domainService.domainChangedComplete())
); );
} }
// GET // GET
getClassificationsByDomain(domain: string, forceRefresh = false): Promise<ClassificationResource> { getClassificationsByDomain(domain: string, forceRefresh = false): Promise<ClassificationPagingList> {
if (this.lastDomain !== domain || !this.classificationResourcePromise || forceRefresh) { if (this.lastDomain !== domain || !this.classificationResourcePromise || forceRefresh) {
this.lastDomain = domain; this.lastDomain = domain;
this.classificationResourcePromise = this.httpClient.get<ClassificationResource>( this.classificationResourcePromise = this.httpClient.get<ClassificationPagingList>(
`${this.url}${TaskanaQueryParameters.getQueryParameters(ClassificationsService.classificationParameters(domain))}` `${this.url}${TaskanaQueryParameters.getQueryParameters(ClassificationsService.classificationParameters(domain))}`
).toPromise(); ).toPromise();
} }
@ -60,8 +60,8 @@ export class ClassificationsService {
} }
// GET // GET
getClassification(id: string): Observable<ClassificationDefinition> { getClassification(id: string): Observable<Classification> {
return this.httpClient.get<ClassificationDefinition>(`${this.url}${id}`); return this.httpClient.get<Classification>(`${this.url}${id}`);
} }
// POST // POST
@ -78,49 +78,4 @@ export class ClassificationsService {
deleteClassification(id: string): Observable<string> { deleteClassification(id: string): Observable<string> {
return this.httpClient.delete<string>(`${this.url}${id}`); return this.httpClient.delete<string>(`${this.url}${id}`);
} }
// #region "Service extras"
triggerClassificationSaved() {
this.classificationSaved.next(Date.now());
}
classificationSavedTriggered(): Observable<number> {
return this.classificationSaved.asObservable();
}
// #endregion
private getClassificationObservable(
classificationRef: Observable<ClassificationResource>,
classificationType: string
): Observable<Array<Classification>> {
return classificationRef.pipe(map(
(resource: ClassificationResource) => (
resource.classifications ? this.buildHierarchy(resource.classifications, classificationType) : []
)
));
}
private buildHierarchy(classifications: Array<Classification>, type: string): Array<Classification> {
const roots = [];
const children = [];
classifications.forEach(item => {
if (item.type === type) {
const parent = item.parentId;
const target = !parent ? roots : (children[parent] || (children[parent] = []));
target.push(item);
}
});
roots.forEach(parent => this.findChildren(parent, children));
return roots;
}
private findChildren(parent: any, children: Array<any>) {
if (children[parent.classificationId]) {
parent.children = children[parent.classificationId];
parent.children.forEach(child => this.findChildren(child, children));
}
}
} }

View File

@ -6,7 +6,7 @@ export class RequestInProgressService {
public requestInProgressTriggered = new Subject<boolean>(); public requestInProgressTriggered = new Subject<boolean>();
setRequestInProgress(value: boolean) { setRequestInProgress(value: boolean) {
setTimeout(() => this.requestInProgressTriggered.next(value), 0); this.requestInProgressTriggered.next(value);
} }
getRequestInProgress(): Observable<boolean> { getRequestInProgress(): Observable<boolean> {

View File

@ -10,7 +10,6 @@ export class SelectedRouteService {
constructor(private router: Router) { } constructor(private router: Router) { }
selectRoute(value) { selectRoute(value) {
this.selectedRouteTriggered.next(this.getRoute(value)); this.selectedRouteTriggered.next(this.getRoute(value));
} }

View File

@ -6,7 +6,6 @@ import { Version } from 'app/shared/models/version';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
@Injectable() @Injectable()
export class TaskanaEngineService { export class TaskanaEngineService {
currentUserInfo: UserInfo; currentUserInfo: UserInfo;

View File

@ -1,4 +1,3 @@
import { throwError as observableThrowError, Observable, Subject } from 'rxjs'; import { throwError as observableThrowError, Observable, Subject } from 'rxjs';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
@ -46,7 +45,6 @@ export class WorkbasketService {
return this.workbasketSummaryRef; return this.workbasketSummaryRef;
} }
return this.domainService.getSelectedDomain() return this.domainService.getSelectedDomain()
.pipe(mergeMap(domain => { .pipe(mergeMap(domain => {
this.workbasketSummaryRef = this.httpClient.get<WorkbasketSummaryResource>( this.workbasketSummaryRef = this.httpClient.get<WorkbasketSummaryResource>(
@ -54,7 +52,6 @@ export class WorkbasketService {
.getQueryParameters(this.workbasketParameters(sortBy, order, name, nameLike, descLike, owner, ownerLike, .getQueryParameters(this.workbasketParameters(sortBy, order, name, nameLike, descLike, owner, ownerLike,
type, key, keyLike, requiredPermission, allPages, domain))}` type, key, keyLike, requiredPermission, allPages, domain))}`
); );
this.workbasketSummaryRef.pipe(tap((workbaskets => workbaskets)));
return this.workbasketSummaryRef; return this.workbasketSummaryRef;
}), }),
tap(() => { tap(() => {
@ -124,7 +121,6 @@ export class WorkbasketService {
return this.httpClient.delete<string>(url); return this.httpClient.delete<string>(url);
} }
// #endregion // #endregion
// #region "Service extras" // #region "Service extras"
selectWorkBasket(id?: string) { selectWorkBasket(id?: string) {
@ -159,7 +155,6 @@ export class WorkbasketService {
return observableThrowError(errMsg); return observableThrowError(errMsg);
} }
private workbasketParameters( private workbasketParameters(
sortBy: string = TaskanaQueryParameters.parameters.KEY, sortBy: string = TaskanaQueryParameters.parameters.KEY,
order: string = Direction.ASC, order: string = Direction.ASC,

View File

@ -10,7 +10,6 @@ import { AccordionModule } from 'ngx-bootstrap/accordion';
import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service'; import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service';
import { ClassificationsService } from 'app/shared/services/classifications/classifications.service'; import { ClassificationsService } from 'app/shared/services/classifications/classifications.service';
/** /**
* Components * Components
*/ */
@ -44,7 +43,6 @@ import { MapToIterable } from './pipes/map-to-iterable.pipe';
import { NumberToArray } from './pipes/number-to-array.pipe'; import { NumberToArray } from './pipes/number-to-array.pipe';
import { DateTimeZonePipe } from './pipes/date-time-zone.pipe'; import { DateTimeZonePipe } from './pipes/date-time-zone.pipe';
/** /**
* Services * Services
*/ */

View File

@ -1,4 +1,3 @@
import { ClassificationDefinition } from '../../models/classification-definition';
import { ACTION } from '../../models/action'; import { ACTION } from '../../models/action';
import { Classification } from '../../models/classification'; import { Classification } from '../../models/classification';
@ -20,13 +19,13 @@ export class DeselectClassification {
export class CreateClassification { export class CreateClassification {
static readonly type = '[Classification] Create a new classification'; static readonly type = '[Classification] Create a new classification';
constructor(public classification: ClassificationDefinition) { constructor(public classification: Classification) {
} }
} }
export class SaveClassification { export class SaveClassification {
static readonly type = '[Classification] Save a classification and select it'; static readonly type = '[Classification] Save a classification and select it';
constructor(public classification: ClassificationDefinition) { constructor(public classification: Classification) {
} }
} }

View File

@ -1,7 +1,8 @@
import { Selector } from '@ngxs/store'; import { Selector } from '@ngxs/store';
import { ClassificationStateModel, ClassificationState } from './classification.state'; import { ClassificationStateModel, ClassificationState } from './classification.state';
import { ClassificationDefinition } from '../../models/classification-definition';
import { ACTION } from '../../models/action'; import { ACTION } from '../../models/action';
import { Classification } from '../../models/classification';
import { CategoriesResponse } from '../../services/classification-categories/classification-categories.service';
export class ClassificationSelectors { export class ClassificationSelectors {
@Selector([ClassificationState]) @Selector([ClassificationState])
@ -20,17 +21,17 @@ export class ClassificationSelectors {
} }
@Selector([ClassificationState]) @Selector([ClassificationState])
static selectClassificationTypesObject(state: ClassificationStateModel): Object { static selectClassificationTypesObject(state: ClassificationStateModel): CategoriesResponse {
return state.classificationTypes; return state.classificationTypes;
} }
@Selector([ClassificationState]) @Selector([ClassificationState])
static classifications(state: ClassificationStateModel): ClassificationDefinition[] { static classifications(state: ClassificationStateModel): Classification[] {
return state.classifications; return state.classifications;
} }
@Selector([ClassificationState]) @Selector([ClassificationState])
static selectedClassification(state: ClassificationStateModel): ClassificationDefinition { static selectedClassification(state: ClassificationStateModel): Classification {
return state.selectedClassification; return state.selectedClassification;
} }

View File

@ -15,9 +15,10 @@ import { CreateClassification,
SetSelectedClassificationType, SetSelectedClassificationType,
UpdateClassification } from './classification.actions'; UpdateClassification } from './classification.actions';
import { ClassificationsService } from '../../services/classifications/classifications.service'; import { ClassificationsService } from '../../services/classifications/classifications.service';
import { ClassificationDefinition } from '../../models/classification-definition';
import { ACTION } from '../../models/action'; import { ACTION } from '../../models/action';
import { DomainService } from '../../services/domain/domain.service'; import { DomainService } from '../../services/domain/domain.service';
import { Classification } from '../../models/classification';
import { ClassificationSummary } from '../../models/classification-summary';
class InitializeStore { class InitializeStore {
static readonly type = '[ClassificationState] Initializing state'; static readonly type = '[ClassificationState] Initializing state';
@ -32,10 +33,23 @@ export class ClassificationState implements NgxsAfterBootstrap {
) { ) {
} }
@Action(InitializeStore)
initializeStore(ctx: StateContext<ClassificationStateModel>): Observable<any> {
return this.categoryService.getClassificationCategoriesByType().pipe(
take(1),
tap(classificationTypes => {
ctx.patchState({
classificationTypes,
classifications: undefined,
selectedClassificationType: Object.keys(classificationTypes)[0],
});
}),
);
}
@Action(SetSelectedClassificationType) @Action(SetSelectedClassificationType)
setSelectedClassificationType(ctx: StateContext<ClassificationStateModel>, action: SetSelectedClassificationType): Observable<null> { setSelectedClassificationType(ctx: StateContext<ClassificationStateModel>, action: SetSelectedClassificationType): Observable<null> {
const state: ClassificationStateModel = ctx.getState(); if (ctx.getState().classificationTypes[action.selectedType]) {
if (state.classificationTypes[action.selectedType]) {
ctx.patchState({ ctx.patchState({
selectedClassificationType: action.selectedType, selectedClassificationType: action.selectedType,
selectedClassification: undefined, selectedClassification: undefined,
@ -46,17 +60,16 @@ export class ClassificationState implements NgxsAfterBootstrap {
} }
@Action(SelectClassification) @Action(SelectClassification)
selectClassification(ctx: StateContext<ClassificationStateModel>, action: SelectClassification): Observable<any|null> { selectClassification(ctx: StateContext<ClassificationStateModel>, action: SelectClassification): Observable<Classification | null> {
if (typeof action.classificationId !== 'undefined') { if (typeof action.classificationId !== 'undefined') {
return this.classificationsService.getClassification(action.classificationId).pipe(take(1), tap( return this.classificationsService.getClassification(action.classificationId).pipe(
selectedClassification => { take(1),
ctx.patchState({ tap(selectedClassification => ctx.patchState({
selectedClassification, selectedClassification,
action: ACTION.DEFAULT, selectedClassificationType: selectedClassification.type,
selectedClassificationType: selectedClassification.type action: ACTION.DEFAULT
}); }))
} );
));
} }
return of(null); return of(null);
} }
@ -70,81 +83,44 @@ export class ClassificationState implements NgxsAfterBootstrap {
return of(null); return of(null);
} }
@Action(InitializeStore)
initializeStore(ctx: StateContext<ClassificationStateModel>): Observable<any> {
return this.categoryService.getClassificationCategoriesByType().pipe(
take(1), tap(classificationTypes => {
ctx.setState({
...ctx.getState(),
classificationTypes,
classifications: undefined,
selectedClassificationType: Object.keys(classificationTypes)[0],
});
}),
);
}
@Action(GetClassifications) @Action(GetClassifications)
getClassifications(ctx: StateContext<ClassificationStateModel>): Observable<any> { getClassifications(ctx: StateContext<ClassificationStateModel>): Observable<any> {
const { selectedClassificationType } = ctx.getState(); const { selectedClassificationType } = ctx.getState();
return this.classificationsService.getClassifications(selectedClassificationType).pipe( return this.classificationsService.getClassifications(selectedClassificationType).pipe(
take(1), tap(classifications => { take(1),
classifications.forEach(classification => { tap(list => ctx.patchState({
classification.children = !classification.children ? [] : classification.children; classifications: list.classifications
}); })),
ctx.patchState({
classifications
});
}),
); );
} }
@Action(CreateClassification) @Action(CreateClassification)
createClassification(ctx: StateContext<ClassificationStateModel>, action: CreateClassification): Observable<any> { createClassification(ctx: StateContext<ClassificationStateModel>, action: CreateClassification): Observable<any> {
return this.classificationsService.postClassification(action.classification).pipe( return this.classificationsService.postClassification(action.classification).pipe(
take(1), tap(classification => { take(1), tap(classification => ctx.patchState({
ctx.patchState( classifications: [...ctx.getState().classifications, classification],
{ selectedClassification: classification,
classifications: [...ctx.getState().classifications, classification], action: ACTION.DEFAULT
selectedClassification: classification, }))
action: ACTION.DEFAULT
}
);
})
); );
} }
@Action(SaveClassification) @Action(SaveClassification)
saveClassification(ctx: StateContext<ClassificationStateModel>, action: SaveClassification): Observable<any> { saveClassification(ctx: StateContext<ClassificationStateModel>, action: SaveClassification): Observable<any> {
return this.classificationsService.putClassification(action.classification).pipe( return this.classificationsService.putClassification(action.classification).pipe(
take(1), tap(savedClassification => { take(1),
ctx.patchState({ tap(savedClassification => ctx.patchState({
classifications: ctx.getState().classifications.map(currentClassification => { classifications: updateClassificationList(ctx.getState().classifications, savedClassification),
if (currentClassification.classificationId === savedClassification.classificationId) { selectedClassification: savedClassification
return savedClassification; }))
}
return currentClassification;
}),
selectedClassification: savedClassification
});
}), tap(() => this.classificationsService.getClassifications(
ctx.getState().selectedClassificationType
).subscribe(
classifications => {
ctx.patchState({
classifications
});
}
))
); );
} }
@Action(RestoreSelectedClassification) @Action(RestoreSelectedClassification)
restoreSelectedClassification(ctx: StateContext<ClassificationStateModel>, action: RestoreSelectedClassification): Observable<any> { restoreSelectedClassification(ctx: StateContext<ClassificationStateModel>, action: RestoreSelectedClassification): Observable<any> {
return this.classificationsService.getClassification(action.classificationId).pipe( return this.classificationsService.getClassification(action.classificationId).pipe(
take(1), tap(selectedClassification => { take(1),
ctx.patchState({ selectedClassification }); tap(selectedClassification => ctx.patchState({ selectedClassification }))
})
); );
} }
@ -152,14 +128,15 @@ export class ClassificationState implements NgxsAfterBootstrap {
setActiveAction(ctx: StateContext<ClassificationStateModel>, action: SetActiveAction): Observable<null> { setActiveAction(ctx: StateContext<ClassificationStateModel>, action: SetActiveAction): Observable<null> {
if (action.action === ACTION.CREATE) { if (action.action === ACTION.CREATE) {
// Initialization of a new classification // Initialization of a new classification
const initialClassification: ClassificationDefinition = new ClassificationDefinition();
const state: ClassificationStateModel = ctx.getState(); const state: ClassificationStateModel = ctx.getState();
initialClassification.type = state.selectedClassificationType;
[initialClassification.category] = state.classificationTypes[initialClassification.type];
const date = TaskanaDate.getDate(); const date = TaskanaDate.getDate();
initialClassification.created = date; const initialClassification: Classification = {
initialClassification.modified = date; type: state.selectedClassificationType,
initialClassification.domain = this.domainService.getSelectedDomainValue(); category: state.classificationTypes[state.selectedClassificationType][0],
created: date,
modified: date,
domain: this.domainService.getSelectedDomainValue(),
};
if (state.selectedClassification) { if (state.selectedClassification) {
initialClassification.parentId = state.selectedClassification.classificationId; initialClassification.parentId = state.selectedClassification.classificationId;
initialClassification.parentKey = state.selectedClassification.key; initialClassification.parentKey = state.selectedClassification.key;
@ -174,17 +151,33 @@ export class ClassificationState implements NgxsAfterBootstrap {
@Action(RemoveSelectedClassification) @Action(RemoveSelectedClassification)
removeSelectedClassification(ctx: StateContext<ClassificationStateModel>): Observable<any> { removeSelectedClassification(ctx: StateContext<ClassificationStateModel>): Observable<any> {
const sel = ctx.getState().selectedClassification; const sel = ctx.getState().selectedClassification;
return this.classificationsService.deleteClassification(sel.classificationId).pipe(take(1), tap(() => { return this.classificationsService.deleteClassification(sel.classificationId).pipe(
const classifications = ctx.getState().classifications.filter(el => el.classificationId !== sel.classificationId); take(1),
ctx.patchState({ selectedClassification: undefined, classifications }); tap(() => {
})); const classifications = ctx.getState().classifications.filter(el => el.classificationId !== sel.classificationId);
ctx.patchState({ selectedClassification: undefined, classifications });
})
);
} }
@Action(UpdateClassification) @Action(UpdateClassification)
updateClassification(ctx: StateContext<ClassificationStateModel>, action: SaveClassification): Observable<any> { updateClassification(ctx: StateContext<ClassificationStateModel>, action: SaveClassification): Observable<any> {
return this.classificationsService.putClassification(action.classification).pipe(take(1), tap( return this.classificationsService.putClassification(action.classification).pipe(
classifications => ctx.patchState({ classifications }) take(1),
)); tap(
classification => {
const state = ctx.getState();
let { selectedClassification } = state;
if (selectedClassification && selectedClassification.classificationId === classification.classificationId) {
selectedClassification = classification;
}
ctx.patchState({
classifications: updateClassificationList(state.classifications, classification),
selectedClassification
});
}
)
);
} }
// initialize after Startup service has configured the taskanaRestUrl properly. // initialize after Startup service has configured the taskanaRestUrl properly.
@ -194,9 +187,18 @@ export class ClassificationState implements NgxsAfterBootstrap {
} }
} }
function updateClassificationList(classifications: ClassificationSummary[], classification: Classification) {
return classifications.map(c => {
if (c.classificationId === classification.classificationId) {
return classification;
}
return c;
});
}
export interface ClassificationStateModel { export interface ClassificationStateModel {
classifications: ClassificationDefinition[], classifications: ClassificationSummary[],
selectedClassification: ClassificationDefinition, selectedClassification: Classification,
selectedClassificationType: string; selectedClassificationType: string;
classificationTypes: CategoriesResponse, classificationTypes: CategoriesResponse,
action: ACTION, action: ACTION,

View File

@ -29,7 +29,6 @@ export class EngineConfigurationState implements NgxsOnInit {
} }
} }
export interface EngineConfigurationStateModel { export interface EngineConfigurationStateModel {
customisation: Customisation, customisation: Customisation,
language: string language: string

View File

@ -9,7 +9,6 @@ import { WorkplaceService } from 'app/workplace/services/workplace.service';
import { TaskListComponent } from './task-list.component'; import { TaskListComponent } from './task-list.component';
import { DateTimeZonePipe } from '../../../shared/pipes/date-time-zone.pipe'; import { DateTimeZonePipe } from '../../../shared/pipes/date-time-zone.pipe';
@Component({ @Component({
selector: 'taskana-dummy-detail', selector: 'taskana-dummy-detail',
template: 'dummydetail' template: 'dummydetail'

View File

@ -35,7 +35,6 @@ xdescribe('TaskMasterComponent', () => {
let component: TaskMasterComponent; let component: TaskMasterComponent;
let fixture: ComponentFixture<TaskMasterComponent>; let fixture: ComponentFixture<TaskMasterComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [FormsModule, TypeaheadModule, imports: [FormsModule, TypeaheadModule,

View File

@ -97,7 +97,6 @@ export class TaskMasterComponent implements OnInit, OnDestroy {
}); });
} }
performSorting(sort: Sorting) { performSorting(sort: Sorting) {
this.sort = sort; this.sort = sort;
this.getTasks(); this.getTasks();

View File

@ -8,7 +8,6 @@ import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.ser
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ClassificationsService } from 'app/shared/services/classifications/classifications.service'; import { ClassificationsService } from 'app/shared/services/classifications/classifications.service';
@Component({ @Component({
selector: 'taskana-task', selector: 'taskana-task',
templateUrl: './task.component.html', templateUrl: './task.component.html',
@ -25,7 +24,6 @@ export class TaskComponent implements OnInit, OnDestroy {
task: Task = null; task: Task = null;
workbaskets: Workbasket[]; workbaskets: Workbasket[];
constructor(private taskService: TaskService, constructor(private taskService: TaskService,
private workbasketService: WorkbasketService, private workbasketService: WorkbasketService,
private classificationService: ClassificationsService, private classificationService: ClassificationsService,

View File

@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { ClassificationsService } from 'app/shared/services/classifications/classifications.service'; import { ClassificationsService } from 'app/shared/services/classifications/classifications.service';
@ -11,9 +11,7 @@ import { Component } from '@angular/core';
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service'; import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
import { SelectedRouteService } from 'app/shared/services/selected-route/selected-route'; import { SelectedRouteService } from 'app/shared/services/selected-route/selected-route';
import { configureTests } from 'app/app.test.configuration'; import { configureTests } from 'app/app.test.configuration';
import { ClassificationResource } from 'app/shared/models/classification-resource'; import { ClassificationPagingList } from 'app/shared/models/classification-paging-list';
import { Classification } from 'app/shared/models/classification';
import { Links } from 'app/shared/models/links';
import { TaskdetailsGeneralFieldsComponent } from './general-fields.component'; import { TaskdetailsGeneralFieldsComponent } from './general-fields.component';
@Component({ @Component({
@ -35,7 +33,7 @@ xdescribe('GeneralComponent', () => {
beforeEach(done => { beforeEach(done => {
const configure = (testBed: TestBed) => { const configure = (testBed: TestBed) => {
TestBed.configureTestingModule({ testBed.configureTestingModule({
imports: [FormsModule, HttpClientModule, RouterTestingModule.withRoutes(routes)], imports: [FormsModule, HttpClientModule, RouterTestingModule.withRoutes(routes)],
declarations: [TaskdetailsGeneralFieldsComponent, DummyDetailComponent], declarations: [TaskdetailsGeneralFieldsComponent, DummyDetailComponent],
providers: [HttpClient, ClassificationCategoriesService, providers: [HttpClient, ClassificationCategoriesService,
@ -43,16 +41,31 @@ xdescribe('GeneralComponent', () => {
}); });
}; };
configureTests(configure).then(testBed => { configureTests(configure).then(testBed => {
classificationsService = TestBed.get(ClassificationsService); classificationsService = testBed.get(ClassificationsService);
spyOn(classificationsService, 'getClassificationsByDomain').and.returnValue(new ClassificationResource( const resource: ClassificationPagingList = {
new Array<Classification>( classifications: [
new Classification('id1', '1', 'category', 'type', 'domain_a', 'classification1', 'parentId', {
1, 'service', new Links({ href: 'someurl' })), classificationId: 'id1',
new Classification('id2', '2', 'category', 'type', 'domain_a', 'classification2', 'parentId2', key: 'key1',
1, 'service', new Links({ href: 'someurl' })) category: 'category',
), type: 'type',
new Links({ href: 'someurl' }) domain: 'DOMAIN_A',
)); name: 'classification1',
parentId: 'parentId',
parentKey: 'parentKey'
}, {
classificationId: 'id2',
key: 'key2',
category: 'category',
type: 'type',
domain: 'DOMAIN_A',
name: 'classification1',
parentId: 'parentId',
parentKey: 'parentKey'
},
]
};
spyOn(classificationsService, 'getClassificationsByDomain').and.returnValue(resource);
done(); done();
}); });
}); });

View File

@ -20,7 +20,6 @@ import { TaskComponent } from './components/task/task.component';
import { GeneralFieldsExtensionComponent } from './components/taskdetails-general-fields-extension/general-fields-extension.component'; import { GeneralFieldsExtensionComponent } from './components/taskdetails-general-fields-extension/general-fields-extension.component';
import { TaskListComponent } from './components/task-list/task-list.component'; import { TaskListComponent } from './components/task-list/task-list.component';
import { TaskService } from './services/task.service'; import { TaskService } from './services/task.service';
import { TokenInterceptor } from './services/token-interceptor.service'; import { TokenInterceptor } from './services/token-interceptor.service';
import { WorkplaceService } from './services/workplace.service'; import { WorkplaceService } from './services/workplace.service';

View File

@ -18,29 +18,24 @@
* BROWSER POLYFILLS * BROWSER POLYFILLS
*/ */
/** IE10 and IE11 requires the following for NgClass support on SVG elements */ /** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`. // import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following to support `@angular/animation`. */ /** IE10 and IE11 requires the following to support `@angular/animation`. */
// import 'web-animations-js'; // Run `npm install --save web-animations-js`. // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/** Evergreen browsers require these. */ /** Evergreen browsers require these. */
import 'core-js/es6/reflect'; import 'core-js/es6/reflect';
import 'core-js/es7/reflect'; import 'core-js/es7/reflect';
/** ALL Firefox browsers require the following to support `@angular/animation`. */ /** ALL Firefox browsers require the following to support `@angular/animation`. */
// import 'web-animations-js'; // Run `npm install --save web-animations-js`. // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/** ************************************************************************************************* /** *************************************************************************************************
* Zone JS is required by Angular itself. * Zone JS is required by Angular itself.
*/ */
import 'zone.js/dist/zone'; // Included with Angular CLI. import 'zone.js/dist/zone'; // Included with Angular CLI.
/** ************************************************************************************************* /** *************************************************************************************************
* APPLICATION IMPORTS * APPLICATION IMPORTS
*/ */