TSK-1395: Update Navbar to work without bootstrap 3 (#1288)
* TSK-1395: update navbar * TSK-1395: fixed CI error * TSK-1395: arrange components correctly * TSK-1395: fit navbar to the other components * TSK-1395: optimize code and add tests * TSK-1395: delete comment * TSK-1395: add missing logout function * TSK-1395: modify components
This commit is contained in:
parent
6a8311a32b
commit
7c83c87f32
|
@ -11,5 +11,6 @@
|
||||||
"[html]": {
|
"[html]": {
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.defaultFormatter": "vscode.html-language-features"
|
"editor.defaultFormatter": "vscode.html-language-features"
|
||||||
}
|
},
|
||||||
|
"eslint.enable": false
|
||||||
}
|
}
|
|
@ -14,4 +14,4 @@
|
||||||
module.exports = (on, config) => {
|
module.exports = (on, config) => {
|
||||||
// `on` is used to hook into various events Cypress emits
|
// `on` is used to hook into various events Cypress emits
|
||||||
// `config` is the resolved Cypress config
|
// `config` is the resolved Cypress config
|
||||||
}
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// ***********************************************************
|
// ***********************************************************
|
||||||
|
|
||||||
// Import commands.js using ES2015 syntax:
|
// Import commands.js using ES2015 syntax:
|
||||||
import './commands'
|
import './commands';
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
// Alternatively you can use CommonJS syntax:
|
||||||
// require('./commands')
|
// require('./commands')
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
"name": "Using fixtures to represent data",
|
"name": "Using fixtures to represent data",
|
||||||
"email": "hello@cypress.io",
|
"email": "hello@cypress.io",
|
||||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// ***********************************************************
|
// ***********************************************************
|
||||||
|
|
||||||
// Import commands.js using ES2015 syntax:
|
// Import commands.js using ES2015 syntax:
|
||||||
import "./commands";
|
import './commands';
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
// Alternatively you can use CommonJS syntax:
|
||||||
// require('./commands')
|
// require('./commands')
|
||||||
|
|
|
@ -44,4 +44,3 @@ td {
|
||||||
.table__access-item-groups {
|
.table__access-item-groups {
|
||||||
height: 80px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ACTION TOOLBAR */
|
/* ACTION TOOLBAR */
|
||||||
.classification-details__headline {
|
.classification-details__headline {
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
|
@ -52,7 +51,6 @@
|
||||||
color: $invalid;
|
color: $invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* DETAILED FIELDS */
|
/* DETAILED FIELDS */
|
||||||
|
|
||||||
.classification__detailed-fields {
|
.classification__detailed-fields {
|
||||||
|
@ -111,4 +109,3 @@
|
||||||
input:invalid.dirty {
|
input:invalid.dirty {
|
||||||
border-color: $invalid;
|
border-color: $invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,5 +6,5 @@
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
taskana-administration-classification-details {
|
taskana-administration-classification-details {
|
||||||
width: 100%
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,5 @@ mat-icon {
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
color: #555
|
color: #555;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,3 @@ svg-icon {
|
||||||
fill: #555;
|
fill: #555;
|
||||||
top: -5px !important;
|
top: -5px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,3 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,29 @@
|
||||||
<taskana-shared-nav-bar></taskana-shared-nav-bar>
|
<mat-sidenav-container class="sidenav">
|
||||||
|
<mat-sidenav #sidenav mode="over" class="sidenav__drawer" [autoFocus]="false">
|
||||||
<div (window:resize)="onResize()" class="">
|
<div class="sidenav__drawer-logout">
|
||||||
<div class="taskana-main">
|
<button mat-icon-button data-toggle="tooltip" title="Logout" (click)="logout()" aria-expanded="true"
|
||||||
|
aria-controls="logout">
|
||||||
|
<mat-icon>exit_to_app</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<a class="sidenav__drawer-user-info">
|
||||||
|
<taskana-shared-user-information></taskana-shared-user-information>
|
||||||
|
</a>
|
||||||
|
<taskana-sidenav-list></taskana-sidenav-list>
|
||||||
|
<div class="sidenav__drawer-version">
|
||||||
|
<p> Taskana version: {{version}} </p>
|
||||||
|
</div>
|
||||||
|
</mat-sidenav>
|
||||||
|
<mat-sidenav-content>
|
||||||
|
<taskana-shared-nav-bar></taskana-shared-nav-bar>
|
||||||
|
<div (window:resize)="onResize()" class="">
|
||||||
|
<div class="taskana-main">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
<taskana-shared-spinner [isRunning]="requestInProgress" isModal=true></taskana-shared-spinner>
|
<taskana-shared-spinner [isRunning]="requestInProgress" isModal=true></taskana-shared-spinner>
|
||||||
<taskana-shared-progress-bar [hidden]="currentProgressValue === 0" currentValue={{currentProgressValue}}></taskana-shared-progress-bar>
|
<taskana-shared-progress-bar [hidden]="currentProgressValue === 0" currentValue={{currentProgressValue}}>
|
||||||
|
</taskana-shared-progress-bar>
|
||||||
|
</div>
|
||||||
|
<taskana-shared-type-ahead></taskana-shared-type-ahead>
|
||||||
</div>
|
</div>
|
||||||
<taskana-shared-type-ahead></taskana-shared-type-ahead>
|
</mat-sidenav-content>
|
||||||
</div>
|
</mat-sidenav-container>
|
|
@ -0,0 +1,57 @@
|
||||||
|
@import '../theme/variables';
|
||||||
|
|
||||||
|
.sidenav {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav__drawer {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 999;
|
||||||
|
box-shadow: none;
|
||||||
|
width: 350px;
|
||||||
|
background-color: $dark-green;
|
||||||
|
box-shadow: 3px 0px 10px -1px $dark-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav__drawer-list-item {
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav__drawer-logout {
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav__drawer-version {
|
||||||
|
color: $grey;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav__drawer-user-info {
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
margin-left: -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-icon-button {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-icon {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-drawer-inner-container {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-drawer {
|
||||||
|
overflow-y: visible !important;
|
||||||
|
}
|
|
@ -1,15 +1,17 @@
|
||||||
import { Component, HostListener, OnDestroy, OnInit } from '@angular/core';
|
import { Component, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { NavigationStart, Router } from '@angular/router';
|
import { NavigationStart, Router } from '@angular/router';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
import { MatSidenav } from '@angular/material';
|
||||||
import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service';
|
import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service';
|
||||||
|
import { SidenavService } from './shared/services/sidenav/sidenav.service';
|
||||||
import { RequestInProgressService } from './shared/services/request-in-progress/request-in-progress.service';
|
import { RequestInProgressService } from './shared/services/request-in-progress/request-in-progress.service';
|
||||||
import { OrientationService } from './shared/services/orientation/orientation.service';
|
import { OrientationService } from './shared/services/orientation/orientation.service';
|
||||||
import { SelectedRouteService } from './shared/services/selected-route/selected-route';
|
import { SelectedRouteService } from './shared/services/selected-route/selected-route';
|
||||||
import { UploadService } from './shared/services/upload/upload.service';
|
import { UploadService } from './shared/services/upload/upload.service';
|
||||||
import { ErrorModel } from './shared/models/error-model';
|
import { ErrorModel } from './shared/models/error-model';
|
||||||
import { NotificationService } from './shared/services/notifications/notification.service';
|
import { TaskanaEngineService } from './shared/services/taskana-engine/taskana-engine.service';
|
||||||
|
import { WindowRefService } from 'app/shared/services/window/window.service';
|
||||||
|
import { environment } from 'environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-root',
|
selector: 'taskana-root',
|
||||||
|
@ -18,7 +20,6 @@ import { NotificationService } from './shared/services/notifications/notificatio
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, OnDestroy {
|
export class AppComponent implements OnInit, OnDestroy {
|
||||||
workbasketsRoute = true;
|
workbasketsRoute = true;
|
||||||
|
|
||||||
selectedRoute = '';
|
selectedRoute = '';
|
||||||
|
|
||||||
requestInProgress = false;
|
requestInProgress = false;
|
||||||
|
@ -29,6 +30,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
routerSubscription: Subscription;
|
routerSubscription: Subscription;
|
||||||
uploadingFileSubscription: Subscription;
|
uploadingFileSubscription: Subscription;
|
||||||
error: ErrorModel;
|
error: ErrorModel;
|
||||||
|
version: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
@ -36,8 +38,10 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
private orientationService: OrientationService,
|
private orientationService: OrientationService,
|
||||||
private selectedRouteService: SelectedRouteService,
|
private selectedRouteService: SelectedRouteService,
|
||||||
private formsValidatorService: FormsValidatorService,
|
private formsValidatorService: FormsValidatorService,
|
||||||
private errorService: NotificationService,
|
public uploadService: UploadService,
|
||||||
public uploadService: UploadService
|
private sidenavService: SidenavService,
|
||||||
|
private taskanaEngineService: TaskanaEngineService,
|
||||||
|
private window: WindowRefService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
|
@ -45,6 +49,8 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
this.orientationService.onResize();
|
this.orientationService.onResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewChild('sidenav') public sidenav: MatSidenav;
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.routerSubscription = this.router.events.subscribe((event) => {
|
this.routerSubscription = this.router.events.subscribe((event) => {
|
||||||
if (event instanceof NavigationStart) {
|
if (event instanceof NavigationStart) {
|
||||||
|
@ -65,9 +71,23 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
this.selectedRoute = value;
|
this.selectedRoute = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.uploadingFileSubscription = this.uploadService.getCurrentProgressValue().subscribe((value) => {
|
this.uploadingFileSubscription = this.uploadService.getCurrentProgressValue().subscribe((value) => {
|
||||||
this.currentProgressValue = value;
|
this.currentProgressValue = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.taskanaEngineService.getVersion().subscribe((restVersion) => {
|
||||||
|
this.version = restVersion.version;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
this.taskanaEngineService.logout();
|
||||||
|
this.window.nativeWindow.location.href = environment.taskanaLogoutUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.sidenavService.setSidenav(this.sidenav);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|
|
@ -13,6 +13,14 @@ import { TabsModule } from 'ngx-bootstrap/tabs';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { TreeModule } from 'angular-tree-component';
|
import { TreeModule } from 'angular-tree-component';
|
||||||
import { SharedModule } from 'app/shared/shared.module';
|
import { SharedModule } from 'app/shared/shared.module';
|
||||||
|
import {
|
||||||
|
MatButtonModule,
|
||||||
|
MatSidenavModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatGridListModule,
|
||||||
|
MatListModule,
|
||||||
|
MatIconModule
|
||||||
|
} from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Services
|
* Services
|
||||||
|
@ -31,6 +39,8 @@ import { NoAccessComponent } from 'app/shared/components/no-access/no-access.com
|
||||||
import { FormsValidatorService } from './shared/services/forms-validator/forms-validator.service';
|
import { FormsValidatorService } from './shared/services/forms-validator/forms-validator.service';
|
||||||
import { UploadService } from './shared/services/upload/upload.service';
|
import { UploadService } from './shared/services/upload/upload.service';
|
||||||
import { NotificationService } from './shared/services/notifications/notification.service';
|
import { NotificationService } from './shared/services/notifications/notification.service';
|
||||||
|
import { SidenavService } from './shared/services/sidenav/sidenav.service';
|
||||||
|
import { SidenavListComponent } from 'app/shared/components/sidenav-list/sidenav-list.component';
|
||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
*/
|
*/
|
||||||
|
@ -62,11 +72,17 @@ const MODULES = [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
TreeModule,
|
TreeModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
MatSidenavModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatGridListModule,
|
||||||
|
MatListModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
NgxsModule.forRoot(STATES, { developmentMode: !environment.production }),
|
NgxsModule.forRoot(STATES, { developmentMode: !environment.production }),
|
||||||
NgxsReduxDevtoolsPluginModule.forRoot({ disabled: environment.production, maxAge: 25 })
|
NgxsReduxDevtoolsPluginModule.forRoot({ disabled: environment.production, maxAge: 25 })
|
||||||
];
|
];
|
||||||
|
|
||||||
const DECLARATIONS = [AppComponent, NavBarComponent, UserInformationComponent, NoAccessComponent];
|
const DECLARATIONS = [AppComponent, NavBarComponent, UserInformationComponent, NoAccessComponent, SidenavListComponent];
|
||||||
|
|
||||||
export function startupServiceFactory(startupService: StartupService): () => Promise<any> {
|
export function startupServiceFactory(startupService: StartupService): () => Promise<any> {
|
||||||
return (): Promise<any> => startupService.load();
|
return (): Promise<any> => startupService.load();
|
||||||
|
@ -97,7 +113,8 @@ export function startupServiceFactory(startupService: StartupService): () => Pro
|
||||||
FormsValidatorService,
|
FormsValidatorService,
|
||||||
UploadService,
|
UploadService,
|
||||||
NotificationService,
|
NotificationService,
|
||||||
ClassificationCategoriesService
|
ClassificationCategoriesService,
|
||||||
|
SidenavService
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
.master-detail {
|
.master-detail {
|
||||||
min-width: 100vw;
|
min-width: 100vw;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +1,11 @@
|
||||||
<nav class="navbar">
|
<nav class="navbar navbar__inverse">
|
||||||
<div class="navbar no-border-radius navbar-inverse no-gutter col-xs-12">
|
<div class="navbar__button">
|
||||||
<div class="pull-left col-sm-3 col-md-4">
|
<button mat-icon-button class="navbar_button-toggle" (click)="toggleSidenav()">
|
||||||
<button type="button" *ngIf="!showNavbar" class="btn btn-default navbar-toggle show pull-left" (click)="toggleNavBar();"
|
<mat-icon>menu</mat-icon>
|
||||||
aria-expanded="true" aria-controls="navbar" data-toggle="tooltip" title="Menu">
|
</button>
|
||||||
<span class="material-icons md-24 white">menu</span>
|
|
||||||
</button>
|
|
||||||
<span> </span>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-6 col-sm-5 col-md-4">
|
|
||||||
<ul class="nav logo">
|
|
||||||
<svg-icon class="logo white hidden-xs" src="./assets/icons/logo-copy.svg"></svg-icon>
|
|
||||||
<h2 class="navbar-brand no-margin"> {{title}}</h2>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="showDomainSelector()" class="pull-right domain-form">
|
|
||||||
<div class="dropdown clearfix btn-group">
|
|
||||||
<label class="control-label hidden-xs">Working on </label>
|
|
||||||
<button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
|
||||||
{{selectedDomain? selectedDomain: 'MASTER DOMAIN'}}
|
|
||||||
<span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu" aria-labelledby="dropdownMenu">
|
|
||||||
<li>
|
|
||||||
<a *ngFor="let domain of domains" (click)="switchDomain(domain)">
|
|
||||||
<label>{{domain? domain: 'MASTER DOMAIN'}}</label>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div [@toggleRight]="showNavbar" *ngIf="showNavbar" class="navbar-inverse sidenav full-height col-xs-9 col-sm-3"
|
<div class="navbar__logo">
|
||||||
data-html="false" aria-expanded="true">
|
<svg-icon class="navbar__logo-icon" src="./assets/icons/logo-copy.svg"></svg-icon>
|
||||||
<div class="row">
|
<h2 class="navbar__title">{{title}}</h2>
|
||||||
<ul class="nav">
|
|
||||||
<svg-icon class="logo white visible-xs" src="./assets/icons/logo.svg"></svg-icon>
|
|
||||||
<h2 class="navbar-brand no-margin logo visible-xs"> {{title}}</h2>
|
|
||||||
<button type="button" class="btn btn-default logout navbar-toggle show pull-right" data-toggle="tooltip" title="Logout"
|
|
||||||
(click)="logout()" aria-expanded="true" aria-controls="logout">
|
|
||||||
<span class="material-icons md-20 white ">exit_to_app</span>
|
|
||||||
</button>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="nav-content">
|
|
||||||
<taskana-shared-user-information></taskana-shared-user-information>
|
|
||||||
<div *ngIf="administrationAccess" class="row menu">
|
|
||||||
<span (click)="toggleNavBar()" routerLink="taskana/administration/workbaskets" aria-controls="administration"
|
|
||||||
routerLinkActive="active">Administration</span>
|
|
||||||
<div class="row submenu" [ngClass]="{'selected': selectedRoute.indexOf('workbaskets') !== -1 }">
|
|
||||||
<span (click)="toggleNavBar()" class="col-xs-6" routerLink="taskana/administration/workbaskets" aria-controls="Workbaskets"
|
|
||||||
routerLinkActive="active">Workbaskets</span>
|
|
||||||
</div>
|
|
||||||
<div class="row submenu" [ngClass]="{'selected': selectedRoute.indexOf('classifications') !== -1}">
|
|
||||||
<span (click)="toggleNavBar()" class="col-xs-6" routerLink="taskana/administration/classifications" aria-controls="Classifications"
|
|
||||||
routerLinkActive="active">Classifications</span>
|
|
||||||
</div>
|
|
||||||
<div class="row submenu" [ngClass]="{'selected': selectedRoute.indexOf('access-items-management') !== -1}">
|
|
||||||
<span (click)="toggleNavBar()" class="col-xs-6" routerLink="taskana/administration/access-items-management"
|
|
||||||
aria-controls="Access items" routerLinkActive="active">Access items</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="monitorAccess" class="row menu" [ngClass]="{'selected': selectedRoute.indexOf('monitor') !== -1}">
|
|
||||||
<span (click)="toggleNavBar()" routerLink="{{monitorUrl}}" aria-controls="Monitor" routerLinkActive="active">Monitor</span>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="workplaceAccess" class="row menu" [ngClass]="{'selected': selectedRoute.indexOf('workplace') !== -1 || selectedRoute === ''}">
|
|
||||||
<span (click)="toggleNavBar()" routerLink="{{workplaceUrl}}" aria-controls="Workplace" routerLinkActive="active">Workplace</span>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="historyAccess" class="row menu" [ngClass]="{'selected': selectedRoute.indexOf('history') !== -1}">
|
|
||||||
<span (click)="toggleNavBar()" routerLink="{{historyUrl}}" aria-controls="history" routerLinkActive="active">History</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="nav-version">
|
|
||||||
<p id="taskana-version"> Taskana version: {{version}} </p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="showNavbar" class="backdrop" (click)="toggleNavBar()"></div>
|
</nav>
|
||||||
</nav>
|
|
|
@ -2,103 +2,7 @@
|
||||||
|
|
||||||
.navbar.main:before {
|
.navbar.main:before {
|
||||||
@include degraded-bar(right, 100%, 3px);
|
@include degraded-bar(right, 100%, 3px);
|
||||||
}
|
box-sizing: border-box;
|
||||||
|
|
||||||
.navbar-inverse {
|
|
||||||
top: 0;
|
|
||||||
border: none;
|
|
||||||
background-color: $dark-green;
|
|
||||||
box-shadow: 0px 1px 5px -1px black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggle {
|
|
||||||
margin: 3px 0px;
|
|
||||||
font-size: 20px;
|
|
||||||
&.logout {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button.navbar-toggle:hover > span {
|
|
||||||
color: $aquamarine;
|
|
||||||
}
|
|
||||||
ul.nav > p {
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
padding-right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-inverse .navbar-toggle,
|
|
||||||
.navbar-toggle:hover,
|
|
||||||
.navbar-inverse .navbar-toggle:focus {
|
|
||||||
border: none;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg-icon.logo {
|
|
||||||
float: left;
|
|
||||||
width: 150px;
|
|
||||||
height: 50px;
|
|
||||||
padding: 5px;
|
|
||||||
position: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2.navbar-brand {
|
|
||||||
vertical-align: middle;
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
|
||||||
padding: 15px 0px 0px 0px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.domain-form {
|
|
||||||
margin: 13px;
|
|
||||||
color: white;
|
|
||||||
font-size: 18px;
|
|
||||||
> div {
|
|
||||||
cursor: pointer;
|
|
||||||
> button {
|
|
||||||
color: white;
|
|
||||||
background-color: $dark-green;
|
|
||||||
border: none;
|
|
||||||
font-size: 16px;
|
|
||||||
border-bottom: 1px solid $dark-green;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-content {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-version {
|
|
||||||
color: $grey;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All side bar links styling.
|
|
||||||
*/
|
|
||||||
.nav-sidebar > li > a {
|
|
||||||
padding-right: 20px;
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu > span:hover,
|
|
||||||
.submenu > span:hover {
|
|
||||||
color: white;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidenav {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 999;
|
|
||||||
box-shadow: none;
|
|
||||||
height: 100%;
|
|
||||||
background-color: $dark-green;
|
|
||||||
box-shadow: 3px 0px 10px -1px $dark-green;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
|
@ -106,40 +10,53 @@ h2.navbar-brand {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu,
|
.navbar__inverse {
|
||||||
.submenu > span {
|
border: none;
|
||||||
margin-top: 15px;
|
background-color: $dark-green;
|
||||||
font-size: 20px;
|
box-shadow: 0px 1px 5px -1px black;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: inherit;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu,
|
.navbar__buttom {
|
||||||
.submenu > span {
|
flex-grow: 1;
|
||||||
padding-left: 12px;
|
display: flex !important;
|
||||||
color: $grey;
|
order: 1;
|
||||||
|
margin-top: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar__logo {
|
||||||
|
display: flex !important;
|
||||||
|
flex-grow: 6;
|
||||||
|
position: relative;
|
||||||
|
left: 40%;
|
||||||
|
margin-top: -3px;
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.navbar__title {
|
||||||
|
display: flex !important;
|
||||||
|
flex-grow: 5;
|
||||||
|
color: white;
|
||||||
|
margin-top: 10px;
|
||||||
|
order: 3;
|
||||||
|
margin-left: 2%;
|
||||||
|
font-size: large;
|
||||||
|
@media only screen and (max-width: 700px) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg-icon.navbar__logo-icon {
|
||||||
|
float: left;
|
||||||
|
width: 150px;
|
||||||
|
height: 50px;
|
||||||
|
position: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-icon-button {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
.menu.selected,
|
|
||||||
.submenu.selected {
|
|
||||||
background-color: transparent;
|
|
||||||
& > span {
|
|
||||||
padding-left: 10px;
|
|
||||||
border-left: $pallete-green 5px solid;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu > .submenu {
|
.mat-icon {
|
||||||
margin-left: 30px;
|
color: white;
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $grey;
|
|
||||||
&:hover {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { NavBarComponent } from './nav-bar.component';
|
||||||
|
import { SelectedRouteService } from '../../../shared/services/selected-route/selected-route';
|
||||||
|
import { MatIconModule } from '@angular/material';
|
||||||
|
import { SidenavService } from '../../../shared/services/sidenav/sidenav.service';
|
||||||
|
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||||
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
|
||||||
|
const SidenavServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<SidenavService> => ({
|
||||||
|
toggleSidenav: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const SelectedRouteServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<SelectedRouteService> => ({
|
||||||
|
getSelectedRoute: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('NavBarComponent', () => {
|
||||||
|
let component: NavBarComponent;
|
||||||
|
let fixture: ComponentFixture<NavBarComponent>;
|
||||||
|
let debugElement: DebugElement;
|
||||||
|
var route = '';
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [NavBarComponent],
|
||||||
|
imports: [MatIconModule, HttpClientTestingModule, AngularSvgIconModule],
|
||||||
|
providers: [
|
||||||
|
{ provide: SidenavService, useClass: SidenavServiceSpy },
|
||||||
|
{ provide: SelectedRouteService, useClass: SelectedRouteServiceSpy }
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NavBarComponent);
|
||||||
|
debugElement = fixture.debugElement;
|
||||||
|
component = fixture.debugElement.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set title to workbasket if workbasket ist selected', () => {
|
||||||
|
route = 'workbaskets';
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.setTitle(route);
|
||||||
|
expect(component.title).toBe('Workbaskets');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should toggle sidenav when button clicked', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.toggle).toBe(false);
|
||||||
|
const button = debugElement.query(By.css('.navbar_button-toggle')).nativeElement;
|
||||||
|
expect(button).toBeTruthy();
|
||||||
|
button.click();
|
||||||
|
expect(component.toggle).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,105 +1,44 @@
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { environment } from 'environments/environment';
|
|
||||||
import { SelectedRouteService } from 'app/shared/services/selected-route/selected-route';
|
import { SelectedRouteService } from 'app/shared/services/selected-route/selected-route';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { DomainService } from 'app/shared/services/domain/domain.service';
|
|
||||||
import { BusinessAdminGuard } from 'app/shared/guards/business-admin.guard';
|
|
||||||
import { MonitorGuard } from 'app/shared/guards/monitor.guard';
|
|
||||||
import { WindowRefService } from 'app/shared/services/window/window.service';
|
|
||||||
import { UserGuard } from 'app/shared/guards/user.guard';
|
|
||||||
import { expandRight } from 'app/shared/animations/expand.animation';
|
import { expandRight } from 'app/shared/animations/expand.animation';
|
||||||
import { TaskanaEngineService } from '../../services/taskana-engine/taskana-engine.service';
|
import { SidenavService } from '../../services/sidenav/sidenav.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-shared-nav-bar',
|
selector: 'taskana-shared-nav-bar',
|
||||||
templateUrl: './nav-bar.component.html',
|
templateUrl: './nav-bar.component.html',
|
||||||
styleUrls: ['./nav-bar.component.scss'],
|
styleUrls: ['./nav-bar.component.scss'],
|
||||||
animations: [expandRight]
|
animations: [expandRight]
|
||||||
})
|
})
|
||||||
export class NavBarComponent implements OnInit, OnDestroy {
|
export class NavBarComponent implements OnInit {
|
||||||
selectedRoute = '';
|
selectedRoute = '';
|
||||||
route: string;
|
|
||||||
title = '';
|
title = '';
|
||||||
|
|
||||||
titleAdministration = 'Administration';
|
|
||||||
titleWorkbaskets = 'Workbaskets';
|
titleWorkbaskets = 'Workbaskets';
|
||||||
titleClassifications = 'Classifications';
|
titleClassifications = 'Classifications';
|
||||||
titleAccessItems = 'Access items';
|
titleAccessItems = 'Access items';
|
||||||
titleMonitor = 'Monitor';
|
titleMonitor = 'Monitor';
|
||||||
titleWorkplace = 'Workplace';
|
titleWorkplace = 'Workplace';
|
||||||
titleHistory = 'History';
|
titleHistory = 'History';
|
||||||
showNavbar = false;
|
toggle: boolean = false;
|
||||||
domains: Array<string> = [];
|
|
||||||
selectedDomain: string;
|
|
||||||
version: string;
|
|
||||||
|
|
||||||
adminUrl = 'taskana/administration';
|
|
||||||
monitorUrl = 'taskana/monitor';
|
|
||||||
workplaceUrl = 'taskana/workplace';
|
|
||||||
historyUrl = 'taskana/history';
|
|
||||||
|
|
||||||
administrationAccess = false;
|
|
||||||
monitorAccess = false;
|
|
||||||
workplaceAccess = false;
|
|
||||||
historyAccess = false;
|
|
||||||
|
|
||||||
selectedRouteSubscription: Subscription;
|
selectedRouteSubscription: Subscription;
|
||||||
getDomainsSubscription: Subscription;
|
|
||||||
|
|
||||||
constructor(
|
constructor(private selectedRouteService: SelectedRouteService, private sidenavService: SidenavService) {}
|
||||||
private selectedRouteService: SelectedRouteService,
|
|
||||||
private domainService: DomainService,
|
|
||||||
private taskanaEngineService: TaskanaEngineService,
|
|
||||||
private window: WindowRefService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.selectedRouteSubscription = this.selectedRouteService.getSelectedRoute().subscribe((value: string) => {
|
this.selectedRouteSubscription = this.selectedRouteService.getSelectedRoute().subscribe((value: string) => {
|
||||||
this.selectedRoute = value;
|
this.selectedRoute = value;
|
||||||
this.setTitle(value);
|
this.setTitle(value);
|
||||||
});
|
});
|
||||||
this.getDomainsSubscription = this.domainService.getDomains().subscribe((domains) => {
|
|
||||||
this.domains = domains;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.domainService.getSelectedDomain().subscribe((domain) => {
|
|
||||||
this.selectedDomain = domain;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.taskanaEngineService.getVersion().subscribe((restVersion) => {
|
|
||||||
this.version = restVersion.version;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.administrationAccess = this.taskanaEngineService.hasRole(BusinessAdminGuard.roles);
|
|
||||||
this.monitorAccess = this.taskanaEngineService.hasRole(MonitorGuard.roles);
|
|
||||||
this.workplaceAccess = this.taskanaEngineService.hasRole(UserGuard.roles);
|
|
||||||
|
|
||||||
this.taskanaEngineService.isHistoryProviderEnabled().subscribe((value) => {
|
|
||||||
this.historyAccess = value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switchDomain(domain) {
|
toggleSidenav() {
|
||||||
this.domainService.switchDomain(domain);
|
this.toggle = !this.toggle;
|
||||||
|
this.sidenavService.toggleSidenav();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleNavBar() {
|
setTitle(value: string = 'workbaskets') {
|
||||||
this.showNavbar = !this.showNavbar;
|
|
||||||
}
|
|
||||||
|
|
||||||
logout() {
|
|
||||||
this.taskanaEngineService.logout().subscribe(() => {});
|
|
||||||
this.window.nativeWindow.location.href = environment.taskanaLogoutUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
showDomainSelector(): boolean {
|
|
||||||
return (
|
|
||||||
this.selectedRoute.indexOf('administration') !== -1 ||
|
|
||||||
this.selectedRoute.indexOf('workbaskets') !== -1 ||
|
|
||||||
this.selectedRoute.indexOf('classifications') !== -1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private setTitle(value: string = 'workbaskets') {
|
|
||||||
if (value.indexOf('workbaskets') === 0) {
|
if (value.indexOf('workbaskets') === 0) {
|
||||||
this.title = this.titleWorkbaskets;
|
this.title = this.titleWorkbaskets;
|
||||||
} else if (value.indexOf('classifications') === 0) {
|
} else if (value.indexOf('classifications') === 0) {
|
||||||
|
@ -114,13 +53,4 @@ export class NavBarComponent implements OnInit, OnDestroy {
|
||||||
this.title = this.titleHistory;
|
this.title = this.titleHistory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
if (this.selectedRouteSubscription) {
|
|
||||||
this.selectedRouteSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
if (this.getDomainsSubscription) {
|
|
||||||
this.getDomainsSubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<mat-nav-list>
|
||||||
|
<a mat-list-item class="list-item list-item__admin" [routerLink]=[workbasketsUrl] [routerLinkActive]="['active']"
|
||||||
|
*ngIf="administrationAccess" (click)="toggleSidenav()">Administration</a>
|
||||||
|
<a mat-list-item class="list-item list-item__admin-workbaskets" [routerLink]=[workbasketsUrl]
|
||||||
|
[routerLinkActive]="['active']" *ngIf="administrationAccess" (click)="toggleSidenav()">Workbaskets</a>
|
||||||
|
<a mat-list-item class="list-item list-item__admin-classifications" [routerLink]=[classificationUrl]
|
||||||
|
[routerLinkActive]="['active']" *ngIf="administrationAccess" (click)="toggleSidenav()">Classifications</a>
|
||||||
|
<a mat-list-item class="list-item list-item__admin-acces-items" [routerLink]=[accessUrl]
|
||||||
|
[routerLinkActive]="['active']" (click)="toggleSidenav()" *ngIf="administrationAccess">Access Items</a>
|
||||||
|
<a mat-list-item class="list-item list-item__monitor" [routerLink]=[monitorUrl] [routerLinkActive]="['active']"
|
||||||
|
*ngIf="monitorAccess" (click)="toggleSidenav()">Monitor</a>
|
||||||
|
<a mat-list-item class="list-item list-item__workplace" [routerLink]=[workplaceUrl] [routerLinkActive]="['active']"
|
||||||
|
*ngIf="workplaceAccess" (click)="toggleSidenav()">Workplace</a>
|
||||||
|
<a mat-list-item class="list-item list-item__history" [routerLink]=[historyUrl] [routerLinkActive]="['active']"
|
||||||
|
*ngIf="historyAccess" (click)="toggleSidenav()">History</a>
|
||||||
|
</mat-nav-list>
|
|
@ -0,0 +1,35 @@
|
||||||
|
@import '../../../../theme/variables';
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
color: $grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
color: $grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
color: $grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item__admin-workbaskets {
|
||||||
|
color: $grey;
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
.list-item__admin-classifications {
|
||||||
|
color: $grey;
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item__admin-acces-items {
|
||||||
|
color: $grey;
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-drawer-container {
|
||||||
|
background-color: white;
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { Component, DebugElement } from '@angular/core';
|
||||||
|
import { SidenavListComponent } from './sidenav-list.component';
|
||||||
|
import { SidenavService } from '../../../shared/services/sidenav/sidenav.service';
|
||||||
|
import {
|
||||||
|
MatButtonModule,
|
||||||
|
MatSidenavModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatGridListModule,
|
||||||
|
MatListModule,
|
||||||
|
MatIconModule
|
||||||
|
} from '@angular/material';
|
||||||
|
import { BrowserModule, By } from '@angular/platform-browser';
|
||||||
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { TaskanaEngineService } from '../../services/taskana-engine/taskana-engine.service';
|
||||||
|
import { TaskanaEngineServiceMock } from '../../services/taskana-engine/taskana-engine.mock.service';
|
||||||
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
|
||||||
|
const SidenavServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<SidenavService> => ({
|
||||||
|
toggleSidenav: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const TaskanaEngingeServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<TaskanaEngineServiceMock> => ({
|
||||||
|
hasRole: jest.fn().mockReturnValue(of()),
|
||||||
|
isHistoryProviderEnabled: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('SidenavListComponent', () => {
|
||||||
|
let component: SidenavListComponent;
|
||||||
|
let fixture: ComponentFixture<SidenavListComponent>;
|
||||||
|
let debugElement: DebugElement;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SidenavListComponent],
|
||||||
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatSidenavModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatGridListModule,
|
||||||
|
MatListModule,
|
||||||
|
MatIconModule,
|
||||||
|
BrowserModule,
|
||||||
|
RouterModule,
|
||||||
|
RouterTestingModule,
|
||||||
|
HttpClientTestingModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: SidenavService, useClass: SidenavServiceSpy },
|
||||||
|
{ provide: TaskanaEngineService, useClass: TaskanaEngingeServiceSpy }
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SidenavListComponent);
|
||||||
|
debugElement = fixture.debugElement;
|
||||||
|
component = fixture.debugElement.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show all links if user has all permissions', () => {
|
||||||
|
component.administrationAccess = true;
|
||||||
|
component.monitorAccess = true;
|
||||||
|
component.workplaceAccess = true;
|
||||||
|
component.historyAccess = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
const menuList = debugElement.queryAll(By.css('.list-item'));
|
||||||
|
expect(menuList.length).toBe(7);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show all links if user has only monitor access', () => {
|
||||||
|
component.administrationAccess = false;
|
||||||
|
component.monitorAccess = true;
|
||||||
|
component.workplaceAccess = false;
|
||||||
|
component.historyAccess = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
const menuList = debugElement.queryAll(By.css('.list-item'));
|
||||||
|
expect(menuList.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should toggle sidenav when link clicked', () => {
|
||||||
|
component.toggle = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
const button = debugElement.query(By.css('.list-item__admin-workbaskets')).nativeElement;
|
||||||
|
expect(button).toBeTruthy();
|
||||||
|
button.click();
|
||||||
|
expect(component.toggle).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { BusinessAdminGuard } from 'app/shared/guards/business-admin.guard';
|
||||||
|
import { MonitorGuard } from 'app/shared/guards/monitor.guard';
|
||||||
|
import { UserGuard } from 'app/shared/guards/user.guard';
|
||||||
|
import { TaskanaEngineService } from '../../services/taskana-engine/taskana-engine.service';
|
||||||
|
import { SidenavService } from '../../services/sidenav/sidenav.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'taskana-sidenav-list',
|
||||||
|
templateUrl: './sidenav-list.component.html',
|
||||||
|
styleUrls: ['./sidenav-list.component.scss']
|
||||||
|
})
|
||||||
|
export class SidenavListComponent implements OnInit {
|
||||||
|
toggle: boolean = false;
|
||||||
|
|
||||||
|
monitorUrl = 'taskana/monitor';
|
||||||
|
workplaceUrl = 'taskana/workplace';
|
||||||
|
historyUrl = 'taskana/history';
|
||||||
|
accessUrl = 'taskana/administration/access-items-management';
|
||||||
|
classificationUrl = 'taskana/administration/classifications';
|
||||||
|
workbasketsUrl = 'taskana/administration/workbaskets';
|
||||||
|
|
||||||
|
administrationAccess = false;
|
||||||
|
monitorAccess = false;
|
||||||
|
workplaceAccess = false;
|
||||||
|
historyAccess = false;
|
||||||
|
|
||||||
|
admin_url_list: any[];
|
||||||
|
|
||||||
|
constructor(private taskanaEngineService: TaskanaEngineService, private sidenavService: SidenavService) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.administrationAccess = this.taskanaEngineService.hasRole(BusinessAdminGuard.roles);
|
||||||
|
this.monitorAccess = this.taskanaEngineService.hasRole(MonitorGuard.roles);
|
||||||
|
this.workplaceAccess = this.taskanaEngineService.hasRole(UserGuard.roles);
|
||||||
|
this.taskanaEngineService.isHistoryProviderEnabled().subscribe((value) => {
|
||||||
|
this.historyAccess = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSidenav() {
|
||||||
|
this.toggle = !this.toggle;
|
||||||
|
this.sidenavService.toggleSidenav();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,14 @@
|
||||||
<div class="row">
|
<div class="icon">
|
||||||
<div class="icon">
|
<div class="icon__wrap">
|
||||||
<div class="icon-wrap col-xs-offset-5">
|
<svg-icon class="blue big" src="./assets/icons/user.svg"></svg-icon>
|
||||||
<svg-icon class="blue big" src="./assets/icons/user.svg"></svg-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="user-info white">
|
|
||||||
<span>Logged as: {{userInformation?.userId}}</span>
|
|
||||||
<button type="button" class="btn btn-default white pull-right transparent" (click)="toggleRoles();" aria-expanded="true" aria-controls="roles">
|
|
||||||
<span>Roles</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="white pull-right roles col-xs-12" [@toggleDown]="showRoles">
|
|
||||||
<span><i>{{roles}}</i></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<span>Logged as: {{userInformation?.userId}}</span>
|
||||||
|
<button mat-button class="user-info__button" (click)="toggleRoles();" aria-expanded="true" aria-controls="roles">
|
||||||
|
<span>Roles</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="roles" [@toggleDown]="showRoles">
|
||||||
|
<span><i>{{roles}}</i></span>
|
||||||
|
</div>
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
.user-info {
|
.user-info {
|
||||||
margin-top: 85px;
|
margin-top: 85px;
|
||||||
|
color: white;
|
||||||
> span {
|
> span {
|
||||||
margin: 0 15px 0 15px;
|
margin: 0 15px 0 15px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
@ -18,13 +19,15 @@
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: absolute;
|
||||||
& > .icon-wrap {
|
|
||||||
|
& > .icon__wrap {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: solid 2px #22a39f;
|
border: solid 2px #22a39f;
|
||||||
width: 72px;
|
width: 72px;
|
||||||
height: 72px;
|
height: 72px;
|
||||||
overflow: hidden;
|
left: 35%;
|
||||||
|
//overflow: hidden;
|
||||||
background-color: #175263;
|
background-color: #175263;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
-webkit-box-shadow: 0px 3px 3px #416b6a;
|
-webkit-box-shadow: 0px 3px 3px #416b6a;
|
||||||
|
@ -36,7 +39,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin: 2px;
|
margin: 7px -0px 0px 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +56,11 @@
|
||||||
box-shadow: 0px 3px 3px #416b6a;
|
box-shadow: 0px 3px 3px #416b6a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-info_roles {
|
||||||
|
margin-left: 16px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
button.transparent {
|
button.transparent {
|
||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
@ -68,3 +76,14 @@ button.transparent {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-button {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
left: 28%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roles {
|
||||||
|
margin-left: 16px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { Component, DebugElement } from '@angular/core';
|
||||||
|
import { UserInformationComponent } from './user-information.component';
|
||||||
|
import { BrowserModule, By } from '@angular/platform-browser';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { TaskanaEngineService } from '../../services/taskana-engine/taskana-engine.service';
|
||||||
|
import { TaskanaEngineServiceMock } from '../../services/taskana-engine/taskana-engine.mock.service';
|
||||||
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
import { expandDown } from '../../animations/expand.animation';
|
||||||
|
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||||
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
|
import { fromEventPattern } from 'rxjs';
|
||||||
|
|
||||||
|
const TaskanaEngingeServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<TaskanaEngineServiceMock> => ({
|
||||||
|
hasRole: jest.fn().mockReturnValue(of()),
|
||||||
|
isHistoryProviderEnabled: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('UserInformationComponent', () => {
|
||||||
|
let component: UserInformationComponent;
|
||||||
|
let fixture: ComponentFixture<UserInformationComponent>;
|
||||||
|
let debugElement: DebugElement;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [UserInformationComponent],
|
||||||
|
imports: [BrowserModule, AngularSvgIconModule, HttpClientTestingModule, BrowserAnimationsModule],
|
||||||
|
providers: [{ provide: TaskanaEngineService, useClass: TaskanaEngingeServiceSpy }]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UserInformationComponent);
|
||||||
|
debugElement = fixture.debugElement;
|
||||||
|
component = fixture.debugElement.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should toggle roles when roles clicked', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.showRoles).toBe(false);
|
||||||
|
const button = debugElement.query(By.css('.user-info__button')).nativeElement;
|
||||||
|
expect(button).toBeTruthy();
|
||||||
|
button.click();
|
||||||
|
expect(component.showRoles).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SidenavService } from './sidenav.service';
|
||||||
|
|
||||||
|
describe('SidenavService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [SidenavService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([SidenavService], (service: SidenavService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MatSidenav } from '@angular/material';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class SidenavService {
|
||||||
|
private sidenav: MatSidenav;
|
||||||
|
state: boolean = false;
|
||||||
|
|
||||||
|
public setSidenav(sidenav: MatSidenav) {
|
||||||
|
this.sidenav = sidenav;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleSidenav(): void {
|
||||||
|
this.sidenav.toggle();
|
||||||
|
this.state = this.sidenav.opened;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,8 +10,9 @@
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<taskana-root></taskana-root>
|
<taskana-root></taskana-root>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -5,13 +5,8 @@
|
||||||
"baseUrl": "",
|
"baseUrl": "",
|
||||||
"types": []
|
"types": []
|
||||||
},
|
},
|
||||||
"files": [
|
"files": ["main.ts", "polyfills.ts"],
|
||||||
"main.ts",
|
"include": ["src/**/*.d.ts"],
|
||||||
"polyfills.ts"
|
|
||||||
],
|
|
||||||
"include": [
|
|
||||||
"src/**/*.d.ts"
|
|
||||||
],
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"enableIvy": false
|
"enableIvy": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,10 @@
|
||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out-tsc/spec",
|
"outDir": "./out-tsc/spec",
|
||||||
"types": [
|
"types": ["jest", "node"],
|
||||||
"jest",
|
|
||||||
"node"
|
|
||||||
],
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"emitDecoratorMetadata": true
|
"emitDecoratorMetadata": true
|
||||||
},
|
},
|
||||||
"files": [
|
"files": ["src/test.ts", "src/polyfills.ts"],
|
||||||
"src/test.ts",
|
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||||
"src/polyfills.ts"
|
|
||||||
],
|
|
||||||
"include": [
|
|
||||||
"src/**/*.spec.ts",
|
|
||||||
"src/**/*.d.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue