material and activeModelIndex fix
This commit is contained in:
		@@ -13,6 +13,8 @@ import {DocumentationDatabaseComponent} from './documentation/documentation-data
 | 
			
		||||
import {PredictionComponent} from './prediction/prediction.component';
 | 
			
		||||
import {ModelTemplatesComponent} from './model-templates/model-templates.component';
 | 
			
		||||
import {DocumentationArchitectureComponent} from './documentation/documentation-architecture/documentation-architecture.component';
 | 
			
		||||
import {MaterialsComponent} from './materials/materials.component';
 | 
			
		||||
import {MaterialComponent} from './material/material.component';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@@ -23,6 +25,8 @@ const routes: Routes = [
 | 
			
		||||
  {path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'samples/edit/:id', component: SampleComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'materials', component: MaterialsComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'materials/edit/:id', component: MaterialComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'templates', component: TemplatesComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'changelog', component: ChangelogComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'users', component: UsersComponent, canActivate: [LoginService]},
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
    <a routerLink="/prediction" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Prediction</a>
 | 
			
		||||
    <a routerLink="/models" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Models</a>
 | 
			
		||||
    <a routerLink="/samples" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.read">Samples</a>
 | 
			
		||||
    <a routerLink="/materials" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Materials</a>
 | 
			
		||||
    <a routerLink="/templates" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">
 | 
			
		||||
      Templates
 | 
			
		||||
    </a>
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,8 @@ import { HelpComponent } from './help/help.component';
 | 
			
		||||
import { ModelTemplatesComponent } from './model-templates/model-templates.component';
 | 
			
		||||
import { SizePipe } from './size.pipe';
 | 
			
		||||
import { DocumentationArchitectureComponent } from './documentation/documentation-architecture/documentation-architecture.component';
 | 
			
		||||
import { MaterialsComponent } from './materials/materials.component';
 | 
			
		||||
import { MaterialComponent } from './material/material.component';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@@ -56,7 +58,9 @@ import { DocumentationArchitectureComponent } from './documentation/documentatio
 | 
			
		||||
    HelpComponent,
 | 
			
		||||
    ModelTemplatesComponent,
 | 
			
		||||
    SizePipe,
 | 
			
		||||
    DocumentationArchitectureComponent
 | 
			
		||||
    DocumentationArchitectureComponent,
 | 
			
		||||
    MaterialsComponent,
 | 
			
		||||
    MaterialComponent
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    LocalStorageModule.forRoot({
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										65
									
								
								src/app/material/material.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/app/material/material.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
<h2>Edit material</h2>
 | 
			
		||||
 | 
			
		||||
<form #materialForm="ngForm" *ngIf="!loading">
 | 
			
		||||
  <rb-form-input name="materialname" label="material name" appValidate="stringNin" [appValidateArgs]="[materialNames]"
 | 
			
		||||
                 required [(ngModel)]="material.name" #materialnameInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{materialnameInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="supplier" label="supplier"
 | 
			
		||||
                 [rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialSuppliers)"
 | 
			
		||||
                 [rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
 | 
			
		||||
                 [(ngModel)]="material.supplier" #supplierInput="ngModel"
 | 
			
		||||
                 (focusout)="checkTypo($event, 'materialSuppliers', 'supplier', modalWarning)"
 | 
			
		||||
                 title="material supplier, eg. BASF">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{supplierInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="group" label="group"
 | 
			
		||||
                 [rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialGroups)"
 | 
			
		||||
                 [rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
 | 
			
		||||
                 [(ngModel)]="material.group" #groupInput="ngModel"
 | 
			
		||||
                 (focusout)="checkTypo($event, 'materialGroups', 'group', modalWarning)"
 | 
			
		||||
                 title="chemical material type, eg. PA66">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{groupInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
 | 
			
		||||
  <ng-template #modalWarning>
 | 
			
		||||
    <rb-alert alertTitle="Warning" type="warning" okBtnLabel="Use suggestion" cancelBtnLabel="Keep value">
 | 
			
		||||
      The specified {{modalText.list}} could not be found in the list. <br>
 | 
			
		||||
      Did you mean {{modalText.suggestion}}?
 | 
			
		||||
    </rb-alert>
 | 
			
		||||
  </ng-template>
 | 
			
		||||
  <rb-array-input [(ngModel)]="material.numbers" name="materialNumbers" [pushTemplate]="''">
 | 
			
		||||
    <rb-form-input *rbArrayInputItem="let item" [rbArrayInputListener]="'materialNumber'" [index]="item.i"
 | 
			
		||||
                   label="material number" appValidate="string" [name]="'materialNumber-' + item.i"
 | 
			
		||||
                   [ngModel]="item.value" title="Bosch material part number, eg. 5515753021"></rb-form-input>
 | 
			
		||||
  </rb-array-input>
 | 
			
		||||
  <rb-form-select name="propertiesSelect" label="Type" title="=overall material group specific properties"
 | 
			
		||||
                  [(ngModel)]="material.properties.material_template">
 | 
			
		||||
    <option *ngFor="let m of d.latest.materialTemplates" [value]="m._id">{{m.name}}</option>
 | 
			
		||||
  </rb-form-select>
 | 
			
		||||
  <rb-form-input *ngFor="let parameter of
 | 
			
		||||
                           d.id.materialTemplates[material.properties.material_template].parameters;
 | 
			
		||||
                         index as i" [name]="'materialParameter' + i"
 | 
			
		||||
                 [label]="parameter.name" appValidate="string" required
 | 
			
		||||
                 [(ngModel)]="material.properties[parameter.name]" #parameterInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
 | 
			
		||||
  <rb-icon-button icon="save" mode="primary" type="submit" (click)="materialSave()"
 | 
			
		||||
                  [disabled]="materialForm.form.invalid">
 | 
			
		||||
    Save material
 | 
			
		||||
  </rb-icon-button>
 | 
			
		||||
  <rb-icon-button class="delete-material" icon="delete" mode="danger" (click)="deleteConfirm(modalDeleteConfirm)">
 | 
			
		||||
    Delete sample
 | 
			
		||||
  </rb-icon-button>
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<ng-template #modalDeleteConfirm>
 | 
			
		||||
  <rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete material'"
 | 
			
		||||
            cancelBtnLabel="Cancel">
 | 
			
		||||
    Do you really want to delete {{material.name}}?
 | 
			
		||||
  </rb-alert>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								src/app/material/material.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/app/material/material.component.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
.delete-material {
 | 
			
		||||
  float: right;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/app/material/material.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/material/material.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { MaterialComponent } from './material.component';
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										137
									
								
								src/app/material/material.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/app/material/material.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
import {AfterContentChecked, Component, OnInit, TemplateRef, ViewChild} from '@angular/core';
 | 
			
		||||
import {MaterialModel} from '../models/material.model';
 | 
			
		||||
import {ApiService} from '../services/api.service';
 | 
			
		||||
import {ActivatedRoute, Router} from '@angular/router';
 | 
			
		||||
import {DataService} from '../services/data.service';
 | 
			
		||||
import strCompare from 'str-compare';
 | 
			
		||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
			
		||||
import {AutocompleteService} from '../services/autocomplete.service';
 | 
			
		||||
import {NgForm, Validators} from '@angular/forms';
 | 
			
		||||
import {ValidationService} from '../services/validation.service';
 | 
			
		||||
import {ErrorComponent} from '../error/error.component';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-material',
 | 
			
		||||
  templateUrl: './material.component.html',
 | 
			
		||||
  styleUrls: ['./material.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class MaterialComponent implements OnInit, AfterContentChecked {
 | 
			
		||||
 | 
			
		||||
  @ViewChild('materialForm') materialForm: NgForm;
 | 
			
		||||
 | 
			
		||||
  material: MaterialModel;
 | 
			
		||||
  materialNames: string[] = [];
 | 
			
		||||
 | 
			
		||||
  modalText = {list: '', suggestion: ''};
 | 
			
		||||
  loading = 0;
 | 
			
		||||
  checkFormAfterInit = true;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private api: ApiService,
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
    public d: DataService,
 | 
			
		||||
    private modal: ModalService,
 | 
			
		||||
    public autocomplete: AutocompleteService,
 | 
			
		||||
    private router: Router,
 | 
			
		||||
    private validation: ValidationService
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.loading = 5;
 | 
			
		||||
    this.api.get<MaterialModel>('/material/' + this.route.snapshot.paramMap.get('id'), data => {
 | 
			
		||||
      this.material = new MaterialModel().deserialize(data);
 | 
			
		||||
      this.loading--;
 | 
			
		||||
      this.d.load('materials', () => {
 | 
			
		||||
        this.materialNames = this.d.arr.materials.map(e => e.name).filter(e => e !== this.material.name);
 | 
			
		||||
        this.loading--;
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    this.d.load('materialSuppliers', () => {
 | 
			
		||||
      this.loading--;
 | 
			
		||||
    });
 | 
			
		||||
    this.d.load('materialGroups', () => {
 | 
			
		||||
      this.loading--;
 | 
			
		||||
    });
 | 
			
		||||
    this.d.load('materialTemplates', () => {
 | 
			
		||||
      this.loading--;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngAfterContentChecked() {
 | 
			
		||||
    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.attachValidator(this.materialForm, 'materialParameter' + i, parameter.range);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.checkFormAfterInit && this.materialForm !== undefined && this.materialForm.form.get('propertiesSelect')) {
 | 
			
		||||
      this.checkFormAfterInit = false;
 | 
			
		||||
      Object.keys(this.materialForm.form.controls).forEach(field => {
 | 
			
		||||
        this.materialForm.form.get(field).updateValueAndValidity();
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // attach validators specified in range to input with name
 | 
			
		||||
  attachValidator(form, name: string, range: {[prop: string]: any}) {
 | 
			
		||||
    if (form && form.form.get(name)) {
 | 
			
		||||
      const validators = [];
 | 
			
		||||
      if (range.hasOwnProperty('required')) {
 | 
			
		||||
        validators.push(Validators.required);
 | 
			
		||||
      }
 | 
			
		||||
      if (range.hasOwnProperty('values')) {
 | 
			
		||||
        validators.push(this.validation.generate('stringOf', [range.values]));
 | 
			
		||||
      }
 | 
			
		||||
      else if (range.hasOwnProperty('min') && range.hasOwnProperty('max')) {
 | 
			
		||||
        validators.push(this.validation.generate('minMax', [range.min, range.max]));
 | 
			
		||||
      }
 | 
			
		||||
      else if (range.hasOwnProperty('min')) {
 | 
			
		||||
        validators.push(this.validation.generate('min', [range.min]));
 | 
			
		||||
      }
 | 
			
		||||
      else if (range.hasOwnProperty('max')) {
 | 
			
		||||
        validators.push(this.validation.generate('max', [range.max]));
 | 
			
		||||
      }
 | 
			
		||||
      form.form.get(name).setValidators(validators);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  materialSave() {
 | 
			
		||||
    this.api.put('/material/' + this.material._id, this.material.sendFormat(), () => {
 | 
			
		||||
      this.router.navigate(['/materials']);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  deleteConfirm(modal) {
 | 
			
		||||
    this.modal.open(modal).then(result => {
 | 
			
		||||
      if (result) {
 | 
			
		||||
        this.api.delete('/material/' + this.material._id, (ignore, error) => {
 | 
			
		||||
          if (error) {
 | 
			
		||||
            const modalRef = this.modal.openComponent(ErrorComponent);
 | 
			
		||||
            modalRef.instance.message = 'Cannot delete material as it is still in use!';
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            this.router.navigate(['/materials']);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  checkTypo(event, list, mKey, modal: TemplateRef<any>) {
 | 
			
		||||
    // user did not click on suggestion and entry is not in list
 | 
			
		||||
    if (!(event.relatedTarget && (event.relatedTarget.className.indexOf('rb-dropdown-item') >= 0 ||
 | 
			
		||||
      event.relatedTarget.className.indexOf('close-btn rb-btn rb-passive-link') >= 0)) &&
 | 
			
		||||
      this.d.arr[list].indexOf(this.material[mKey]) < 0) {
 | 
			
		||||
      this.modalText.list = mKey;
 | 
			
		||||
      this.modalText.suggestion = this.d.arr[list]  // find possible entry from list
 | 
			
		||||
        .map(e => ({v: e, s: strCompare.sorensenDice(e, this.material[mKey])}))
 | 
			
		||||
        .sort((a, b) => b.s - a.s)[0].v;
 | 
			
		||||
      this.modal.open(modal).then(result => {
 | 
			
		||||
        if (result) {  // use suggestion
 | 
			
		||||
          this.material[mKey] = this.modalText.suggestion;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								src/app/materials/materials.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/app/materials/materials.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
<div class="header-addnew">
 | 
			
		||||
  <h2>Materials</h2>
 | 
			
		||||
  <rb-icon-button *ngIf="sampleSelect" mode="secondary" icon="close" (click)="sampleSelect = false"
 | 
			
		||||
                  class="validation-close" iconOnly></rb-icon-button>
 | 
			
		||||
  <rb-icon-button [icon]="sampleSelect ? 'checkmark' : 'clear-all'"
 | 
			
		||||
                  mode="secondary" (click)="validate()">
 | 
			
		||||
    {{sampleSelect ? 'Validate' : 'Validation'}}
 | 
			
		||||
  </rb-icon-button>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="status-selection">
 | 
			
		||||
  <label class="label">Status</label>
 | 
			
		||||
  <rb-form-checkbox name="status-validated" [(ngModel)]="materialStatus.validated"
 | 
			
		||||
                    [disabled]="!materialStatus.new && !materialStatus.deleted"
 | 
			
		||||
                    (ngModelChange)="loadMaterials()">
 | 
			
		||||
    validated
 | 
			
		||||
  </rb-form-checkbox>
 | 
			
		||||
  <rb-form-checkbox name="status-new" [(ngModel)]="materialStatus.new"
 | 
			
		||||
                    [disabled]="!materialStatus.validated && !materialStatus.deleted"
 | 
			
		||||
                    (ngModelChange)="loadMaterials()">
 | 
			
		||||
    new
 | 
			
		||||
  </rb-form-checkbox>
 | 
			
		||||
  <rb-form-checkbox name="status-deleted" [(ngModel)]="materialStatus.deleted"
 | 
			
		||||
                    [disabled]="!materialStatus.validated && !materialStatus.new"
 | 
			
		||||
                    (ngModelChange)="loadMaterials()">
 | 
			
		||||
    deleted
 | 
			
		||||
  </rb-form-checkbox>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<ng-container *ngTemplateOutlet="paging"></ng-container>
 | 
			
		||||
 | 
			
		||||
<rb-table ellipsis scrollTop>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <th *ngIf="sampleSelect">
 | 
			
		||||
      <rb-form-checkbox name="select-all" (change)="selectAll($event)">all</rb-form-checkbox>
 | 
			
		||||
    </th>
 | 
			
		||||
    <th>Name</th>
 | 
			
		||||
    <th>Supplier</th>
 | 
			
		||||
    <th>Group</th>
 | 
			
		||||
    <th *ngFor="let key of templateKeys">{{key.label}}</th>
 | 
			
		||||
    <th>Numbers</th>
 | 
			
		||||
    <th></th>
 | 
			
		||||
  </tr>
 | 
			
		||||
  <tr *ngFor="let material of (materials || []).slice((page - 1) * pageSize, page * pageSize); index as i">
 | 
			
		||||
    <td *ngIf="sampleSelect">
 | 
			
		||||
      <rb-form-checkbox *ngIf="material.status !== 'deleted'" [name]="'validate-' + i"
 | 
			
		||||
                        [(ngModel)]="material.selected">
 | 
			
		||||
      </rb-form-checkbox>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td>{{material.name}}</td>
 | 
			
		||||
    <td>{{material.supplier}}</td>
 | 
			
		||||
    <td>{{material.group}}</td>
 | 
			
		||||
    <td *ngFor="let key of templateKeys">{{material.properties[key.key] | exists}}</td>
 | 
			
		||||
    <td>{{material.numbers}}</td>
 | 
			
		||||
    <td>
 | 
			
		||||
      <a [routerLink]="'/materials/edit/' + material._id" *ngIf="material.status !== 'deleted'">
 | 
			
		||||
        <span class="rb-ic rb-ic-edit clickable"></span>
 | 
			
		||||
      </a>
 | 
			
		||||
      <span class="rb-ic rb-ic-undo clickable" *ngIf="material.status === 'deleted'"
 | 
			
		||||
            (click)="restoreMaterial(material._id, restoreConfirm)"></span>
 | 
			
		||||
  </td>
 | 
			
		||||
  </tr>
 | 
			
		||||
</rb-table>
 | 
			
		||||
 | 
			
		||||
<ng-container *ngTemplateOutlet="paging"></ng-container>
 | 
			
		||||
 | 
			
		||||
<ng-template #paging>
 | 
			
		||||
  <div class="paging">
 | 
			
		||||
    <button class="rb-btn rb-link" type="button" (click)="page = page - 1" [disabled]="page === 1">
 | 
			
		||||
      <span class="rb-ic  rb-ic-back-left"></span>
 | 
			
		||||
    </button>
 | 
			
		||||
    <rb-form-input label="page" [(ngModel)]="page"></rb-form-input>
 | 
			
		||||
    <span>
 | 
			
		||||
      of {{pages}}
 | 
			
		||||
    </span>
 | 
			
		||||
    <button class="rb-btn rb-link" type="button" (click)="page = page + 1" [disabled]="page >= pages">
 | 
			
		||||
      <span class="rb-ic  rb-ic-forward-right"></span>
 | 
			
		||||
    </button>
 | 
			
		||||
  </div>
 | 
			
		||||
</ng-template>
 | 
			
		||||
 | 
			
		||||
<ng-template #restoreConfirm>
 | 
			
		||||
  <rb-dialog dialogTitle="Restore sample">
 | 
			
		||||
    Do you really want to restore this sample?
 | 
			
		||||
  </rb-dialog>
 | 
			
		||||
</ng-template>
 | 
			
		||||
							
								
								
									
										53
									
								
								src/app/materials/materials.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/app/materials/materials.component.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
.paging {
 | 
			
		||||
  height: 50px;
 | 
			
		||||
  float: left;
 | 
			
		||||
 | 
			
		||||
  rb-form-input {
 | 
			
		||||
    max-width: 65px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  > * {
 | 
			
		||||
    float: left;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  > button {
 | 
			
		||||
    margin-top: 18px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  > span {
 | 
			
		||||
    margin-top: 20px;
 | 
			
		||||
    margin-left: 5px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.status-selection {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
  float: left;
 | 
			
		||||
  margin-right: 15px;
 | 
			
		||||
 | 
			
		||||
  label {
 | 
			
		||||
    display: block;
 | 
			
		||||
    font-weight: 700;
 | 
			
		||||
    font-size: 10px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  rb-form-checkbox {
 | 
			
		||||
    float: left;
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
    margin-top: -10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.header-addnew {
 | 
			
		||||
  margin-bottom: 40px;
 | 
			
		||||
 | 
			
		||||
  & > * {
 | 
			
		||||
    display: inline;
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  rb-icon-button {
 | 
			
		||||
    float: right;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/app/materials/materials.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/materials/materials.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { MaterialsComponent } from './materials.component';
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										92
									
								
								src/app/materials/materials.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/app/materials/materials.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import {DataService} from '../services/data.service';
 | 
			
		||||
import {MaterialModel} from '../models/material.model';
 | 
			
		||||
import {ApiService} from '../services/api.service';
 | 
			
		||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-materials',
 | 
			
		||||
  templateUrl: './materials.component.html',
 | 
			
		||||
  styleUrls: ['./materials.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class MaterialsComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  materials: MaterialModel[] = [];
 | 
			
		||||
  templateKeys: {key: string, label: string}[] = [];
 | 
			
		||||
  materialStatus = {validated: true, new: true, deleted: false};
 | 
			
		||||
  sampleSelect = false;
 | 
			
		||||
 | 
			
		||||
  page = 1;
 | 
			
		||||
  pages = 0;
 | 
			
		||||
  pageSize = 25;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private api: ApiService,
 | 
			
		||||
    public d: DataService,
 | 
			
		||||
    private modal: ModalService
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.loadMaterials();
 | 
			
		||||
    this.d.load('materialTemplates', () => {
 | 
			
		||||
      this.d.arr.materialTemplates.forEach(template => {
 | 
			
		||||
        template.parameters.forEach(parameter => {
 | 
			
		||||
          this.templateKeys.push({key: parameter.name, label: `${this.ucFirst(template.name)} ${parameter.name}`});
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      this.templateKeys = this.templateKeys.filter((e, i, a) => !a.slice(0, i).find(el => el.key === e.key));
 | 
			
		||||
      console.log(this.templateKeys);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadMaterials() {
 | 
			
		||||
    this.api.get<MaterialModel[]>('/materials?' +
 | 
			
		||||
      Object.entries(this.materialStatus).filter(e => e[1]).map(e => 'status[]=' + e[0]).join('&'), data => {
 | 
			
		||||
      this.materials = data.map(e => new MaterialModel().deserialize(e));
 | 
			
		||||
      this.pages = Math.ceil(this.materials.length / this.pageSize);
 | 
			
		||||
      this.page = 1;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validate() {
 | 
			
		||||
    if (this.sampleSelect) {
 | 
			
		||||
      this.materials.forEach(sample => {
 | 
			
		||||
        if (sample.selected) {
 | 
			
		||||
          this.api.put('/material/validate/' + sample._id);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      this.loadMaterials();
 | 
			
		||||
      this.sampleSelect = false;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      this.sampleSelect = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  selectAll(event) {
 | 
			
		||||
    this.materials.forEach(material => {
 | 
			
		||||
      if (material.status !== 'deleted') {
 | 
			
		||||
        material.selected = event.target.checked;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        material.selected = false;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  restoreMaterial(id, modal) {
 | 
			
		||||
    this.modal.open(modal).then(res => {
 | 
			
		||||
      if (res) {
 | 
			
		||||
        this.api.put('/sample/restore/' + id, {}, ignore => {
 | 
			
		||||
          this.materials.find(e => e._id === id).status = 'new';
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ucFirst(string) {
 | 
			
		||||
    return string[0].toUpperCase() + string.slice(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,7 @@ import {ModelItemModel} from '../models/model-item.model';
 | 
			
		||||
import {ApiService} from '../services/api.service';
 | 
			
		||||
import {AutocompleteService} from '../services/autocomplete.service';
 | 
			
		||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
 | 
			
		||||
import omit from 'lodash/omit';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-model-templates',
 | 
			
		||||
@@ -44,7 +45,7 @@ export class ModelTemplatesComponent implements OnInit {
 | 
			
		||||
    if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) {  // group was changed, delete model in old group
 | 
			
		||||
      this.delete(null, this.oldModelGroup, this.oldModelName);
 | 
			
		||||
    }
 | 
			
		||||
    this.api.post('/model/' + this.modelGroup, this.model, () => {
 | 
			
		||||
    this.api.post('/model/' + this.modelGroup, omit(this.model, '_id'), () => {
 | 
			
		||||
      this.newModel = false;
 | 
			
		||||
      this.loadGroups();
 | 
			
		||||
      this.modelGroup = '';
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,8 @@ export class MaterialModel extends BaseModel {
 | 
			
		||||
  group = '';
 | 
			
		||||
  properties: {material_template: string, [prop: string]: string} = {material_template: null};
 | 
			
		||||
  numbers: string[] = [''];
 | 
			
		||||
  selected = false;
 | 
			
		||||
  status = '';
 | 
			
		||||
 | 
			
		||||
  sendFormat() {
 | 
			
		||||
    return pick(this, ['name', 'supplier', 'group', 'numbers', 'properties']);
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
    <h4>
 | 
			
		||||
      Average result: {{result.mean}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
 | 
			
		||||
    </h4>
 | 
			
		||||
    <a href="javascript:" class="rb-details-toggle" rbDetailsToggle #triggerDetails="rbDetailsToggle">Details</a>
 | 
			
		||||
    <a class="rb-details-toggle" rbDetailsToggle #triggerDetails="rbDetailsToggle">Details</a>
 | 
			
		||||
    <div *ngIf="triggerDetails.open" class="space-below">
 | 
			
		||||
      <p *ngFor="let prediction of result.predictions; index as i">
 | 
			
		||||
        {{spectrumNames[i]}}: {{prediction}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
 | 
			
		||||
 
 | 
			
		||||
@@ -99,6 +99,8 @@ export class PredictionComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  loadPrediction() {
 | 
			
		||||
    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.result = {
 | 
			
		||||
        predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
 | 
			
		||||
@@ -114,7 +116,9 @@ export class PredictionComponent implements OnInit {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  groupChange(index) {
 | 
			
		||||
    console.log(index);
 | 
			
		||||
    this.activeGroup = this.d.arr.modelGroups[index];
 | 
			
		||||
    this.activeModelIndex = 0;
 | 
			
		||||
    this.result = undefined;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
<script src="samples.component.ts"></script>
 | 
			
		||||
<div class="header-addnew">
 | 
			
		||||
  <h2>Samples</h2>
 | 
			
		||||
  <a routerLink="/samples/new" *ngIf="login.isLevel.write">
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ export class DataService {
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  private collectionMap = {
 | 
			
		||||
    materials: {path: '/materials?status=all', model: MaterialModel, type: 'idArray'},
 | 
			
		||||
    materials: {path: '/materials?status[]=validated&status[]=new', model: MaterialModel, type: 'idArray'},
 | 
			
		||||
    materialSuppliers: {path: '/material/suppliers', model: null, type: 'idArray'},
 | 
			
		||||
    materialGroups: {path: '/material/groups', model: null, type: 'idArray'},
 | 
			
		||||
    materialTemplates: {path: '/template/materials', model: TemplateModel, type: 'template'},
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import {DataService} from './data.service';
 | 
			
		||||
export class LoginService implements CanActivate {
 | 
			
		||||
 | 
			
		||||
  private pathPermissions = [
 | 
			
		||||
    {path: 'materials', permission: 'dev'},
 | 
			
		||||
    {path: 'templates', permission: 'dev'},
 | 
			
		||||
    {path: 'changelog', permission: 'dev'},
 | 
			
		||||
    {path: 'users', permission: 'admin'}
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,14 @@ export class ValidationService {
 | 
			
		||||
    return {ok: true, error: ''};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stringNin(data, list) {
 | 
			
		||||
    const {ignore, error} = Joi.string().invalid(...list).validate(data);
 | 
			
		||||
    if (error) {
 | 
			
		||||
      return {ok: false, error: 'value not allowed'};
 | 
			
		||||
    }
 | 
			
		||||
    return {ok: true, error: ''};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stringLength(data, length) {
 | 
			
		||||
    const {ignore, error} = Joi.string().max(length).allow('').validate(data);
 | 
			
		||||
    if (error) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user