diff --git a/web/.angular-cli.json b/web/.angular-cli.json index 52f0ca8ca..886ad2b27 100644 --- a/web/.angular-cli.json +++ b/web/.angular-cli.json @@ -19,9 +19,7 @@ "testTsconfig": "tsconfig.spec.json", "prefix": "app", "styles": [ - "../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap.scss", - "./assets/_site.scss", - "./assets/_forms.scss" + "./assets/_styles.scss" ], "scripts": [ "../node_modules/jquery/dist/jquery.min.js", diff --git a/web/package-lock.json b/web/package-lock.json index 87abc474b..d82e8ecf5 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -571,13 +571,20 @@ "integrity": "sha512-wIRpoQ3PwytxA4MRe9cgmdytXrHgTGUuTdmIFtAQvCcftUSWWkzkVaXF1QSlFip6ipHf/YacdJHFYXpnW2lWPQ==" }, "angular-tree-component": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/angular-tree-component/-/angular-tree-component-7.0.1.tgz", - "integrity": "sha1-/I0OctjDS4cTGjuivTKtIJRWiaw=", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/angular-tree-component/-/angular-tree-component-7.1.0.tgz", + "integrity": "sha512-i0Kk4gnuU+i6p5ZsIcDcGrtHPnDLOyHk8Vqez6IpSuOLvVPZ3Y7/Y1MEOoj7Nx6qRU5NuuVaPLy2idOEB7ClRw==", "requires": { - "lodash": "4.17.4", - "mobx": "3.4.1", + "lodash": "4.17.5", + "mobx": "3.6.2", "mobx-angular": "2.1.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==" + } } }, "ansi-html": { @@ -7259,9 +7266,9 @@ } }, "mobx": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-3.4.1.tgz", - "integrity": "sha1-N6vl7ogtQBgo2fJsbBovR2FLu+8=" + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-3.6.2.tgz", + "integrity": "sha512-Dq3boJFLpZEvuh5a/MbHLUIyN9XobKWIb0dBfkNOJffNkE3vtuY0C9kSDVpfH8BB0BPkVw8g22qCv7d05LEhKg==" }, "mobx-angular": { "version": "2.1.1", diff --git a/web/package.json b/web/package.json index 1807b593c..f64a92677 100644 --- a/web/package.json +++ b/web/package.json @@ -25,7 +25,7 @@ "@angular/router": "5.2.1", "file-saver": "1.3.3", "angular-svg-icon": "5.0.0", - "angular-tree-component": "7.0.1", + "angular-tree-component": "7.1.0", "bootstrap": "3.3.7", "bootstrap-sass": "3.3.7", "core-js": "2.5.3", diff --git a/web/src/app/administration/classification/master/list/classification-list.component.html b/web/src/app/administration/classification/master/list/classification-list.component.html index 8bd22802c..1b0568e85 100644 --- a/web/src/app/administration/classification/master/list/classification-list.component.html +++ b/web/src/app/administration/classification/master/list/classification-list.component.html @@ -1,40 +1,20 @@
- - -
+ \ No newline at end of file diff --git a/web/src/app/administration/classification/master/list/classification-list.component.scss b/web/src/app/administration/classification/master/list/classification-list.component.scss index 661be407c..34a43965c 100644 --- a/web/src/app/administration/classification/master/list/classification-list.component.scss +++ b/web/src/app/administration/classification/master/list/classification-list.component.scss @@ -2,21 +2,15 @@ height: calc(100vh - 55px); } -.row.list-group { - margin-left: 2px; +.list-group-item { + padding: 5px 0px; + border: none; } -.list-group > li { - border-left: none; - border-right: none; -} +.tab-align{ + margin-bottom: 0px; -a > label { - height: 2em; - width: 100%; -} - -.tab-align { - border-bottom: 1px solid #ddd; - padding-bottom: 12px; + &>div{ + margin: 6px 0px; + } } diff --git a/web/src/app/administration/classification/master/list/classification-list.component.spec.ts b/web/src/app/administration/classification/master/list/classification-list.component.spec.ts index e324ebdcc..2bf053a86 100644 --- a/web/src/app/administration/classification/master/list/classification-list.component.spec.ts +++ b/web/src/app/administration/classification/master/list/classification-list.component.spec.ts @@ -1,25 +1,46 @@ -import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import { Component, Input } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpClient, HttpClientModule } from '@angular/common/http'; +import { Observable } from 'rxjs/Observable'; -import {ClassificationListComponent} from './classification-list.component'; -import {ImportExportComponent} from 'app/shared/import-export/import-export.component'; -import {SpinnerComponent} from 'app/shared/spinner/spinner.component'; -import {WorkbasketService} from 'app/services/workbasket/workbasket.service'; -import {HttpClient, HttpClientModule} from '@angular/common/http'; -import {WorkbasketDefinitionService} from 'app/services/workbasket/workbasketDefinition.service'; -import {AlertService} from 'app/services/alert/alert.service'; -import {ClassificationService} from 'app/services/classification/classification.service'; -import {DomainService} from 'app/services/domains/domain.service'; +import { TreeNode } from 'app/models/tree-node'; + +import { ClassificationListComponent } from './classification-list.component'; +import { ImportExportComponent } from 'app/shared/import-export/import-export.component'; +import { SpinnerComponent } from 'app/shared/spinner/spinner.component'; +import { ClassificationTypesSelectorComponent } from 'app/shared/classification-types-selector/classification-types-selector.component'; +import { MapValuesPipe } from 'app/pipes/mapValues/map-values.pipe'; + +import { WorkbasketService } from 'app/services/workbasket/workbasket.service'; +import { WorkbasketDefinitionService } from 'app/services/workbasket-definition/workbasket-definition.service'; +import { AlertService } from 'app/services/alert/alert.service'; +import { ClassificationsService } from 'app/services/classifications/classifications.service'; +import { ClassificationDefinitionService } from 'app/services/classification-definition/classification-definition.service'; +import { DomainService } from 'app/services/domains/domain.service'; + +@Component({ + selector: 'taskana-tree', + template: '' +}) +class TreeComponent { + @Input() treeNodes; +} describe('ClassificationListComponent', () => { let component: ClassificationListComponent; let fixture: ComponentFixture; + const treeNodes: Array = new Array(new TreeNode()); + const classificationTypes: Map = new Map([['type1', 'type1'], ['type2', 'type2']]) + let classificationsSpy, classificationsTypesSpy; + let classificationsService; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ClassificationListComponent, ImportExportComponent, SpinnerComponent], + declarations: [ClassificationListComponent, ImportExportComponent, SpinnerComponent, ClassificationTypesSelectorComponent, + TreeComponent, MapValuesPipe], imports: [HttpClientModule], providers: [ - WorkbasketService, HttpClient, WorkbasketDefinitionService, AlertService, ClassificationService, DomainService + HttpClient, WorkbasketDefinitionService, AlertService, ClassificationsService, DomainService, ClassificationDefinitionService ] }) .compileComponents(); @@ -28,6 +49,9 @@ describe('ClassificationListComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ClassificationListComponent); component = fixture.componentInstance; + classificationsService = TestBed.get(ClassificationsService); + classificationsSpy = spyOn(classificationsService, 'getClassifications').and.returnValue(Observable.of(treeNodes)); + classificationsTypesSpy = spyOn(classificationsService, 'getClassificationTypes').and.returnValue(Observable.of(classificationTypes)); fixture.detectChanges(); }); diff --git a/web/src/app/administration/classification/master/list/classification-list.component.ts b/web/src/app/administration/classification/master/list/classification-list.component.ts index 56df57762..c765f40d3 100644 --- a/web/src/app/administration/classification/master/list/classification-list.component.ts +++ b/web/src/app/administration/classification/master/list/classification-list.component.ts @@ -1,19 +1,55 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs/Subscription'; + import { ImportType } from 'app/models/import-type'; +import { Classification } from 'app/models/classification'; +import { TreeNode } from 'app/models/tree-node'; + +import { ClassificationsService } from 'app/services/classifications/classifications.service'; @Component({ - selector: 'taskana-classification-list', - templateUrl: './classification-list.component.html', - styleUrls: ['./classification-list.component.scss'] + selector: 'taskana-classification-list', + templateUrl: './classification-list.component.html', + styleUrls: ['./classification-list.component.scss'] }) -export class ClassificationListComponent implements OnInit { +export class ClassificationListComponent implements OnInit, OnDestroy { - selectionToImport = ImportType.CLASSIFICATIONS; - requestInProgress = false; - constructor() { - } + selectionToImport = ImportType.CLASSIFICATIONS; + requestInProgress = false; - ngOnInit() { - } + classifications: Array = []; + classificationsTypes: Map = new Map(); + classificationTypeSelected: string; + classificationServiceSubscription: Subscription; + classificationTypeServiceSubscription: Subscription; + constructor(private classificationService: ClassificationsService) { + } + + ngOnInit() { + this.classificationServiceSubscription = this.classificationService.getClassifications() + .subscribe((classifications: Array) => { + this.classifications = classifications; + this.classificationTypeServiceSubscription = this.classificationService.getClassificationTypes() + .subscribe((classificationsTypes: Map) => { + this.classificationsTypes = classificationsTypes; + this.classificationTypeSelected = this.classifications[0].type; + }); + }); + } + + selectClassificationType(classificationTypeSelected: string) { + this.classificationService.getClassifications(true, classificationTypeSelected) + .subscribe((classifications: Array) => { + this.classifications = classifications; + }); + } + + addClassification() { } + removeClassification() { } + + ngOnDestroy(): void { + if (this.classificationServiceSubscription) { this.classificationServiceSubscription.unsubscribe(); } + if (this.classificationTypeServiceSubscription) { this.classificationTypeServiceSubscription.unsubscribe(); } + } } diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.html b/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.html index 60426ac54..d43dd5bf6 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.html +++ b/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.html @@ -7,9 +7,6 @@ - diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.scss b/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.scss index 96c05108f..4e1feae22 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.scss +++ b/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.scss @@ -1,11 +1,11 @@ .list-group-item { - padding: 0px 15px; + padding: 5px 0px; border: none; } .tab-align{ margin-bottom: 0px; - padding: 0px; + &>div{ margin: 6px 0px; } diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.spec.ts b/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.spec.ts index df5df8523..662001f6d 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.spec.ts +++ b/web/src/app/administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component.spec.ts @@ -26,8 +26,8 @@ import { ErrorModalService } from 'app/services/errorModal/error-modal.service'; import { WorkbasketService } from 'app/services/workbasket/workbasket.service'; import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service'; import { AlertService } from 'app/services/alert/alert.service'; -import { ClassificationService } from 'app/services/classification/classification.service'; -import { WorkbasketDefinitionService } from 'app/services/workbasket/workbasketDefinition.service'; +import { ClassificationDefinitionService } from 'app/services/classification-definition/classification-definition.service'; +import { WorkbasketDefinitionService } from 'app/services/workbasket-definition/workbasket-definition.service'; import { DomainService } from 'app/services/domains/domain.service'; @Component({ @@ -54,7 +54,7 @@ describe('WorkbasketListToolbarComponent', () => { declarations: [WorkbasketListToolbarComponent, SortComponent, FilterComponent, IconTypeComponent, DummyDetailComponent, MapValuesPipe, ImportExportComponent], providers: [ErrorModalService, WorkbasketService, RequestInProgressService, AlertService, - ClassificationService, WorkbasketDefinitionService, DomainService] + ClassificationDefinitionService, WorkbasketDefinitionService, DomainService] }) .compileComponents(); })); diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list.component.html b/web/src/app/administration/workbasket/master/list/workbasket-list.component.html index 6508c5748..07fdce0e1 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list.component.html +++ b/web/src/app/administration/workbasket/master/list/workbasket-list.component.html @@ -7,7 +7,7 @@
    -
  • +
  • div.row > dl:first-child { margin-left: 10px; } -.no-border { +.no-space { border-top: none; + padding: 0px } diff --git a/web/src/app/administration/workbasket/master/list/workbasket-list.component.spec.ts b/web/src/app/administration/workbasket/master/list/workbasket-list.component.spec.ts index ae610a551..7afa13772 100644 --- a/web/src/app/administration/workbasket/master/list/workbasket-list.component.spec.ts +++ b/web/src/app/administration/workbasket/master/list/workbasket-list.component.spec.ts @@ -28,8 +28,8 @@ import { ImportExportComponent } from 'app/shared/import-export/import-export.co import { RemoveNoneTypePipe } from 'app/pipes/removeNoneType/remove-none-type.pipe'; import { MapValuesPipe } from 'app/pipes/mapValues/map-values.pipe'; -import { WorkbasketDefinitionService } from 'app/services/workbasket/workbasketDefinition.service'; -import { ClassificationService } from 'app/services/classification/classification.service'; +import { WorkbasketDefinitionService } from 'app/services/workbasket-definition/workbasket-definition.service'; +import { ClassificationDefinitionService } from 'app/services/classification-definition/classification-definition.service'; import { DomainService } from 'app/services/domains/domain.service'; @Component({ @@ -91,7 +91,7 @@ describe('WorkbasketListComponent', () => { RouterTestingModule.withRoutes(routes) ], providers: [WorkbasketService, ErrorModalService, RequestInProgressService, AlertService, - WorkbasketDefinitionService, OrientationService, DomainService, ClassificationService] + WorkbasketDefinitionService, OrientationService, DomainService, ClassificationDefinitionService] }) .compileComponents(); diff --git a/web/src/app/app-routing.module.ts b/web/src/app/app-routing.module.ts index 28540b78c..c3145a76a 100644 --- a/web/src/app/app-routing.module.ts +++ b/web/src/app/app-routing.module.ts @@ -3,13 +3,13 @@ import { RouterModule, Routes } from '@angular/router'; import { AppComponent } from './app.component'; import { WorkbasketListComponent } from './administration/workbasket/master/list/workbasket-list.component'; import { WorkbasketDetailsComponent } from './administration/workbasket/details/workbasket-details.component'; -import { MasterAndDetailComponent } from './shared/masterAndDetail/master-and-detail.component'; +import { MasterAndDetailComponent } from './shared/master-and-detail/master-and-detail.component'; import { NoAccessComponent } from './administration/workbasket/details/noAccess/no-access.component'; import {ClassificationListComponent} from './administration/classification/master/list/classification-list.component'; const appRoutes: Routes = [ { - path: 'workbaskets', + path: 'administration/workbaskets', component: MasterAndDetailComponent, children: [ { @@ -30,7 +30,7 @@ const appRoutes: Routes = [ ] }, { - path: 'classifications', + path: 'administration/classifications', component: MasterAndDetailComponent, children: [ { @@ -42,7 +42,7 @@ const appRoutes: Routes = [ }, { path: '', - redirectTo: 'workbaskets', + redirectTo: 'administration/workbaskets', pathMatch: 'full' } ]; diff --git a/web/src/app/app.component.html b/web/src/app/app.component.html index bfdaf9b59..f43517e59 100644 --- a/web/src/app/app.component.html +++ b/web/src/app/app.component.html @@ -12,10 +12,10 @@ diff --git a/web/src/app/app.component.spec.ts b/web/src/app/app.component.spec.ts index 5bf59243f..52532b714 100644 --- a/web/src/app/app.component.spec.ts +++ b/web/src/app/app.component.spec.ts @@ -9,6 +9,7 @@ import { ErrorModalService } from './services/errorModal/error-modal.service'; import { RequestInProgressService } from './services/requestInProgress/request-in-progress.service'; import { AlertService } from './services/alert/alert.service'; import { OrientationService } from './services/orientation/orientation.service'; +import { SelectedRouteService } from './services/selected-route/selected-route'; import { GeneralMessageModalComponent } from './shared/general-message-modal/general-message-modal.component' import { SpinnerComponent } from './shared/spinner/spinner.component' @@ -20,7 +21,7 @@ describe('AppComponent', () => { let app, fixture, debugElement; const routes: Routes = [ - { path: 'categories', component: AppComponent } + { path: 'classifications', component: AppComponent } ]; beforeEach(async(() => { @@ -33,7 +34,7 @@ describe('AppComponent', () => { RouterTestingModule.withRoutes(routes), HttpClientModule ], - providers: [ErrorModalService, RequestInProgressService, AlertService, OrientationService] + providers: [ErrorModalService, RequestInProgressService, AlertService, OrientationService, SelectedRouteService] }).compileComponents(); fixture = TestBed.createComponent(AppComponent); @@ -59,11 +60,11 @@ describe('AppComponent', () => { expect(debugElement.querySelector('ul p a').textContent).toContain('Taskana administration'); })); - it('should call Router.navigateByUrl("categories") and workbasketRoute should be false', (inject([Router], (router: Router) => { + it('should call Router.navigateByUrl("classifications") and workbasketRoute should be false', (inject([Router], (router: Router) => { expect(app.workbasketsRoute).toBe(true); fixture.detectChanges(); - router.navigateByUrl(`/categories`); + router.navigateByUrl(`/classifications`); expect(app.workbasketsRoute).toBe(false); }))); diff --git a/web/src/app/app.component.ts b/web/src/app/app.component.ts index 548371184..5b76d8866 100644 --- a/web/src/app/app.component.ts +++ b/web/src/app/app.component.ts @@ -1,19 +1,21 @@ -import { Component, OnInit, HostListener } from '@angular/core'; +import { Component, OnInit, HostListener, OnDestroy } from '@angular/core'; import { environment } from '../environments/environment'; import { Router, NavigationStart } from '@angular/router'; +import { Subscription } from 'rxjs/Subscription'; import { ErrorModel } from './models/modal-error'; import { ErrorModalService } from './services/errorModal/error-modal.service'; import { RequestInProgressService } from './services/requestInProgress/request-in-progress.service'; import { OrientationService } from './services/orientation/orientation.service'; +import { SelectedRouteService } from './services/selected-route/selected-route'; @Component({ selector: 'taskana-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent implements OnInit { +export class AppComponent implements OnInit, OnDestroy { title = 'Taskana administration'; adminUrl: string = environment.taskanaAdminUrl; @@ -23,9 +25,15 @@ export class AppComponent implements OnInit { modalErrorMessage = ''; modalTitle = ''; + selectedRoute = ''; requestInProgress = false; + errorModalSubscription: Subscription; + requestInProgressSubscription: Subscription; + selectedRouteSubscription: Subscription; + routerSubscription: Subscription; + @HostListener('window:resize', ['$event']) onResize(event) { this.orientationService.onResize(); @@ -35,25 +43,38 @@ export class AppComponent implements OnInit { private router: Router, private errorModalService: ErrorModalService, private requestInProgressService: RequestInProgressService, - private orientationService: OrientationService) { + private orientationService: OrientationService, + private selectedRouteService: SelectedRouteService) { } ngOnInit() { - this.router.events.subscribe(event => { + + this.routerSubscription = this.router.events.subscribe(event => { if (event instanceof NavigationStart) { - if (event.url.indexOf('categories') !== -1) { - this.workbasketsRoute = false; - } + this.selectedRouteService.selectRoute(event); } }); - - this.errorModalService.getError().subscribe((error: ErrorModel) => { + this.errorModalSubscription = this.errorModalService.getError().subscribe((error: ErrorModel) => { this.modalErrorMessage = error.message; this.modalTitle = error.title; }) - this.requestInProgressService.getRequestInProgress().subscribe((value: boolean) => { + this.requestInProgressSubscription = this.requestInProgressService.getRequestInProgress().subscribe((value: boolean) => { this.requestInProgress = value; }) + + this.selectedRouteSubscription = this.selectedRouteService.getSelectedRoute().subscribe((value: string) => { + if (value.indexOf('classifications') !== -1) { + this.workbasketsRoute = false; + } + this.selectedRoute = value; + }) + } + + ngOnDestroy() { + if (this.routerSubscription) { this.routerSubscription.unsubscribe(); } + if (this.errorModalSubscription) { this.errorModalSubscription.unsubscribe(); } + if (this.requestInProgressSubscription) { this.requestInProgressSubscription.unsubscribe(); } + if (this.selectedRouteSubscription) { this.selectedRouteSubscription.unsubscribe(); } } } diff --git a/web/src/app/app.module.ts b/web/src/app/app.module.ts index f27254ef6..f813ca0fb 100644 --- a/web/src/app/app.module.ts +++ b/web/src/app/app.module.ts @@ -2,61 +2,66 @@ /** * Modules */ -import {BrowserModule} from '@angular/platform-browser'; -import {NgModule} from '@angular/core'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; -import {AppRoutingModule} from './app-routing.module'; -import {AlertModule} from 'ngx-bootstrap'; -import {AngularSvgIconModule} from 'angular-svg-icon'; -import {TabsModule} from 'ngx-bootstrap/tabs'; -import {TreeModule} from 'angular-tree-component'; -import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; +import { AppRoutingModule } from './app-routing.module'; +import { AlertModule } from 'ngx-bootstrap'; +import { AngularSvgIconModule } from 'angular-svg-icon'; +import { TabsModule } from 'ngx-bootstrap/tabs'; +import { TreeModule } from 'angular-tree-component'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; /** * Components */ -import {AppComponent} from './app.component'; -import {WorkbasketListComponent} from './administration/workbasket/master/list/workbasket-list.component'; -import {WorkbasketListToolbarComponent} from './administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component' -import {WorkbasketDetailsComponent} from './administration/workbasket/details/workbasket-details.component'; -import {WorkbasketInformationComponent} from './administration/workbasket/details/information/workbasket-information.component'; -import {DistributionTargetsComponent} from './administration/workbasket/details/distribution-targets/distribution-targets.component'; -import {DualListComponent} from './administration/workbasket/details/distribution-targets/dual-list/dual-list.component'; -import {AccessItemsComponent} from './administration/workbasket/details/access-items/access-items.component'; -import {NoAccessComponent} from './administration/workbasket/details/noAccess/no-access.component'; -import {SpinnerComponent} from './shared/spinner/spinner.component'; -import {FilterComponent} from './shared/filter/filter.component'; -import {IconTypeComponent} from './shared/type-icon/icon-type.component'; -import {AlertComponent} from './shared/alert/alert.component'; -import {SortComponent} from './shared/sort/sort.component'; -import {GeneralMessageModalComponent} from './shared/general-message-modal/general-message-modal.component'; -import {PaginationComponent} from './administration/workbasket/master/list/pagination/pagination.component'; -import {ClassificationListComponent} from './administration/classification/master/list/classification-list.component'; -import {ImportExportComponent} from './shared/import-export/import-export.component'; -// Shared -import {MasterAndDetailComponent} from './shared/masterAndDetail/master-and-detail.component'; +import { AppComponent } from './app.component'; +import { WorkbasketListComponent } from './administration/workbasket/master/list/workbasket-list.component'; +import { WorkbasketListToolbarComponent } from './administration/workbasket/master/list/workbasket-list-toolbar/workbasket-list-toolbar.component' +import { WorkbasketDetailsComponent } from './administration/workbasket/details/workbasket-details.component'; +import { WorkbasketInformationComponent } from './administration/workbasket/details/information/workbasket-information.component'; +import { DistributionTargetsComponent } from './administration/workbasket/details/distribution-targets/distribution-targets.component'; +import { DualListComponent } from './administration/workbasket/details/distribution-targets/dual-list/dual-list.component'; +import { AccessItemsComponent } from './administration/workbasket/details/access-items/access-items.component'; +import { NoAccessComponent } from './administration/workbasket/details/noAccess/no-access.component'; +import { SpinnerComponent } from './shared/spinner/spinner.component'; +import { FilterComponent } from './shared/filter/filter.component'; +import { IconTypeComponent } from './shared/type-icon/icon-type.component'; +import { AlertComponent } from './shared/alert/alert.component'; +import { SortComponent } from './shared/sort/sort.component'; +import { GeneralMessageModalComponent } from './shared/general-message-modal/general-message-modal.component'; +import { PaginationComponent } from './administration/workbasket/master/list/pagination/pagination.component'; +import { ClassificationListComponent } from './administration/classification/master/list/classification-list.component'; +import { ImportExportComponent } from './shared/import-export/import-export.component'; +import { MasterAndDetailComponent } from './shared/master-and-detail/master-and-detail.component'; +import { ClassificationTypesSelectorComponent } from './shared/classification-types-selector/classification-types-selector.component'; +import { TreeComponent } from './shared/tree/tree.component'; + /** * Services */ -import {WorkbasketService} from './services/workbasket/workbasket.service'; -import {MasterAndDetailService} from './services/masterAndDetail/master-and-detail.service'; -import {HttpClientInterceptor} from './services/httpClientInterceptor/http-client-interceptor.service'; -import {PermissionService} from './services/permission/permission.service'; -import {AlertService} from './services/alert/alert.service'; -import {ErrorModalService} from './services/errorModal/error-modal.service'; -import {RequestInProgressService} from './services/requestInProgress/request-in-progress.service'; -import {SavingWorkbasketService} from './services/saving-workbaskets/saving-workbaskets.service'; -import {OrientationService} from './services/orientation/orientation.service'; -import {ClassificationService} from './services/classification/classification.service'; -import {WorkbasketDefinitionService} from './services/workbasket/workbasketDefinition.service'; +import { WorkbasketService } from './services/workbasket/workbasket.service'; +import { MasterAndDetailService } from './services/masterAndDetail/master-and-detail.service'; +import { HttpClientInterceptor } from './services/httpClientInterceptor/http-client-interceptor.service'; +import { PermissionService } from './services/permission/permission.service'; +import { AlertService } from './services/alert/alert.service'; +import { ErrorModalService } from './services/errorModal/error-modal.service'; +import { RequestInProgressService } from './services/requestInProgress/request-in-progress.service'; +import { SavingWorkbasketService } from './services/saving-workbaskets/saving-workbaskets.service'; +import { OrientationService } from './services/orientation/orientation.service'; +import { ClassificationDefinitionService } from './services/classification-definition/classification-definition.service'; +import { WorkbasketDefinitionService } from './services/workbasket-definition/workbasket-definition.service'; +import { SelectedRouteService } from './services/selected-route/selected-route'; +import { ClassificationsService } from './services/classifications/classifications.service'; + /** * Pipes */ -import {MapValuesPipe} from './pipes/mapValues/map-values.pipe'; -import {RemoveNoneTypePipe} from './pipes/removeNoneType/remove-none-type.pipe'; -import {SelectWorkBasketPipe} from './pipes/selectedWorkbasket/seleted-workbasket.pipe'; -import {SpreadNumberPipe} from './pipes/spreadNumber/spread-number'; -import {DomainService} from './services/domains/domain.service'; +import { MapValuesPipe } from './pipes/mapValues/map-values.pipe'; +import { RemoveNoneTypePipe } from './pipes/removeNoneType/remove-none-type.pipe'; +import { SelectWorkBasketPipe } from './pipes/selectedWorkbasket/seleted-workbasket.pipe'; +import { SpreadNumberPipe } from './pipes/spreadNumber/spread-number'; +import { DomainService } from './services/domains/domain.service'; const MODULES = [ BrowserModule, @@ -91,6 +96,8 @@ const DECLARATIONS = [ PaginationComponent, ClassificationListComponent, ImportExportComponent, + TreeComponent, + ClassificationTypesSelectorComponent, MapValuesPipe, RemoveNoneTypePipe, SelectWorkBasketPipe, @@ -104,7 +111,7 @@ const DECLARATIONS = [ WorkbasketService, MasterAndDetailService, PermissionService, - ClassificationService, + ClassificationDefinitionService, WorkbasketDefinitionService, DomainService, { @@ -116,7 +123,9 @@ const DECLARATIONS = [ ErrorModalService, RequestInProgressService, SavingWorkbasketService, - OrientationService + OrientationService, + SelectedRouteService, + ClassificationsService ], bootstrap: [AppComponent] }) diff --git a/web/src/app/models/classification-definition.ts b/web/src/app/models/classification-definition.ts new file mode 100644 index 000000000..6e27518f5 --- /dev/null +++ b/web/src/app/models/classification-definition.ts @@ -0,0 +1,24 @@ +export class ClassificationDefinition { + constructor(public classificationId: string, + public key: string, + public parentId: string, + public category: string, + public domain: string, + public isValidInDomain: boolean, + public created: string, + public modifies: string, + public name: string, + public description: string, + public priority: number, + public serviceLevel: string, + public applicationEntryPoint: string, + public custom1: string, + public custom2: string, + public custom3: string, + public custom4: string, + public custom5: string, + public custom6: string, + public custom7: string, + public custom8: string) { + } +} diff --git a/web/src/app/models/classification.ts b/web/src/app/models/classification.ts index acb43acde..0fd875006 100644 --- a/web/src/app/models/classification.ts +++ b/web/src/app/models/classification.ts @@ -1,24 +1,12 @@ export class Classification { - constructor(public classificationId: string, - public key: string, - public parentId: string, - public category: string, - public domain: string, - public isValidInDomain: boolean, - public created: string, - public modifies: string, - public name: string, - public description: string, - public priority: number, - public serviceLevel: string, - public applicationEntryPoint: string, - public custom1: string, - public custom2: string, - public custom3: string, - public custom4: string, - public custom5: string, - public custom6: string, - public custom7: string, - public custom8: string) { + constructor(public 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) { } } diff --git a/web/src/app/models/tree-node.ts b/web/src/app/models/tree-node.ts new file mode 100644 index 000000000..589c98161 --- /dev/null +++ b/web/src/app/models/tree-node.ts @@ -0,0 +1,16 @@ +import { Classification } from 'app/models/classification'; + +export class TreeNode extends Classification { + constructor(public id: string = '', + public key: string = '', + public category: string = '', + public type: string = '', + public domain: string = '', + public name: string = '', + public parentId: string = '', + public priority: number = 0, + public serviceLevel: string = '', + public children: Array = undefined) { + super(id, key, category, type, domain, name, parentId, priority, serviceLevel); + } +} diff --git a/web/src/app/services/classification/classification.service.ts b/web/src/app/services/classification-definition/classification-definition.service.ts similarity index 87% rename from web/src/app/services/classification/classification.service.ts rename to web/src/app/services/classification-definition/classification-definition.service.ts index 95b65e911..38b458d71 100644 --- a/web/src/app/services/classification/classification.service.ts +++ b/web/src/app/services/classification-definition/classification-definition.service.ts @@ -2,13 +2,13 @@ import {Injectable} from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; import {environment} from '../../../environments/environment'; import {AlertService} from '../alert/alert.service'; -import {Classification} from '../../models/classification'; +import {ClassificationDefinition} from '../../models/classification-definition'; import {AlertModel, AlertType} from '../../models/alert'; import {saveAs} from 'file-saver/FileSaver'; import {TaskanaDate} from '../../shared/util/taskana.date'; @Injectable() -export class ClassificationService { +export class ClassificationDefinitionService { url = environment.taskanaRestUrl + '/v1/classificationdefinitions'; @@ -25,7 +25,7 @@ export class ClassificationService { // GET exportClassifications(domain: string) { domain = (domain === '' ? '' : '?domain=' + domain); - this.httpClient.get(this.url + domain, this.httpOptions) + this.httpClient.get(this.url + domain, this.httpOptions) .subscribe( response => saveAs(new Blob([JSON.stringify(response)], {type: 'text/plain;charset=utf-8'}), 'Classifications_' + TaskanaDate.getDate() + '.json') diff --git a/web/src/app/services/classifications/classifications.service.ts b/web/src/app/services/classifications/classifications.service.ts new file mode 100644 index 000000000..f187972fd --- /dev/null +++ b/web/src/app/services/classifications/classifications.service.ts @@ -0,0 +1,84 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { environment } from '../../../environments/environment'; + +import { Classification } from 'app/models/classification'; +import { TreeNode } from 'app/models/tree-node'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; + +@Injectable() +export class ClassificationsService { + + url = environment.taskanaRestUrl + '/v1/classifications'; + + httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + 'Authorization': 'Basic VEVBTUxFQURfMTpURUFNTEVBRF8x' + }) + }; + + private classificationRef: Observable>; + private classificationTypes: Array; + + constructor(private httpClient: HttpClient) { + } + + // GET + getClassifications(forceRequest = false, type = 'TASK', domain = ''): Observable> { + if (!forceRequest && this.classificationRef) { + return this.classificationRef.map((response: Array) => { + return this.buildHierarchy(response, type, domain); + }); + } + this.classificationRef = this.httpClient.get>(`${environment.taskanaRestUrl}/v1/classifications`, + this.httpOptions); + + return this.classificationRef.map((response: Array) => { + return this.buildHierarchy(response, type, domain); + }); + } + + getClassificationTypes(): Observable> { + const typesSubject = new Subject>(); + this.classificationRef.subscribe((classifications: Array) => { + const types = new Map(); + classifications.forEach(element => { + types.set(element.type, element.type); + }); + typesSubject.next(types); + }); + return typesSubject.asObservable(); + } + + private buildHierarchy(classifications: Array, type: string, domain: string) { + const roots = [] + const children = new Array(); + + for (let index = 0, len = classifications.length; index < len; ++index) { + const item = classifications[index]; + if (item.type === type) { + const parent = item.parentId, + target = !parent ? roots : (children[parent] || (children[parent] = [])); + + target.push(item); + } + } + for (let index = 0, len = roots.length; index < len; ++index) { + this.findChildren(roots[index], children); + } + return roots; + } + + + private findChildren(parent: any, children: Array) { + if (children[parent.id]) { + parent.children = children[parent.id]; + for (let index = 0, len = parent.children.length; index < len; ++index) { + this.findChildren(parent.children[index], children); + } + } + } +} + diff --git a/web/src/app/services/selected-route/selected-route.ts b/web/src/app/services/selected-route/selected-route.ts new file mode 100644 index 000000000..35cd0da49 --- /dev/null +++ b/web/src/app/services/selected-route/selected-route.ts @@ -0,0 +1,39 @@ +import { Injectable, OnInit } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import { Observable } from 'rxjs/Observable'; +import { Router, ActivatedRoute, NavigationStart } from '@angular/router'; + +@Injectable() +export class SelectedRouteService { + + public selectedRouteTriggered = new Subject(); + + private detailRoutes: Array = ['workbaskets', 'classifications']; + + constructor(private route: ActivatedRoute, private router: Router) { } + + + selectRoute(value) { + this.selectedRouteTriggered.next(this.getRoute(value)); + } + + getSelectedRoute(): Observable { + return this.selectedRouteTriggered.asObservable(); + } + + private getRoute(event): string { + if (event === undefined) { + return this.checkUrl(this.router.url); + } + return this.checkUrl(event.url) + } + + private checkUrl(url: string): string { + for (const routeDetail of this.detailRoutes) { + if (url.indexOf(routeDetail) !== -1) { + return routeDetail; + } + } + return ''; + } +} diff --git a/web/src/app/services/workbasket/workbasketDefinition.service.ts b/web/src/app/services/workbasket-definition/workbasket-definition.service.ts similarity index 100% rename from web/src/app/services/workbasket/workbasketDefinition.service.ts rename to web/src/app/services/workbasket-definition/workbasket-definition.service.ts diff --git a/web/src/app/shared/classification-types-selector/classification-types-selector.component.html b/web/src/app/shared/classification-types-selector/classification-types-selector.component.html new file mode 100644 index 000000000..a3b5fcc0f --- /dev/null +++ b/web/src/app/shared/classification-types-selector/classification-types-selector.component.html @@ -0,0 +1,19 @@ +
  • + + + +
  • +
+ \ No newline at end of file diff --git a/web/src/app/shared/classification-types-selector/classification-types-selector.component.scss b/web/src/app/shared/classification-types-selector/classification-types-selector.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/web/src/app/shared/classification-types-selector/classification-types-selector.component.spec.ts b/web/src/app/shared/classification-types-selector/classification-types-selector.component.spec.ts new file mode 100644 index 000000000..bbf4df7a3 --- /dev/null +++ b/web/src/app/shared/classification-types-selector/classification-types-selector.component.spec.ts @@ -0,0 +1,26 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClassificationTypesSelectorComponent } from './classification-types-selector.component'; +import { MapValuesPipe } from 'app/pipes/mapValues/map-values.pipe'; + +describe('ClassificationTypesSelectorComponent', () => { + let component: ClassificationTypesSelectorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ClassificationTypesSelectorComponent, MapValuesPipe ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClassificationTypesSelectorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/shared/classification-types-selector/classification-types-selector.component.ts b/web/src/app/shared/classification-types-selector/classification-types-selector.component.ts new file mode 100644 index 000000000..a7cdb1d57 --- /dev/null +++ b/web/src/app/shared/classification-types-selector/classification-types-selector.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; + +@Component({ + selector: 'taskana-classification-types-selector', + templateUrl: './classification-types-selector.component.html', + styleUrls: ['./classification-types-selector.component.scss'] +}) +export class ClassificationTypesSelectorComponent implements OnInit { + + @Input() classificationTypes: Map = new Map(); + @Input() + classificationTypeSelected: string = undefined; + @Output() + classificationTypeSelectedChange = new EventEmitter(); + @Output() + classificationTypeChanged = new EventEmitter(); + + constructor() { } + + ngOnInit() { + } + + select(value: string) { + this.classificationTypeSelected = value; + this.classificationTypeChanged.emit(value); + } +} diff --git a/web/src/app/shared/import-export/import-export.component.spec.ts b/web/src/app/shared/import-export/import-export.component.spec.ts index 16f020ee5..9e1c1ec9d 100644 --- a/web/src/app/shared/import-export/import-export.component.spec.ts +++ b/web/src/app/shared/import-export/import-export.component.spec.ts @@ -2,8 +2,8 @@ import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {ImportExportComponent} from './import-export.component'; import {WorkbasketService} from '../../services/workbasket/workbasket.service'; -import {ClassificationService} from '../../services/classification/classification.service'; -import {WorkbasketDefinitionService} from '../../services/workbasket/workbasketDefinition.service'; +import {ClassificationDefinitionService} from '../../services/classification-definition/classification-definition.service'; +import {WorkbasketDefinitionService} from '../../services/workbasket-definition/workbasket-definition.service'; import {AlertService} from '../../services/alert/alert.service'; import {HttpClientModule} from '@angular/common/http'; import {DomainService} from '../../services/domains/domain.service'; @@ -17,7 +17,7 @@ describe('ImportExportComponent', () => { TestBed.configureTestingModule({ declarations: [ImportExportComponent], imports: [HttpClientModule], - providers: [WorkbasketService, ClassificationService, WorkbasketDefinitionService, AlertService, DomainService] + providers: [WorkbasketService, ClassificationDefinitionService, WorkbasketDefinitionService, AlertService, DomainService] }) .compileComponents(); })); diff --git a/web/src/app/shared/import-export/import-export.component.ts b/web/src/app/shared/import-export/import-export.component.ts index 71b8c5a0f..033e5514e 100644 --- a/web/src/app/shared/import-export/import-export.component.ts +++ b/web/src/app/shared/import-export/import-export.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; -import { ClassificationService } from 'app/services/classification/classification.service'; -import { WorkbasketDefinitionService } from 'app/services/workbasket/workbasketDefinition.service'; +import { ClassificationDefinitionService } from 'app/services/classification-definition/classification-definition.service'; +import { WorkbasketDefinitionService } from 'app/services/workbasket-definition/workbasket-definition.service'; import { DomainService } from 'app/services/domains/domain.service'; import { ImportType } from 'app/models/import-type'; @@ -15,7 +15,7 @@ export class ImportExportComponent implements OnInit { domains: string[] = []; constructor(private domainService: DomainService, private workbasketDefinitionService: WorkbasketDefinitionService, - private classificationService: ClassificationService) { + private classificationDefinitionService: ClassificationDefinitionService) { } ngOnInit() { @@ -33,7 +33,7 @@ export class ImportExportComponent implements OnInit { if (this.currentSelection === ImportType.WORKBASKETS) { reader.onload = (e) => this.workbasketDefinitionService.importWorkbasketDefinitions(e.target.result); } else { - reader.onload = (e) => this.classificationService.importClassifications(e.target.result); + reader.onload = (e) => this.classificationDefinitionService.importClassifications(e.target.result); } reader.readAsText(file); } @@ -42,7 +42,7 @@ export class ImportExportComponent implements OnInit { if (this.currentSelection === ImportType.WORKBASKETS) { this.workbasketDefinitionService.exportWorkbaskets(domain); } else { - this.classificationService.exportClassifications(domain); + this.classificationDefinitionService.exportClassifications(domain); } } } diff --git a/web/src/app/shared/masterAndDetail/master-and-detail.component.html b/web/src/app/shared/master-and-detail/master-and-detail.component.html similarity index 100% rename from web/src/app/shared/masterAndDetail/master-and-detail.component.html rename to web/src/app/shared/master-and-detail/master-and-detail.component.html diff --git a/web/src/app/shared/masterAndDetail/master-and-detail.component.scss b/web/src/app/shared/master-and-detail/master-and-detail.component.scss similarity index 100% rename from web/src/app/shared/masterAndDetail/master-and-detail.component.scss rename to web/src/app/shared/master-and-detail/master-and-detail.component.scss diff --git a/web/src/app/shared/masterAndDetail/master-and-detail.component.spec.ts b/web/src/app/shared/master-and-detail/master-and-detail.component.spec.ts similarity index 100% rename from web/src/app/shared/masterAndDetail/master-and-detail.component.spec.ts rename to web/src/app/shared/master-and-detail/master-and-detail.component.spec.ts diff --git a/web/src/app/shared/masterAndDetail/master-and-detail.component.ts b/web/src/app/shared/master-and-detail/master-and-detail.component.ts similarity index 98% rename from web/src/app/shared/masterAndDetail/master-and-detail.component.ts rename to web/src/app/shared/master-and-detail/master-and-detail.component.ts index 53df25b91..943dcc643 100644 --- a/web/src/app/shared/masterAndDetail/master-and-detail.component.ts +++ b/web/src/app/shared/master-and-detail/master-and-detail.component.ts @@ -9,7 +9,7 @@ import { MasterAndDetailService } from 'app/services/masterAndDetail/master-and- }) export class MasterAndDetailComponent implements OnInit { - private detailRoutes: Array = ['/workbaskets/(detail', 'classifications']; + private detailRoutes: Array = ['/workbaskets/(detail', 'classifications/(detail']; private sub: any; showDetail: Boolean = false; diff --git a/web/src/app/shared/tree/tree.component.html b/web/src/app/shared/tree/tree.component.html new file mode 100644 index 000000000..3f23576cb --- /dev/null +++ b/web/src/app/shared/tree/tree.component.html @@ -0,0 +1,14 @@ + + + + + + + {{ node.data.key }} + + - {{ node.data.name }} + + \ No newline at end of file diff --git a/web/src/app/shared/tree/tree.component.scss b/web/src/app/shared/tree/tree.component.scss new file mode 100644 index 000000000..313401d26 --- /dev/null +++ b/web/src/app/shared/tree/tree.component.scss @@ -0,0 +1,3 @@ +.text-top{ + vertical-align: text-top; +} diff --git a/web/src/app/shared/tree/tree.component.spec.ts b/web/src/app/shared/tree/tree.component.spec.ts new file mode 100644 index 000000000..bc813e8a4 --- /dev/null +++ b/web/src/app/shared/tree/tree.component.spec.ts @@ -0,0 +1,43 @@ +import { Input, Component } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TreeComponent } from './tree.component'; + +import { AngularSvgIconModule } from 'angular-svg-icon'; +import { HttpClientModule } from '@angular/common/http'; +import { HttpModule } from '@angular/http'; + +// tslint:disable:component-selector +@Component({ + selector: 'tree-root', + template: '' +}) +class TreeVendorComponent { + @Input() options; + @Input() state; + @Input() nodes; +} + +// tslint:enable:component-selector +fdescribe('TreeComponent', () => { + let component: TreeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [AngularSvgIconModule, HttpClientModule, HttpModule], + declarations: [TreeComponent, TreeVendorComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TreeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/shared/tree/tree.component.ts b/web/src/app/shared/tree/tree.component.ts new file mode 100644 index 000000000..a104f7500 --- /dev/null +++ b/web/src/app/shared/tree/tree.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { TreeNode } from 'app/models/tree-node'; +import { TREE_ACTIONS, KEYS, IActionMapping, ITreeOptions, ITreeState } from 'angular-tree-component'; + +@Component({ + selector: 'taskana-tree', + templateUrl: './tree.component.html', + styleUrls: ['./tree.component.scss'] +}) +export class TreeComponent implements OnInit { + + @Input() treeNodes: TreeNode; + @Output() treeNodesChange = new EventEmitter>(); + + options: ITreeOptions = { + displayField: 'name', + idField: 'id', + actionMapping: { + keys: { + [KEYS.ENTER]: (tree, node, $event) => { + node.toggleExpanded(); + } + } + }, + animateExpand: true, + animateSpeed: 20, + levelPadding: 20 + } + + state: ITreeState = { + activeNodeIds: { ['']: true }, + } + + constructor() { } + + ngOnInit() { + + } + +} + + diff --git a/web/src/assets/_colors.scss b/web/src/assets/_colors.scss new file mode 100644 index 000000000..0a5666f87 --- /dev/null +++ b/web/src/assets/_colors.scss @@ -0,0 +1,6 @@ +$blue-green: #479ea9; +$blue: #337ab7; +$green: green; +$grey: grey; +$brown: #f0ad4e; +$invalid: #a94442; \ No newline at end of file diff --git a/web/src/assets/_forms.scss b/web/src/assets/_forms.scss index 856ce8a0f..c42dd0edf 100644 --- a/web/src/assets/_forms.scss +++ b/web/src/assets/_forms.scss @@ -1,11 +1,11 @@ .ng-invalid:not(form) { - border-color: #a94442; + border-color: $invalid; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .required-text { padding-left: 15px; - color: #a94442; + color: $invalid; } \ No newline at end of file diff --git a/web/src/assets/_site.scss b/web/src/assets/_site.scss index 7ba283931..555cf8240 100644 --- a/web/src/assets/_site.scss +++ b/web/src/assets/_site.scss @@ -1,4 +1,3 @@ - .placeholder { margin-bottom: 20px; } @@ -70,38 +69,58 @@ } .blue{ - color: #337ab7; + color: $blue; & svg { - fill: #337ab7; + fill: $blue; } } .green { - color: green; + color: $green; & svg { - fill: green; + fill: $green; } } .grey { - color:grey; + color:$grey; & svg { - fill: grey; + fill: $grey; } } .brown { - color: #f0ad4e; + color: $brown; & svg { - fill: #f0ad4e; + fill: $brown; } } - -.panel-default > .panel-heading .badge.warning { - background-color: #f0ad4e; +.red { + color: crimson; + & svg { + fill: crimson; + } } +.green-blue { + color: $blue-green; + & svg { + fill: $blue-green; + } +} + +svg-icon.fa-fw > svg { + text-align: center; + width: 1.25em; +} + +.panel-default > .panel-heading .badge.warning { + background-color: $brown; +} +.badge.priority { + background-color: #e1e1e1; +} /* @@ -247,3 +266,8 @@ taskana-workbasket-information, taskana-workbasket-access-items, taskana-workbas } } } + +tree-viewport { + border-top: 1px solid #ddd; + height: calc(100vh - 110px); +} diff --git a/web/src/assets/_styles.scss b/web/src/assets/_styles.scss new file mode 100644 index 000000000..f5675116f --- /dev/null +++ b/web/src/assets/_styles.scss @@ -0,0 +1,6 @@ +@import 'variables'; +@import '../../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap'; +@import '../../node_modules/angular-tree-component/dist/angular-tree-component.css'; +@import 'site'; +@import 'forms'; +@import 'tree'; \ No newline at end of file diff --git a/web/src/assets/_tree.scss b/web/src/assets/_tree.scss new file mode 100644 index 000000000..bb7aa0ece --- /dev/null +++ b/web/src/assets/_tree.scss @@ -0,0 +1,97 @@ + +tree-node-expander { + + & .toggle-children { + top: 2px; + @extend .glyphicon; + @extend .glyphicon-plus; + @extend .blue; + background: white; + background-image: none; + color: $blue-green; + padding-left:3px; + } +} +.toggle-children-wrapper { + padding: 0px; + font-size: 16px; +} + +tree-node-expander .toggle-children-wrapper-expanded { + & .toggle-children { + @extend .glyphicon-minus ; + transform: none; + } +} +tree-node-collection > div > tree-node > .tree-node { + padding-left: 10px; +} + +.node-content-wrapper { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 16px; + margin: 0px 10px; +} + +.node-wrapper { + padding: 4px 0px; +} + +.node-drop-slot { + height: 0px; +} + +.node-content-wrapper, .tree-children { + position: relative; + } + +.node-content-wrapper::before, .tree-children::after { + content: ""; + position: absolute; +} + +.node-content-wrapper-active, .node-content-wrapper.node-content-wrapper-active:hover, .node-content-wrapper-active.node-content-wrapper-focused { + background-color: $blue; + & >tree-node-content{ + color:white; + & >span >svg-icon{ + @extend .white; + } + } +} + + +/* START Children branch lines*/ + .node-content-wrapper::before { + border-bottom: 1px dotted $blue-green; + border-left: 1px dotted $blue-green; + height: 28px; + top: -17px; + width: 20px; + left: -28px; + } + + .tree-node-level-1 > tree-node-wrapper > .node-wrapper > .node-content-wrapper::before { + display: none; + } + + .tree-node-leaf > .node-wrapper > .node-content-wrapper::before { + width: 25px; + } + + .tree-children::after { + border-left: 1px dotted $blue-green; + + height: 100%; + top: -15px; + left: -15px; + } + + tree-node:last-child > .tree-node > .tree-children::after { + border-left: none; + } + + .toggle-children { + z-index: 1; + } +/* END children branch lines */ diff --git a/web/src/assets/_variables.scss b/web/src/assets/_variables.scss new file mode 100644 index 000000000..b9d79002c --- /dev/null +++ b/web/src/assets/_variables.scss @@ -0,0 +1,2 @@ +@import '_colors'; +$icon-font-path: '../../node_modules/bootstrap-sass/assets/fonts/bootstrap/'; diff --git a/web/src/assets/icons/automatic.svg b/web/src/assets/icons/automatic.svg new file mode 100644 index 000000000..8aa998082 --- /dev/null +++ b/web/src/assets/icons/automatic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/icons/closed.svg b/web/src/assets/icons/closed.svg new file mode 100644 index 000000000..e12791604 --- /dev/null +++ b/web/src/assets/icons/closed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/icons/external.svg b/web/src/assets/icons/external.svg new file mode 100644 index 000000000..50924c96f --- /dev/null +++ b/web/src/assets/icons/external.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/icons/manual.svg b/web/src/assets/icons/manual.svg new file mode 100644 index 000000000..b1312d608 --- /dev/null +++ b/web/src/assets/icons/manual.svg @@ -0,0 +1 @@ + \ No newline at end of file