Merge pull request #33 in ~VLE2FE/definma-ui from development to master
* commit '250b04e096b8a476af8608c571f43a2636897a6a': material and activeModelIndex fix
This commit is contained in:
commit
b814335130
@ -13,6 +13,8 @@ import {DocumentationDatabaseComponent} from './documentation/documentation-data
|
|||||||
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 {MaterialComponent} from './material/material.component';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
@ -23,6 +25,8 @@ const routes: Routes = [
|
|||||||
{path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
|
{path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
|
||||||
{path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},
|
{path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},
|
||||||
{path: 'samples/edit/:id', 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: 'templates', component: TemplatesComponent, canActivate: [LoginService]},
|
||||||
{path: 'changelog', component: ChangelogComponent, canActivate: [LoginService]},
|
{path: 'changelog', component: ChangelogComponent, canActivate: [LoginService]},
|
||||||
{path: 'users', component: UsersComponent, 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="/prediction" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Prediction</a>
|
||||||
<a routerLink="/models" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Models</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="/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">
|
<a routerLink="/templates" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">
|
||||||
Templates
|
Templates
|
||||||
</a>
|
</a>
|
||||||
|
@ -32,6 +32,8 @@ 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 { MaterialComponent } from './material/material.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -56,7 +58,9 @@ import { DocumentationArchitectureComponent } from './documentation/documentatio
|
|||||||
HelpComponent,
|
HelpComponent,
|
||||||
ModelTemplatesComponent,
|
ModelTemplatesComponent,
|
||||||
SizePipe,
|
SizePipe,
|
||||||
DocumentationArchitectureComponent
|
DocumentationArchitectureComponent,
|
||||||
|
MaterialsComponent,
|
||||||
|
MaterialComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
LocalStorageModule.forRoot({
|
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 {ApiService} from '../services/api.service';
|
||||||
import {AutocompleteService} from '../services/autocomplete.service';
|
import {AutocompleteService} from '../services/autocomplete.service';
|
||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
||||||
|
import omit from 'lodash/omit';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-model-templates',
|
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
|
if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) { // group was changed, delete model in old group
|
||||||
this.delete(null, this.oldModelGroup, this.oldModelName);
|
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.newModel = false;
|
||||||
this.loadGroups();
|
this.loadGroups();
|
||||||
this.modelGroup = '';
|
this.modelGroup = '';
|
||||||
|
@ -9,6 +9,8 @@ export class MaterialModel extends BaseModel {
|
|||||||
group = '';
|
group = '';
|
||||||
properties: {material_template: string, [prop: string]: string} = {material_template: null};
|
properties: {material_template: string, [prop: string]: string} = {material_template: null};
|
||||||
numbers: string[] = [''];
|
numbers: string[] = [''];
|
||||||
|
selected = false;
|
||||||
|
status = '';
|
||||||
|
|
||||||
sendFormat() {
|
sendFormat() {
|
||||||
return pick(this, ['name', 'supplier', 'group', 'numbers', 'properties']);
|
return pick(this, ['name', 'supplier', 'group', 'numbers', 'properties']);
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<h4>
|
<h4>
|
||||||
Average result: {{result.mean}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
Average result: {{result.mean}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
||||||
</h4>
|
</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">
|
<div *ngIf="triggerDetails.open" class="space-below">
|
||||||
<p *ngFor="let prediction of result.predictions; index as i">
|
<p *ngFor="let prediction of result.predictions; index as i">
|
||||||
{{spectrumNames[i]}}: {{prediction}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
{{spectrumNames[i]}}: {{prediction}}<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
||||||
|
@ -99,6 +99,8 @@ 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 = {
|
||||||
predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
|
predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
|
||||||
@ -114,7 +116,9 @@ export class PredictionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
groupChange(index) {
|
groupChange(index) {
|
||||||
|
console.log(index);
|
||||||
this.activeGroup = this.d.arr.modelGroups[index];
|
this.activeGroup = this.d.arr.modelGroups[index];
|
||||||
|
this.activeModelIndex = 0;
|
||||||
this.result = undefined;
|
this.result = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
<script src="samples.component.ts"></script>
|
|
||||||
<div class="header-addnew">
|
<div class="header-addnew">
|
||||||
<h2>Samples</h2>
|
<h2>Samples</h2>
|
||||||
<a routerLink="/samples/new" *ngIf="login.isLevel.write">
|
<a routerLink="/samples/new" *ngIf="login.isLevel.write">
|
||||||
|
@ -17,7 +17,7 @@ export class DataService {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
private collectionMap = {
|
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'},
|
materialSuppliers: {path: '/material/suppliers', model: null, type: 'idArray'},
|
||||||
materialGroups: {path: '/material/groups', model: null, type: 'idArray'},
|
materialGroups: {path: '/material/groups', model: null, type: 'idArray'},
|
||||||
materialTemplates: {path: '/template/materials', model: TemplateModel, type: 'template'},
|
materialTemplates: {path: '/template/materials', model: TemplateModel, type: 'template'},
|
||||||
|
@ -11,6 +11,7 @@ import {DataService} from './data.service';
|
|||||||
export class LoginService implements CanActivate {
|
export class LoginService implements CanActivate {
|
||||||
|
|
||||||
private pathPermissions = [
|
private pathPermissions = [
|
||||||
|
{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'}
|
||||||
|
@ -71,6 +71,14 @@ export class ValidationService {
|
|||||||
return {ok: true, error: ''};
|
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) {
|
stringLength(data, 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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user