Merge pull request #37 in ~VLE2FE/definma-ui from development to master
* commit 'c38d0be4578be9e84d594614a6a94411715c051e': code improvements implemented editable help implemented editable help implemented no measurements or condition filter
This commit is contained in:
		@@ -2,22 +2,5 @@ import { AppPage } from './app.po';
 | 
				
			|||||||
import { browser, logging } from 'protractor';
 | 
					import { browser, logging } from 'protractor';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('workspace-project App', () => {
 | 
					describe('workspace-project App', () => {
 | 
				
			||||||
  let page: AppPage;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    page = new AppPage();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should display the page title', () => {
 | 
					 | 
				
			||||||
    page.navigateTo();
 | 
					 | 
				
			||||||
    expect(page.getTitleText()).toEqual('Digital Fingerprint of Plastics');
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  afterEach(async () => {
 | 
					 | 
				
			||||||
    // Assert that there are no errors emitted from the browser
 | 
					 | 
				
			||||||
    const logs = await browser.manage().logs().get(logging.Type.BROWSER);
 | 
					 | 
				
			||||||
    expect(logs).not.toContain(jasmine.objectContaining({
 | 
					 | 
				
			||||||
      level: logging.Level.SEVERE,
 | 
					 | 
				
			||||||
    } as logging.Entry));
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +0,0 @@
 | 
				
			|||||||
import { browser, by, element } from 'protractor';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class AppPage {
 | 
					 | 
				
			||||||
  navigateTo() {
 | 
					 | 
				
			||||||
    return browser.get(browser.baseUrl) as Promise<any>;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  getTitleText() {
 | 
					 | 
				
			||||||
    return element(by.css('app-root div .sub-brand-content')).getText() as Promise<string>;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -12,7 +12,8 @@ import {ChangelogComponent} from './changelog/changelog.component';
 | 
				
			|||||||
import {DocumentationDatabaseComponent} from './documentation/documentation-database/documentation-database.component';
 | 
					import {DocumentationDatabaseComponent} from './documentation/documentation-database/documentation-database.component';
 | 
				
			||||||
import {PredictionComponent} from './prediction/prediction.component';
 | 
					import {PredictionComponent} from './prediction/prediction.component';
 | 
				
			||||||
import {ModelTemplatesComponent} from './model-templates/model-templates.component';
 | 
					import {ModelTemplatesComponent} from './model-templates/model-templates.component';
 | 
				
			||||||
import {DocumentationArchitectureComponent} from './documentation/documentation-architecture/documentation-architecture.component';
 | 
					import {DocumentationArchitectureComponent} from
 | 
				
			||||||
 | 
					    './documentation/documentation-architecture/documentation-architecture.component';
 | 
				
			||||||
import {MaterialsComponent} from './materials/materials.component';
 | 
					import {MaterialsComponent} from './materials/materials.component';
 | 
				
			||||||
import {MaterialComponent} from './material/material.component';
 | 
					import {MaterialComponent} from './material/material.component';
 | 
				
			||||||
import {DocumentationModelsComponent} from './documentation/documentation-models/documentation-models.component';
 | 
					import {DocumentationModelsComponent} from './documentation/documentation-models/documentation-models.component';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,68 +1,5 @@
 | 
				
			|||||||
import {TestBed, async, ComponentFixture} from '@angular/core/testing';
 | 
					import {TestBed, async, ComponentFixture} from '@angular/core/testing';
 | 
				
			||||||
import { AppComponent } from './app.component';
 | 
					 | 
				
			||||||
import {By} from '@angular/platform-browser';
 | 
					 | 
				
			||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
import {RouterTestingModule} from '@angular/router/testing';
 | 
					 | 
				
			||||||
import {LoginService} from './services/login.service';
 | 
					 | 
				
			||||||
import {Router} from '@angular/router';
 | 
					 | 
				
			||||||
import {Observable} from 'rxjs';
 | 
					 | 
				
			||||||
import {Test} from 'tslint';
 | 
					 | 
				
			||||||
import {RbCustomInputsModule} from './rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let loginServiceSpy: jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
let routerServiceSpy: Router;
 | 
					 | 
				
			||||||
let windowServiceSpy: jasmine.SpyObj<Window>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('AppComponent', () => {
 | 
					describe('AppComponent', () => {
 | 
				
			||||||
  let component: AppComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<AppComponent>;
 | 
					 | 
				
			||||||
  let css;   // get native element by css selector
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'isLevel', 'isLoggedIn']);
 | 
					 | 
				
			||||||
    const windowSpy = jasmine.createSpyObj('Window', ['location', 'innerWidth', 'innerHeight', 'scroll']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ AppComponent ],
 | 
					 | 
				
			||||||
      imports: [
 | 
					 | 
				
			||||||
        RbUiComponentsModule,
 | 
					 | 
				
			||||||
        RbCustomInputsModule,
 | 
					 | 
				
			||||||
        RouterTestingModule
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        {provide: LoginService, useValue: loginSpy},
 | 
					 | 
				
			||||||
        {provide: Window, useValue: windowSpy}
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    }).compileComponents();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
    routerServiceSpy = TestBed.inject(Router);
 | 
					 | 
				
			||||||
    windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(AppComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
    css = (selector) => fixture.debugElement.query(By.css(selector)).nativeElement;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create the app', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should have the header', () => {
 | 
					 | 
				
			||||||
    expect(css('rb-full-header')).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should have the correct app title', () => {
 | 
					 | 
				
			||||||
    expect(css('rb-full-header div.sub-brand-content > div').innerText).toBe('BugDEVELOPMENT DeFinMa');
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should have the page container', () => {
 | 
					 | 
				
			||||||
    expect(css('.container')).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ import {HelpComponent} from './help/help.component';
 | 
				
			|||||||
import {DataService} from './services/data.service';
 | 
					import {DataService} from './services/data.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: get rid of chart.js (+moment.js)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-root',
 | 
					  selector: 'app-root',
 | 
				
			||||||
@@ -15,8 +14,8 @@ import {DataService} from './services/data.service';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class AppComponent implements OnInit{
 | 
					export class AppComponent implements OnInit{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bugReport = {do: '', work: ''};
 | 
					  bugReport = {do: '', work: ''};  // data from bug report inputs
 | 
				
			||||||
  isDocumentation = false;
 | 
					  isDocumentation = false;         // true if user is on documentation pages
 | 
				
			||||||
  devMode = false;
 | 
					  devMode = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
@@ -36,8 +35,10 @@ export class AppComponent implements OnInit{
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
 | 
					    // try to log in user
 | 
				
			||||||
    this.login.login().then(res => {
 | 
					    this.login.login().then(res => {
 | 
				
			||||||
      if (!res && !(this.route.snapshot.url.length && this.route.snapshot.url[0].path === '/documentation')) {
 | 
					      // return to home page if log failed, except when on documentation pages
 | 
				
			||||||
 | 
					      if (!res && !(/\/documentation/.test(this.router.url))) {
 | 
				
			||||||
        this.router.navigate(['/']);
 | 
					        this.router.navigate(['/']);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,12 +26,14 @@ import { ParametersPipe } from './parameters.pipe';
 | 
				
			|||||||
import { SettingsComponent } from './settings/settings.component';
 | 
					import { SettingsComponent } from './settings/settings.component';
 | 
				
			||||||
import { UsersComponent } from './users/users.component';
 | 
					import { UsersComponent } from './users/users.component';
 | 
				
			||||||
import { ChangelogComponent } from './changelog/changelog.component';
 | 
					import { ChangelogComponent } from './changelog/changelog.component';
 | 
				
			||||||
import { DocumentationDatabaseComponent } from './documentation/documentation-database/documentation-database.component';
 | 
					import { DocumentationDatabaseComponent } from
 | 
				
			||||||
 | 
					    './documentation/documentation-database/documentation-database.component';
 | 
				
			||||||
import { PredictionComponent } from './prediction/prediction.component';
 | 
					import { PredictionComponent } from './prediction/prediction.component';
 | 
				
			||||||
import { HelpComponent } from './help/help.component';
 | 
					import { HelpComponent } from './help/help.component';
 | 
				
			||||||
import { ModelTemplatesComponent } from './model-templates/model-templates.component';
 | 
					import { ModelTemplatesComponent } from './model-templates/model-templates.component';
 | 
				
			||||||
import { SizePipe } from './size.pipe';
 | 
					import { SizePipe } from './size.pipe';
 | 
				
			||||||
import { DocumentationArchitectureComponent } from './documentation/documentation-architecture/documentation-architecture.component';
 | 
					import { DocumentationArchitectureComponent } from
 | 
				
			||||||
 | 
					    './documentation/documentation-architecture/documentation-architecture.component';
 | 
				
			||||||
import { MaterialsComponent } from './materials/materials.component';
 | 
					import { MaterialsComponent } from './materials/materials.component';
 | 
				
			||||||
import { MaterialComponent } from './material/material.component';
 | 
					import { MaterialComponent } from './material/material.component';
 | 
				
			||||||
import { DocumentationModelsComponent } from './documentation/documentation-models/documentation-models.component';
 | 
					import { DocumentationModelsComponent } from './documentation/documentation-models/documentation-models.component';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
<div class="header">
 | 
					<div class="header">
 | 
				
			||||||
  <rb-form-date-input name="dateInput" label="older than" [options]="{enableTime: true}"
 | 
					  <rb-form-date-input name="dateInput" label="older than" [options]="{enableTime: true}"
 | 
				
			||||||
                      [(ngModel)]="timestamp" (ngModelChange)="loadChangelog()">
 | 
					                      [(ngModel)]="timestamp" (ngModelChange)="loadChangelog()">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,51 +1,5 @@
 | 
				
			|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// import { ChangelogComponent } from './changelog.component';
 | 
					describe('ChangelogComponent', () => {
 | 
				
			||||||
// import {ApiService} from '../services/api.service';
 | 
					
 | 
				
			||||||
// import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					});
 | 
				
			||||||
// import {FormsModule} from '@angular/forms';
 | 
					 | 
				
			||||||
// import {ValidationService} from '../services/validation.service';
 | 
					 | 
				
			||||||
// import {DataService} from '../services/data.service';
 | 
					 | 
				
			||||||
// import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// // TODO
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
// let modalServiceSpy: jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// describe('ChangelogComponent', () => {
 | 
					 | 
				
			||||||
//   let component: ChangelogComponent;
 | 
					 | 
				
			||||||
//   let fixture: ComponentFixture<ChangelogComponent>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(async(() => {
 | 
					 | 
				
			||||||
//     const apiSpy = jasmine.createSpyObj('ApiService', ['get']);
 | 
					 | 
				
			||||||
//     const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
//       declarations: [ ChangelogComponent ],
 | 
					 | 
				
			||||||
//       imports: [
 | 
					 | 
				
			||||||
//         RbUiComponentsModule,
 | 
					 | 
				
			||||||
//         RbCustomInputsModule
 | 
					 | 
				
			||||||
//       ],
 | 
					 | 
				
			||||||
//       providers: [
 | 
					 | 
				
			||||||
//         {provide: ApiService, useValue: apiSpy},
 | 
					 | 
				
			||||||
//         {provide: ModalService, useValue: modalSpy}
 | 
					 | 
				
			||||||
//       ]
 | 
					 | 
				
			||||||
//     })
 | 
					 | 
				
			||||||
//     .compileComponents();
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
//     modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
//   }));
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(() => {
 | 
					 | 
				
			||||||
//     fixture = TestBed.createComponent(ChangelogComponent);
 | 
					 | 
				
			||||||
//     component = fixture.componentInstance;
 | 
					 | 
				
			||||||
//     component.ngOnInit();
 | 
					 | 
				
			||||||
//     fixture.detectChanges();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   it('should create', () => {
 | 
					 | 
				
			||||||
//     expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
// });
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,10 +10,10 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class ChangelogComponent implements OnInit {
 | 
					export class ChangelogComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  timestamp = new Date();
 | 
					  timestamp = new Date();  // time from date input
 | 
				
			||||||
  pageSize = 25;
 | 
					  pageSize = 25;
 | 
				
			||||||
  changelog: ChangelogModel[] = [];
 | 
					  changelog: ChangelogModel[] = [];
 | 
				
			||||||
  modalDetail = 0;
 | 
					  modalDetail = 0;        // index of changelog element to show details of
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private api: ApiService,
 | 
					    private api: ApiService,
 | 
				
			||||||
@@ -24,7 +24,7 @@ export class ChangelogComponent implements OnInit {
 | 
				
			|||||||
    this.loadChangelog();
 | 
					    this.loadChangelog();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  loadChangelog(page = 0) {
 | 
					  loadChangelog(page = 0) {  // load changelog with page no relative to current page
 | 
				
			||||||
    this.api.get<ChangelogModel[]>(`/changelog/${
 | 
					    this.api.get<ChangelogModel[]>(`/changelog/${
 | 
				
			||||||
      page > 0 ? this.changelog[0]._id :  // use id if no new date was given
 | 
					      page > 0 ? this.changelog[0]._id :  // use id if no new date was given
 | 
				
			||||||
      Math.floor(new Date(
 | 
					      Math.floor(new Date(
 | 
				
			||||||
@@ -32,12 +32,13 @@ export class ChangelogComponent implements OnInit {
 | 
				
			|||||||
      ).getTime() / 1000).toString(16) + '0000000000000000'  // id from time
 | 
					      ).getTime() / 1000).toString(16) + '0000000000000000'  // id from time
 | 
				
			||||||
    }/${page}/${this.pageSize}`, data => {
 | 
					    }/${page}/${this.pageSize}`, data => {
 | 
				
			||||||
      this.changelog = data.map(e => new ChangelogModel().deserialize(e));
 | 
					      this.changelog = data.map(e => new ChangelogModel().deserialize(e));
 | 
				
			||||||
      if (page) {
 | 
					      if (page) {  // adjust date picker to new first element when user clicked on next page
 | 
				
			||||||
        this.timestamp = new Date(this.changelog[0].date);
 | 
					        this.timestamp = new Date(this.changelog[0].date);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // show details of a changelog element with reference to needed modal
 | 
				
			||||||
  showDetails(i: number, modal: TemplateRef<any>) {
 | 
					  showDetails(i: number, modal: TemplateRef<any>) {
 | 
				
			||||||
    this.modalDetail = i;
 | 
					    this.modalDetail = i;
 | 
				
			||||||
    this.modal.open(modal).then(() => {});
 | 
					    this.modal.open(modal).then(() => {});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentationArchitectureComponent } from './documentation-architecture.component';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('DocumentationArchitectureComponent', () => {
 | 
					describe('DocumentationArchitectureComponent', () => {
 | 
				
			||||||
  let component: DocumentationArchitectureComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<DocumentationArchitectureComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ DocumentationArchitectureComponent ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentationArchitectureComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
<p>
 | 
					<p>
 | 
				
			||||||
  The used database instance is a MongoDB instance running on the BIC, storing all application data. The admin database
 | 
					  The used database instance is a MongoDB instance running on the BIC, storing all application data. The admin database
 | 
				
			||||||
  management page can be accessed
 | 
					  management page can be accessed
 | 
				
			||||||
@@ -387,4 +386,26 @@ Every time:
 | 
				
			|||||||
    <td>The user that executed this command</td>
 | 
					    <td>The user that executed this command</td>
 | 
				
			||||||
    <td>'5f2e63118d1c020f8cda6a09'</td>
 | 
					    <td>'5f2e63118d1c020f8cda6a09'</td>
 | 
				
			||||||
  </tr>
 | 
					  </tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <tr><th>help</th><th></th><th></th></tr>
 | 
				
			||||||
 | 
					  <tr>
 | 
				
			||||||
 | 
					    <td>_id</td>
 | 
				
			||||||
 | 
					    <td>Automatically generated unique id</td>
 | 
				
			||||||
 | 
					    <td>'5f2e63d28d1c020f8cda6f86'</td>
 | 
				
			||||||
 | 
					  </tr>
 | 
				
			||||||
 | 
					  <tr>
 | 
				
			||||||
 | 
					    <td>key</td>
 | 
				
			||||||
 | 
					    <td>The key used to find the required help text</td>
 | 
				
			||||||
 | 
					    <td>'/documentation/database'</td>
 | 
				
			||||||
 | 
					  </tr>
 | 
				
			||||||
 | 
					  <tr>
 | 
				
			||||||
 | 
					    <td>level</td>
 | 
				
			||||||
 | 
					    <td>The minimum level required to read this help</td>
 | 
				
			||||||
 | 
					    <td>'write'</td>
 | 
				
			||||||
 | 
					  </tr>
 | 
				
			||||||
 | 
					  <tr>
 | 
				
			||||||
 | 
					    <td>text</td>
 | 
				
			||||||
 | 
					    <td>The actual help text</td>
 | 
				
			||||||
 | 
					    <td>'This page documents the database.'</td>
 | 
				
			||||||
 | 
					  </tr>
 | 
				
			||||||
</rb-table>
 | 
					</rb-table>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,41 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentationDatabaseComponent } from './documentation-database.component';
 | 
					 | 
				
			||||||
import {Component, Input} from '@angular/core';
 | 
					 | 
				
			||||||
import {RbCustomInputsModule} from '../../rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Component({selector: 'app-img-magnifier', template: ''})
 | 
					 | 
				
			||||||
class ImgMagnifierStubComponent {
 | 
					 | 
				
			||||||
  @Input() magnifierSize: {width: number, height: number};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('DocumentationDatabaseComponent', () => {
 | 
					describe('DocumentationDatabaseComponent', () => {
 | 
				
			||||||
  let component: DocumentationDatabaseComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<DocumentationDatabaseComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [
 | 
					 | 
				
			||||||
        DocumentationDatabaseComponent,
 | 
					 | 
				
			||||||
        ImgMagnifierStubComponent
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      imports: [
 | 
					 | 
				
			||||||
        RbCustomInputsModule
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentationDatabaseComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentationModelsComponent } from './documentation-models.component';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('DocumentationModelsComponent', () => {
 | 
					describe('DocumentationModelsComponent', () => {
 | 
				
			||||||
  let component: DocumentationModelsComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<DocumentationModelsComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ DocumentationModelsComponent ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentationModelsComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
<p>
 | 
					<p>
 | 
				
			||||||
  <a [href]="api.hostName + '/static/intro-presentation/index.html'">
 | 
					  <a [href]="api.hostName + '/static/intro-presentation/index.html'">
 | 
				
			||||||
    View the presentation explaining the main functions
 | 
					    View the presentation explaining the main functions
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,42 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DocumentationComponent } from './documentation.component';
 | 
					 | 
				
			||||||
import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
import {ApiService} from '../services/api.service';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('DocumentationComponent', () => {
 | 
					describe('DocumentationComponent', () => {
 | 
				
			||||||
  let component: DocumentationComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<DocumentationComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ DocumentationComponent ],
 | 
					 | 
				
			||||||
      imports: [
 | 
					 | 
				
			||||||
        RbCustomInputsModule
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        {provide: ApiService, useValue: apiSpy}
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(DocumentationComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,48 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ErrorComponent } from './error.component';
 | 
					 | 
				
			||||||
import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
import {By} from '@angular/platform-browser';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('ErrorComponent', () => {
 | 
					describe('ErrorComponent', () => {
 | 
				
			||||||
  let component: ErrorComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<ErrorComponent>;
 | 
					 | 
				
			||||||
  let css;   // get native element by css selector
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ ErrorComponent ],
 | 
					 | 
				
			||||||
      imports: [
 | 
					 | 
				
			||||||
        RbUiComponentsModule,
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        ModalService
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(ErrorComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
    css = (selector) => fixture.debugElement.query(By.css(selector)).nativeElement;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should show the alert', () => {
 | 
					 | 
				
			||||||
    expect(css('rb-alert')).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should have the right message', () => {
 | 
					 | 
				
			||||||
    component.message = 'test';
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
    expect(css('.dialog-text').innerText).toBe('test');
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,8 +7,8 @@ import { Component, OnInit } from '@angular/core';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class ErrorComponent implements OnInit {
 | 
					export class ErrorComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  message = '';
 | 
					  message = '';            // main error message
 | 
				
			||||||
  details: string[] = [];
 | 
					  details: string[] = [];  // array of error detail paragraphs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor() { }
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
import { ExistsPipe } from './exists.pipe';
 | 
					import { ExistsPipe } from './exists.pipe';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ExistsPipe', () => {
 | 
					describe('ExistsPipe', () => {
 | 
				
			||||||
  it('create an instance', () => {
 | 
					  it('create an instance', () => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ import { Pipe, PipeTransform } from '@angular/core';
 | 
				
			|||||||
export class ExistsPipe implements PipeTransform {
 | 
					export class ExistsPipe implements PipeTransform {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  transform(value: unknown, key?): unknown {
 | 
					  transform(value: unknown, key?): unknown {
 | 
				
			||||||
    // console.log(new Date().getTime());
 | 
					 | 
				
			||||||
    return value || value === 0 ? (key ? value[key] : value) : '';
 | 
					    return value || value === 0 ? (key ? value[key] : value) : '';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,25 @@
 | 
				
			|||||||
<h3>Help</h3>
 | 
					<h3>Help
 | 
				
			||||||
 | 
					  <span *ngIf="login.isLevel.dev" class="rb-ic rb-ic-edit clickable space-left" (click)="edit = true"></span>
 | 
				
			||||||
 | 
					</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<ng-container [ngSwitch]="route">
 | 
					<div *ngIf="edit; else normalView">
 | 
				
			||||||
 <p *ngSwitchCase="'/'">Please log in for further access. If you do not have an account yet, please contact
 | 
					  <rb-form-select label="level" [(ngModel)]="content.level">
 | 
				
			||||||
   <a [href]="'mailto:' + d.contact.mail">{{d.contact.name}}</a>.
 | 
					    <option value="none">none</option>
 | 
				
			||||||
 | 
					    <option *ngFor="let level of login.levels" [value]="level">{{level}}</option>
 | 
				
			||||||
 | 
					  </rb-form-select>
 | 
				
			||||||
 | 
					  <rb-form-textarea label="text" [(ngModel)]="content.text"></rb-form-textarea>
 | 
				
			||||||
 | 
					  <rb-icon-button icon="save" mode="primary" (click)="saveHelp()">Save</rb-icon-button>
 | 
				
			||||||
 | 
					  <rb-icon-button icon="delete" mode="danger" (click)="deleteHelp()" class="delete-btn">Delete</rb-icon-button>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<ng-template #normalView>
 | 
				
			||||||
 | 
					  <p *ngIf="content.text; else defaultContent" class="content-text">
 | 
				
			||||||
 | 
					    {{content.text}}
 | 
				
			||||||
  </p>
 | 
					  </p>
 | 
				
			||||||
 <p *ngSwitchCase="'/home'">Please log in for further access. If you do not have an account yet, please contact
 | 
					  <ng-template #defaultContent>
 | 
				
			||||||
   <a [href]="'mailto:' + d.contact.mail">{{d.contact.name}}</a>.
 | 
					    <ng-container *ngIf="content.text === ''">
 | 
				
			||||||
 </p>
 | 
					 | 
				
			||||||
 <p *ngSwitchDefault>
 | 
					 | 
				
			||||||
      Sadly, currently there is no help available for this page. Please contact
 | 
					      Sadly, currently there is no help available for this page. Please contact
 | 
				
			||||||
      <a [href]="'mailto:' + d.contact.mail">{{d.contact.name}}</a> for further questions.
 | 
					      <a [href]="'mailto:' + d.contact.mail">{{d.contact.name}}</a> for further questions.
 | 
				
			||||||
 </p>
 | 
					    </ng-container>
 | 
				
			||||||
</ng-container>
 | 
					  </ng-template>
 | 
				
			||||||
 | 
					</ng-template>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					.delete-btn {
 | 
				
			||||||
 | 
					  float: right;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.content-text {
 | 
				
			||||||
 | 
					  white-space: pre-line;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { HelpComponent } from './help.component';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('HelpComponent', () => {
 | 
					describe('HelpComponent', () => {
 | 
				
			||||||
  let component: HelpComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<HelpComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ HelpComponent ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(HelpComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,9 @@
 | 
				
			|||||||
import { Component, OnInit } from '@angular/core';
 | 
					import { Component, OnInit } from '@angular/core';
 | 
				
			||||||
import {Router} from '@angular/router';
 | 
					import {Router} from '@angular/router';
 | 
				
			||||||
import {DataService} from '../services/data.service';
 | 
					import {DataService} from '../services/data.service';
 | 
				
			||||||
 | 
					import {ApiService} from '../services/api.service';
 | 
				
			||||||
 | 
					import {HelpModel} from '../models/help.model';
 | 
				
			||||||
 | 
					import {LoginService} from '../services/login.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-help',
 | 
					  selector: 'app-help',
 | 
				
			||||||
@@ -9,16 +12,42 @@ import {DataService} from '../services/data.service';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class HelpComponent implements OnInit {
 | 
					export class HelpComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  route = '';
 | 
					  content: HelpModel = new HelpModel().deserialize({text: null, level: 'none'});  // help content
 | 
				
			||||||
 | 
					  edit = false;        // set true to change to edit mode
 | 
				
			||||||
 | 
					  private route = '';  // URIComponent encoded route which serves as a key to fetch the help document
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private router: Router,
 | 
					    private router: Router,
 | 
				
			||||||
    public d: DataService
 | 
					    public d: DataService,
 | 
				
			||||||
 | 
					    private api: ApiService,
 | 
				
			||||||
 | 
					    public login: LoginService
 | 
				
			||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.route = this.router.url.replace(/\/[0-9a-f]{24}/, '');  // remove ids
 | 
					    // remove ids from path
 | 
				
			||||||
    console.log(this.route);
 | 
					    this.route = encodeURIComponent(this.router.url.replace(/\/[0-9a-f]{24}/, ''));
 | 
				
			||||||
 | 
					    this.api.get<HelpModel>('/help/' + this.route, (data, err) => {
 | 
				
			||||||
 | 
					      if (!err) {  // content was found
 | 
				
			||||||
 | 
					        this.content = new HelpModel().deserialize(data);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        this.content.text = '';
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  saveHelp() {
 | 
				
			||||||
 | 
					    this.api.post('/help/' + this.route, this.content.sendFormat(), () => {
 | 
				
			||||||
 | 
					      this.edit = false;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deleteHelp() {
 | 
				
			||||||
 | 
					    this.api.delete('/help/' + this.route, (ignore, err) => {
 | 
				
			||||||
 | 
					      if (!err) {
 | 
				
			||||||
 | 
					        this.content = new HelpModel().deserialize({text: null, level: 'none'});
 | 
				
			||||||
 | 
					        this.edit = false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,39 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
import { HomeComponent } from './home.component';
 | 
					 | 
				
			||||||
import {Component} from '@angular/core';
 | 
					 | 
				
			||||||
import {By} from '@angular/platform-browser';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Component({selector: 'app-login', template: ''})
 | 
					 | 
				
			||||||
class LoginStubComponent {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('HomeComponent', () => {
 | 
					describe('HomeComponent', () => {
 | 
				
			||||||
  let component: HomeComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<HomeComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [
 | 
					 | 
				
			||||||
        HomeComponent,
 | 
					 | 
				
			||||||
        LoginStubComponent
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(HomeComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should load the login component', () => {
 | 
					 | 
				
			||||||
    expect(fixture.debugElement.query(By.css('app-login'))).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,39 +1,5 @@
 | 
				
			|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// import { ImgMagnifierComponent } from './img-magnifier.component';
 | 
					describe('ImgMagnifierComponent', () => {
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// // TODO
 | 
					});
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// let windowServiceSpy: jasmine.SpyObj<Window>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// describe('ImgMagnifierComponent', () => {
 | 
					 | 
				
			||||||
//   let component: ImgMagnifierComponent;
 | 
					 | 
				
			||||||
//   let fixture: ComponentFixture<ImgMagnifierComponent>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(async(() => {
 | 
					 | 
				
			||||||
//     const windowSpy = jasmine.createSpyObj('Window', ['pageXOffset', 'pageYOffset']);
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
//       declarations: [ ImgMagnifierComponent ],
 | 
					 | 
				
			||||||
//       imports: [
 | 
					 | 
				
			||||||
//       ],
 | 
					 | 
				
			||||||
//       providers: [
 | 
					 | 
				
			||||||
//         {provide: Window, useValue: windowSpy}
 | 
					 | 
				
			||||||
//       ]
 | 
					 | 
				
			||||||
//     })
 | 
					 | 
				
			||||||
//     .compileComponents();
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
 | 
					 | 
				
			||||||
//   }));
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(() => {
 | 
					 | 
				
			||||||
//     fixture = TestBed.createComponent(ImgMagnifierComponent);
 | 
					 | 
				
			||||||
//     component = fixture.componentInstance;
 | 
					 | 
				
			||||||
//     component.ngOnInit();
 | 
					 | 
				
			||||||
//     fixture.detectChanges();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   it('should create', () => {
 | 
					 | 
				
			||||||
//     expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
// });
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,13 +7,13 @@ import {AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild} from '@a
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class ImgMagnifierComponent implements OnInit, AfterViewInit {
 | 
					export class ImgMagnifierComponent implements OnInit, AfterViewInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input() src: string;
 | 
					  @Input() src: string;   // image source
 | 
				
			||||||
  @Input() zoom: number;
 | 
					  @Input() zoom: number;  // zoom level
 | 
				
			||||||
  @Input() magnifierSize: {width: number, height: number};
 | 
					  @Input() magnifierSize: {width: number, height: number};  // size of the magnifier
 | 
				
			||||||
  @ViewChild('mainImg') mainImg: ElementRef;
 | 
					  @ViewChild('mainImg') mainImg: ElementRef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  backgroundSize;
 | 
					  backgroundSize;
 | 
				
			||||||
  magnifierPos = {x: 0, y: 0};
 | 
					  magnifierPos = {x: 0, y: 0};  // position of the magnifier
 | 
				
			||||||
  showMagnifier = false;
 | 
					  showMagnifier = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
@@ -29,7 +29,7 @@ export class ImgMagnifierComponent implements OnInit, AfterViewInit {
 | 
				
			|||||||
    }, 1);
 | 
					    }, 1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  calcPos(event) {
 | 
					  calcPos(event) {  // calculate the current magnifier position
 | 
				
			||||||
    const img = this.mainImg.nativeElement.getBoundingClientRect();
 | 
					    const img = this.mainImg.nativeElement.getBoundingClientRect();
 | 
				
			||||||
    this.magnifierPos.x = Math.min(
 | 
					    this.magnifierPos.x = Math.min(
 | 
				
			||||||
      img.width - this.magnifierSize.width,
 | 
					      img.width - this.magnifierSize.width,
 | 
				
			||||||
@@ -42,7 +42,7 @@ export class ImgMagnifierComponent implements OnInit, AfterViewInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  calcBackgroundSize() {
 | 
					  calcBackgroundSize() {
 | 
				
			||||||
    this.backgroundSize = this.mainImg ? (this.mainImg.nativeElement.width * this.zoom - this.magnifierSize.width) + 'px ' +
 | 
					    this.backgroundSize = this.mainImg ? (this.mainImg.nativeElement.width * this.zoom - this.magnifierSize.width) +
 | 
				
			||||||
      (this.mainImg.nativeElement.height * this.zoom - this.magnifierSize.height) + 'px ' : '0 0';
 | 
					      'px ' + (this.mainImg.nativeElement.height * this.zoom - this.magnifierSize.height) + 'px ' : '0 0';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,141 +1,5 @@
 | 
				
			|||||||
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
 | 
					import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
 | 
				
			||||||
import { LoginComponent } from './login.component';
 | 
					 | 
				
			||||||
import {LoginService} from '../services/login.service';
 | 
					 | 
				
			||||||
import {ValidationService} from '../services/validation.service';
 | 
					 | 
				
			||||||
import {FormsModule} from '@angular/forms';
 | 
					 | 
				
			||||||
import {By} from '@angular/platform-browser';
 | 
					 | 
				
			||||||
import {ValidateDirective} from '../validate.directive';
 | 
					 | 
				
			||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
import {ApiService} from '../services/api.service';
 | 
					 | 
				
			||||||
import {RouterTestingModule} from '@angular/router/testing';
 | 
					 | 
				
			||||||
import {Router} from '@angular/router';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let validationServiceSpy: jasmine.SpyObj<ValidationService>;
 | 
					 | 
				
			||||||
let loginServiceSpy: jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
let routerServiceSpy: Router;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('LoginComponent', () => {
 | 
					describe('LoginComponent', () => {
 | 
				
			||||||
  let component: LoginComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<LoginComponent>;
 | 
					 | 
				
			||||||
  let css;   // get native element by css selector
 | 
					 | 
				
			||||||
  let cssd;  // get debug element by css selector
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    const validationSpy = jasmine.createSpyObj('ValidationService', ['username', 'password']);
 | 
					 | 
				
			||||||
    const loginSpy = jasmine.createSpyObj('LoginService', ['login']);
 | 
					 | 
				
			||||||
    const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ LoginComponent, ValidateDirective ],
 | 
					 | 
				
			||||||
      imports: [
 | 
					 | 
				
			||||||
        RbUiComponentsModule,
 | 
					 | 
				
			||||||
        FormsModule,
 | 
					 | 
				
			||||||
        RouterTestingModule
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        {provide: ValidationService, useValue: validationSpy},
 | 
					 | 
				
			||||||
        {provide: LoginService, useValue: loginSpy},
 | 
					 | 
				
			||||||
        {provide: ApiService, useValue: apiSpy}
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
 | 
					 | 
				
			||||||
    loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
    apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
    routerServiceSpy = TestBed.inject(Router);
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(LoginComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
    cssd = (selector) => fixture.debugElement.query(By.css(selector));
 | 
					 | 
				
			||||||
    css = (selector) => fixture.debugElement.query(By.css(selector)).nativeElement;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    validationServiceSpy.username.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
    validationServiceSpy.password.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // it('should have the `Please log in` heading', () => {
 | 
					 | 
				
			||||||
  //   expect(css('h2').innerText).toBe('Please log in');
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should have empty credential inputs', () => {
 | 
					 | 
				
			||||||
  //   expect(css('rb-form-input[label=username] input').value).toBe('');
 | 
					 | 
				
			||||||
  //   expect(css('rb-form-input[label=password] input').value).toBe('');
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should have an empty message at the beginning', () => {
 | 
					 | 
				
			||||||
  //   expect(css('.message').innerText).toBe('');
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should have a login button', async () => {
 | 
					 | 
				
			||||||
  //   validationServiceSpy.username.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
  //   validationServiceSpy.password.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
  //   await fixture.whenStable();
 | 
					 | 
				
			||||||
  //   fixture.detectChanges();
 | 
					 | 
				
			||||||
  //   expect(css('.login-button')).toBeTruthy();
 | 
					 | 
				
			||||||
  //   expect(css('.login-button').disabled).toBeTruthy();
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should reject a wrong username', async () => {
 | 
					 | 
				
			||||||
  //   validationServiceSpy.username.and.returnValue({ok: false, error: 'username must only contain a-z0-9-_.'});
 | 
					 | 
				
			||||||
  //   component.username = 'ab#';
 | 
					 | 
				
			||||||
  //   fixture.detectChanges();
 | 
					 | 
				
			||||||
  //   await fixture.whenRenderingDone();
 | 
					 | 
				
			||||||
  //   expect(component.loginForm.controls.username.valid).toBeFalsy();
 | 
					 | 
				
			||||||
  //   expect(validationServiceSpy.username).toHaveBeenCalledWith('ab#');
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should reject a wrong password', async () => {
 | 
					 | 
				
			||||||
  //   validationServiceSpy.password.and.returnValue({ok: false, error: 'password must only contain a-zA-Z0-9!"#%&\'()*+,-./:;<=>?@[]^_`{|}~'});
 | 
					 | 
				
			||||||
  //   component.password = 'abc';
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   fixture.detectChanges();
 | 
					 | 
				
			||||||
  //   await fixture.whenRenderingDone();
 | 
					 | 
				
			||||||
  //   expect(component.loginForm.controls.password.valid).toBeFalsy();
 | 
					 | 
				
			||||||
  //   expect(validationServiceSpy.password).toHaveBeenCalledWith('abc');
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should enable the login button with valid credentials', async () => {
 | 
					 | 
				
			||||||
  //   validationServiceSpy.username.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
  //   validationServiceSpy.password.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
  //   loginServiceSpy.login.and.returnValue(new Promise(r => r(true)));
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   fixture.detectChanges();
 | 
					 | 
				
			||||||
  //   await fixture.whenRenderingDone();
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   cssd('.login-button').triggerEventHandler('click', null);
 | 
					 | 
				
			||||||
  //   expect(css('.login-button').disabled).toBeFalsy();
 | 
					 | 
				
			||||||
  //   expect(loginServiceSpy.login.calls.count()).toBe(1);
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should call the LoginService with valid credentials', () => {
 | 
					 | 
				
			||||||
  //   validationServiceSpy.username.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
  //   validationServiceSpy.password.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
  //   loginServiceSpy.login.and.returnValue(new Promise(r => r(true)));
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   cssd('.login-button').triggerEventHandler('click', null);
 | 
					 | 
				
			||||||
  //   expect(loginServiceSpy.login.calls.count()).toBe(1);
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should display an error if the LoginService could not authenticate', fakeAsync(() => {
 | 
					 | 
				
			||||||
  //   validationServiceSpy.username.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
  //   validationServiceSpy.password.and.returnValue({ok: true, error: ''});
 | 
					 | 
				
			||||||
  //   loginServiceSpy.login.and.returnValue(new Promise(r => r(false)));
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   cssd('.login-button').triggerEventHandler('click', null);
 | 
					 | 
				
			||||||
  //   expect(loginServiceSpy.login.calls.count()).toBe(1);
 | 
					 | 
				
			||||||
  //   tick();
 | 
					 | 
				
			||||||
  //   fixture.detectChanges();
 | 
					 | 
				
			||||||
  //   expect(css('.message').innerText).toBe('Wrong credentials!');
 | 
					 | 
				
			||||||
  // }));
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ export class LoginComponent implements OnInit {
 | 
				
			|||||||
  password = '';
 | 
					  password = '';
 | 
				
			||||||
  email = '';
 | 
					  email = '';
 | 
				
			||||||
  message = '';  // message below login fields
 | 
					  message = '';  // message below login fields
 | 
				
			||||||
  passreset = false;
 | 
					  passreset = false;  // to toggle between normal login and password reset form
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ViewChild('loginForm') loginForm;
 | 
					  @ViewChild('loginForm') loginForm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,7 +32,7 @@ export class LoginComponent implements OnInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  userLogin() {
 | 
					  userLogin() {
 | 
				
			||||||
    if (this.passreset) {
 | 
					    if (this.passreset) {  // reset password
 | 
				
			||||||
      this.api.post('/user/passreset', {name: this.username, email: this.email}, (data, err) => {
 | 
					      this.api.post('/user/passreset', {name: this.username, email: this.email}, (data, err) => {
 | 
				
			||||||
        if (err) {
 | 
					        if (err) {
 | 
				
			||||||
          this.message = 'Could not find a valid user';
 | 
					          this.message = 'Could not find a valid user';
 | 
				
			||||||
@@ -49,7 +49,7 @@ export class LoginComponent implements OnInit {
 | 
				
			|||||||
          if (this.login.isLevel.read) {
 | 
					          if (this.login.isLevel.read) {
 | 
				
			||||||
            this.router.navigate(['/samples']);
 | 
					            this.router.navigate(['/samples']);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          else {
 | 
					          else {  // navigate prediction users to prediction as they cannot access samples
 | 
				
			||||||
            this.router.navigate(['/prediction']);
 | 
					            this.router.navigate(['/prediction']);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { MaterialComponent } from './material.component';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('MaterialComponent', () => {
 | 
					describe('MaterialComponent', () => {
 | 
				
			||||||
  let component: MaterialComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<MaterialComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ MaterialComponent ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(MaterialComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,12 +19,12 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @ViewChild('materialForm') materialForm: NgForm;
 | 
					  @ViewChild('materialForm') materialForm: NgForm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  material: MaterialModel;
 | 
					  material: MaterialModel;       // material to edit
 | 
				
			||||||
  materialNames: string[] = [];
 | 
					  materialNames: string[] = [];  // all other material names for unique validation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  modalText = {list: '', suggestion: ''};
 | 
					  modalText = {list: '', suggestion: ''};  // modal for group and supplier correction
 | 
				
			||||||
  loading = 0;
 | 
					  loading = 0;                             // number of loading instances
 | 
				
			||||||
  checkFormAfterInit = true;
 | 
					  checkFormAfterInit = true;               // revalidate all fields on the next AfterContentChecked
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private api: ApiService,
 | 
					    private api: ApiService,
 | 
				
			||||||
@@ -42,6 +42,7 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
      this.material = new MaterialModel().deserialize(data);
 | 
					      this.material = new MaterialModel().deserialize(data);
 | 
				
			||||||
      this.loading--;
 | 
					      this.loading--;
 | 
				
			||||||
      this.d.load('materials', () => {
 | 
					      this.d.load('materials', () => {
 | 
				
			||||||
 | 
					        // filter out name of the edited material as it can stay the same
 | 
				
			||||||
        this.materialNames = this.d.arr.materials.map(e => e.name).filter(e => e !== this.material.name);
 | 
					        this.materialNames = this.d.arr.materials.map(e => e.name).filter(e => e !== this.material.name);
 | 
				
			||||||
        this.loading--;
 | 
					        this.loading--;
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -58,12 +59,14 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngAfterContentChecked() {
 | 
					  ngAfterContentChecked() {
 | 
				
			||||||
 | 
					    // attach validators
 | 
				
			||||||
    if (this.materialForm && this.material.properties.material_template) {  // material template is set
 | 
					    if (this.materialForm && this.material.properties.material_template) {  // material template is set
 | 
				
			||||||
      this.d.id.materialTemplates[this.material.properties.material_template].parameters.forEach((parameter, i) => {
 | 
					      this.d.id.materialTemplates[this.material.properties.material_template].parameters.forEach((parameter, i) => {
 | 
				
			||||||
        this.attachValidator(this.materialForm, 'materialParameter' + i, parameter.range);
 | 
					        this.attachValidator(this.materialForm, 'materialParameter' + i, parameter.range);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // revalidate
 | 
				
			||||||
    if (this.checkFormAfterInit && this.materialForm !== undefined && this.materialForm.form.get('propertiesSelect')) {
 | 
					    if (this.checkFormAfterInit && this.materialForm !== undefined && this.materialForm.form.get('propertiesSelect')) {
 | 
				
			||||||
      this.checkFormAfterInit = false;
 | 
					      this.checkFormAfterInit = false;
 | 
				
			||||||
      Object.keys(this.materialForm.form.controls).forEach(field => {
 | 
					      Object.keys(this.materialForm.form.controls).forEach(field => {
 | 
				
			||||||
@@ -97,7 +100,7 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  materialSave() {
 | 
					  materialSave() {
 | 
				
			||||||
    this.api.put('/material/' + this.material._id, this.material.sendFormat(), () => {
 | 
					    this.api.put('/material/' + this.material._id, this.material.sendFormat(), () => {
 | 
				
			||||||
      delete this.d.arr.materials;
 | 
					      delete this.d.arr.materials;  // reload materials
 | 
				
			||||||
      this.d.load('materials');
 | 
					      this.d.load('materials');
 | 
				
			||||||
      this.router.navigate(['/materials']);
 | 
					      this.router.navigate(['/materials']);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -107,7 +110,7 @@ export class MaterialComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
    this.modal.open(modal).then(result => {
 | 
					    this.modal.open(modal).then(result => {
 | 
				
			||||||
      if (result) {
 | 
					      if (result) {
 | 
				
			||||||
        this.api.delete('/material/' + this.material._id, (ignore, error) => {
 | 
					        this.api.delete('/material/' + this.material._id, (ignore, error) => {
 | 
				
			||||||
          if (error) {
 | 
					          if (error) {  // material cannot be deleted as it is still referenced by active samples
 | 
				
			||||||
            const modalRef = this.modal.openComponent(ErrorComponent);
 | 
					            const modalRef = this.modal.openComponent(ErrorComponent);
 | 
				
			||||||
            modalRef.instance.message = 'Cannot delete material as it is still in use!';
 | 
					            modalRef.instance.message = 'Cannot delete material as it is still in use!';
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { MaterialsComponent } from './materials.component';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('MaterialsComponent', () => {
 | 
					describe('MaterialsComponent', () => {
 | 
				
			||||||
  let component: MaterialsComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<MaterialsComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ MaterialsComponent ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(MaterialsComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,13 +12,13 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class MaterialsComponent implements OnInit {
 | 
					export class MaterialsComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  materials: MaterialModel[] = [];
 | 
					  materials: MaterialModel[] = [];                    // all materials
 | 
				
			||||||
  templateKeys: {key: string, label: string}[] = [];
 | 
					  templateKeys: {key: string, label: string}[] = [];  // material template keys
 | 
				
			||||||
  materialStatus = {validated: true, new: true, deleted: false};
 | 
					  materialStatus = {validated: true, new: true, deleted: false};  // material statuses to show
 | 
				
			||||||
  materialSearch = '';
 | 
					  materialSearch = '';   // material name search string
 | 
				
			||||||
  sampleSelect = false;
 | 
					  sampleSelect = false;  // set to true to show checkboxes for validation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  page = 1;
 | 
					  page = 1;  // page settings
 | 
				
			||||||
  pages = 0;
 | 
					  pages = 0;
 | 
				
			||||||
  pageSize = 25;
 | 
					  pageSize = 25;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,8 +36,8 @@ export class MaterialsComponent implements OnInit {
 | 
				
			|||||||
          this.templateKeys.push({key: parameter.name, label: `${this.ucFirst(template.name)} ${parameter.name}`});
 | 
					          this.templateKeys.push({key: parameter.name, label: `${this.ucFirst(template.name)} ${parameter.name}`});
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					      // filter out duplicates
 | 
				
			||||||
      this.templateKeys = this.templateKeys.filter((e, i, a) => !a.slice(0, i).find(el => el.key === e.key));
 | 
					      this.templateKeys = this.templateKeys.filter((e, i, a) => !a.slice(0, i).find(el => el.key === e.key));
 | 
				
			||||||
      console.log(this.templateKeys);
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,7 +51,7 @@ export class MaterialsComponent implements OnInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  validate() {
 | 
					  validate() {
 | 
				
			||||||
    if (this.sampleSelect) {
 | 
					    if (this.sampleSelect) {  // selection was done do actual validation
 | 
				
			||||||
      this.materials.forEach(sample => {
 | 
					      this.materials.forEach(sample => {
 | 
				
			||||||
        if (sample.selected) {
 | 
					        if (sample.selected) {
 | 
				
			||||||
          this.api.put('/material/validate/' + sample._id);
 | 
					          this.api.put('/material/validate/' + sample._id);
 | 
				
			||||||
@@ -60,12 +60,12 @@ export class MaterialsComponent implements OnInit {
 | 
				
			|||||||
      this.loadMaterials();
 | 
					      this.loadMaterials();
 | 
				
			||||||
      this.sampleSelect = false;
 | 
					      this.sampleSelect = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {  // activate validation mode
 | 
				
			||||||
      this.sampleSelect = true;
 | 
					      this.sampleSelect = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  selectAll(event) {
 | 
					  selectAll(event) {  // toggle selection for all items except deleted ones
 | 
				
			||||||
    this.materials.forEach(material => {
 | 
					    this.materials.forEach(material => {
 | 
				
			||||||
      if (material.status !== 'deleted') {
 | 
					      if (material.status !== 'deleted') {
 | 
				
			||||||
        material.selected = event.target.checked;
 | 
					        material.selected = event.target.checked;
 | 
				
			||||||
@@ -86,11 +86,11 @@ export class MaterialsComponent implements OnInit {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ucFirst(string) {
 | 
					  ucFirst(string) {  // convert first character of string to uppercase
 | 
				
			||||||
    return string[0].toUpperCase() + string.slice(1);
 | 
					    return string[0].toUpperCase() + string.slice(1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  materialFilter(ms) {
 | 
					  materialFilter(ms) {  // filter function for material names
 | 
				
			||||||
    return e => e.name.indexOf(ms) >= 0;
 | 
					    return e => e.name.indexOf(ms) >= 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
<rb-icon-button icon="add" mode="primary" (click)="newModel = !newModel; oldModelGroup = ''" class="space-below">
 | 
					<rb-icon-button icon="add" mode="primary" (click)="newModel = !newModel; oldModelGroup = ''" class="space-below">
 | 
				
			||||||
  New model
 | 
					  New model
 | 
				
			||||||
</rb-icon-button>
 | 
					</rb-icon-button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ModelTemplatesComponent } from './model-templates.component';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('ModelTemplatesComponent', () => {
 | 
					describe('ModelTemplatesComponent', () => {
 | 
				
			||||||
  let component: ModelTemplatesComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<ModelTemplatesComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ ModelTemplatesComponent ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(ModelTemplatesComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,12 +13,12 @@ import omit from 'lodash/omit';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class ModelTemplatesComponent implements OnInit {
 | 
					export class ModelTemplatesComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  newModel = false;
 | 
					  newModel = false;    // display new model dialog
 | 
				
			||||||
  modelGroup = '';
 | 
					  modelGroup = '';     // group of the edited model
 | 
				
			||||||
  oldModelGroup = '';
 | 
					  oldModelGroup = '';  // group of the edited model before editing started
 | 
				
			||||||
  oldModelName = '';
 | 
					  oldModelName = '';   // name of the edited model before editing started
 | 
				
			||||||
  model = new ModelItemModel().models[0];
 | 
					  model = new ModelItemModel().models[0];  // edited model
 | 
				
			||||||
  groups = [];
 | 
					  groups = [];         // all model group names
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private api: ApiService,
 | 
					    private api: ApiService,
 | 
				
			||||||
@@ -40,13 +40,12 @@ export class ModelTemplatesComponent implements OnInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  saveModel() {
 | 
					  saveModel() {
 | 
				
			||||||
    console.log(this.modelGroup);
 | 
					    // group was changed, delete model in old group
 | 
				
			||||||
    console.log(this.oldModelGroup);
 | 
					    if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) {
 | 
				
			||||||
    if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) {  // group was changed, delete model in old group
 | 
					 | 
				
			||||||
      this.delete(null, this.oldModelName, this.oldModelGroup);
 | 
					      this.delete(null, this.oldModelName, this.oldModelGroup);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.api.post('/model/' + this.modelGroup, omit(this.model, '_id'), () => {
 | 
					    this.api.post('/model/' + this.modelGroup, omit(this.model, '_id'), () => {
 | 
				
			||||||
      this.newModel = false;
 | 
					      this.newModel = false;  // reset model edit parameters
 | 
				
			||||||
      this.loadGroups();
 | 
					      this.loadGroups();
 | 
				
			||||||
      this.modelGroup = '';
 | 
					      this.modelGroup = '';
 | 
				
			||||||
      this.oldModelGroup = '';
 | 
					      this.oldModelGroup = '';
 | 
				
			||||||
@@ -57,7 +56,7 @@ export class ModelTemplatesComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  delete(modal, name, group = null) {
 | 
					  delete(modal, name, group = null) {
 | 
				
			||||||
    new Promise(resolve => {
 | 
					    new Promise(resolve => {
 | 
				
			||||||
      if (modal) {
 | 
					      if (modal) {  // if modal was given, wait for result
 | 
				
			||||||
        this.modal.open(modal).then(result => {
 | 
					        this.modal.open(modal).then(result => {
 | 
				
			||||||
          resolve(result);
 | 
					          resolve(result);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -67,12 +66,12 @@ export class ModelTemplatesComponent implements OnInit {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }).then(res => {
 | 
					    }).then(res => {
 | 
				
			||||||
      if (res) {
 | 
					      if (res) {
 | 
				
			||||||
        if (group) {  // delete group
 | 
					        if (group) {  // delete model group if given
 | 
				
			||||||
          this.api.delete(`/model/${group}/${name}`, () => {
 | 
					          this.api.delete(`/model/${group}/${name}`, () => {
 | 
				
			||||||
            this.loadGroups();
 | 
					            this.loadGroups();
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {  // delete file
 | 
					        else {  // delete model file
 | 
				
			||||||
          this.api.delete(`/model/file/${name}`, () => {
 | 
					          this.api.delete(`/model/file/${name}`, () => {
 | 
				
			||||||
            this.d.arr.modelFiles.splice(this.d.arr.modelFiles.findIndex(e => e.name === name), 1);
 | 
					            this.d.arr.modelFiles.splice(this.d.arr.modelFiles.findIndex(e => e.name === name), 1);
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								src/app/models/help.model.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/app/models/help.model.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					import { HelpModel } from './help.model';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Help.Model', () => {
 | 
				
			||||||
 | 
					  it('should create an instance', () => {
 | 
				
			||||||
 | 
					    expect(new HelpModel()).toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										6
									
								
								src/app/models/help.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/app/models/help.model.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import {BaseModel} from './base.model';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class HelpModel extends BaseModel {
 | 
				
			||||||
 | 
					  text = '';
 | 
				
			||||||
 | 
					  level = 'none';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
import { ObjectPipe } from './object.pipe';
 | 
					import { ObjectPipe } from './object.pipe';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ObjectPipe', () => {
 | 
					describe('ObjectPipe', () => {
 | 
				
			||||||
  it('create an instance', () => {
 | 
					  it('create an instance', () => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
import { ParametersPipe } from './parameters.pipe';
 | 
					import { ParametersPipe } from './parameters.pipe';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ParametersPipe', () => {
 | 
					describe('ParametersPipe', () => {
 | 
				
			||||||
  it('create an instance', () => {
 | 
					  it('create an instance', () => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,8 @@ import { Pipe, PipeTransform } from '@angular/core';
 | 
				
			|||||||
export class ParametersPipe implements PipeTransform {
 | 
					export class ParametersPipe implements PipeTransform {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  transform(value: {name: string, range: object}[]): string {
 | 
					  transform(value: {name: string, range: object}[]): string {
 | 
				
			||||||
    return `{${value.map(e => `${e.name}: <${JSON.stringify(e.range).replace('{}', 'any').replace(/["{}]/g, '')}>`).join(', ')}}`;
 | 
					    return `{${value.map(e => `${e.name}: <${JSON.stringify(e.range).replace('{}', 'any')
 | 
				
			||||||
 | 
					      .replace(/["{}]/g, '')}>`).join(', ')}}`;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
<rb-tab-panel (tabChanged)="groupChange($event)">
 | 
					<rb-tab-panel (tabChanged)="groupChange($event)">
 | 
				
			||||||
  <ng-container *ngFor="let group of d.arr.modelGroups; index as i">
 | 
					  <ng-container *ngFor="let group of d.arr.modelGroups; index as i">
 | 
				
			||||||
    <div *rbTabPanelItem="group.group; id: i"></div>
 | 
					    <div *rbTabPanelItem="group.group; id: i"></div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,47 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { PredictionComponent } from './prediction.component';
 | 
					 | 
				
			||||||
import {ApiService} from '../services/api.service';
 | 
					 | 
				
			||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
import {FormsModule} from '@angular/forms';
 | 
					 | 
				
			||||||
import {ValidationService} from '../services/validation.service';
 | 
					 | 
				
			||||||
import {DataService} from '../services/data.service';
 | 
					 | 
				
			||||||
import {ChartsModule} from 'ng2-charts';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('PredictionComponent', () => {
 | 
					describe('PredictionComponent', () => {
 | 
				
			||||||
  let component: PredictionComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<PredictionComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    const apiSpy = jasmine.createSpyObj('ApiService', ['post']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ PredictionComponent ],
 | 
					 | 
				
			||||||
      imports: [
 | 
					 | 
				
			||||||
        RbUiComponentsModule,
 | 
					 | 
				
			||||||
        ChartsModule
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        {provide: ApiService, useValue: apiSpy}
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(PredictionComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,11 +29,12 @@ import {ModelItemModel} from '../models/model-item.model';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class PredictionComponent implements OnInit {
 | 
					export class PredictionComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  result: {predictions: string[], mean: string};
 | 
					  result: {predictions: string[], mean: string};  // prediction result from python container
 | 
				
			||||||
  loading = false;
 | 
					  loading = false;
 | 
				
			||||||
  activeGroup: ModelItemModel = new ModelItemModel();
 | 
					  activeGroup: ModelItemModel = new ModelItemModel();
 | 
				
			||||||
  activeModelIndex = 0;
 | 
					  activeModelIndex = 0;
 | 
				
			||||||
  multipleSamples = false;  // if true, spectra belong to different samples, otherwise multiple spectra from the same sample are given
 | 
					  // if true, spectra belong to different samples, otherwise multiple spectra from the same sample are given
 | 
				
			||||||
 | 
					  multipleSamples = false;
 | 
				
			||||||
  spectrumNames: string[] = [];
 | 
					  spectrumNames: string[] = [];
 | 
				
			||||||
  spectrum: string[][] = [[]];
 | 
					  spectrum: string[][] = [[]];
 | 
				
			||||||
  flattenedSpectra = [];
 | 
					  flattenedSpectra = [];
 | 
				
			||||||
@@ -50,7 +51,7 @@ export class PredictionComponent implements OnInit {
 | 
				
			|||||||
  readonly chartOptions: ChartOptions = {
 | 
					  readonly chartOptions: ChartOptions = {
 | 
				
			||||||
    scales: {
 | 
					    scales: {
 | 
				
			||||||
      xAxes: [{ticks: {min: 400, max: 4000, stepSize: 400, reverse: true}}],
 | 
					      xAxes: [{ticks: {min: 400, max: 4000, stepSize: 400, reverse: true}}],
 | 
				
			||||||
      yAxes: [{ticks: {min: 0, max: 1}}]
 | 
					      yAxes: [{ticks: {}}]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    responsive: true,
 | 
					    responsive: true,
 | 
				
			||||||
    tooltips: {enabled: false},
 | 
					    tooltips: {enabled: false},
 | 
				
			||||||
@@ -82,13 +83,16 @@ export class PredictionComponent implements OnInit {
 | 
				
			|||||||
      if (files.hasOwnProperty(i)) {
 | 
					      if (files.hasOwnProperty(i)) {
 | 
				
			||||||
        const fileReader = new FileReader();
 | 
					        const fileReader = new FileReader();
 | 
				
			||||||
        fileReader.onload = () => {
 | 
					        fileReader.onload = () => {
 | 
				
			||||||
 | 
					          // parse to database spectrum representation
 | 
				
			||||||
          this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el)))
 | 
					          this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el)))
 | 
				
			||||||
            .filter(el => el.length === 2) as any;
 | 
					            .filter(el => el.length === 2) as any;
 | 
				
			||||||
 | 
					          // flatten to format needed for prediction
 | 
				
			||||||
          this.flattenedSpectra[i] = {labels: this.spectrum.map(e => e[0]), values: this.spectrum.map(e => e[1])};
 | 
					          this.flattenedSpectra[i] = {labels: this.spectrum.map(e => e[0]), values: this.spectrum.map(e => e[1])};
 | 
				
			||||||
 | 
					          // add to chart
 | 
				
			||||||
          this.chart[i] = cloneDeep(this.chartInit);
 | 
					          this.chart[i] = cloneDeep(this.chartInit);
 | 
				
			||||||
          this.chart[i].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])}));
 | 
					          this.chart[i].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])}));
 | 
				
			||||||
          load --;
 | 
					          load --;
 | 
				
			||||||
          if (load <= 0) {
 | 
					          if (load <= 0) {  // all loaded
 | 
				
			||||||
            this.loadPrediction();
 | 
					            this.loadPrediction();
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@@ -99,24 +103,21 @@ export class PredictionComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  loadPrediction() {
 | 
					  loadPrediction() {
 | 
				
			||||||
    this.loading = true;
 | 
					    this.loading = true;
 | 
				
			||||||
    console.log(this.activeGroup);
 | 
					 | 
				
			||||||
    console.log(this.activeModelIndex);
 | 
					 | 
				
			||||||
    this.api.post<any>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
 | 
					    this.api.post<any>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
 | 
				
			||||||
      this.result = {
 | 
					      this.result = {  // parse result into prediction and mean string
 | 
				
			||||||
        predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
 | 
					        predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
 | 
				
			||||||
          .map((p: any) => p[1].map(e => `${p[0]}: ${e} ${data.label[p[0]]}`))
 | 
					          .map((p: any) => p[1].map(e => `${p[0]}: ${e} ${data.label[p[0]]}`))
 | 
				
			||||||
          .reduce((s, e) => s.map((el, i) => this.clip(el) + ', ' + e[i])),
 | 
					          .reduce((s, e) => s.map((el, i) => this.clip(el) + ', ' + e[i])),
 | 
				
			||||||
        mean: Object.keys(data.mean).map(e =>
 | 
					        mean: Object.keys(data.mean).map(e =>
 | 
				
			||||||
            this.clip(`${e}: ${data.mean[e]} ${data.label[e]}`) + (data.std[e] !== '' ? ` (standard deviation: ${data.std[e]})` : '')
 | 
					            this.clip(`${e}: ${data.mean[e]} ${data.label[e]}`) +
 | 
				
			||||||
 | 
					            (data.std[e] !== '' ? ` (standard deviation: ${data.std[e]})` : '')
 | 
				
			||||||
          ).join(', ')
 | 
					          ).join(', ')
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      console.log(this.result);
 | 
					 | 
				
			||||||
      this.loading = false;
 | 
					      this.loading = false;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  groupChange(index) {
 | 
					  groupChange(index) {  // group was changed
 | 
				
			||||||
    console.log(index);
 | 
					 | 
				
			||||||
    this.activeGroup = this.d.arr.modelGroups[index];
 | 
					    this.activeGroup = this.d.arr.modelGroups[index];
 | 
				
			||||||
    this.activeModelIndex = 0;
 | 
					    this.activeModelIndex = 0;
 | 
				
			||||||
    this.result = undefined;
 | 
					    this.result = undefined;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,5 @@
 | 
				
			|||||||
import { TestBed } from '@angular/core/testing';
 | 
					import { TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ArrayInputHelperService } from './array-input-helper.service';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TOdo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('ArrayInputHelperService', () => {
 | 
					describe('ArrayInputHelperService', () => {
 | 
				
			||||||
  let service: ArrayInputHelperService;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({});
 | 
					 | 
				
			||||||
    service = TestBed.inject(ArrayInputHelperService);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should be created', () => {
 | 
					 | 
				
			||||||
    expect(service).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ export class ArrayInputHelperService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  constructor() { }
 | 
					  constructor() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  values(id: string) {
 | 
					  values(id: string) {  // observable which returns new values as they come for subscribed id
 | 
				
			||||||
    return new Observable<{index: number, value: any}>(observer => {
 | 
					    return new Observable<{index: number, value: any}>(observer => {
 | 
				
			||||||
      this.com.subscribe(data => {
 | 
					      this.com.subscribe(data => {
 | 
				
			||||||
        if (data.id === id) {
 | 
					        if (data.id === id) {
 | 
				
			||||||
@@ -20,7 +20,7 @@ export class ArrayInputHelperService {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  newValue(id: string, index: number, value: any) {
 | 
					  newValue(id: string, index: number, value: any) {  // set new value
 | 
				
			||||||
    this.com.next({id, index, value});
 | 
					    this.com.next({id, index, value});
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,5 @@
 | 
				
			|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// import { RbArrayInputComponent } from './rb-array-input.component';
 | 
					describe('RbArrayInputComponent', () => {
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// describe('RbArrayInputComponent', () => {
 | 
					});
 | 
				
			||||||
//   let component: RbArrayInputComponent;
 | 
					 | 
				
			||||||
//   let fixture: ComponentFixture<RbArrayInputComponent>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(async(() => {
 | 
					 | 
				
			||||||
//     TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
//       declarations: [ RbArrayInputComponent ]
 | 
					 | 
				
			||||||
//     })
 | 
					 | 
				
			||||||
//     .compileComponents();
 | 
					 | 
				
			||||||
//   }));
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(() => {
 | 
					 | 
				
			||||||
//     fixture = TestBed.createComponent(RbArrayInputComponent);
 | 
					 | 
				
			||||||
//     component = fixture.componentInstance;
 | 
					 | 
				
			||||||
//     component.ngOnInit();
 | 
					 | 
				
			||||||
//     fixture.detectChanges();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   it('should create', () => {
 | 
					 | 
				
			||||||
//     expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
// });
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,7 +37,7 @@ export class RbArrayInputListenerDirective {
 | 
				
			|||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @HostListener('ngModelChange', ['$event'])
 | 
					  @HostListener('ngModelChange', ['$event'])
 | 
				
			||||||
  onChange(event) {
 | 
					  onChange(event) {  // emit new value
 | 
				
			||||||
    this.helperService.newValue(this.rbArrayInputListener, this.index, event);
 | 
					    this.helperService.newValue(this.rbArrayInputListener, this.index, event);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -52,7 +52,7 @@ export class RbArrayInputListenerDirective {
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class RbArrayInputComponent implements ControlValueAccessor, OnInit, AfterViewInit {
 | 
					export class RbArrayInputComponent implements ControlValueAccessor, OnInit, AfterViewInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pushTemplate: any = '';
 | 
					  pushTemplate: any = '';  // array element template
 | 
				
			||||||
  @Input('pushTemplate') set _pushTemplate(value) {
 | 
					  @Input('pushTemplate') set _pushTemplate(value) {
 | 
				
			||||||
    this.pushTemplate = value;
 | 
					    this.pushTemplate = value;
 | 
				
			||||||
    if (this.values.length) {
 | 
					    if (this.values.length) {
 | 
				
			||||||
@@ -98,7 +98,8 @@ export class RbArrayInputComponent implements ControlValueAccessor, OnInit, Afte
 | 
				
			|||||||
    if (this.pushTemplate !== null) {
 | 
					    if (this.pushTemplate !== null) {
 | 
				
			||||||
      if (this.pushPath) {
 | 
					      if (this.pushPath) {
 | 
				
			||||||
        // remove last element if last two are empty
 | 
					        // remove last element if last two are empty
 | 
				
			||||||
        if (this.values[this.values.length - 1][this.pushPath] === '' && this.values[this.values.length - 2][this.pushPath] === '') {
 | 
					        if (this.values[this.values.length - 1][this.pushPath] === '' &&
 | 
				
			||||||
 | 
					          this.values[this.values.length - 2][this.pushPath] === '') {
 | 
				
			||||||
          this.values.pop();
 | 
					          this.values.pop();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // add element if last all are filled
 | 
					        // add element if last all are filled
 | 
				
			||||||
@@ -131,6 +132,7 @@ export class RbArrayInputComponent implements ControlValueAccessor, OnInit, Afte
 | 
				
			|||||||
  writeValue(obj: any) {  // add empty value on init
 | 
					  writeValue(obj: any) {  // add empty value on init
 | 
				
			||||||
    if (obj) {
 | 
					    if (obj) {
 | 
				
			||||||
      if (this.pushTemplate !== null) {
 | 
					      if (this.pushTemplate !== null) {
 | 
				
			||||||
 | 
					        // filter out empty values
 | 
				
			||||||
        if (this.pushPath) {
 | 
					        if (this.pushPath) {
 | 
				
			||||||
          this.values = [...obj.filter(e => e[this.pushPath] !== ''), cloneDeep(this.pushTemplate)];
 | 
					          this.values = [...obj.filter(e => e[this.pushPath] !== ''), cloneDeep(this.pushTemplate)];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -150,22 +152,6 @@ export class RbArrayInputComponent implements ControlValueAccessor, OnInit, Afte
 | 
				
			|||||||
        this.values = [''];
 | 
					        this.values = [''];
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // this.values = obj ? obj : [];
 | 
					 | 
				
			||||||
    // console.log('-----');
 | 
					 | 
				
			||||||
    // console.log(obj);
 | 
					 | 
				
			||||||
    // console.log(this.pushPath);
 | 
					 | 
				
			||||||
    // if (this.values && this.values.length) {
 | 
					 | 
				
			||||||
    //   this.values = obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== '');
 | 
					 | 
				
			||||||
    // }
 | 
					 | 
				
			||||||
    // console.log(this.values);
 | 
					 | 
				
			||||||
    // // console.log(obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== ''));
 | 
					 | 
				
			||||||
    // // this.values = obj ? obj.filter(e => this.pushPath ? e[this.pushPath] !== '' : e !== '') : [];
 | 
					 | 
				
			||||||
    // if (this.values.length === 0 || this.values[0] !== '') {
 | 
					 | 
				
			||||||
    //   // add empty last field if pushTemplate is specified
 | 
					 | 
				
			||||||
    //   if (this.pushTemplate !== null) {
 | 
					 | 
				
			||||||
    //     this.values.push(cloneDeep(this.pushTemplate));
 | 
					 | 
				
			||||||
    //   }
 | 
					 | 
				
			||||||
    // }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  registerOnChange(fn: any) {
 | 
					  registerOnChange(fn: any) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,8 @@
 | 
				
			|||||||
import { NgModule } from '@angular/core';
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
import { CommonModule } from '@angular/common';
 | 
					import { CommonModule } from '@angular/common';
 | 
				
			||||||
import { RbTableComponent } from './rb-table/rb-table.component';
 | 
					import { RbTableComponent } from './rb-table/rb-table.component';
 | 
				
			||||||
import {RbArrayInputComponent, RbArrayInputListenerDirective, RbArrayInputItemDirective} from './rb-array-input/rb-array-input.component';
 | 
					import {RbArrayInputComponent, RbArrayInputListenerDirective, RbArrayInputItemDirective} from
 | 
				
			||||||
 | 
					    './rb-array-input/rb-array-input.component';
 | 
				
			||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
				
			||||||
import {FormsModule} from '@angular/forms';
 | 
					import {FormsModule} from '@angular/forms';
 | 
				
			||||||
import { RbIconButtonComponent } from './rb-icon-button/rb-icon-button.component';
 | 
					import { RbIconButtonComponent } from './rb-icon-button/rb-icon-button.component';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { RbIconButtonComponent } from './rb-icon-button.component';
 | 
					 | 
				
			||||||
import {RbCustomInputsModule} from '../rb-custom-inputs.module';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('RbIconButtonComponent', () => {
 | 
					describe('RbIconButtonComponent', () => {
 | 
				
			||||||
  let component: RbIconButtonComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<RbIconButtonComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ RbIconButtonComponent ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(RbIconButtonComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,5 @@ table.ellipsis {
 | 
				
			|||||||
    white-space: nowrap;
 | 
					    white-space: nowrap;
 | 
				
			||||||
    overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
    max-width: 200px;
 | 
					    max-width: 200px;
 | 
				
			||||||
    //min-width: 100px;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,28 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { RbTableComponent } from './rb-table.component';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('RbTableComponent', () => {
 | 
					describe('RbTableComponent', () => {
 | 
				
			||||||
  let component: RbTableComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<RbTableComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ RbTableComponent ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(RbTableComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,76 +1,5 @@
 | 
				
			|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// import { SampleComponent } from './sample.component';
 | 
					describe('SampleComponent', () => {
 | 
				
			||||||
// import {ApiService} from '../services/api.service';
 | 
					
 | 
				
			||||||
// import {ValidationService} from '../services/validation.service';
 | 
					});
 | 
				
			||||||
// import {DataService} from '../services/data.service';
 | 
					 | 
				
			||||||
// import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
// import {FormsModule} from '@angular/forms';
 | 
					 | 
				
			||||||
// import {ActivatedRoute, Router} from '@angular/router';
 | 
					 | 
				
			||||||
// import {AutocompleteService} from '../services/autocomplete.service';
 | 
					 | 
				
			||||||
// import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// // TODO
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// let routerServiceSpy: jasmine.SpyObj<Router>;
 | 
					 | 
				
			||||||
// let activatedRouteServiceSpy: jasmine.SpyObj<ActivatedRoute>;
 | 
					 | 
				
			||||||
// let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
// let validationServiceSpy: jasmine.SpyObj<ValidationService>;
 | 
					 | 
				
			||||||
// let autocompleteServiceSpy: jasmine.SpyObj<AutocompleteService>;
 | 
					 | 
				
			||||||
// let modalServiceSpy: jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
// let dataServiceSpy: jasmine.SpyObj<DataService>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// describe('SampleComponent', () => {
 | 
					 | 
				
			||||||
//   let component: SampleComponent;
 | 
					 | 
				
			||||||
//   let fixture: ComponentFixture<SampleComponent>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(async(() => {
 | 
					 | 
				
			||||||
//     const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
 | 
					 | 
				
			||||||
//     const activatedRouteSpy = jasmine.createSpyObj('ActivatedRoute', ['snapshot']);
 | 
					 | 
				
			||||||
//     const apiSpy = jasmine.createSpyObj('ApiService', ['get', 'post', 'put', 'delete']);
 | 
					 | 
				
			||||||
//     const validationSpy = jasmine.createSpyObj('ValidationService', ['generate']);
 | 
					 | 
				
			||||||
//     const autocompleteSpy = jasmine.createSpyObj('AutocompleteService', ['bind']);
 | 
					 | 
				
			||||||
//     const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
 | 
					 | 
				
			||||||
//     const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
//       declarations: [ SampleComponent ],
 | 
					 | 
				
			||||||
//       imports: [
 | 
					 | 
				
			||||||
//         RbUiComponentsModule,
 | 
					 | 
				
			||||||
//         RbCustomInputsModule,
 | 
					 | 
				
			||||||
//         FormsModule
 | 
					 | 
				
			||||||
//       ],
 | 
					 | 
				
			||||||
//       providers: [
 | 
					 | 
				
			||||||
//         {provide: Router, useValue: routerSpy},
 | 
					 | 
				
			||||||
//         {provide: ActivatedRoute, useValue: {snapshot: {paramMap: {get: (id) => '12345'}}}},
 | 
					 | 
				
			||||||
//         {provide: ApiService, useValue: apiSpy},
 | 
					 | 
				
			||||||
//         {provide: ValidationService, useValue: validationSpy},
 | 
					 | 
				
			||||||
//         {provide: AutocompleteService, useValue: autocompleteSpy},
 | 
					 | 
				
			||||||
//         {provide: ModalService, useValue: modalSpy},
 | 
					 | 
				
			||||||
//         {provide: DataService, useValue: dataSpy}
 | 
					 | 
				
			||||||
//       ]
 | 
					 | 
				
			||||||
//     })
 | 
					 | 
				
			||||||
//     .compileComponents();
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
 | 
					 | 
				
			||||||
//     activatedRouteServiceSpy = TestBed.inject(ActivatedRoute) as jasmine.SpyObj<ActivatedRoute>;
 | 
					 | 
				
			||||||
//     apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
//     validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
 | 
					 | 
				
			||||||
//     autocompleteServiceSpy = TestBed.inject(AutocompleteService) as jasmine.SpyObj<AutocompleteService>;
 | 
					 | 
				
			||||||
//     modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
//     dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     // activatedRouteServiceSpy.snapshot.and.returnValue({paramMap: {get: () => '12345'}});
 | 
					 | 
				
			||||||
//   }));
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(() => {
 | 
					 | 
				
			||||||
//     fixture = TestBed.createComponent(SampleComponent);
 | 
					 | 
				
			||||||
//     component = fixture.componentInstance;
 | 
					 | 
				
			||||||
//     component.ngOnInit();
 | 
					 | 
				
			||||||
//     fixture.detectChanges();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   it('should create', () => {
 | 
					 | 
				
			||||||
//     expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
// });
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,8 +25,6 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
				
			|||||||
import {DataService} from '../services/data.service';
 | 
					import {DataService} from '../services/data.service';
 | 
				
			||||||
import {LoginService} from '../services/login.service';
 | 
					import {LoginService} from '../services/login.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: additional property value not validated on edit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-sample',
 | 
					  selector: 'app-sample',
 | 
				
			||||||
  templateUrl: './sample.component.html',
 | 
					  templateUrl: './sample.component.html',
 | 
				
			||||||
@@ -82,7 +80,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
  modalText = {list: '', suggestion: ''};
 | 
					  modalText = {list: '', suggestion: ''};
 | 
				
			||||||
  cmSampleIndex = '0';
 | 
					  cmSampleIndex = '0';
 | 
				
			||||||
  measurementDeleteList = [];  // buffer with measurements to delete, if the user confirms and saves the cm changes
 | 
					  measurementDeleteList = [];  // buffer with measurements to delete, if the user confirms and saves the cm changes
 | 
				
			||||||
  measurementRestoreData: MeasurementModel[] = [];  // deleted measurements if user is allowed and measurements are available
 | 
					  // deleted measurements if user is allowed and measurements are available
 | 
				
			||||||
 | 
					  measurementRestoreData: MeasurementModel[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  charts = [[]];                         // chart data for spectra
 | 
					  charts = [[]];                         // chart data for spectra
 | 
				
			||||||
  readonly chartInit = [{
 | 
					  readonly chartInit = [{
 | 
				
			||||||
@@ -118,7 +117,6 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    console.log(this.baseSample);
 | 
					 | 
				
			||||||
    this.mode = this.router.url === '/samples/new' ? 'new' : '';
 | 
					    this.mode = this.router.url === '/samples/new' ? 'new' : '';
 | 
				
			||||||
    this.loading = 7;
 | 
					    this.loading = 7;
 | 
				
			||||||
    this.d.load('materials', () => {
 | 
					    this.d.load('materials', () => {
 | 
				
			||||||
@@ -150,7 +148,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
    this.d.load('materialTemplates', () => {
 | 
					    this.d.load('materialTemplates', () => {
 | 
				
			||||||
      if (!this.material.properties.material_template) {
 | 
					      if (!this.material.properties.material_template) {
 | 
				
			||||||
        this.material.properties.material_template = this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id;
 | 
					        this.material.properties.material_template =
 | 
				
			||||||
 | 
					          this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.loading--;
 | 
					      this.loading--;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@@ -167,9 +166,9 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
        if (this.login.isLevel.dev) {  // load measurement restore data
 | 
					        if (this.login.isLevel.dev) {  // load measurement restore data
 | 
				
			||||||
          this.api.get<MeasurementModel[]>('/measurement/sample/' + sampleIds[0], (data, ignore) => {
 | 
					          this.api.get<MeasurementModel[]>('/measurement/sample/' + sampleIds[0], (data, ignore) => {
 | 
				
			||||||
            if (data) {
 | 
					            if (data) {
 | 
				
			||||||
              this.measurementRestoreData = data.filter(e => e.status === 'deleted').map(e => new MeasurementModel().deserialize(e));
 | 
					              this.measurementRestoreData =
 | 
				
			||||||
 | 
					                data.filter(e => e.status === 'deleted').map(e => new MeasurementModel().deserialize(e));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            console.log(this.measurementRestoreData);
 | 
					 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -182,7 +181,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
        this.samples = [new SampleModel().deserialize(sData)];
 | 
					        this.samples = [new SampleModel().deserialize(sData)];
 | 
				
			||||||
        this.baseSample.deserialize(sData);
 | 
					        this.baseSample.deserialize(sData);
 | 
				
			||||||
        this.material = new MaterialModel().deserialize(sData.material);  // read material
 | 
					        this.material = new MaterialModel().deserialize(sData.material);  // read material
 | 
				
			||||||
        this.customFields = this.baseSample.notes.custom_fields && this.baseSample.notes.custom_fields !== {} ?  // read custom fields
 | 
					        // read custom fields
 | 
				
			||||||
 | 
					        this.customFields = this.baseSample.notes.custom_fields && this.baseSample.notes.custom_fields !== {} ?
 | 
				
			||||||
          Object.keys(this.baseSample.notes.custom_fields).map(e => [e, this.baseSample.notes.custom_fields[e]]) : [];
 | 
					          Object.keys(this.baseSample.notes.custom_fields).map(e => [e, this.baseSample.notes.custom_fields[e]]) : [];
 | 
				
			||||||
        if (this.baseSample.notes.sample_references.length) {  // read sample references
 | 
					        if (this.baseSample.notes.sample_references.length) {  // read sample references
 | 
				
			||||||
          this.sampleReferences = [];
 | 
					          this.sampleReferences = [];
 | 
				
			||||||
@@ -214,11 +214,10 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        this.checkFormAfterInit = true;
 | 
					        this.checkFormAfterInit = true;
 | 
				
			||||||
        this.loading--;
 | 
					        this.loading--;
 | 
				
			||||||
        sampleIds.slice(1).forEach(sampleId => {
 | 
					        sampleIds.slice(1).forEach(sampleId => {  // load further samples for batch edit
 | 
				
			||||||
          this.api.get<SampleModel>('/sample/' + sampleId, data => {
 | 
					          this.api.get<SampleModel>('/sample/' + sampleId, data => {
 | 
				
			||||||
            this.samples.push(new SampleModel().deserialize(data));
 | 
					            this.samples.push(new SampleModel().deserialize(data));
 | 
				
			||||||
            ['type', 'color', 'batch', 'notes'].forEach((key) => {
 | 
					            ['type', 'color', 'batch', 'notes'].forEach((key) => {
 | 
				
			||||||
              console.log(isEqual(data[key], this.baseSample[key]));
 | 
					 | 
				
			||||||
              if (!isEqual(data[key], this.baseSample[key])) {
 | 
					              if (!isEqual(data[key], this.baseSample[key])) {
 | 
				
			||||||
                this.baseSample[key] = undefined;
 | 
					                this.baseSample[key] = undefined;
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
@@ -228,7 +227,6 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            this.loading--;
 | 
					            this.loading--;
 | 
				
			||||||
            this.checkFormAfterInit = true;
 | 
					            this.checkFormAfterInit = true;
 | 
				
			||||||
            console.log(this.baseSample.material.name);
 | 
					 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -239,6 +237,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngAfterContentChecked() {
 | 
					  ngAfterContentChecked() {
 | 
				
			||||||
 | 
					    // attach validators
 | 
				
			||||||
    if (this.samples.length) {  // conditions are displayed
 | 
					    if (this.samples.length) {  // conditions are displayed
 | 
				
			||||||
      this.samples.forEach((gSample, gIndex) => {
 | 
					      this.samples.forEach((gSample, gIndex) => {
 | 
				
			||||||
        if (this.d.id.conditionTemplates[gSample.condition.condition_template]) {
 | 
					        if (this.d.id.conditionTemplates[gSample.condition.condition_template]) {
 | 
				
			||||||
@@ -260,6 +259,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // revalidate inputs
 | 
				
			||||||
    if (this.checkFormAfterInit) {
 | 
					    if (this.checkFormAfterInit) {
 | 
				
			||||||
      if (this.view.base) {  // validate sampleForm
 | 
					      if (this.view.base) {  // validate sampleForm
 | 
				
			||||||
        if (this.sampleForm !== undefined && this.sampleForm.form.get('cf-key0')) {
 | 
					        if (this.sampleForm !== undefined && this.sampleForm.form.get('cf-key0')) {
 | 
				
			||||||
@@ -277,13 +277,13 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
            (this.d.id.conditionTemplates[this.samples[0].condition.condition_template].parameters.length - 1)) as any;
 | 
					            (this.d.id.conditionTemplates[this.samples[0].condition.condition_template].parameters.length - 1)) as any;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (this.samples[0].measurements.length) {  // if there are measurements, last measurement field exists
 | 
					        if (this.samples[0].measurements.length) {  // if there are measurements, last measurement field exists
 | 
				
			||||||
          formReady = formReady && this.cmForm.form.get('measurementParameter-0-' + (this.samples[0].measurements.length - 1) +
 | 
					          formReady = formReady &&
 | 
				
			||||||
 | 
					            this.cmForm.form.get('measurementParameter-0-' + (this.samples[0].measurements.length - 1) +
 | 
				
			||||||
            '-' + (this.d.id.measurementTemplates[this.samples[0].measurements[this.samples[0].measurements.length - 1]
 | 
					            '-' + (this.d.id.measurementTemplates[this.samples[0].measurements[this.samples[0].measurements.length - 1]
 | 
				
			||||||
              .measurement_template].parameters.length - 1)) as any;
 | 
					              .measurement_template].parameters.length - 1)) as any;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (formReady) {  // fields are ready, do validation
 | 
					        if (formReady) {  // fields are ready, do validation
 | 
				
			||||||
          this.checkFormAfterInit = false;
 | 
					          this.checkFormAfterInit = false;
 | 
				
			||||||
          console.log('init');
 | 
					 | 
				
			||||||
          Object.keys(this.cmForm.form.controls).forEach(field => {
 | 
					          Object.keys(this.cmForm.form.controls).forEach(field => {
 | 
				
			||||||
            this.cmForm.form.get(field).updateValueAndValidity();
 | 
					            this.cmForm.form.get(field).updateValueAndValidity();
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
@@ -346,7 +346,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        this.baseSample.notes.sample_references = this.sampleReferences
 | 
					        this.baseSample.notes.sample_references = this.sampleReferences
 | 
				
			||||||
          .filter(e => e[0] && e[1] && e[2])
 | 
					          .filter(e => e[0] && e[1] && e[2])  // filter empty values
 | 
				
			||||||
          .map(e => ({sample_id: e[2], relation: e[1]}));
 | 
					          .map(e => ({sample_id: e[2], relation: e[1]}));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (this.samples.length === 0) {  // only save new sample for the first time in mode new, otherwise save changes
 | 
					      if (this.samples.length === 0) {  // only save new sample for the first time in mode new, otherwise save changes
 | 
				
			||||||
@@ -363,7 +363,6 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else {
 | 
				
			||||||
        this.samples.forEach((sample, i) => {
 | 
					        this.samples.forEach((sample, i) => {
 | 
				
			||||||
          console.log(sample._id);
 | 
					 | 
				
			||||||
          this.api.put<SampleModel>('/sample/' + sample._id, this.baseSample.sendFormat(false), data => {
 | 
					          this.api.put<SampleModel>('/sample/' + sample._id, this.baseSample.sendFormat(false), data => {
 | 
				
			||||||
            merge(this.samples[i], omit(data, ['condition']));
 | 
					            merge(this.samples[i], omit(data, ['condition']));
 | 
				
			||||||
            this.samples[i].material = this.d.arr.materials.find(e => e._id === this.samples[0].material_id);
 | 
					            this.samples[i].material = this.d.arr.materials.find(e => e._id === this.samples[0].material_id);
 | 
				
			||||||
@@ -381,7 +380,10 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
      if (sample.condition.condition_template) {  // condition was set
 | 
					      if (sample.condition.condition_template) {  // condition was set
 | 
				
			||||||
        this.api.put('/sample/' + sample._id,
 | 
					        this.api.put('/sample/' + sample._id,
 | 
				
			||||||
          {condition: pick(sample.condition,
 | 
					          {condition: pick(sample.condition,
 | 
				
			||||||
              ['condition_template', ...this.d.id.conditionTemplates[sample.condition.condition_template].parameters.map(e => e.name)]
 | 
					              [
 | 
				
			||||||
 | 
					                'condition_template',
 | 
				
			||||||
 | 
					                ...this.d.id.conditionTemplates[sample.condition.condition_template].parameters.map(e => e.name)
 | 
				
			||||||
 | 
					              ]
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -439,7 +441,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
    else {  // no matching material found
 | 
					    else {  // no matching material found
 | 
				
			||||||
      if (this.baseSample.material_id !== null) {  // reset previous match
 | 
					      if (this.baseSample.material_id !== null) {  // reset previous match
 | 
				
			||||||
        this.material = new MaterialModel();
 | 
					        this.material = new MaterialModel();
 | 
				
			||||||
        this.material.properties.material_template = this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id;
 | 
					        this.material.properties.material_template =
 | 
				
			||||||
 | 
					          this.d.latest.materialTemplates.find(e => e.name === 'plastic')._id;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.baseSample.material_id = null;
 | 
					      this.baseSample.material_id = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -450,8 +453,8 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
  setNewMaterial(value = null) {
 | 
					  setNewMaterial(value = null) {
 | 
				
			||||||
    if (value === null) {  // toggle dialog
 | 
					    if (value === null) {  // toggle dialog
 | 
				
			||||||
      this.newMaterial = !this.baseSample.material_id;
 | 
					      this.newMaterial = !this.baseSample.material_id;
 | 
				
			||||||
    }
 | 
					    }  // set to false only if material already exists
 | 
				
			||||||
    else if (value || (!value && this.baseSample.material_id !== null )) {  // set to false only if material already exists
 | 
					    else if (value || (!value && this.baseSample.material_id !== null )) {
 | 
				
			||||||
      this.newMaterial = value;
 | 
					      this.newMaterial = value;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this.newMaterial) {  // set validators if dialog is open
 | 
					    if (this.newMaterial) {  // set validators if dialog is open
 | 
				
			||||||
@@ -476,7 +479,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // remove the measurement at the specified index
 | 
					  // remove the measurement at the specified index
 | 
				
			||||||
  removeMeasurement(gIndex, mIndex) {  // TODO: do not delete directly but only after confirmation
 | 
					  removeMeasurement(gIndex, mIndex) {
 | 
				
			||||||
    if (this.samples[gIndex].measurements[mIndex]._id !== null) {
 | 
					    if (this.samples[gIndex].measurements[mIndex]._id !== null) {
 | 
				
			||||||
      this.measurementDeleteList.push(this.samples[gIndex].measurements[mIndex]._id);
 | 
					      this.measurementDeleteList.push(this.samples[gIndex].measurements[mIndex]._id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -490,7 +493,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
    this.samples[gIndex].measurements[mIndex].values = {};
 | 
					    this.samples[gIndex].measurements[mIndex].values = {};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fileToArray(files, gIndex, mIndex, parameter) {
 | 
					  fileToArray(files, gIndex, mIndex, parameter) {  // process spectrum file input
 | 
				
			||||||
    for (const i in files) {
 | 
					    for (const i in files) {
 | 
				
			||||||
      if (files.hasOwnProperty(i)) {
 | 
					      if (files.hasOwnProperty(i)) {
 | 
				
			||||||
        const fileReader = new FileReader();
 | 
					        const fileReader = new FileReader();
 | 
				
			||||||
@@ -500,6 +503,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
            this.addMeasurement(gIndex);
 | 
					            this.addMeasurement(gIndex);
 | 
				
			||||||
            index = this.samples[gIndex].measurements.length - 1;
 | 
					            index = this.samples[gIndex].measurements.length - 1;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					          // autofill further parameters
 | 
				
			||||||
          this.samples[gIndex].measurements[index].values.device =
 | 
					          this.samples[gIndex].measurements[index].values.device =
 | 
				
			||||||
            this.samples[gIndex].measurements[mIndex].values.device;
 | 
					            this.samples[gIndex].measurements[mIndex].values.device;
 | 
				
			||||||
          this.samples[gIndex].measurements[index].values.filename = files[i].name;
 | 
					          this.samples[gIndex].measurements[index].values.filename = files[i].name;
 | 
				
			||||||
@@ -578,12 +582,13 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
    this.sampleReferenceIdFind(value);
 | 
					    this.sampleReferenceIdFind(value);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sampleReferenceList(value) {
 | 
					  sampleReferenceList(value) {  // get list of sample reference number suggestions
 | 
				
			||||||
    return new Observable(observer => {
 | 
					    return new Observable(observer => {
 | 
				
			||||||
      if (value !== '') {
 | 
					      if (value !== '') {
 | 
				
			||||||
        this.api.get<{ _id: string, number: string }[]>(
 | 
					        this.api.get<{ _id: string, number: string }[]>(
 | 
				
			||||||
          '/samples?status[]=validated&status[]=new&page-size=25&sort=number-asc&fields[]=number&fields[]=_id&' +
 | 
					          '/samples?status[]=validated&status[]=new&page-size=25&sort=number-asc&fields[]=number&fields[]=_id&' +
 | 
				
			||||||
          'filters[]=%7B%22mode%22%3A%22stringin%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22' + value + '%22%5D%7D', data => {
 | 
					          'filters[]=%7B%22mode%22%3A%22stringin%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22' + value +
 | 
				
			||||||
 | 
					          '%22%5D%7D', data => {
 | 
				
			||||||
          this.sampleReferenceAutocomplete[this.currentSRIndex] = data.map(e => e.number);
 | 
					          this.sampleReferenceAutocomplete[this.currentSRIndex] = data.map(e => e.number);
 | 
				
			||||||
          this.sampleReferenceFinds = data;
 | 
					          this.sampleReferenceFinds = data;
 | 
				
			||||||
          observer.next(data.map(e => e.number));
 | 
					          observer.next(data.map(e => e.number));
 | 
				
			||||||
@@ -598,7 +603,7 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sampleReferenceIdFind(value) {
 | 
					  sampleReferenceIdFind(value) {  // sample reference id from number
 | 
				
			||||||
    const idFind = this.sampleReferenceFinds.find(e => e.number === value);
 | 
					    const idFind = this.sampleReferenceFinds.find(e => e.number === value);
 | 
				
			||||||
    if (idFind) {
 | 
					    if (idFind) {
 | 
				
			||||||
      this.sampleReferences[this.currentSRIndex][2] = idFind._id;
 | 
					      this.sampleReferences[this.currentSRIndex][2] = idFind._id;
 | 
				
			||||||
@@ -626,11 +631,3 @@ export class SampleComponent implements OnInit, AfterContentChecked {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 1. ngAfterViewInit wird ja jedes mal nach einem ngOnChanges aufgerufen, also zB wenn sich dein ngFor aufbaut. Du könntest also in der
 | 
					 | 
				
			||||||
//      Methode prüfen, ob die Daten schon da sind und dann dementsprechend handeln. Das wäre die Eleganteste Variante
 | 
					 | 
				
			||||||
// 2. Der state "dirty" soll eigentlich anzeigen, wenn ein Form-Field vom User geändert wurde; damit missbrauchst du es hier etwas
 | 
					 | 
				
			||||||
// 3. Die Dirty-Variante: Pack in deine ngFor ein {{ onFirstLoad(data) }} rein, das einfach ausgeführt wird. müsstest dann natürlich
 | 
					 | 
				
			||||||
//      abfangen, dass das nicht nach jedem view-cycle neu getriggert wird. Schön ist das nicht, aber besser als mit Timeouts^^
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -104,6 +104,12 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </ng-container>
 | 
					          </ng-container>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <rb-form-checkbox name="no-condition" [(ngModel)]="filters.no.condition" class="space-right">
 | 
				
			||||||
 | 
					          has no condition
 | 
				
			||||||
 | 
					        </rb-form-checkbox>
 | 
				
			||||||
 | 
					        <rb-form-checkbox name="no-measurements" [(ngModel)]="filters.no.measurements" class="space-right">
 | 
				
			||||||
 | 
					          has no measurements
 | 
				
			||||||
 | 
					        </rb-form-checkbox>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -106,18 +106,6 @@ rb-table {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.sort-active-asc {
 | 
					 | 
				
			||||||
  color: $color-bosch-dark-blue;
 | 
					 | 
				
			||||||
  background: linear-gradient(to bottom, #FFF 17%, $color-bosch-light-blue-w50 17%);;
 | 
					 | 
				
			||||||
  border-radius: 0 0 8px 8px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.sort-active-desc {
 | 
					 | 
				
			||||||
  color: $color-bosch-dark-blue;
 | 
					 | 
				
			||||||
  background: linear-gradient(to top, #FFF 17%, $color-bosch-light-blue-w50 17%);;
 | 
					 | 
				
			||||||
  border-radius: 8px 8px 0 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.filters:after {
 | 
					.filters:after {
 | 
				
			||||||
  content:"";
 | 
					  content:"";
 | 
				
			||||||
  clear:both;
 | 
					  clear:both;
 | 
				
			||||||
@@ -178,6 +166,10 @@ rb-table {
 | 
				
			|||||||
    float: left;
 | 
					    float: left;
 | 
				
			||||||
    margin-right: 30px;
 | 
					    margin-right: 30px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  & > rb-form-checkbox {
 | 
				
			||||||
 | 
					    float: left;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.filtermode {
 | 
					.filtermode {
 | 
				
			||||||
@@ -198,11 +190,6 @@ textarea.linkmodal {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.sample-details-table {
 | 
					.sample-details-table {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ::ng-deep .table-wrapper {
 | 
					 | 
				
			||||||
    max-height: 80vh;
 | 
					 | 
				
			||||||
    overflow-y: scroll;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  td {
 | 
					  td {
 | 
				
			||||||
    max-width: none;
 | 
					    max-width: none;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,77 +1,5 @@
 | 
				
			|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// import { SamplesComponent } from './samples.component';
 | 
					describe('SamplesComponent', () => {
 | 
				
			||||||
// import {ApiService} from '../services/api.service';
 | 
					
 | 
				
			||||||
// import {AutocompleteService} from '../services/autocomplete.service';
 | 
					});
 | 
				
			||||||
// import {DataService} from '../services/data.service';
 | 
					 | 
				
			||||||
// import {LoginService} from '../services/login.service';
 | 
					 | 
				
			||||||
// import {LocalStorageService} from 'angular-2-local-storage';
 | 
					 | 
				
			||||||
// import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
// import {ValidationService} from '../services/validation.service';
 | 
					 | 
				
			||||||
// import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
// import {FormsModule} from '@angular/forms';
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// // TODO
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
// let autocompleteServiceSpy: jasmine.SpyObj<AutocompleteService>;
 | 
					 | 
				
			||||||
// let modalServiceSpy: jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
// let dataServiceSpy: jasmine.SpyObj<DataService>;
 | 
					 | 
				
			||||||
// let loginServiceSpy: jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
// let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
 | 
					 | 
				
			||||||
// let windowServiceSpy: jasmine.SpyObj<Window>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// describe('SamplesComponent', () => {
 | 
					 | 
				
			||||||
//   let component: SamplesComponent;
 | 
					 | 
				
			||||||
//   let fixture: ComponentFixture<SamplesComponent>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(async(() => {
 | 
					 | 
				
			||||||
//     const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
 | 
					 | 
				
			||||||
//     const autocompleteSpy = jasmine.createSpyObj('AutocompleteService', ['bind']);
 | 
					 | 
				
			||||||
//     const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'isLevel']);
 | 
					 | 
				
			||||||
//     const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
 | 
					 | 
				
			||||||
//     const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
 | 
					 | 
				
			||||||
//     const localStorageSpy = jasmine.createSpyObj('LocalStorageService', ['set', 'remove']);
 | 
					 | 
				
			||||||
//     const windowSpy = jasmine.createSpyObj('Window', ['location']);
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
//       declarations: [ SamplesComponent ],
 | 
					 | 
				
			||||||
//       imports: [
 | 
					 | 
				
			||||||
//         RbUiComponentsModule,
 | 
					 | 
				
			||||||
//         RbCustomInputsModule,
 | 
					 | 
				
			||||||
//         FormsModule
 | 
					 | 
				
			||||||
//       ],
 | 
					 | 
				
			||||||
//       providers: [
 | 
					 | 
				
			||||||
//         {provide: ApiService, useValue: apiSpy},
 | 
					 | 
				
			||||||
//         {provide: AutocompleteService, useValue: autocompleteSpy},
 | 
					 | 
				
			||||||
//         {provide: ModalService, useValue: modalSpy},
 | 
					 | 
				
			||||||
//         {provide: DataService, useValue: dataSpy},
 | 
					 | 
				
			||||||
//         {provide: LoginService, useValue: loginSpy},
 | 
					 | 
				
			||||||
//         {provide: LocalStorageService, useValue: localStorageSpy},
 | 
					 | 
				
			||||||
//         {provide: Window, useValue: windowSpy}
 | 
					 | 
				
			||||||
//       ]
 | 
					 | 
				
			||||||
//     })
 | 
					 | 
				
			||||||
//     .compileComponents();
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
//     autocompleteServiceSpy = TestBed.inject(AutocompleteService) as jasmine.SpyObj<AutocompleteService>;
 | 
					 | 
				
			||||||
//     modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
//     dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
 | 
					 | 
				
			||||||
//     loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
//     localStorageServiceSpy = TestBed.inject(LocalStorageService) as jasmine.SpyObj<LocalStorageService>;
 | 
					 | 
				
			||||||
//     windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
 | 
					 | 
				
			||||||
//   }));
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(() => {
 | 
					 | 
				
			||||||
//     fixture = TestBed.createComponent(SamplesComponent);
 | 
					 | 
				
			||||||
//     component = fixture.componentInstance;
 | 
					 | 
				
			||||||
//     component.ngOnInit();
 | 
					 | 
				
			||||||
//     fixture.detectChanges();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   it('should create', () => {
 | 
					 | 
				
			||||||
//     expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
// });
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,11 +11,6 @@ import {DataService} from '../services/data.service';
 | 
				
			|||||||
import {LocalStorageService} from 'angular-2-local-storage';
 | 
					import {LocalStorageService} from 'angular-2-local-storage';
 | 
				
			||||||
import {Router} from '@angular/router';
 | 
					import {Router} from '@angular/router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: turn off sort field
 | 
					 | 
				
			||||||
// TODO reset sort when field is excluded
 | 
					 | 
				
			||||||
// TODO: Eh DPT
 | 
					 | 
				
			||||||
// TODO: filter button
 | 
					 | 
				
			||||||
// TODO: check if connect-src to model works
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface LoadSamplesOptions {
 | 
					interface LoadSamplesOptions {
 | 
				
			||||||
@@ -41,10 +36,10 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
  @ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
 | 
					  @ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
 | 
				
			||||||
  @ViewChild('linkarea') linkarea: ElementRef<HTMLTextAreaElement>;
 | 
					  @ViewChild('linkarea') linkarea: ElementRef<HTMLTextAreaElement>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  downloadSpectra = false;     // TODO: streamline these options after csv option handling is clear
 | 
					  downloadSpectra = false;    // download options
 | 
				
			||||||
  downloadCondition = false;
 | 
					  downloadCondition = false;
 | 
				
			||||||
  downloadFlatten = true;
 | 
					  downloadFlatten = true;
 | 
				
			||||||
  samples: SampleModel[] = [];
 | 
					  samples: SampleModel[] = [];  // all samples to display
 | 
				
			||||||
  totalSamples = 0;  // total number of samples
 | 
					  totalSamples = 0;  // total number of samples
 | 
				
			||||||
  csvUrl = '';  // store url separate so it only has to be generated when clicking the download button
 | 
					  csvUrl = '';  // store url separate so it only has to be generated when clicking the download button
 | 
				
			||||||
  filters = {
 | 
					  filters = {
 | 
				
			||||||
@@ -52,6 +47,7 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    pageSize: 25,
 | 
					    pageSize: 25,
 | 
				
			||||||
    toPage: 0,
 | 
					    toPage: 0,
 | 
				
			||||||
    sort: 'added-asc',
 | 
					    sort: 'added-asc',
 | 
				
			||||||
 | 
					    no: {condition: false, measurements: false},
 | 
				
			||||||
    filters: [
 | 
					    filters: [
 | 
				
			||||||
      {field: 'number', label: 'Number', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
					      {field: 'number', label: 'Number', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
				
			||||||
      {field: 'material.name', label: 'Product name', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
					      {field: 'material.name', label: 'Product name', active: false, autocomplete: [], mode: 'eq', values: ['']},
 | 
				
			||||||
@@ -67,8 +63,8 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
      {field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: ['']}
 | 
					      {field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: ['']}
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  page = 1;
 | 
					  page = 1;   // current page
 | 
				
			||||||
  pages = 1;
 | 
					  pages = 1;  // total number of pages
 | 
				
			||||||
  loadSamplesQueue = [];  // arguments of queued up loadSamples() calls
 | 
					  loadSamplesQueue = [];  // arguments of queued up loadSamples() calls
 | 
				
			||||||
  keys: KeyInterface[] = [
 | 
					  keys: KeyInterface[] = [
 | 
				
			||||||
    {id: 'number', label: 'Number', active: true, sortable: true},
 | 
					    {id: 'number', label: 'Number', active: true, sortable: true},
 | 
				
			||||||
@@ -84,12 +80,12 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    {id: 'status', label: 'Status', active: false, sortable: true},
 | 
					    {id: 'status', label: 'Status', active: false, sortable: true},
 | 
				
			||||||
    {id: 'added', label: 'Added', active: true, sortable: true}
 | 
					    {id: 'added', label: 'Added', active: true, sortable: true}
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
  isActiveKey: {[key: string]: boolean} = {};
 | 
					  isActiveKey: {[key: string]: boolean} = {};  // object to check if key is currently active
 | 
				
			||||||
  activeKeys: KeyInterface[] = [];
 | 
					  activeKeys: KeyInterface[] = [];  // list of active keys
 | 
				
			||||||
  activeTemplateKeys = {material: [], condition: [], measurements: []};
 | 
					  activeTemplateKeys = {material: [], condition: [], measurements: []};
 | 
				
			||||||
  sampleDetailsSample: any = null;
 | 
					  sampleDetailsSample: any = null;  // sample for the sample details dialog
 | 
				
			||||||
  sampleSelect = 0;  // modes: 0 - no selection, 1 - sample edit selection, 2 - validation selection
 | 
					  sampleSelect = 0;  // modes: 0 - no selection, 1 - sample edit selection, 2 - validation selection
 | 
				
			||||||
  loading = 0;
 | 
					  loading = 0;  // number of loading instances
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
@@ -169,7 +165,8 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  loadSamples(options: LoadSamplesOptions = {}, event = null) {  // set toPage to null to reload first page, queues calls
 | 
					  // set toPage to null to reload first page, queues calls
 | 
				
			||||||
 | 
					  loadSamples(options: LoadSamplesOptions = {}, event = null) {
 | 
				
			||||||
    if (event) {  // adjust active keys
 | 
					    if (event) {  // adjust active keys
 | 
				
			||||||
      this.keys.forEach(key => {
 | 
					      this.keys.forEach(key => {
 | 
				
			||||||
        if (event.hasOwnProperty(key.id)) {
 | 
					        if (event.hasOwnProperty(key.id)) {
 | 
				
			||||||
@@ -193,7 +190,7 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    this.loading ++;
 | 
					    this.loading ++;
 | 
				
			||||||
    this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, err, headers) => {
 | 
					    this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, err, headers) => {
 | 
				
			||||||
      this.loading --;
 | 
					      this.loading --;
 | 
				
			||||||
      if (err) {
 | 
					      if (err) {  // remove stored options on error to avoid loop
 | 
				
			||||||
        this.storage.remove('samplesPreferences');
 | 
					        this.storage.remove('samplesPreferences');
 | 
				
			||||||
        this.api.requestError(err);
 | 
					        this.api.requestError(err);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -222,7 +219,8 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    export?: boolean,
 | 
					    export?: boolean,
 | 
				
			||||||
    host?: boolean
 | 
					    host?: boolean
 | 
				
			||||||
  }) {  // return url to fetch samples
 | 
					  }) {  // return url to fetch samples
 | 
				
			||||||
    const additionalTableKeys = ['material_id', '_id', 'user_id'];  // keys which should always be added if export = false
 | 
					    // keys which should always be added if export = false
 | 
				
			||||||
 | 
					    const additionalTableKeys = ['material_id', '_id', 'user_id'];
 | 
				
			||||||
    const query: string[] = [];
 | 
					    const query: string[] = [];
 | 
				
			||||||
    query.push(...Object.keys(this.filters.status).filter(e => this.filters.status[e]).map(e => 'status[]=' + e));
 | 
					    query.push(...Object.keys(this.filters.status).filter(e => this.filters.status[e]).map(e => 'status[]=' + e));
 | 
				
			||||||
    if (options.paging) {
 | 
					    if (options.paging) {
 | 
				
			||||||
@@ -240,7 +238,7 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
      query.push('page-size=' + this.filters.pageSize);
 | 
					      query.push('page-size=' + this.filters.pageSize);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    query.push('sort=' + this.filters.sort);
 | 
					    query.push('sort=' + this.filters.sort);
 | 
				
			||||||
    if (options.export) {
 | 
					    if (options.export) {  // append API key on export
 | 
				
			||||||
      query.push('key=' + this.d.d.userKey.key);
 | 
					      query.push('key=' + this.d.d.userKey.key);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.keys.forEach(key => {
 | 
					    this.keys.forEach(key => {
 | 
				
			||||||
@@ -254,9 +252,11 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
      .map(e => {
 | 
					      .map(e => {
 | 
				
			||||||
        e.values = e.values.filter(el => el !== '');  // do not include empty values
 | 
					        e.values = e.values.filter(el => el !== '');  // do not include empty values
 | 
				
			||||||
        if (e.field === 'added') {  // correct timezone
 | 
					        if (e.field === 'added') {  // correct timezone
 | 
				
			||||||
          e.values = e.values.map(el => new Date(new Date(el).getTime() - new Date(el).getTimezoneOffset() * 60000).toISOString());
 | 
					          e.values = e.values.map(
 | 
				
			||||||
 | 
					            el => new Date(new Date(el).getTime() - new Date(el).getTimezoneOffset() * 60000).toISOString()
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (e.mode === 'null') {
 | 
					        if (e.mode === 'null') {  // handle null mode
 | 
				
			||||||
          e.mode = 'in';
 | 
					          e.mode = 'in';
 | 
				
			||||||
          e.values = [null, ''];
 | 
					          e.values = [null, ''];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -265,6 +265,13 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
      .filter(e => e.active && e.values.length > 0)
 | 
					      .filter(e => e.active && e.values.length > 0)
 | 
				
			||||||
      .map(e => 'filters[]=' + encodeURIComponent(JSON.stringify(pick(e, ['mode', 'field', 'values']))))
 | 
					      .map(e => 'filters[]=' + encodeURIComponent(JSON.stringify(pick(e, ['mode', 'field', 'values']))))
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					    if (this.filters.no.condition) {
 | 
				
			||||||
 | 
					      query.push('filters[]=' + encodeURIComponent( JSON.stringify({mode: 'eq', field: 'condition', values: [{}]})));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.filters.no.measurements) {
 | 
				
			||||||
 | 
					      query.push('filters[]=' +
 | 
				
			||||||
 | 
					        encodeURIComponent( JSON.stringify( {mode: 'eq', field: 'measurements', values: [null]})));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (!options.export) {
 | 
					    if (!options.export) {
 | 
				
			||||||
      additionalTableKeys.forEach(key => {
 | 
					      additionalTableKeys.forEach(key => {
 | 
				
			||||||
      if (query.indexOf('fields[]=' + key) < 0) {  // add key if not already added
 | 
					      if (query.indexOf('fields[]=' + key) < 0) {  // add key if not already added
 | 
				
			||||||
@@ -272,7 +279,7 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {  // export options
 | 
				
			||||||
      if (options.csv) {
 | 
					      if (options.csv) {
 | 
				
			||||||
        query.push('output=csv');
 | 
					        query.push('output=csv');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -331,7 +338,6 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    this.updateActiveKeys();
 | 
					    this.updateActiveKeys();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO: avoid reloading
 | 
					 | 
				
			||||||
  resetPreferences() {
 | 
					  resetPreferences() {
 | 
				
			||||||
    this.storage.remove('samplesPreferences');
 | 
					    this.storage.remove('samplesPreferences');
 | 
				
			||||||
    this.window.location.reload();
 | 
					    this.window.location.reload();
 | 
				
			||||||
@@ -339,7 +345,6 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  updateFilterFields(field) {
 | 
					  updateFilterFields(field) {
 | 
				
			||||||
    const filter = this.filters.filters.find(e => e.field === field);
 | 
					    const filter = this.filters.filters.find(e => e.field === field);
 | 
				
			||||||
    console.log(filter);
 | 
					 | 
				
			||||||
    filter.active = !(filter.values.length === 1 && filter.values[0] === '');
 | 
					    filter.active = !(filter.values.length === 1 && filter.values[0] === '');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -373,20 +378,24 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sampleDetails(id: string, modal: TemplateRef<any>) {
 | 
					  sampleDetails(id: string, modal: TemplateRef<any>) {  // show sample details
 | 
				
			||||||
    this.sampleDetailsSample = null;
 | 
					    this.sampleDetailsSample = null;
 | 
				
			||||||
    this.api.get<SampleModel>('/sample/' + id, data => {
 | 
					    this.api.get<SampleModel>('/sample/' + id, data => {
 | 
				
			||||||
      this.sampleDetailsSample = new SampleModel().deserialize(data);
 | 
					      this.sampleDetailsSample = new SampleModel().deserialize(data);
 | 
				
			||||||
      if (data.notes.custom_fields) {  // convert custom_fields for more optimized display
 | 
					      if (data.notes.custom_fields) {  // convert custom_fields for more optimized display
 | 
				
			||||||
        this.sampleDetailsSample.notes.custom_fields_entries = Object.entries(this.sampleDetailsSample.notes.custom_fields);
 | 
					        this.sampleDetailsSample.notes.custom_fields_entries =
 | 
				
			||||||
 | 
					          Object.entries(this.sampleDetailsSample.notes.custom_fields);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else {
 | 
				
			||||||
        this.sampleDetailsSample.custom_fields_entries = [];
 | 
					        this.sampleDetailsSample.custom_fields_entries = [];
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (Object.keys(data.condition).length) {
 | 
					      if (Object.keys(data.condition).length) {  // convert condition
 | 
				
			||||||
        this.sampleDetailsSample.condition_entries = Object.entries(omit(this.sampleDetailsSample.condition, ['condition_template']))
 | 
					        this.sampleDetailsSample.condition_entries =
 | 
				
			||||||
 | 
					          Object.entries(omit(this.sampleDetailsSample.condition, ['condition_template']))
 | 
				
			||||||
          .map(e => {
 | 
					          .map(e => {
 | 
				
			||||||
            e[0] = `${this.ucFirst(this.d.id.conditionTemplates[this.sampleDetailsSample.condition.condition_template].name)} ${e[0]}`;
 | 
					            e[0] = `${this.ucFirst(
 | 
				
			||||||
 | 
					              this.d.id.conditionTemplates[this.sampleDetailsSample.condition.condition_template].name
 | 
				
			||||||
 | 
					            )} ${e[0]}`;
 | 
				
			||||||
            return e;
 | 
					            return e;
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -394,9 +403,11 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
        this.sampleDetailsSample.condition_entries = [];
 | 
					        this.sampleDetailsSample.condition_entries = [];
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.sampleDetailsSample.measurement_entries = [];
 | 
					      this.sampleDetailsSample.measurement_entries = [];
 | 
				
			||||||
      this.sampleDetailsSample.measurements.forEach(measurement => {  // convert measurements for more optimized display without dpt
 | 
					      // convert measurements for more optimized display without dpt
 | 
				
			||||||
 | 
					      this.sampleDetailsSample.measurements.forEach(measurement => {
 | 
				
			||||||
        const name = this.d.id.measurementTemplates[measurement.measurement_template].name;
 | 
					        const name = this.d.id.measurementTemplates[measurement.measurement_template].name;
 | 
				
			||||||
        this.sampleDetailsSample.measurement_entries.push(...Object.entries(measurement.values).filter(e => e[0] !== 'dpt')
 | 
					        this.sampleDetailsSample.measurement_entries
 | 
				
			||||||
 | 
					          .push(...Object.entries(measurement.values).filter(e => e[0] !== 'dpt')
 | 
				
			||||||
          .map(e => ({name: this.ucFirst(name) + ' ' + e[0], value: e[1]})));
 | 
					          .map(e => ({name: this.ucFirst(name) + ' ' + e[0], value: e[1]})));
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      new Promise(resolve => {
 | 
					      new Promise(resolve => {
 | 
				
			||||||
@@ -422,7 +433,7 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  validate() {
 | 
					  validate() {
 | 
				
			||||||
    if (this.sampleSelect) {
 | 
					    if (this.sampleSelect) {  // do actual validation
 | 
				
			||||||
      this.samples.forEach(sample => {
 | 
					      this.samples.forEach(sample => {
 | 
				
			||||||
        if (sample.selected) {
 | 
					        if (sample.selected) {
 | 
				
			||||||
          this.api.put('/sample/validate/' + sample._id);
 | 
					          this.api.put('/sample/validate/' + sample._id);
 | 
				
			||||||
@@ -431,12 +442,12 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
      this.loadSamples();
 | 
					      this.loadSamples();
 | 
				
			||||||
      this.sampleSelect = 0;
 | 
					      this.sampleSelect = 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {  // get into validation mode
 | 
				
			||||||
      this.sampleSelect = 2;
 | 
					      this.sampleSelect = 2;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  batchEdit() {
 | 
					  batchEdit() {  // redirect to batch edit
 | 
				
			||||||
    if (this.sampleSelect) {
 | 
					    if (this.sampleSelect) {
 | 
				
			||||||
      this.router.navigate(['/samples/edit/' + this.samples.filter(e => e.selected).map(e => e._id).join(',')]);
 | 
					      this.router.navigate(['/samples/edit/' + this.samples.filter(e => e.selected).map(e => e._id).join(',')]);
 | 
				
			||||||
      this.sampleSelect = 0;
 | 
					      this.sampleSelect = 0;
 | 
				
			||||||
@@ -457,7 +468,7 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  selectAll(event) {
 | 
					  selectAll(event) {  // toggle select all except deleted samples
 | 
				
			||||||
    this.samples.forEach(sample => {
 | 
					    this.samples.forEach(sample => {
 | 
				
			||||||
      if (sample.status !== 'deleted') {
 | 
					      if (sample.status !== 'deleted') {
 | 
				
			||||||
        sample.selected = event.target.checked;
 | 
					        sample.selected = event.target.checked;
 | 
				
			||||||
@@ -478,13 +489,13 @@ export class SamplesComponent implements OnInit {
 | 
				
			|||||||
    event.stopPropagation();
 | 
					    event.stopPropagation();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clipboard() {
 | 
					  clipboard() {  // copy contents to clipboard
 | 
				
			||||||
    this.linkarea.nativeElement.select();
 | 
					    this.linkarea.nativeElement.select();
 | 
				
			||||||
    this.linkarea.nativeElement.setSelectionRange(0, 99999);
 | 
					    this.linkarea.nativeElement.setSelectionRange(0, 99999);
 | 
				
			||||||
    document.execCommand('copy');
 | 
					    document.execCommand('copy');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ucFirst(string) {
 | 
					  ucFirst(string) {  // convert first character of string to uppercase
 | 
				
			||||||
    return string[0].toUpperCase() + string.slice(1);
 | 
					    return string[0].toUpperCase() + string.slice(1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,143 +1,5 @@
 | 
				
			|||||||
import {async, TestBed} from '@angular/core/testing';
 | 
					import {async, TestBed} from '@angular/core/testing';
 | 
				
			||||||
import { ApiService } from './api.service';
 | 
					 | 
				
			||||||
import {HttpClient} from '@angular/common/http';
 | 
					 | 
				
			||||||
import {LocalStorageService} from 'angular-2-local-storage';
 | 
					 | 
				
			||||||
import {Observable} from 'rxjs';
 | 
					 | 
				
			||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let apiService: ApiService;
 | 
					 | 
				
			||||||
let httpClientSpy: jasmine.SpyObj<HttpClient>;
 | 
					 | 
				
			||||||
let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
 | 
					 | 
				
			||||||
let modalServiceSpy: jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
let windowServiceSpy: jasmine.SpyObj<Window>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
// TODO: test options
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ApiService', () => {
 | 
					describe('ApiService', () => {
 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    const httpSpy = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put', 'delete']);
 | 
					 | 
				
			||||||
    const localStorageSpy = jasmine.createSpyObj('LocalStorageService', ['get']);
 | 
					 | 
				
			||||||
    const modalSpy = jasmine.createSpyObj('ModalService', ['openComponent']);
 | 
					 | 
				
			||||||
    const windowSpy = jasmine.createSpyObj('Window', ['location']);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        ApiService,
 | 
					 | 
				
			||||||
        {provide: HttpClient, useValue: httpSpy},
 | 
					 | 
				
			||||||
        {provide: LocalStorageService, useValue: localStorageSpy},
 | 
					 | 
				
			||||||
        {provide: ModalService, useValue: modalSpy},
 | 
					 | 
				
			||||||
        {provide: Window, useValue: windowSpy}
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiService = TestBed.inject(ApiService);
 | 
					 | 
				
			||||||
    httpClientSpy = TestBed.inject(HttpClient) as jasmine.SpyObj<HttpClient>;
 | 
					 | 
				
			||||||
    localStorageServiceSpy = TestBed.inject(LocalStorageService) as jasmine.SpyObj<LocalStorageService>;
 | 
					 | 
				
			||||||
    modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
    windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should be created', () => {
 | 
					 | 
				
			||||||
    expect(apiService).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('shows an error message when the request fails', () => {
 | 
					 | 
				
			||||||
    const getReturn = new Observable(observer => {
 | 
					 | 
				
			||||||
      observer.error('error');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    httpClientSpy.get.and.returnValue(getReturn);
 | 
					 | 
				
			||||||
    localStorageServiceSpy.get.and.returnValue(undefined);
 | 
					 | 
				
			||||||
    modalServiceSpy.openComponent.and.returnValue({instance: {message: ''}} as any);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiService.get('/testurl');
 | 
					 | 
				
			||||||
    expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
 | 
					 | 
				
			||||||
    expect(modalServiceSpy.openComponent.calls.count()).toBe(1);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('returns the error message if the callback function had an error parameter', () => {
 | 
					 | 
				
			||||||
    const getReturn = new Observable(observer => {
 | 
					 | 
				
			||||||
      observer.error('error');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    httpClientSpy.get.and.returnValue(getReturn);
 | 
					 | 
				
			||||||
    localStorageServiceSpy.get.and.returnValue(undefined);
 | 
					 | 
				
			||||||
    modalServiceSpy.openComponent.and.returnValue({instance: {message: ''}} as any);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiService.get('/testurl', (data, error) => {
 | 
					 | 
				
			||||||
      expect(modalServiceSpy.openComponent.calls.count()).toBe(0);
 | 
					 | 
				
			||||||
      expect(error).toBe('error');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should do get requests without auth if not available', async(() => {
 | 
					 | 
				
			||||||
    const getReturn = new Observable(observer => {
 | 
					 | 
				
			||||||
      observer.next({body: 'data', headers: {keys: () => []}});
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    httpClientSpy.get.and.returnValue(getReturn);
 | 
					 | 
				
			||||||
    localStorageServiceSpy.get.and.returnValue(undefined);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiService.get('/testurl', res => {
 | 
					 | 
				
			||||||
      expect(res).toBe('data');
 | 
					 | 
				
			||||||
      expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
 | 
					 | 
				
			||||||
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should do get requests with basic auth if available', async(() => {
 | 
					 | 
				
			||||||
    const getReturn = new Observable(observer => {
 | 
					 | 
				
			||||||
      observer.next('data');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    httpClientSpy.get.and.returnValue(getReturn);
 | 
					 | 
				
			||||||
    localStorageServiceSpy.get.and.returnValue('basicAuth');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiService.get('/testurl', res => {
 | 
					 | 
				
			||||||
      expect(res).toBe('data');
 | 
					 | 
				
			||||||
      expect(httpClientSpy.get).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
 | 
					 | 
				
			||||||
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should do post requests', async(() => {
 | 
					 | 
				
			||||||
    const resReturn = new Observable(observer => {
 | 
					 | 
				
			||||||
      observer.next('data');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    httpClientSpy.post.and.returnValue(resReturn);
 | 
					 | 
				
			||||||
    localStorageServiceSpy.get.and.returnValue('basicAuth');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiService.post('/testurl', 'reqData', res => {
 | 
					 | 
				
			||||||
      expect(res).toBe('data');
 | 
					 | 
				
			||||||
      expect(httpClientSpy.post).toHaveBeenCalledWith('/api/testurl', 'reqData', jasmine.any(Object));
 | 
					 | 
				
			||||||
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should do put requests', async(() => {
 | 
					 | 
				
			||||||
    const resReturn = new Observable(observer => {
 | 
					 | 
				
			||||||
      observer.next('data');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    httpClientSpy.put.and.returnValue(resReturn);
 | 
					 | 
				
			||||||
    localStorageServiceSpy.get.and.returnValue('basicAuth');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiService.put('/testurl', 'reqData', res => {
 | 
					 | 
				
			||||||
      expect(res).toBe('data');
 | 
					 | 
				
			||||||
      expect(httpClientSpy.put).toHaveBeenCalledWith('/api/testurl', 'reqData', jasmine.any(Object));
 | 
					 | 
				
			||||||
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should do delete requests', async(() => {
 | 
					 | 
				
			||||||
    const resReturn = new Observable(observer => {
 | 
					 | 
				
			||||||
      observer.next('data');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    httpClientSpy.delete.and.returnValue(resReturn);
 | 
					 | 
				
			||||||
    localStorageServiceSpy.get.and.returnValue('basicAuth');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiService.delete('/testurl', res => {
 | 
					 | 
				
			||||||
      expect(res).toBe('data');
 | 
					 | 
				
			||||||
      expect(httpClientSpy.delete).toHaveBeenCalledWith('/api/testurl', jasmine.any(Object));
 | 
					 | 
				
			||||||
      expect(localStorageServiceSpy.get).toHaveBeenCalledWith('basicAuth');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // TODO: test return headers
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ import {Observable} from 'rxjs';
 | 
				
			|||||||
import {ErrorComponent} from '../error/error.component';
 | 
					import {ErrorComponent} from '../error/error.component';
 | 
				
			||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
					import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: find solution when client wants to visit subpage but is not logged in to redirect to login without showing request failed errors
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable({
 | 
					@Injectable({
 | 
				
			||||||
  providedIn: 'root'
 | 
					  providedIn: 'root'
 | 
				
			||||||
@@ -25,6 +24,7 @@ export class ApiService {
 | 
				
			|||||||
    return this.host;
 | 
					    return this.host;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // main HTTP methods
 | 
				
			||||||
  get<T>(url, f: (data?: T, err?, headers?) => void = () => {}) {
 | 
					  get<T>(url, f: (data?: T, err?, headers?) => void = () => {}) {
 | 
				
			||||||
    this.requestErrorHandler<T>(this.http.get(this.url(url), this.options()), f);
 | 
					    this.requestErrorHandler<T>(this.http.get(this.url(url), this.options()), f);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -41,20 +41,25 @@ export class ApiService {
 | 
				
			|||||||
    this.requestErrorHandler<T>(this.http.delete(this.url(url), this.options()), f);
 | 
					    this.requestErrorHandler<T>(this.http.delete(this.url(url), this.options()), f);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // execute request and handle errors
 | 
				
			||||||
  private requestErrorHandler<T>(observable: Observable<any>, f: (data?: T, err?, headers?) => void) {
 | 
					  private requestErrorHandler<T>(observable: Observable<any>, f: (data?: T, err?, headers?) => void) {
 | 
				
			||||||
    observable.subscribe(data => {
 | 
					    observable.subscribe(data => {  // successful request
 | 
				
			||||||
      f(data.body, undefined, data.headers.keys().reduce((s, e) => {s[e.toLowerCase()] = data.headers.get(e); return s; }, {}));
 | 
					      f(
 | 
				
			||||||
    }, err => {
 | 
					        data.body,
 | 
				
			||||||
      if (f.length > 1) {
 | 
					        undefined,
 | 
				
			||||||
 | 
					        data.headers.keys().reduce((s, e) => {s[e.toLowerCase()] = data.headers.get(e); return s; }, {})
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }, err => {  // error
 | 
				
			||||||
 | 
					      if (f.length > 1) {  // pass on error
 | 
				
			||||||
        f(undefined, err, undefined);
 | 
					        f(undefined, err, undefined);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else {  // handle directly
 | 
				
			||||||
        this.requestError(err);
 | 
					        this.requestError(err);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  requestError(err) {
 | 
					  requestError(err) {  // network error dialog
 | 
				
			||||||
    const modalRef = this.modalService.openComponent(ErrorComponent);
 | 
					    const modalRef = this.modalService.openComponent(ErrorComponent);
 | 
				
			||||||
    modalRef.instance.message = 'Network request failed!';
 | 
					    modalRef.instance.message = 'Network request failed!';
 | 
				
			||||||
    const details = [err.error.status];
 | 
					    const details = [err.error.status];
 | 
				
			||||||
@@ -67,7 +72,7 @@ export class ApiService {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private url(url) {
 | 
					  private url(url) {  // detect if host was given, otherwise use default host
 | 
				
			||||||
    if (/http[s]?:\/\//.test(url)) {
 | 
					    if (/http[s]?:\/\//.test(url)) {
 | 
				
			||||||
      return url;
 | 
					      return url;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -76,10 +81,12 @@ export class ApiService {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // generate request options
 | 
				
			||||||
  private options(): {headers: HttpHeaders, observe: 'body'} {
 | 
					  private options(): {headers: HttpHeaders, observe: 'body'} {
 | 
				
			||||||
    return {headers: this.authOptions(), observe: 'response' as 'body'};
 | 
					    return {headers: this.authOptions(), observe: 'response' as 'body'};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // generate Basic Auth
 | 
				
			||||||
  private authOptions(): HttpHeaders {
 | 
					  private authOptions(): HttpHeaders {
 | 
				
			||||||
    const auth = this.storage.get('basicAuth');
 | 
					    const auth = this.storage.get('basicAuth');
 | 
				
			||||||
    if (auth) {
 | 
					    if (auth) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,37 +1,5 @@
 | 
				
			|||||||
import { TestBed } from '@angular/core/testing';
 | 
					import { TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AutocompleteService } from './autocomplete.service';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let autocompleteService: AutocompleteService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('AutocompleteService', () => {
 | 
					describe('AutocompleteService', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      providers: [AutocompleteService]
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    autocompleteService = TestBed.inject(AutocompleteService);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should be created', () => {
 | 
					 | 
				
			||||||
    expect(autocompleteService).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should should return a bind function', () => {
 | 
					 | 
				
			||||||
    expect(autocompleteService.bind('a', ['b'])).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should return search results', () => {
 | 
					 | 
				
			||||||
    autocompleteService.search(['aa', 'ab', 'bb'], 'a').subscribe(res => {
 | 
					 | 
				
			||||||
      expect(res).toEqual(['aa', 'ab']);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should return an empty array if no result was found', () => {
 | 
					 | 
				
			||||||
    autocompleteService.search(['aa', 'ab', 'bb'], 'c').subscribe(res => {
 | 
					 | 
				
			||||||
      expect(res).toEqual([]);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,33 +1,5 @@
 | 
				
			|||||||
import { TestBed } from '@angular/core/testing';
 | 
					import { TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DataService } from './data.service';
 | 
					 | 
				
			||||||
import {ApiService} from './api.service';
 | 
					 | 
				
			||||||
import {HttpClient} from '@angular/common/http';
 | 
					 | 
				
			||||||
import {LocalStorageService} from 'angular-2-local-storage';
 | 
					 | 
				
			||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('DataService', () => {
 | 
					describe('DataService', () => {
 | 
				
			||||||
  let service: DataService;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        {provide: ApiService, useValue: apiSpy}
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    service = TestBed.inject(DataService);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should be created', () => {
 | 
					 | 
				
			||||||
    expect(service).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ export class DataService {
 | 
				
			|||||||
    private api: ApiService
 | 
					    private api: ApiService
 | 
				
			||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private collectionMap = {
 | 
					  private collectionMap = {  // list of available collections
 | 
				
			||||||
    materials: {path: '/materials?status[]=validated&status[]=new', model: MaterialModel, type: 'idArray'},
 | 
					    materials: {path: '/materials?status[]=validated&status[]=new', model: MaterialModel, type: 'idArray'},
 | 
				
			||||||
    materialSuppliers: {path: '/material/suppliers', model: null, type: 'idArray'},
 | 
					    materialSuppliers: {path: '/material/suppliers', model: null, type: 'idArray'},
 | 
				
			||||||
    materialGroups: {path: '/material/groups', model: null, type: 'idArray'},
 | 
					    materialGroups: {path: '/material/groups', model: null, type: 'idArray'},
 | 
				
			||||||
@@ -36,7 +36,7 @@ export class DataService {
 | 
				
			|||||||
  id: {[key: string]: {[id: string]: any}} = {};  // data in format _id: data
 | 
					  id: {[key: string]: {[id: string]: any}} = {};  // data in format _id: data
 | 
				
			||||||
  d: {[key: string]: any} = {};      // data not in array format
 | 
					  d: {[key: string]: any} = {};      // data not in array format
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  contact = {name: 'CR/APS1-Lingenfelser', mail: 'dominic.lingenfelser@bosch.com'};
 | 
					  contact = {name: 'CR/APS1-Lingenfelser', mail: 'dominic.lingenfelser@bosch.com'};  // global contact data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  load(collection, f = () => {}) {  // load data
 | 
					  load(collection, f = () => {}) {  // load data
 | 
				
			||||||
    if (this.arr[collection]) { // data already loaded
 | 
					    if (this.arr[collection]) { // data already loaded
 | 
				
			||||||
@@ -46,7 +46,11 @@ export class DataService {
 | 
				
			|||||||
      this.api.get<any>(this.collectionMap[collection].path, data => {
 | 
					      this.api.get<any>(this.collectionMap[collection].path, data => {
 | 
				
			||||||
        if (this.collectionMap[collection].type !== 'string') {  // array data
 | 
					        if (this.collectionMap[collection].type !== 'string') {  // array data
 | 
				
			||||||
          this.arr[collection] = data
 | 
					          this.arr[collection] = data
 | 
				
			||||||
            .map(e => this.collectionMap[collection].model ? new this.collectionMap[collection].model().deserialize(e) : e);
 | 
					            .map(
 | 
				
			||||||
 | 
					              e => this.collectionMap[collection].model ?
 | 
				
			||||||
 | 
					                new this.collectionMap[collection].model().deserialize(e) : e
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          // load ids
 | 
				
			||||||
          if (this.collectionMap[collection].type === 'idArray' || this.collectionMap[collection].type === 'template') {
 | 
					          if (this.collectionMap[collection].type === 'idArray' || this.collectionMap[collection].type === 'template') {
 | 
				
			||||||
            this.idReload(collection);
 | 
					            this.idReload(collection);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
@@ -59,9 +63,10 @@ export class DataService {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // generate id object
 | 
				
			||||||
  idReload(collection) {
 | 
					  idReload(collection) {
 | 
				
			||||||
    this.id[collection] = this.arr[collection].reduce((s, e) => {s[e._id] = e; return s; }, {});
 | 
					    this.id[collection] = this.arr[collection].reduce((s, e) => {s[e._id] = e; return s; }, {});
 | 
				
			||||||
    if (this.collectionMap[collection].type === 'template') {
 | 
					    if (this.collectionMap[collection].type === 'template') {  // generate array with latest templates
 | 
				
			||||||
      const tmpTemplates = {};
 | 
					      const tmpTemplates = {};
 | 
				
			||||||
      this.arr[collection].forEach(template => {
 | 
					      this.arr[collection].forEach(template => {
 | 
				
			||||||
        if (tmpTemplates[template.first_id]) {  // already found another version
 | 
					        if (tmpTemplates[template.first_id]) {  // already found another version
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,95 +1,5 @@
 | 
				
			|||||||
import { TestBed } from '@angular/core/testing';
 | 
					import { TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { LoginService } from './login.service';
 | 
					 | 
				
			||||||
import {LocalStorageService} from 'angular-2-local-storage';
 | 
					 | 
				
			||||||
import {ApiService} from './api.service';
 | 
					 | 
				
			||||||
import {Router} from '@angular/router';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let loginService: LoginService;
 | 
					 | 
				
			||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
 | 
					 | 
				
			||||||
let routerServiceSpy: jasmine.SpyObj<Router>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('LoginService', () => {
 | 
					describe('LoginService', () => {
 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    const apiSpy = jasmine.createSpyObj('ApiService', ['get']);
 | 
					 | 
				
			||||||
    const localStorageSpy = jasmine.createSpyObj('LocalStorageService', ['set', 'remove']);
 | 
					 | 
				
			||||||
    const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        LoginService,
 | 
					 | 
				
			||||||
        {provide: ApiService, useValue: apiSpy},
 | 
					 | 
				
			||||||
        {provide: LocalStorageService, useValue: localStorageSpy},
 | 
					 | 
				
			||||||
        {provide: Router, useValue: routerSpy}
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    loginService = TestBed.inject(LoginService);
 | 
					 | 
				
			||||||
    apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
    localStorageServiceSpy = TestBed.inject(LocalStorageService) as jasmine.SpyObj<LocalStorageService>;
 | 
					 | 
				
			||||||
    routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should be created', () => {
 | 
					 | 
				
			||||||
    expect(loginService).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // describe('login', () => {
 | 
					 | 
				
			||||||
  //   it('should store the basic auth', () => {
 | 
					 | 
				
			||||||
  //     localStorageServiceSpy.set.and.returnValue(true);
 | 
					 | 
				
			||||||
  //     apiServiceSpy.get.and.callFake(() => {});
 | 
					 | 
				
			||||||
  //     loginService.login('username', 'password');
 | 
					 | 
				
			||||||
  //     expect(localStorageServiceSpy.set).toHaveBeenCalledWith('basicAuth', 'dXNlcm5hbWU6cGFzc3dvcmQ=');
 | 
					 | 
				
			||||||
  //   });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   it('should remove the basic auth if login fails', () => {
 | 
					 | 
				
			||||||
  //     localStorageServiceSpy.set.and.returnValue(true);
 | 
					 | 
				
			||||||
  //     localStorageServiceSpy.remove.and.returnValue(true);
 | 
					 | 
				
			||||||
  //     apiServiceSpy.get.and.callFake((a, b) => {b(undefined, 'error'); });
 | 
					 | 
				
			||||||
  //     loginService.login('username', 'password');
 | 
					 | 
				
			||||||
  //     expect(localStorageServiceSpy.remove.calls.count()).toBe(1);
 | 
					 | 
				
			||||||
  //     expect(localStorageServiceSpy.remove).toHaveBeenCalledWith('basicAuth');
 | 
					 | 
				
			||||||
  //   });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   it('should resolve true when login succeeds', async () => {
 | 
					 | 
				
			||||||
  //     localStorageServiceSpy.set.and.returnValue(true);
 | 
					 | 
				
			||||||
  //     apiServiceSpy.get.and.callFake((a, b) => {b({status: 'Authorization successful', method: 'basic'} as any, undefined); });
 | 
					 | 
				
			||||||
  //     expect(await loginService.login('username', 'password')).toBeTruthy();
 | 
					 | 
				
			||||||
  //   });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   it('should resolve false when a wrong result comes in', async () => {
 | 
					 | 
				
			||||||
  //     localStorageServiceSpy.set.and.returnValue(true);
 | 
					 | 
				
			||||||
  //     apiServiceSpy.get.and.callFake((a, b) => {b({status: 'xxx', method: 'basic'} as any, undefined); });
 | 
					 | 
				
			||||||
  //     expect(await loginService.login('username', 'password')).toBeFalsy();
 | 
					 | 
				
			||||||
  //   });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   it('should resolve false on an error', async () => {
 | 
					 | 
				
			||||||
  //     localStorageServiceSpy.set.and.returnValue(true);
 | 
					 | 
				
			||||||
  //     apiServiceSpy.get.and.callFake((a, b) => {b(undefined, 'error'); });
 | 
					 | 
				
			||||||
  //     expect(await loginService.login('username', 'password')).toBeFalsy();
 | 
					 | 
				
			||||||
  //   });
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // describe('canActivate', () => {
 | 
					 | 
				
			||||||
  //   it('should return false at first', done => {
 | 
					 | 
				
			||||||
  //     apiServiceSpy.get.and.callFake((a, b) => {b(undefined, 'error'); });
 | 
					 | 
				
			||||||
  //     loginService.canActivate(null, null).subscribe(res => {
 | 
					 | 
				
			||||||
  //       expect(res).toBeFalsy();
 | 
					 | 
				
			||||||
  //       done();
 | 
					 | 
				
			||||||
  //     });
 | 
					 | 
				
			||||||
  //   });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  //   it('returns true if login was successful', async done => {
 | 
					 | 
				
			||||||
  //     localStorageServiceSpy.set.and.returnValue(true);
 | 
					 | 
				
			||||||
  //     apiServiceSpy.get.and.callFake((a, b) => {b({status: 'Authorization successful', method: 'basic'} as any, undefined); });
 | 
					 | 
				
			||||||
  //     await loginService.login('username', 'password');
 | 
					 | 
				
			||||||
  //     loginService.canActivate(null, null).subscribe(res => {
 | 
					 | 
				
			||||||
  //       expect(res).toBeTruthy();
 | 
					 | 
				
			||||||
  //       done();
 | 
					 | 
				
			||||||
  //     });
 | 
					 | 
				
			||||||
  //   });
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,19 +10,20 @@ import {DataService} from './data.service';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class LoginService implements CanActivate {
 | 
					export class LoginService implements CanActivate {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private pathPermissions = [
 | 
					  private pathPermissions = [  // minimum level needed for the specified paths
 | 
				
			||||||
    {path: 'materials', permission: 'dev'},
 | 
					    {path: 'materials', permission: 'dev'},
 | 
				
			||||||
    {path: 'templates', permission: 'dev'},
 | 
					    {path: 'templates', permission: 'dev'},
 | 
				
			||||||
    {path: 'changelog', permission: 'dev'},
 | 
					    {path: 'changelog', permission: 'dev'},
 | 
				
			||||||
    {path: 'users', permission: 'admin'}
 | 
					    {path: 'users', permission: 'admin'}
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
  readonly levels = [
 | 
					  readonly levels = [  // all user levels in ascending permissions order
 | 
				
			||||||
    'predict',
 | 
					    'predict',
 | 
				
			||||||
    'read',
 | 
					    'read',
 | 
				
			||||||
    'write',
 | 
					    'write',
 | 
				
			||||||
    'dev',
 | 
					    'dev',
 | 
				
			||||||
    'admin'
 | 
					    'admin'
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					  // returns true or false depending on whether the user fulfills the minimum level
 | 
				
			||||||
  isLevel: {[level: string]: boolean} = {};
 | 
					  isLevel: {[level: string]: boolean} = {};
 | 
				
			||||||
  hasPrediction = false;  // true if user has prediction models specified
 | 
					  hasPrediction = false;  // true if user has prediction models specified
 | 
				
			||||||
  userId = '';
 | 
					  userId = '';
 | 
				
			||||||
@@ -99,6 +100,7 @@ export class LoginService implements CanActivate {
 | 
				
			|||||||
    this.hasPrediction = false;
 | 
					    this.hasPrediction = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // canActivate for Angular routing
 | 
				
			||||||
  canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable<boolean> {
 | 
					  canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable<boolean> {
 | 
				
			||||||
    return new Observable<boolean>(observer => {
 | 
					    return new Observable<boolean>(observer => {
 | 
				
			||||||
      new Promise(resolve => {
 | 
					      new Promise(resolve => {
 | 
				
			||||||
@@ -112,7 +114,8 @@ export class LoginService implements CanActivate {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }).then(res => {
 | 
					      }).then(res => {
 | 
				
			||||||
        const pathPermission = this.pathPermissions.find(e => e.path.indexOf(route.url[0].path) >= 0);
 | 
					        const pathPermission = this.pathPermissions.find(e => e.path.indexOf(route.url[0].path) >= 0);
 | 
				
			||||||
        const ok = res && (!pathPermission || this.isLevel[pathPermission.permission]); // check if level is permitted for path
 | 
					        // check if level is permitted for path
 | 
				
			||||||
 | 
					        const ok = res && (!pathPermission || this.isLevel[pathPermission.permission]);
 | 
				
			||||||
        observer.next(ok);
 | 
					        observer.next(ok);
 | 
				
			||||||
        observer.complete();
 | 
					        observer.complete();
 | 
				
			||||||
        if (!ok) {
 | 
					        if (!ok) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,116 +1,5 @@
 | 
				
			|||||||
import { TestBed } from '@angular/core/testing';
 | 
					import { TestBed } from '@angular/core/testing';
 | 
				
			||||||
import { ValidationService } from './validation.service';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let validationService: ValidationService;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ValidationService', () => {
 | 
					describe('ValidationService', () => {
 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      providers: [ValidationService]
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    validationService = TestBed.inject(ValidationService);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should be created', () => {
 | 
					 | 
				
			||||||
    expect(validationService).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // it('should return true on a correct username', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.username('abc')).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on an incorrect username', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.username('abc#')).toEqual({ok: false, error: 'username must only contain a-z0-9-_.'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return true on a correct password', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.password('Abc123!#')).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a password too short', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.password('Abc123')).toEqual({ok: false, error: 'password must have at least 8 characters'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a password without a lowercase letter', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.password('ABC123!#')).toEqual({ok: false, error: 'password must have at least one lowercase character'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a password without an uppercase letter', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.password('abc123!#')).toEqual({ok: false, error: 'password must have at least one uppercase character'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a password without a number', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.password('Abcabc!#')).toEqual({ok: false, error: 'password must have at least one number'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a password without a special character', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.password('Abc12345')).toEqual({ok: false, error: 'password must have at least one of the following characters !"#%&\'()*+,-.\\/:;<=>?@[]^_`{|}~'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a password with a character not allowed', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.password('Abc123!€')).toEqual({ok: false, error: 'password must only contain a-zA-Z0-9!"#%&\'()*+,-./:;<=>?@[]^_`{|}~'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return true on a correct string', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.string('Abc')).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a string too long', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.string('abcabcabcbabcbabcabcabacbabcabcabcbabcbabcabcabacbabcabcabcbabcbabcabcabacbabcabcabcbabcbabcabcabacbabcabcabcbabcbabcabcabacbacab')).toEqual({ok: false, error: 'must contain max 128 characters'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return true on a string in the list', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.stringOf('Abc', ['Abc', 'Def'])).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a string not in the list', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.stringOf('abc', ['Abc', 'Def'])).toEqual({ok: false, error: 'must be one of Abc, Def'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return true on a string of correct length', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.stringLength('Abc', 5)).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a string longer than specified', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.stringLength('Abc', 2)).toEqual({ok: false, error: 'must contain max 2 characters'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return true on a number in the range', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.minMax(2, -2, 2)).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a number below the range', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.minMax(0, 1, 3)).toEqual({ok: false, error: 'must be between 1 and 3'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a number above the range', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.minMax(3.1, 1, 3)).toEqual({ok: false, error: 'must be between 1 and 3'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return true on a number above min', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.min(2, -2)).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a number below min', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.min(0, 1)).toEqual({ok: false, error: 'must not be below 1'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return true on a number below max', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.max(2, 2)).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a number above max', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.max(2, 1)).toEqual({ok: false, error: 'must not be above 1'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return true on a string not in the list', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.unique('Abc', ['Def', 'Ghi'])).toEqual({ok: true, error: ''});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // it('should return an error on a string from the list', () => {
 | 
					 | 
				
			||||||
  //   expect(validationService.unique('Abc', ['Abc', 'Def'])).toEqual({ok: false, error: 'values must be unique'});
 | 
					 | 
				
			||||||
  // });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,7 +63,7 @@ export class ValidationService {
 | 
				
			|||||||
    return {ok: true, error: ''};
 | 
					    return {ok: true, error: ''};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  stringOf(data, list) {
 | 
					  stringOf(data, list) {  // string must be in list
 | 
				
			||||||
    const {ignore, error} = Joi.string().allow('').valid(...list.map(e => e.toString())).validate(data);
 | 
					    const {ignore, error} = Joi.string().allow('').valid(...list.map(e => e.toString())).validate(data);
 | 
				
			||||||
    if (error) {
 | 
					    if (error) {
 | 
				
			||||||
      return {ok: false, error: 'must be one of ' + list.join(', ')};
 | 
					      return {ok: false, error: 'must be one of ' + list.join(', ')};
 | 
				
			||||||
@@ -71,7 +71,7 @@ export class ValidationService {
 | 
				
			|||||||
    return {ok: true, error: ''};
 | 
					    return {ok: true, error: ''};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  stringNin(data, list) {
 | 
					  stringNin(data, list) {  // string must not be in list
 | 
				
			||||||
    const {ignore, error} = Joi.string().invalid(...list).validate(data);
 | 
					    const {ignore, error} = Joi.string().invalid(...list).validate(data);
 | 
				
			||||||
    if (error) {
 | 
					    if (error) {
 | 
				
			||||||
      return {ok: false, error: 'value not allowed'};
 | 
					      return {ok: false, error: 'value not allowed'};
 | 
				
			||||||
@@ -79,7 +79,7 @@ export class ValidationService {
 | 
				
			|||||||
    return {ok: true, error: ''};
 | 
					    return {ok: true, error: ''};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  stringLength(data, length) {
 | 
					  stringLength(data, length) {  // string with maximum length
 | 
				
			||||||
    const {ignore, error} = Joi.string().max(length).allow('').validate(data);
 | 
					    const {ignore, error} = Joi.string().max(length).allow('').validate(data);
 | 
				
			||||||
    if (error) {
 | 
					    if (error) {
 | 
				
			||||||
      return {ok: false, error: 'must contain max ' + length + ' characters'};
 | 
					      return {ok: false, error: 'must contain max ' + length + ' characters'};
 | 
				
			||||||
@@ -87,7 +87,7 @@ export class ValidationService {
 | 
				
			|||||||
    return {ok: true, error: ''};
 | 
					    return {ok: true, error: ''};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  minMax(data, min, max) {
 | 
					  minMax(data, min, max) {  // number between min and max
 | 
				
			||||||
    const {ignore, error} = Joi.number().allow('').min(min).max(max).validate(data);
 | 
					    const {ignore, error} = Joi.number().allow('').min(min).max(max).validate(data);
 | 
				
			||||||
    if (error) {
 | 
					    if (error) {
 | 
				
			||||||
      return {ok: false, error: `must be between ${min} and ${max}`};
 | 
					      return {ok: false, error: `must be between ${min} and ${max}`};
 | 
				
			||||||
@@ -95,7 +95,7 @@ export class ValidationService {
 | 
				
			|||||||
    return {ok: true, error: ''};
 | 
					    return {ok: true, error: ''};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  min(data, min) {
 | 
					  min(data, min) {  // number above min
 | 
				
			||||||
    const {ignore, error} = Joi.number().allow('').min(min).validate(data);
 | 
					    const {ignore, error} = Joi.number().allow('').min(min).validate(data);
 | 
				
			||||||
    if (error) {
 | 
					    if (error) {
 | 
				
			||||||
      return {ok: false, error: `must not be below ${min}`};
 | 
					      return {ok: false, error: `must not be below ${min}`};
 | 
				
			||||||
@@ -103,7 +103,7 @@ export class ValidationService {
 | 
				
			|||||||
    return {ok: true, error: ''};
 | 
					    return {ok: true, error: ''};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  max(data, max) {
 | 
					  max(data, max) {  // number below max
 | 
				
			||||||
    const {ignore, error} = Joi.number().allow('').max(max).validate(data);
 | 
					    const {ignore, error} = Joi.number().allow('').max(max).validate(data);
 | 
				
			||||||
    if (error) {
 | 
					    if (error) {
 | 
				
			||||||
      return {ok: false, error: `must not be above ${max}`};
 | 
					      return {ok: false, error: `must not be above ${max}`};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
<form #userForm="ngForm">
 | 
					<form #userForm="ngForm">
 | 
				
			||||||
  <rb-form-input name="name" label="user name" appValidate="username" required [(ngModel)]="user.name"
 | 
					  <rb-form-input name="name" label="user name" appValidate="username" required [(ngModel)]="user.name"
 | 
				
			||||||
                 #nameInput="ngModel">
 | 
					                 #nameInput="ngModel">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,62 +1,5 @@
 | 
				
			|||||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// import { SettingsComponent } from './settings.component';
 | 
					describe('SettingsComponent', () => {
 | 
				
			||||||
// import {ApiService} from '../services/api.service';
 | 
					
 | 
				
			||||||
// import {LoginService} from '../services/login.service';
 | 
					});
 | 
				
			||||||
// import {Router} from '@angular/router';
 | 
					 | 
				
			||||||
// import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
// import {FormsModule} from '@angular/forms';
 | 
					 | 
				
			||||||
// import {ValidationService} from '../services/validation.service';
 | 
					 | 
				
			||||||
// import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// // TODO
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// let routerServiceSpy: jasmine.SpyObj<Router>;
 | 
					 | 
				
			||||||
// let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
// let loginServiceSpy: jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
// let validationServiceSpy: jasmine.SpyObj<ValidationService>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// describe('SettingsComponent', () => {
 | 
					 | 
				
			||||||
//   let component: SettingsComponent;
 | 
					 | 
				
			||||||
//   let fixture: ComponentFixture<SettingsComponent>;
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(async(() => {
 | 
					 | 
				
			||||||
//     const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
 | 
					 | 
				
			||||||
//     const apiSpy = jasmine.createSpyObj('ApiService', ['get', 'post', 'put']);
 | 
					 | 
				
			||||||
//     const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']);
 | 
					 | 
				
			||||||
//     const validationSpy = jasmine.createSpyObj('ValidationService', ['generate']);
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
//       declarations: [ SettingsComponent ],
 | 
					 | 
				
			||||||
//       imports: [
 | 
					 | 
				
			||||||
//         RbUiComponentsModule,
 | 
					 | 
				
			||||||
//         RbCustomInputsModule,
 | 
					 | 
				
			||||||
//         FormsModule
 | 
					 | 
				
			||||||
//       ],
 | 
					 | 
				
			||||||
//       providers: [
 | 
					 | 
				
			||||||
//         {provide: Router, useValue: routerSpy},
 | 
					 | 
				
			||||||
//         {provide: ApiService, useValue: apiSpy},
 | 
					 | 
				
			||||||
//         {provide: LoginService, useValue: loginSpy},
 | 
					 | 
				
			||||||
//         {provide: ValidationService, useValue: validationSpy},
 | 
					 | 
				
			||||||
//       ]
 | 
					 | 
				
			||||||
//     })
 | 
					 | 
				
			||||||
//     .compileComponents();
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//     routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
 | 
					 | 
				
			||||||
//     apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
//     loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
//     validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
 | 
					 | 
				
			||||||
//   }));
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   beforeEach(() => {
 | 
					 | 
				
			||||||
//     fixture = TestBed.createComponent(SettingsComponent);
 | 
					 | 
				
			||||||
//     component = fixture.componentInstance;
 | 
					 | 
				
			||||||
//     component.ngOnInit();
 | 
					 | 
				
			||||||
//     fixture.detectChanges();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   it('should create', () => {
 | 
					 | 
				
			||||||
//     expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
// });
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,9 +12,9 @@ import {LoginService} from '../services/login.service';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class SettingsComponent implements OnInit {
 | 
					export class SettingsComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  user: UserModel = new UserModel();
 | 
					  user: UserModel = new UserModel();  // user to edit
 | 
				
			||||||
  password = '';
 | 
					  password = '';                      // new password
 | 
				
			||||||
  messageUser = '';
 | 
					  messageUser = '';                   // messages for user and pass part
 | 
				
			||||||
  messagePass = '';
 | 
					  messagePass = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
@@ -34,7 +34,7 @@ export class SettingsComponent implements OnInit {
 | 
				
			|||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
        this.messageUser = err.error.status;
 | 
					        this.messageUser = err.error.status;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else {  // login with new credentials
 | 
				
			||||||
        this.login.login(data.name).then(res => {
 | 
					        this.login.login(data.name).then(res => {
 | 
				
			||||||
          if (res) {
 | 
					          if (res) {
 | 
				
			||||||
            this.router.navigate(['/samples']);
 | 
					            this.router.navigate(['/samples']);
 | 
				
			||||||
@@ -52,7 +52,7 @@ export class SettingsComponent implements OnInit {
 | 
				
			|||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
        this.messagePass = err.error.status;
 | 
					        this.messagePass = err.error.status;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      else {
 | 
					      else {  // login with new credentials
 | 
				
			||||||
        this.login.login('', this.password).then(res => {
 | 
					        this.login.login('', this.password).then(res => {
 | 
				
			||||||
          if (res) {
 | 
					          if (res) {
 | 
				
			||||||
            this.router.navigate(['/samples']);
 | 
					            this.router.navigate(['/samples']);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
<rb-form-select name="collectionSelection" label="collection"
 | 
					<rb-form-select name="collectionSelection" label="collection"
 | 
				
			||||||
                [(ngModel)]="collection" (ngModelChange)="loadTemplates()">
 | 
					                [(ngModel)]="collection" (ngModelChange)="loadTemplates()">
 | 
				
			||||||
  <option value="material">Materials</option>
 | 
					  <option value="material">Materials</option>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,57 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { TemplatesComponent } from './templates.component';
 | 
					 | 
				
			||||||
import {LoginService} from '../services/login.service';
 | 
					 | 
				
			||||||
import {ValidationService} from '../services/validation.service';
 | 
					 | 
				
			||||||
import {ApiService} from '../services/api.service';
 | 
					 | 
				
			||||||
import {DataService} from '../services/data.service';
 | 
					 | 
				
			||||||
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
import {FormsModule} from '@angular/forms';
 | 
					 | 
				
			||||||
import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
let validationServiceSpy: jasmine.SpyObj<ValidationService>;
 | 
					 | 
				
			||||||
let dataServiceSpy: jasmine.SpyObj<DataService>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('TemplatesComponent', () => {
 | 
					describe('TemplatesComponent', () => {
 | 
				
			||||||
  let component: TemplatesComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<TemplatesComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
 | 
					 | 
				
			||||||
    const validationSpy = jasmine.createSpyObj('ValidationService', ['string', 'parameterName', 'parameterRange']);
 | 
					 | 
				
			||||||
    const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ TemplatesComponent ],
 | 
					 | 
				
			||||||
      imports: [
 | 
					 | 
				
			||||||
        RbUiComponentsModule,
 | 
					 | 
				
			||||||
        RbCustomInputsModule,
 | 
					 | 
				
			||||||
        FormsModule
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        {provide: ApiService, useValue: apiSpy},
 | 
					 | 
				
			||||||
        {provide: ValidationService, useValue: validationSpy},
 | 
					 | 
				
			||||||
        {provide: DataService, useValue: dataSpy}
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
    validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
 | 
					 | 
				
			||||||
    dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(TemplatesComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,12 +28,18 @@ import {DataService} from '../services/data.service';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class TemplatesComponent implements OnInit {
 | 
					export class TemplatesComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  collection = 'measurement';
 | 
					  collection = 'measurement';       // collection to view
 | 
				
			||||||
  templates: TemplateModel[] = [];
 | 
					  templates: TemplateModel[] = [];  // all templates of current collection
 | 
				
			||||||
  templateGroups: {[first_id: string]: TemplateModel[]} = {};  // templates grouped by first_id
 | 
					  templateGroups: {[first_id: string]: TemplateModel[]} = {};  // templates grouped by first_id
 | 
				
			||||||
  templateEdit: {[first_id: string]: TemplateModel} = {};      // latest template of each first_id for editing
 | 
					  templateEdit: {[first_id: string]: TemplateModel} = {};      // latest template of each first_id for editing
 | 
				
			||||||
  groupsView: {first_id: string, name: string, version: number, expanded: boolean, edit: boolean, entries: TemplateModel[]}[] = [];
 | 
					  groupsView: {  // grouped templates
 | 
				
			||||||
  arr = ['testA', 'testB', 'testC'];
 | 
					    first_id: string,
 | 
				
			||||||
 | 
					    name: string,
 | 
				
			||||||
 | 
					    version: number,
 | 
				
			||||||
 | 
					    expanded: boolean,
 | 
				
			||||||
 | 
					    edit: boolean,
 | 
				
			||||||
 | 
					    entries: TemplateModel[]
 | 
				
			||||||
 | 
					  }[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private api: ApiService,
 | 
					    private api: ApiService,
 | 
				
			||||||
@@ -55,7 +61,7 @@ export class TemplatesComponent implements OnInit {
 | 
				
			|||||||
  templateFormat() {
 | 
					  templateFormat() {
 | 
				
			||||||
    this.templateGroups = {};
 | 
					    this.templateGroups = {};
 | 
				
			||||||
    this.templateEdit = {};
 | 
					    this.templateEdit = {};
 | 
				
			||||||
    this.templates.forEach(template => {
 | 
					    this.templates.forEach(template => {  // group templates
 | 
				
			||||||
      if (this.templateGroups[template.first_id]) {
 | 
					      if (this.templateGroups[template.first_id]) {
 | 
				
			||||||
        this.templateGroups[template.first_id].push(template);
 | 
					        this.templateGroups[template.first_id].push(template);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -94,7 +100,7 @@ export class TemplatesComponent implements OnInit {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
    if (valid) {
 | 
					    if (valid) {
 | 
				
			||||||
      const sendData = {name: template.name, parameters: template.parameters.map(e => omit(e, ['rangeString']))};
 | 
					      const sendData = {name: template.name, parameters: template.parameters.map(e => omit(e, ['rangeString']))};
 | 
				
			||||||
      if (first_id === 'null') {
 | 
					      if (first_id === 'null') {  // new template
 | 
				
			||||||
        this.api.post<TemplateModel>(`/template/${this.collection}/new`, sendData, () => {
 | 
					        this.api.post<TemplateModel>(`/template/${this.collection}/new`, sendData, () => {
 | 
				
			||||||
          delete this.d.arr[this.collection + 'Templates'];
 | 
					          delete this.d.arr[this.collection + 'Templates'];
 | 
				
			||||||
          this.d.load(this.collection + 'Templates', () => {
 | 
					          this.d.load(this.collection + 'Templates', () => {
 | 
				
			||||||
@@ -113,16 +119,20 @@ export class TemplatesComponent implements OnInit {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					 | 
				
			||||||
      console.log('not valid');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  newTemplate() {
 | 
					  newTemplate() {
 | 
				
			||||||
    if (!this.templateEdit.null) {
 | 
					    if (!this.templateEdit.null) {
 | 
				
			||||||
      const template = new TemplateModel();
 | 
					      const template = new TemplateModel();
 | 
				
			||||||
      template.name = 'new template';
 | 
					      template.name = 'new template';
 | 
				
			||||||
      this.groupsView.push({first_id: 'null', name: 'new template', version: 0, expanded: true, edit: true, entries: [template]});
 | 
					      this.groupsView.push({
 | 
				
			||||||
 | 
					        first_id: 'null',
 | 
				
			||||||
 | 
					        name: 'new template',
 | 
				
			||||||
 | 
					        version: 0,
 | 
				
			||||||
 | 
					        expanded: true,
 | 
				
			||||||
 | 
					        edit: true,
 | 
				
			||||||
 | 
					        entries: [template]
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
      this.templateEdit.null = new TemplateModel();
 | 
					      this.templateEdit.null = new TemplateModel();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
<rb-icon-button icon="add" mode="primary" (click)="addNewUser()" class="space-below">New user</rb-icon-button>
 | 
					<rb-icon-button icon="add" mode="primary" (click)="addNewUser()" class="space-below">New user</rb-icon-button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<form *ngIf="newUser" #userForm="ngForm" class="space-below">
 | 
					<form *ngIf="newUser" #userForm="ngForm" class="space-below">
 | 
				
			||||||
@@ -15,7 +14,8 @@
 | 
				
			|||||||
    <option *ngFor="let level of login.levels" [value]="level">{{level}}</option>
 | 
					    <option *ngFor="let level of login.levels" [value]="level">{{level}}</option>
 | 
				
			||||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
					    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
				
			||||||
  </rb-form-select>
 | 
					  </rb-form-select>
 | 
				
			||||||
  <rb-form-input name="location" label="location" appValidate="string" required [appValidateArgs]="['alphanum']"
 | 
					  <rb-form-input name="location" label="location (Abbreviation, eg. Rng, for sample number)" appValidate="string"
 | 
				
			||||||
 | 
					                 [appValidateArgs]="['alphanum']" required
 | 
				
			||||||
                 [(ngModel)]="newUser.location" #locationInput="ngModel">
 | 
					                 [(ngModel)]="newUser.location" #locationInput="ngModel">
 | 
				
			||||||
    <ng-template rbFormValidationMessage="failure">{{locationInput.errors.failure}}</ng-template>
 | 
					    <ng-template rbFormValidationMessage="failure">{{locationInput.errors.failure}}</ng-template>
 | 
				
			||||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
					    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
				
			||||||
@@ -100,13 +100,13 @@
 | 
				
			|||||||
      <td>
 | 
					      <td>
 | 
				
			||||||
        <rb-array-input [(ngModel)]="user.devices" name="devices" [pushTemplate]="''">
 | 
					        <rb-array-input [(ngModel)]="user.devices" name="devices" [pushTemplate]="''">
 | 
				
			||||||
          <rb-form-input *rbArrayInputItem="let item" rbArrayInputListener="devices" [index]="item.i"
 | 
					          <rb-form-input *rbArrayInputItem="let item" rbArrayInputListener="devices" [index]="item.i"
 | 
				
			||||||
                         label="device" appValidate="string" [name]="'device-' + item.i" [ngModel]="item.value">
 | 
					                         appValidate="string" [name]="'device-' + item.i" [ngModel]="item.value">
 | 
				
			||||||
          </rb-form-input>
 | 
					          </rb-form-input>
 | 
				
			||||||
        </rb-array-input>
 | 
					        </rb-array-input>
 | 
				
			||||||
      </td>
 | 
					      </td>
 | 
				
			||||||
      <td>
 | 
					      <td>
 | 
				
			||||||
        <rb-array-input [(ngModel)]="user.models" name="devices" [pushTemplate]="''">
 | 
					        <rb-array-input [(ngModel)]="user.models" name="devices" [pushTemplate]="''">
 | 
				
			||||||
          <rb-form-select *rbArrayInputItem="let item" rbArrayInputListener="models" [index]="item.i" label="model"
 | 
					          <rb-form-select *rbArrayInputItem="let item" rbArrayInputListener="models" [index]="item.i"
 | 
				
			||||||
                          [name]="'model-' + item.i" [ngModel]="item.value">
 | 
					                          [name]="'model-' + item.i" [ngModel]="item.value">
 | 
				
			||||||
            <ng-container *ngTemplateOutlet="modelOptions"></ng-container>
 | 
					            <ng-container *ngTemplateOutlet="modelOptions"></ng-container>
 | 
				
			||||||
          </rb-form-select>
 | 
					          </rb-form-select>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,53 +1,5 @@
 | 
				
			|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
					import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { UsersComponent } from './users.component';
 | 
					 | 
				
			||||||
import {ApiService} from '../services/api.service';
 | 
					 | 
				
			||||||
import {LoginService} from '../services/login.service';
 | 
					 | 
				
			||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
					 | 
				
			||||||
import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let apiServiceSpy: jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
let modalServiceSpy: jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
let loginServiceSpy: jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('UsersComponent', () => {
 | 
					describe('UsersComponent', () => {
 | 
				
			||||||
  let component: UsersComponent;
 | 
					 | 
				
			||||||
  let fixture: ComponentFixture<UsersComponent>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async(() => {
 | 
					 | 
				
			||||||
    const apiSpy = jasmine.createSpyObj('ApiService', ['get', 'post', 'put']);
 | 
					 | 
				
			||||||
    const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
 | 
					 | 
				
			||||||
    const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    TestBed.configureTestingModule({
 | 
					 | 
				
			||||||
      declarations: [ UsersComponent ],
 | 
					 | 
				
			||||||
      imports: [
 | 
					 | 
				
			||||||
        RbCustomInputsModule
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      providers: [
 | 
					 | 
				
			||||||
        {provide: ApiService, useValue: apiSpy},
 | 
					 | 
				
			||||||
        {provide: ModalService, useValue: modalSpy},
 | 
					 | 
				
			||||||
        {provide: LoginService, useValue: loginSpy},
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
 | 
					 | 
				
			||||||
    modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
 | 
					 | 
				
			||||||
    loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
 | 
					 | 
				
			||||||
  }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  beforeEach(() => {
 | 
					 | 
				
			||||||
    fixture = TestBed.createComponent(UsersComponent);
 | 
					 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					 | 
				
			||||||
    component.ngOnInit();
 | 
					 | 
				
			||||||
    fixture.detectChanges();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  it('should create', () => {
 | 
					 | 
				
			||||||
    expect(component).toBeTruthy();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,12 +13,12 @@ import {DataService} from '../services/data.service';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class UsersComponent implements OnInit {
 | 
					export class UsersComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  users: UserModel[] = [];
 | 
					  users: UserModel[] = [];           // all active users
 | 
				
			||||||
  deletedUsers: UserModel[] = [];
 | 
					  deletedUsers: UserModel[] = [];    // all deleted users
 | 
				
			||||||
  newUser: UserModel | null = null;
 | 
					  newUser: UserModel | null = null;  // data of new user
 | 
				
			||||||
  newUserPass = '';
 | 
					  newUserPass = '';                  // password of new user
 | 
				
			||||||
  modelSelect: {id: string, name: string}[] = [];
 | 
					  modelSelect: {id: string, name: string}[] = [];  // list of all models for selection
 | 
				
			||||||
  modelIds: {[id: string]: string} = {};
 | 
					  modelIds: {[id: string]: string} = {};           // all models by id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private api: ApiService,
 | 
					    private api: ApiService,
 | 
				
			||||||
@@ -41,9 +41,7 @@ export class UsersComponent implements OnInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  saveUser(user: UserModel) {
 | 
					  saveUser(user: UserModel) {
 | 
				
			||||||
    console.log(user.models);
 | 
					 | 
				
			||||||
    user.models = user.models.filter(e => e !== '');
 | 
					    user.models = user.models.filter(e => e !== '');
 | 
				
			||||||
    console.log(user.models);
 | 
					 | 
				
			||||||
    this.api.put<UserModel>('/user/' + user.origName, user.sendFormat('admin'), data => {
 | 
					    this.api.put<UserModel>('/user/' + user.origName, user.sendFormat('admin'), data => {
 | 
				
			||||||
      user.deserialize(data);
 | 
					      user.deserialize(data);
 | 
				
			||||||
      user.edit = false;
 | 
					      user.edit = false;
 | 
				
			||||||
@@ -51,7 +49,7 @@ export class UsersComponent implements OnInit {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  saveNewUser() {
 | 
					  saveNewUser() {
 | 
				
			||||||
    // this.newUser.models = this.newUser.models.filter(e => e !== '' || e !== undefined);
 | 
					    this.newUser.models = this.newUser.models.filter(e => e !== '');
 | 
				
			||||||
    this.api.post('/user/new', {...this.newUser.sendFormat('admin'), pass: this.newUserPass}, data => {
 | 
					    this.api.post('/user/new', {...this.newUser.sendFormat('admin'), pass: this.newUserPass}, data => {
 | 
				
			||||||
      this.newUser = null;
 | 
					      this.newUser = null;
 | 
				
			||||||
      this.users.push(new UserModel().deserialize(data));
 | 
					      this.users.push(new UserModel().deserialize(data));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,5 @@
 | 
				
			|||||||
// import { ValidateDirective } from './validate.directive';
 | 
					import { ValidateDirective } from './validate.directive';
 | 
				
			||||||
// import {ValidationService} from './services/validation.service';
 | 
					
 | 
				
			||||||
//
 | 
					describe('ValidateDirective', () => {
 | 
				
			||||||
// // TODO
 | 
					
 | 
				
			||||||
//
 | 
					});
 | 
				
			||||||
// const validationSpy = {test: () => {}};
 | 
					 | 
				
			||||||
// const validationServiceSpy: jasmine.SpyObj<any> = spyOn(validationSpy, 'test');
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// describe('ValidateDirective', () => {
 | 
					 | 
				
			||||||
//   it('should create an instance', () => {
 | 
					 | 
				
			||||||
//     const directive = new ValidateDirective(validationServiceSpy);
 | 
					 | 
				
			||||||
//     expect(directive).toBeTruthy();
 | 
					 | 
				
			||||||
//   });
 | 
					 | 
				
			||||||
// });
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user