Properly indent all source files
This commit is contained in:
parent
2d3782cc82
commit
2f6231a6b5
@ -13,43 +13,43 @@ 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
|
import {DocumentationArchitectureComponent} from
|
||||||
'./documentation/documentation-architecture/documentation-architecture.component';
|
'./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';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', component: HomeComponent},
|
{path: '', component: HomeComponent},
|
||||||
{path: 'home', component: HomeComponent},
|
{path: 'home', component: HomeComponent},
|
||||||
{path: 'prediction', component: PredictionComponent},
|
{path: 'prediction', component: PredictionComponent},
|
||||||
{path: 'models', component: ModelTemplatesComponent},
|
{path: 'models', component: ModelTemplatesComponent},
|
||||||
{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', component: MaterialsComponent, canActivate: [LoginService]},
|
||||||
{path: 'materials/edit/:id', component: MaterialComponent, 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]},
|
||||||
{path: 'settings', component: SettingsComponent, canActivate: [LoginService]},
|
{path: 'settings', component: SettingsComponent, canActivate: [LoginService]},
|
||||||
{path: 'documentation', component: DocumentationComponent},
|
{path: 'documentation', component: DocumentationComponent},
|
||||||
{path: 'documentation/architecture', component: DocumentationArchitectureComponent},
|
{path: 'documentation/architecture', component: DocumentationArchitectureComponent},
|
||||||
{path: 'documentation/database', component: DocumentationDatabaseComponent},
|
{path: 'documentation/database', component: DocumentationDatabaseComponent},
|
||||||
{path: 'documentation/models', component: DocumentationModelsComponent},
|
{path: 'documentation/models', component: DocumentationModelsComponent},
|
||||||
|
|
||||||
// If not authenticated
|
// If not authenticated
|
||||||
{ path: '**', redirectTo: '' }
|
{ path: '**', redirectTo: '' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const routerOptions: ExtraOptions = {
|
const routerOptions: ExtraOptions = {
|
||||||
scrollPositionRestoration: 'enabled',
|
scrollPositionRestoration: 'enabled',
|
||||||
anchorScrolling: 'enabled',
|
anchorScrolling: 'enabled',
|
||||||
scrollOffset: [0, 64],
|
scrollOffset: [0, 64],
|
||||||
};
|
};
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes, routerOptions)],
|
imports: [RouterModule.forRoot(routes, routerOptions)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class AppRoutingModule { }
|
export class AppRoutingModule { }
|
||||||
|
@ -1,73 +1,73 @@
|
|||||||
<rb-full-header id="top">
|
<rb-full-header id="top">
|
||||||
<nav *rbMainNavItems>
|
<nav *rbMainNavItems>
|
||||||
<a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a>
|
<a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a>
|
||||||
<a routerLink="/prediction" routerLinkActive="active" rbLoadingLink *ngIf="login.hasPrediction">Prediction</a>
|
<a routerLink="/prediction" routerLinkActive="active" rbLoadingLink *ngIf="login.hasPrediction">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="/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>
|
||||||
<a routerLink="/changelog" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Changelog</a>
|
<a routerLink="/changelog" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">Changelog</a>
|
||||||
<a routerLink="/users" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.admin">Users</a>
|
<a routerLink="/users" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.admin">Users</a>
|
||||||
<a routerLink="/documentation" routerLinkActive="active" rbLoadingLink>Documentation</a>
|
<a routerLink="/documentation" routerLinkActive="active" rbLoadingLink>Documentation</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<ng-container *ngIf="isDocumentation">
|
<ng-container *ngIf="isDocumentation">
|
||||||
<nav *rbSubNavItems>
|
<nav *rbSubNavItems>
|
||||||
<a routerLink="/documentation" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">General</a>
|
<a routerLink="/documentation" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">General</a>
|
||||||
<a routerLink="/documentation/architecture" routerLinkActive="active">Architecture</a>
|
<a routerLink="/documentation/architecture" routerLinkActive="active">Architecture</a>
|
||||||
<a routerLink="/documentation/database" routerLinkActive="active">Database</a>
|
<a routerLink="/documentation/database" routerLinkActive="active">Database</a>
|
||||||
<a routerLink="/documentation/models" routerLinkActive="active">Models</a>
|
<a routerLink="/documentation/models" routerLinkActive="active">Models</a>
|
||||||
</nav>
|
</nav>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<nav *rbMetaNavItems>
|
<nav *rbMetaNavItems>
|
||||||
<span class="rb-ic rb-ic-question-frame clickable space-above" (click)="help()"></span>
|
<span class="rb-ic rb-ic-question-frame clickable space-above" (click)="help()"></span>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<ng-container *ngIf="login.isLoggedIn">
|
<ng-container *ngIf="login.isLoggedIn">
|
||||||
<nav *rbActionNavItems>
|
<nav *rbActionNavItems>
|
||||||
<a href="javascript:" [rbPopover]="userPopover" [anchor]="popoverAnchor">
|
<a href="javascript:" [rbPopover]="userPopover" [anchor]="popoverAnchor">
|
||||||
{{login.username}} <span class="rb-ic rb-ic-my-brand-frame" #popoverAnchor></span></a>
|
{{login.username}} <span class="rb-ic rb-ic-my-brand-frame" #popoverAnchor></span></a>
|
||||||
</nav>
|
</nav>
|
||||||
<ng-template #userPopover let-close="close">
|
<ng-template #userPopover let-close="close">
|
||||||
<div class="spacing">
|
<div class="spacing">
|
||||||
<a routerLink="/settings" (click)="close()"><span class="rb-ic rb-ic-settings"></span> Settings</a>
|
<a routerLink="/settings" (click)="close()"><span class="rb-ic rb-ic-settings"></span> Settings</a>
|
||||||
<button type="button" class="rb-btn rb-primary" (click)="logout()">Logout</button>
|
<button type="button" class="rb-btn rb-primary" (click)="logout()">Logout</button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div *rbSubBrandHeader>
|
<div *rbSubBrandHeader>
|
||||||
<rb-icon-button icon="bug" mode="secondary" class="space-right" [rbModal]="bugModal">
|
<rb-icon-button icon="bug" mode="secondary" class="space-right" [rbModal]="bugModal">
|
||||||
Bug
|
Bug
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
<ng-template let-close="close" #bugModal>
|
<ng-template let-close="close" #bugModal>
|
||||||
<h3>Report a bug</h3>
|
<h3>Report a bug</h3>
|
||||||
<rb-form-textarea class="bug-textarea" label="What did you do?" [(ngModel)]="bugReport.do"></rb-form-textarea>
|
<rb-form-textarea class="bug-textarea" label="What did you do?" [(ngModel)]="bugReport.do"></rb-form-textarea>
|
||||||
<rb-form-textarea class="bug-textarea" label="What did not work?" [(ngModel)]="bugReport.work"></rb-form-textarea>
|
<rb-form-textarea class="bug-textarea" label="What did not work?" [(ngModel)]="bugReport.work"></rb-form-textarea>
|
||||||
<a [href]="bugReportContent()">
|
<a [href]="bugReportContent()">
|
||||||
<rb-icon-button icon="mail" mode="primary" (mouseup)="closeBugReport(close)">Send report</rb-icon-button>
|
<rb-icon-button icon="mail" mode="primary" (mouseup)="closeBugReport(close)">Send report</rb-icon-button>
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<span class="dev-label" *ngIf="devMode">DEVELOPMENT</span>
|
<span class="dev-label" *ngIf="devMode">DEVELOPMENT</span>
|
||||||
DeFinMa
|
DeFinMa
|
||||||
</div>
|
</div>
|
||||||
</rb-full-header>
|
</rb-full-header>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<rb-icon-button icon="up" mode="primary" iconOnly (click)="toTheTop()" *ngIf="window.scrollY > 300" class="to-the-top">
|
<rb-icon-button icon="up" mode="primary" iconOnly (click)="toTheTop()" *ngIf="window.scrollY > 300" class="to-the-top">
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
|
|
||||||
<rb-footer-nav>
|
<rb-footer-nav>
|
||||||
<span class="copyright">
|
<span class="copyright">
|
||||||
CR/APS1 and CR/ANA1 2020
|
CR/APS1 and CR/ANA1 2020
|
||||||
</span>
|
</span>
|
||||||
<nav>
|
<nav>
|
||||||
<a [href]="'mailto:' + d.contact.mail">Contact</a>
|
<a [href]="'mailto:' + d.contact.mail">Contact</a>
|
||||||
</nav>
|
</nav>
|
||||||
</rb-footer-nav>
|
</rb-footer-nav>
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
.dev-label {
|
.dev-label {
|
||||||
color: #F00;
|
color: #F00;
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
margin-right: 40px;
|
margin-right: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spacing {
|
.spacing {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
grid-row-gap: 10px;
|
grid-row-gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bug-textarea {
|
.bug-textarea {
|
||||||
width: 800px;
|
width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.to-the-top {
|
.to-the-top {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 1rem;
|
right: 1rem;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
}
|
}
|
||||||
|
@ -8,76 +8,76 @@ import {DataService} from './services/data.service';
|
|||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit{
|
export class AppComponent implements OnInit{
|
||||||
|
|
||||||
bugReport = {do: '', work: ''}; // Data from bug report inputs
|
bugReport = {do: '', work: ''}; // Data from bug report inputs
|
||||||
isDocumentation = false; // True if user is on documentation pages
|
isDocumentation = false; // True if user is on documentation pages
|
||||||
devMode = false;
|
devMode = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public login: LoginService,
|
public login: LoginService,
|
||||||
public router: Router,
|
public router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
public window: Window,
|
public window: Window,
|
||||||
private modal: ModalService,
|
private modal: ModalService,
|
||||||
public d: DataService
|
public d: DataService
|
||||||
) {
|
) {
|
||||||
this.devMode = isDevMode();
|
this.devMode = isDevMode();
|
||||||
this.router.events.subscribe(event => {
|
this.router.events.subscribe(event => {
|
||||||
if (event instanceof NavigationStart) {
|
if (event instanceof NavigationStart) {
|
||||||
this.isDocumentation = /\/documentation/.test(event.url);
|
this.isDocumentation = /\/documentation/.test(event.url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
// Try to log in user
|
// Try to log in user
|
||||||
this.login.login().then(res => {
|
this.login.login().then(res => {
|
||||||
// Return to home page if log failed, except when on documentation pages
|
// Return to home page if log failed, except when on documentation pages
|
||||||
if (!res && !(/\/documentation/.test(this.router.url))) {
|
if (!res && !(/\/documentation/.test(this.router.url))) {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
this.login.logout();
|
this.login.logout();
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
|
||||||
help() {
|
help() {
|
||||||
this.modal.openComponent(HelpComponent);
|
this.modal.openComponent(HelpComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bugReportContent() {
|
bugReportContent() {
|
||||||
return `mailto:${this.d.contact.mail}?subject=Bug report&body=Thanks for sending the report! Your bug will be
|
return `mailto:${this.d.contact.mail}?subject=Bug report&body=Thanks for sending the report! Your bug will be
|
||||||
(hopefully) fixed soon.
|
(hopefully) fixed soon.
|
||||||
%0D%0A%0D%0A--- REPORT DATA ---
|
%0D%0A%0D%0A--- REPORT DATA ---
|
||||||
%0D%0A%0D%0ATime: ${new Date().toString()}%0D%0A
|
%0D%0A%0D%0ATime: ${new Date().toString()}%0D%0A
|
||||||
URL: ${this.window.location}%0D%0A%0D%0AWhat did you do?%0D%0A${encodeURIComponent(this.bugReport.do)}
|
URL: ${this.window.location}%0D%0A%0D%0AWhat did you do?%0D%0A${encodeURIComponent(this.bugReport.do)}
|
||||||
%0D%0A%0D%0AWhat did not work?%0D%0A${encodeURIComponent(this.bugReport.work)}%0D%0A%0D%0ABrowser:%0D%0A
|
%0D%0A%0D%0AWhat did not work?%0D%0A${encodeURIComponent(this.bugReport.work)}%0D%0A%0D%0ABrowser:%0D%0A
|
||||||
%0D%0AappCodeName: ${navigator.appCodeName}
|
%0D%0AappCodeName: ${navigator.appCodeName}
|
||||||
%0D%0AappVersion: ${navigator.appVersion}
|
%0D%0AappVersion: ${navigator.appVersion}
|
||||||
%0D%0Alanguage: ${navigator.language}
|
%0D%0Alanguage: ${navigator.language}
|
||||||
%0D%0AonLine: ${navigator.onLine}
|
%0D%0AonLine: ${navigator.onLine}
|
||||||
%0D%0Aoscpu: ${navigator.oscpu}
|
%0D%0Aoscpu: ${navigator.oscpu}
|
||||||
%0D%0Aplatform: ${navigator.platform}
|
%0D%0Aplatform: ${navigator.platform}
|
||||||
%0D%0AuserAgent: ${navigator.userAgent}
|
%0D%0AuserAgent: ${navigator.userAgent}
|
||||||
%0D%0AinnerWidth: ${this.window.innerWidth}
|
%0D%0AinnerWidth: ${this.window.innerWidth}
|
||||||
%0D%0AinnerHeight: ${this.window.innerHeight}`;
|
%0D%0AinnerHeight: ${this.window.innerHeight}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
closeBugReport(close) {
|
closeBugReport(close) {
|
||||||
setTimeout(() => close(), 1);
|
setTimeout(() => close(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
toTheTop() {
|
toTheTop() {
|
||||||
this.window.scroll(0, 0);
|
this.window.scroll(0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,66 +27,66 @@ 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
|
import { DocumentationDatabaseComponent } from
|
||||||
'./documentation/documentation-database/documentation-database.component';
|
'./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
|
import { DocumentationArchitectureComponent } from
|
||||||
'./documentation/documentation-architecture/documentation-architecture.component';
|
'./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';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
SamplesComponent,
|
SamplesComponent,
|
||||||
SampleComponent,
|
SampleComponent,
|
||||||
ValidateDirective,
|
ValidateDirective,
|
||||||
ErrorComponent,
|
ErrorComponent,
|
||||||
ObjectPipe,
|
ObjectPipe,
|
||||||
DocumentationComponent,
|
DocumentationComponent,
|
||||||
ImgMagnifierComponent,
|
ImgMagnifierComponent,
|
||||||
ExistsPipe,
|
ExistsPipe,
|
||||||
TemplatesComponent,
|
TemplatesComponent,
|
||||||
ParametersPipe,
|
ParametersPipe,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
UsersComponent,
|
UsersComponent,
|
||||||
ChangelogComponent,
|
ChangelogComponent,
|
||||||
DocumentationDatabaseComponent,
|
DocumentationDatabaseComponent,
|
||||||
PredictionComponent,
|
PredictionComponent,
|
||||||
HelpComponent,
|
HelpComponent,
|
||||||
ModelTemplatesComponent,
|
ModelTemplatesComponent,
|
||||||
SizePipe,
|
SizePipe,
|
||||||
DocumentationArchitectureComponent,
|
DocumentationArchitectureComponent,
|
||||||
MaterialsComponent,
|
MaterialsComponent,
|
||||||
MaterialComponent,
|
MaterialComponent,
|
||||||
DocumentationModelsComponent
|
DocumentationModelsComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
LocalStorageModule.forRoot({
|
LocalStorageModule.forRoot({
|
||||||
prefix: 'definma',
|
prefix: 'definma',
|
||||||
storageType: 'localStorage'
|
storageType: 'localStorage'
|
||||||
}),
|
}),
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
RbUiComponentsModule,
|
RbUiComponentsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
RbCustomInputsModule,
|
RbCustomInputsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
FormFieldsModule,
|
FormFieldsModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
ChartsModule
|
ChartsModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ModalService,
|
ModalService,
|
||||||
{ provide: Window, useValue: window }
|
{ provide: Window, useValue: window }
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
@ -1,46 +1,46 @@
|
|||||||
<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()">
|
||||||
</rb-form-date-input>
|
</rb-form-date-input>
|
||||||
|
|
||||||
<rb-form-select name="pageSizeSelection" label="page size" [(ngModel)]="pageSize" (ngModelChange)="loadChangelog()">
|
<rb-form-select name="pageSizeSelection" label="page size" [(ngModel)]="pageSize" (ngModelChange)="loadChangelog()">
|
||||||
<option value="3">3</option>
|
<option value="3">3</option>
|
||||||
<option value="10">10</option>
|
<option value="10">10</option>
|
||||||
<option value="25">25</option>
|
<option value="25">25</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
<option value="100">100</option>
|
<option value="100">100</option>
|
||||||
<option value="250">250</option>
|
<option value="250">250</option>
|
||||||
<option value="500">500</option>
|
<option value="500">500</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
|
|
||||||
<button class="rb-btn rb-link" type="button" (click)="loadChangelog(1)">
|
<button class="rb-btn rb-link" type="button" (click)="loadChangelog(1)">
|
||||||
next page
|
next page
|
||||||
<span class="rb-ic rb-ic-forward-right"></span>
|
<span class="rb-ic rb-ic-forward-right"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<rb-table ellipsis>
|
<rb-table ellipsis>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
<th>Data</th>
|
<th>Data</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let entry of changelog; index as i" class="clickable" (click)="showDetails(i, sampleModal)">
|
<tr *ngFor="let entry of changelog; index as i" class="clickable" (click)="showDetails(i, sampleModal)">
|
||||||
<td>{{entry.date}}</td>
|
<td>{{entry.date}}</td>
|
||||||
<td>{{entry.action}}</td>
|
<td>{{entry.action}}</td>
|
||||||
<td>{{entry.data | json}}</td>
|
<td>{{entry.data | json}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
|
|
||||||
<ng-template #sampleModal>
|
<ng-template #sampleModal>
|
||||||
<h4>Date</h4>
|
<h4>Date</h4>
|
||||||
<p class="space-below">{{changelog[modalDetail].date}}</p>
|
<p class="space-below">{{changelog[modalDetail].date}}</p>
|
||||||
<h4>Action</h4>
|
<h4>Action</h4>
|
||||||
<p class="space-below">{{changelog[modalDetail].action}}</p>
|
<p class="space-below">{{changelog[modalDetail].action}}</p>
|
||||||
<h4>Collection</h4>
|
<h4>Collection</h4>
|
||||||
<p class="space-below">{{changelog[modalDetail].collection}}</p>
|
<p class="space-below">{{changelog[modalDetail].collection}}</p>
|
||||||
<h4>Conditions</h4>
|
<h4>Conditions</h4>
|
||||||
<p class="space-below">{{changelog[modalDetail].conditions | json}}</p>
|
<p class="space-below">{{changelog[modalDetail].conditions | json}}</p>
|
||||||
<h4>Data</h4>
|
<h4>Data</h4>
|
||||||
<p class="space-below">{{changelog[modalDetail].data | json}}</p>
|
<p class="space-below">{{changelog[modalDetail].data | json}}</p>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.clickable {
|
tr.clickable {
|
||||||
background: none;
|
background: none;
|
||||||
transition: background-color 0.5s;
|
transition: background-color 0.5s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $color-gray-mercury;
|
background: $color-gray-mercury;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,44 +4,44 @@ import {ApiService} from '../services/api.service';
|
|||||||
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-changelog',
|
selector: 'app-changelog',
|
||||||
templateUrl: './changelog.component.html',
|
templateUrl: './changelog.component.html',
|
||||||
styleUrls: ['./changelog.component.scss']
|
styleUrls: ['./changelog.component.scss']
|
||||||
})
|
})
|
||||||
export class ChangelogComponent implements OnInit {
|
export class ChangelogComponent implements OnInit {
|
||||||
|
|
||||||
timestamp = new Date(); // Time from date input
|
timestamp = new Date(); // Time from date input
|
||||||
pageSize = 25;
|
pageSize = 25;
|
||||||
changelog: ChangelogModel[] = [];
|
changelog: ChangelogModel[] = [];
|
||||||
modalDetail = 0; // Index of changelog element to show details of
|
modalDetail = 0; // Index of changelog element to show details of
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
private modal: ModalService
|
private modal: ModalService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadChangelog();
|
this.loadChangelog();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadChangelog(page = 0) { // Load changelog with page no relative to current page
|
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(
|
||||||
new Date(this.timestamp).getTime() - new Date(this.timestamp).getTimezoneOffset() * 60000 // Adjust timezone
|
new Date(this.timestamp).getTime() - new Date(this.timestamp).getTimezoneOffset() * 60000 // Adjust timezone
|
||||||
).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) { // Adjust date picker to new first element when user clicked on next 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
|
// 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,50 +1,50 @@
|
|||||||
<p>
|
<p>
|
||||||
Bosch IoT Cloud space where all applications are hosted:
|
Bosch IoT Cloud space where all applications are hosted:
|
||||||
<a href="https://apps.sys.de1.bosch-iot-cloud.com/organizations/b28baba5-f95f-4ce5-bc9c-3f45acd1dfb2">
|
<a href="https://apps.sys.de1.bosch-iot-cloud.com/organizations/b28baba5-f95f-4ce5-bc9c-3f45acd1dfb2">
|
||||||
https://apps.sys.de1.bosch-iot-cloud.com/organizations/b28baba5-f95f-4ce5-bc9c-3f45acd1dfb2
|
https://apps.sys.de1.bosch-iot-cloud.com/organizations/b28baba5-f95f-4ce5-bc9c-3f45acd1dfb2
|
||||||
</a><br>
|
</a><br>
|
||||||
Find the API documentation here:
|
Find the API documentation here:
|
||||||
<a href="https://definma-api.apps.de1.bosch-iot-cloud.com/api-doc/">
|
<a href="https://definma-api.apps.de1.bosch-iot-cloud.com/api-doc/">
|
||||||
https://definma-api.apps.de1.bosch-iot-cloud.com/api-doc/
|
https://definma-api.apps.de1.bosch-iot-cloud.com/api-doc/
|
||||||
</a><br>
|
</a><br>
|
||||||
Admin database management page:
|
Admin database management page:
|
||||||
<a href="https://definma-db.apps.de1.bosch-iot-cloud.com/">
|
<a href="https://definma-db.apps.de1.bosch-iot-cloud.com/">
|
||||||
https://definma-db.apps.de1.bosch-iot-cloud.com/
|
https://definma-db.apps.de1.bosch-iot-cloud.com/
|
||||||
</a><br>
|
</a><br>
|
||||||
Code repository UI
|
Code repository UI
|
||||||
<a href="https://sourcecode.socialcoding.bosch.com/users/vle2fe/repos/definma-ui">
|
<a href="https://sourcecode.socialcoding.bosch.com/users/vle2fe/repos/definma-ui">
|
||||||
https://sourcecode.socialcoding.bosch.com/users/vle2fe/repos/definma-ui
|
https://sourcecode.socialcoding.bosch.com/users/vle2fe/repos/definma-ui
|
||||||
</a><br>
|
</a><br>
|
||||||
Code repository API
|
Code repository API
|
||||||
<a href="https://sourcecode.socialcoding.bosch.com/users/vle2fe/repos/definma-api">
|
<a href="https://sourcecode.socialcoding.bosch.com/users/vle2fe/repos/definma-api">
|
||||||
https://sourcecode.socialcoding.bosch.com/users/vle2fe/repos/definma-api
|
https://sourcecode.socialcoding.bosch.com/users/vle2fe/repos/definma-api
|
||||||
</a><br>
|
</a><br>
|
||||||
Code repository Models
|
Code repository Models
|
||||||
<a href="https://sourcecode.socialcoding.bosch.com/users/poe2rng/repos/definma-models">
|
<a href="https://sourcecode.socialcoding.bosch.com/users/poe2rng/repos/definma-models">
|
||||||
https://sourcecode.socialcoding.bosch.com/users/poe2rng/repos/definma-models
|
https://sourcecode.socialcoding.bosch.com/users/poe2rng/repos/definma-models
|
||||||
</a><br>
|
</a><br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
All applications are hosted in the bosch IoT Cloud. The API is hosted in a Node.js container, with a bound MongoDB
|
All applications are hosted in the bosch IoT Cloud. The API is hosted in a Node.js container, with a bound MongoDB
|
||||||
instance. <br>
|
instance. <br>
|
||||||
The Angular UI is hosted in a staticfile container, serving the built Angular bundle. <br>
|
The Angular UI is hosted in a staticfile container, serving the built Angular bundle. <br>
|
||||||
Finally any machine learning models are hosted in Python containers.
|
Finally any machine learning models are hosted in Python containers.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>Development setup</h4>
|
<h4>Development setup</h4>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
In any case, it is good to have PxProxy working, following the
|
In any case, it is good to have PxProxy working, following the
|
||||||
<a href="https://inside-docupedia.bosch.com/confluence/pages/viewpage.action?pageId=608652557">
|
<a href="https://inside-docupedia.bosch.com/confluence/pages/viewpage.action?pageId=608652557">
|
||||||
Bosch installation guide
|
Bosch installation guide
|
||||||
</a>. <br>
|
</a>. <br>
|
||||||
For pushing to the BIC, you need the CloudFoundry CLI as described in the
|
For pushing to the BIC, you need the CloudFoundry CLI as described in the
|
||||||
<a href="https://inside-docupedia.bosch.com/confluence/display/BICI/Cloud+Foundry+CLI">BIC documentation</a>.
|
<a href="https://inside-docupedia.bosch.com/confluence/display/BICI/Cloud+Foundry+CLI">BIC documentation</a>.
|
||||||
If the the downloaded version of the CLI should not work, there is an old installer with a definitely working version
|
If the the downloaded version of the CLI should not work, there is an old installer with a definitely working version
|
||||||
on the file share in the bin folder.
|
on the file share in the bin folder.
|
||||||
Please consider that you need to have a login to the BIC and be marked as space developer for the DeFinMa Org. <br>
|
Please consider that you need to have a login to the BIC and be marked as space developer for the DeFinMa Org. <br>
|
||||||
For front and back end development, you need a
|
For front and back end development, you need a
|
||||||
<a href="https://www.mongodb.com/try/download/community">local MongoDB server</a> as well as
|
<a href="https://www.mongodb.com/try/download/community">local MongoDB server</a> as well as
|
||||||
<a href="https://nodejs.org/en/">Node.js</a>.
|
<a href="https://nodejs.org/en/">Node.js</a>.
|
||||||
</p>
|
</p>
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-documentation-architecture',
|
selector: 'app-documentation-architecture',
|
||||||
templateUrl: './documentation-architecture.component.html',
|
templateUrl: './documentation-architecture.component.html',
|
||||||
styleUrls: ['./documentation-architecture.component.scss']
|
styleUrls: ['./documentation-architecture.component.scss']
|
||||||
})
|
})
|
||||||
export class DocumentationArchitectureComponent implements OnInit {
|
export class DocumentationArchitectureComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
<p>
|
<p>
|
||||||
The used database instance is a MongoDB instance running on the BIC, storing all application data.
|
The used database instance is a MongoDB instance running on the BIC, storing all application data.
|
||||||
The admin database management page can be accessed <a href="https://definma-db.apps.de1.bosch-iot-cloud.com/api-doc/">here</a>.
|
The admin database management page can be accessed <a href="https://definma-db.apps.de1.bosch-iot-cloud.com/api-doc/">here</a>.
|
||||||
However, it is recommended to use a dedicated MongoDB application for anything apart from a quick look.
|
However, it is recommended to use a dedicated MongoDB application for anything apart from a quick look.
|
||||||
The two tested applications are <a href="https://www.mongodb.com/products/compass">MongoDB Compass</a> and
|
The two tested applications are <a href="https://www.mongodb.com/products/compass">MongoDB Compass</a> and
|
||||||
<a href="https://robomongo.org/s">Robo 3T</a> (recommended for trying queries).
|
<a href="https://robomongo.org/s">Robo 3T</a> (recommended for trying queries).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
To connect to the BIC instance from your local application follow the
|
To connect to the BIC instance from your local application follow the
|
||||||
<a href="https://inside-docupedia.bosch.com/confluence/display/BICI/MongoDB+Client+Tool+via+SSH+Tunnel">BIC guide</a>.
|
<a href="https://inside-docupedia.bosch.com/confluence/display/BICI/MongoDB+Client+Tool+via+SSH+Tunnel">BIC guide</a>.
|
||||||
<br>
|
<br>
|
||||||
TL;DR:
|
TL;DR:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>For the first time:</p>
|
<p>For the first time:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>cf login</li>
|
<li>cf login</li>
|
||||||
<li>cf enable-ssh definma-api</li>
|
<li>cf enable-ssh definma-api</li>
|
||||||
<li>cf create-service-key definmadb <key name, can be whatever></li>
|
<li>cf create-service-key definmadb <key name, can be whatever></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Every time:</p>
|
<p>Every time:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>cf ssh -L 1120:st0cvm200118.internal-mongodb.de1.bosch-iot-cloud.com:30000 definma-api</li>
|
<li>cf ssh -L 1120:st0cvm200118.internal-mongodb.de1.bosch-iot-cloud.com:30000 definma-api</li>
|
||||||
<li>
|
<li>
|
||||||
Login into your application with localhost:1120 and use credentials and authentication database from the BIC
|
Login into your application with localhost:1120 and use credentials and authentication database from the BIC
|
||||||
(Data can be found at Home > DeFinMa > production > definma-api > Services > dots on the right of definmadb > View Credentials)
|
(Data can be found at Home > DeFinMa > production > definma-api > Services > dots on the right of definmadb > View Credentials)
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4>Backup Instructions</h4>
|
<h4>Backup Instructions</h4>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
For creating a database backup, you must follow the same steps from above (except the last one).
|
For creating a database backup, you must follow the same steps from above (except the last one).
|
||||||
Additionally you need the <a href="https://www.mongodb.com/try/download/database-tools">MongoDB Tools</a> installed.
|
Additionally you need the <a href="https://www.mongodb.com/try/download/database-tools">MongoDB Tools</a> installed.
|
||||||
Extract the ZIP file to a location on your hard drive and launch a Git Bash in that directory.
|
Extract the ZIP file to a location on your hard drive and launch a Git Bash in that directory.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>To back up the database from the BIC:</p>
|
<p>To back up the database from the BIC:</p>
|
||||||
@ -51,377 +51,377 @@
|
|||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
More information can be found inside the
|
More information can be found inside the
|
||||||
<a href="https://docs.mongodb.com/database-tools/">documentation</a>. <br>
|
<a href="https://docs.mongodb.com/database-tools/">documentation</a>. <br>
|
||||||
The BIC service also creates an internal backup, which can be requested to restore, see
|
The BIC service also creates an internal backup, which can be requested to restore, see
|
||||||
<a href="https://inside-docupedia.bosch.com/confluence/pages/viewpage.action?pageId=565402281">MongoDB FAQ</a>
|
<a href="https://inside-docupedia.bosch.com/confluence/pages/viewpage.action?pageId=565402281">MongoDB FAQ</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>Database Model</h4>
|
<h4>Database Model</h4>
|
||||||
|
|
||||||
<app-img-magnifier src="assets/imgs/db_structure_latest.svg" zoom="2" [magnifierSize]="{width: 400, height: 300}"
|
<app-img-magnifier src="assets/imgs/db_structure_latest.svg" zoom="2" [magnifierSize]="{width: 400, height: 300}"
|
||||||
id="db-structure"></app-img-magnifier>
|
id="db-structure"></app-img-magnifier>
|
||||||
|
|
||||||
<h4>Field Reference</h4>
|
<h4>Field Reference</h4>
|
||||||
|
|
||||||
<rb-table class="field-reference">
|
<rb-table class="field-reference">
|
||||||
<tr><th>samples</th><th></th><th>Example</th></tr>
|
<tr><th>samples</th><th></th><th>Example</th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e63c98d1c020f8cda6e06'</td>
|
<td>'5f2e63c98d1c020f8cda6e06'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>type</td>
|
<td>type</td>
|
||||||
<td>
|
<td>
|
||||||
The material status of the sample, can be either <span class="name">as-delivered/raw</span> or
|
The material status of the sample, can be either <span class="name">as-delivered/raw</span> or
|
||||||
<span class="name">processed</span>.
|
<span class="name">processed</span>.
|
||||||
</td>
|
</td>
|
||||||
<td>'processed'</td>
|
<td>'processed'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>number</td>
|
<td>number</td>
|
||||||
<td>The sample number, generated by the server for new samples</td>
|
<td>The sample number, generated by the server for new samples</td>
|
||||||
<td>'An31'</td>
|
<td>'An31'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>color</td>
|
<td>color</td>
|
||||||
<td>Sample color</td>
|
<td>Sample color</td>
|
||||||
<td>'black'</td>
|
<td>'black'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>batch</td>
|
<td>batch</td>
|
||||||
<td>Batch number the sample was from</td>
|
<td>Batch number the sample was from</td>
|
||||||
<td>'2264486614'</td>
|
<td>'2264486614'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>status</td>
|
<td>status</td>
|
||||||
<td>Status of the document, can be either <span class="name">new</span>, <span class="name">validated</span> or
|
<td>Status of the document, can be either <span class="name">new</span>, <span class="name">validated</span> or
|
||||||
<span class="name">deleted</span>.</td>
|
<span class="name">deleted</span>.</td>
|
||||||
<td>'new'</td>
|
<td>'new'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>condition</td>
|
<td>condition</td>
|
||||||
<td>sample condition with <span class="name">condition_template</span> reference and all fields defined by the
|
<td>sample condition with <span class="name">condition_template</span> reference and all fields defined by the
|
||||||
template</td>
|
template</td>
|
||||||
<td>{{'{'}}condition_template: '5f3151b5b8a886007d2de9ed', time in minutes: 30{{'}'}}</td>
|
<td>{{'{'}}condition_template: '5f3151b5b8a886007d2de9ed', time in minutes: 30{{'}'}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>material_id</td>
|
<td>material_id</td>
|
||||||
<td>Reference to the sample material</td>
|
<td>Reference to the sample material</td>
|
||||||
<td>'5f2e63118d1c020f8cda6a0a'</td>
|
<td>'5f2e63118d1c020f8cda6a0a'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>note_id</td>
|
<td>note_id</td>
|
||||||
<td>Reference to the sample note</td>
|
<td>Reference to the sample note</td>
|
||||||
<td>'5f2e63c98d1c020f8cda6e08'</td>
|
<td>'5f2e63c98d1c020f8cda6e08'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>user_id</td>
|
<td>user_id</td>
|
||||||
<td>Reference to the user who created the sample</td>
|
<td>Reference to the user who created the sample</td>
|
||||||
<td>'5f294dd4aa9ea5085c7d7315'</td>
|
<td>'5f294dd4aa9ea5085c7d7315'</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>notes</th><th></th><th></th></tr>
|
<tr><th>notes</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e63e98d1c020f8cda70cc'</td>
|
<td>'5f2e63e98d1c020f8cda70cc'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>comment</td>
|
<td>comment</td>
|
||||||
<td>General remarks</td>
|
<td>General remarks</td>
|
||||||
<td>'stabilized'</td>
|
<td>'stabilized'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>sample_references</td>
|
<td>sample_references</td>
|
||||||
<td>Array of references to other samples, each reference containing the referenced <span class="name">sample_id
|
<td>Array of references to other samples, each reference containing the referenced <span class="name">sample_id
|
||||||
</span> as well as a <span class="name">relation</span> field describing the relationship</td>
|
</span> as well as a <span class="name">relation</span> field describing the relationship</td>
|
||||||
<td>{{'{'}}sample_id: '5f2e63d68d1c020f8cda701c', relation: 'belongs to'{{'}'}}</td>
|
<td>{{'{'}}sample_id: '5f2e63d68d1c020f8cda701c', relation: 'belongs to'{{'}'}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>custom_fields</td>
|
<td>custom_fields</td>
|
||||||
<td>Additional information as key value pairs for the sample, making it easier to process this information</td>
|
<td>Additional information as key value pairs for the sample, making it easier to process this information</td>
|
||||||
<td>{{'{'}}vwz: '0 min'{{'}'}}</td>
|
<td>{{'{'}}vwz: '0 min'{{'}'}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>note_fields</th><th></th><th></th></tr>
|
<tr><th>note_fields</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e63e98d1c020f8cda70ce'</td>
|
<td>'5f2e63e98d1c020f8cda70ce'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>name</td>
|
<td>name</td>
|
||||||
<td>name of the <span class="name">custom_fields</span> key</td>
|
<td>name of the <span class="name">custom_fields</span> key</td>
|
||||||
<td>'test series'</td>
|
<td>'test series'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>qty</td>
|
<td>qty</td>
|
||||||
<td>number of notes with this key</td>
|
<td>number of notes with this key</td>
|
||||||
<td>24</td>
|
<td>24</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>materials</th><th></th><th></th></tr>
|
<tr><th>materials</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e63e98d1c020f8cda70d0'</td>
|
<td>'5f2e63e98d1c020f8cda70d0'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>name</td>
|
<td>name</td>
|
||||||
<td>Trade name of the material</td>
|
<td>Trade name of the material</td>
|
||||||
<td>'Ultradur B4300 G6'</td>
|
<td>'Ultradur B4300 G6'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>numbers</td>
|
<td>numbers</td>
|
||||||
<td>Bosch material part numbers</td>
|
<td>Bosch material part numbers</td>
|
||||||
<td>['5515753021, '5515753404']</td>
|
<td>['5515753021, '5515753404']</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>properties</td>
|
<td>properties</td>
|
||||||
<td>material class specific properties with <span class="name">material_template</span> reference and all fields
|
<td>material class specific properties with <span class="name">material_template</span> reference and all fields
|
||||||
defined by the template</td>
|
defined by the template</td>
|
||||||
<td>{{'{'}}material_template: '5f2e89874ac96c007fb06e83', mineral: 0, glass_fiber: 30, carbon_fiber: 0{{'}'}}</td>
|
<td>{{'{'}}material_template: '5f2e89874ac96c007fb06e83', mineral: 0, glass_fiber: 30, carbon_fiber: 0{{'}'}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>group_id</td>
|
<td>group_id</td>
|
||||||
<td>Reference to the material group</td>
|
<td>Reference to the material group</td>
|
||||||
<td>'5f2e631191c5d68f8a0708c4'</td>
|
<td>'5f2e631191c5d68f8a0708c4'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>supplier_id</td>
|
<td>supplier_id</td>
|
||||||
<td>Reference to the material supplier</td>
|
<td>Reference to the material supplier</td>
|
||||||
<td>'5f2e631191c5d68f8a0708c7'</td>
|
<td>'5f2e631191c5d68f8a0708c7'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>status</td>
|
<td>status</td>
|
||||||
<td>Status of the document, can be either <span class="name">new</span>, <span class="name">validated</span> or
|
<td>Status of the document, can be either <span class="name">new</span>, <span class="name">validated</span> or
|
||||||
<span class="name">deleted</span>.</td>
|
<span class="name">deleted</span>.</td>
|
||||||
<td>'new'</td>
|
<td>'new'</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>material_groups</th><th></th><th></th></tr>
|
<tr><th>material_groups</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e631291c5d68f8a0708f9'</td>
|
<td>'5f2e631291c5d68f8a0708f9'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>name</td>
|
<td>name</td>
|
||||||
<td>The chemical material type</td>
|
<td>The chemical material type</td>
|
||||||
<td>'PA66'</td>
|
<td>'PA66'</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>material_supplier</th><th></th><th></th></tr>
|
<tr><th>material_supplier</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e631991c5d68f8a0709c3'</td>
|
<td>'5f2e631991c5d68f8a0709c3'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>name</td>
|
<td>name</td>
|
||||||
<td>The material supplier</td>
|
<td>The material supplier</td>
|
||||||
<td>'BASF'</td>
|
<td>'BASF'</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>measurements</th><th></th><th></th></tr>
|
<tr><th>measurements</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f294d25aa9ea5085c7d7305'</td>
|
<td>'5f294d25aa9ea5085c7d7305'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>sample_id</td>
|
<td>sample_id</td>
|
||||||
<td>Reference to the sample this measurement belongs to</td>
|
<td>Reference to the sample this measurement belongs to</td>
|
||||||
<td>'5f2e63c98d1c020f8cda6e06'</td>
|
<td>'5f2e63c98d1c020f8cda6e06'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>measurement_template</td>
|
<td>measurement_template</td>
|
||||||
<td>Reference to the Template defining the structure of the measurement values</td>
|
<td>Reference to the Template defining the structure of the measurement values</td>
|
||||||
<td>'5f294d25aa9ea5085c7d7305'</td>
|
<td>'5f294d25aa9ea5085c7d7305'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>values</td>
|
<td>values</td>
|
||||||
<td>Measurement values in defined format</td>
|
<td>Measurement values in defined format</td>
|
||||||
<td>{{'{'}}vn: 100.4{{'}'}}</td>
|
<td>{{'{'}}vn: 100.4{{'}'}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>status</td>
|
<td>status</td>
|
||||||
<td>Status of the document, can be either <span class="name">new</span>, <span class="name">validated</span> or
|
<td>Status of the document, can be either <span class="name">new</span>, <span class="name">validated</span> or
|
||||||
<span class="name">deleted</span>.</td>
|
<span class="name">deleted</span>.</td>
|
||||||
<td>'new'</td>
|
<td>'new'</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th><collection>_templates</th><th></th><th></th></tr>
|
<tr><th><collection>_templates</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e63ee8d1c020f8cda7128'</td>
|
<td>'5f2e63ee8d1c020f8cda7128'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>name</td>
|
<td>name</td>
|
||||||
<td>Display name of the template</td>
|
<td>Display name of the template</td>
|
||||||
<td>'spectrum'</td>
|
<td>'spectrum'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>version</td>
|
<td>version</td>
|
||||||
<td>Version number of the template</td>
|
<td>Version number of the template</td>
|
||||||
<td>2</td>
|
<td>2</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>first_id</td>
|
<td>first_id</td>
|
||||||
<td>Reference to the first instance of this template with <span class="name">version</span> number 1</td>
|
<td>Reference to the first instance of this template with <span class="name">version</span> number 1</td>
|
||||||
<td>'5f2e89bb4ac96c007fb06e86'</td>
|
<td>'5f2e89bb4ac96c007fb06e86'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>parameters</td>
|
<td>parameters</td>
|
||||||
<td>Specified parameters of this template. The <span class="name">name</span> property is used as the key in the
|
<td>Specified parameters of this template. The <span class="name">name</span> property is used as the key in the
|
||||||
document using this template, the range can have the following properties: <span class="name">min</span> specifies
|
document using this template, the range can have the following properties: <span class="name">min</span> specifies
|
||||||
the minimum numeric value, <span class="name">max</span> specifies the maximum numeric value, <span class="name">
|
the minimum numeric value, <span class="name">max</span> specifies the maximum numeric value, <span class="name">
|
||||||
values</span> specifies an array of allowed values of this parameter and <span class="name">type: 'array'</span>
|
values</span> specifies an array of allowed values of this parameter and <span class="name">type: 'array'</span>
|
||||||
specifies that this parameter must be an array</td>
|
specifies that this parameter must be an array</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>users</th><th></th><th></th></tr>
|
<tr><th>users</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e63cc8d1c020f8cda6e6a'</td>
|
<td>'5f2e63cc8d1c020f8cda6e6a'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>name</td>
|
<td>name</td>
|
||||||
<td>The username</td>
|
<td>The username</td>
|
||||||
<td>'admin'</td>
|
<td>'admin'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>email</td>
|
<td>email</td>
|
||||||
<td>The user's email address used for password reset</td>
|
<td>The user's email address used for password reset</td>
|
||||||
<td>'test@bosch.com'</td>
|
<td>'test@bosch.com'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>location</td>
|
<td>location</td>
|
||||||
<td>The abbreviation of the Bosch site of the user</td>
|
<td>The abbreviation of the Bosch site of the user</td>
|
||||||
<td>'Rng'</td>
|
<td>'Rng'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>level</td>
|
<td>level</td>
|
||||||
<td>The permission level, can be either <span class="name">read</span>, <span class="name">write</span>,
|
<td>The permission level, can be either <span class="name">read</span>, <span class="name">write</span>,
|
||||||
<span class="name">dev</span> or <span class="name">admin</span>. The exact level permissions can be found at the
|
<span class="name">dev</span> or <span class="name">admin</span>. The exact level permissions can be found at the
|
||||||
<a routerLink="/documentation">general documentation</a></td>
|
<a routerLink="/documentation">general documentation</a></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>devices</td>
|
<td>devices</td>
|
||||||
<td>Array of all spectrum measurement devices the user has access to</td>
|
<td>Array of all spectrum measurement devices the user has access to</td>
|
||||||
<td>['Rng01', 'Rng02']</td>
|
<td>['Rng01', 'Rng02']</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>pass</td>
|
<td>pass</td>
|
||||||
<td>The user's password in hashed form using bcrypt</td>
|
<td>The user's password in hashed form using bcrypt</td>
|
||||||
<td>'$2a$10$m8DqvZR3plZEv8EPwPo7Luvyrm/ZQDiPzfBh6bpU/1XFWOGONkJyG'</td>
|
<td>'$2a$10$m8DqvZR3plZEv8EPwPo7Luvyrm/ZQDiPzfBh6bpU/1XFWOGONkJyG'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>key</td>
|
<td>key</td>
|
||||||
<td>The API key, generated when the user is created</td>
|
<td>The API key, generated when the user is created</td>
|
||||||
<td>'5f294dd4aa9ea5085c7d7314'</td>
|
<td>'5f294dd4aa9ea5085c7d7314'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>models</td>
|
<td>models</td>
|
||||||
<td>
|
<td>
|
||||||
Reference to the prediction models the user should have access to. Ignored for <span class="name">dev</span> and
|
Reference to the prediction models the user should have access to. Ignored for <span class="name">dev</span> and
|
||||||
<span class="name">admin</span> as they automatically have access to all models.
|
<span class="name">admin</span> as they automatically have access to all models.
|
||||||
</td>
|
</td>
|
||||||
<td>['5f466fb1e566810dd8b3e919', '5f294d8aaa9ea5085c7d730b']</td>
|
<td>['5f466fb1e566810dd8b3e919', '5f294d8aaa9ea5085c7d730b']</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>status</td>
|
<td>status</td>
|
||||||
<td>Status of the document, can be either <span class="name">new</span>, <span class="name">validated</span> or
|
<td>Status of the document, can be either <span class="name">new</span>, <span class="name">validated</span> or
|
||||||
<span class="name">deleted</span>.</td>
|
<span class="name">deleted</span>.</td>
|
||||||
<td>'new'</td>
|
<td>'new'</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>models</th><th></th><th></th></tr>
|
<tr><th>models</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>group</td>
|
<td>group</td>
|
||||||
<td>group the model belongs to</td>
|
<td>group the model belongs to</td>
|
||||||
<td>'VN'</td>
|
<td>'VN'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>models</td>
|
<td>models</td>
|
||||||
<td>models of the group with _id, name and url to the Python endpoint</td>
|
<td>models of the group with _id, name and url to the Python endpoint</td>
|
||||||
<td>{{'{'}}_id: '5f466fb1e566810dd8b3e919', name: POM, url: 'http://localhost:9099/test'{{'}'}}</td>
|
<td>{{'{'}}_id: '5f466fb1e566810dd8b3e919', name: POM, url: 'http://localhost:9099/test'{{'}'}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>model_files</th><th></th><th></th></tr>
|
<tr><th>model_files</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f294d47aa9ea5085c7d7308'</td>
|
<td>'5f294d47aa9ea5085c7d7308'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>name</td>
|
<td>name</td>
|
||||||
<td>The name of the model</td>
|
<td>The name of the model</td>
|
||||||
<td>'humidity-1'</td>
|
<td>'humidity-1'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>data</td>
|
<td>data</td>
|
||||||
<td>The Python model data in binary format</td>
|
<td>The Python model data in binary format</td>
|
||||||
<td><binary data></td>
|
<td><binary data></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr><th>changelogs</th><th></th><th></th></tr>
|
<tr><th>changelogs</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e63cc8d1c020f8cda6e6e'</td>
|
<td>'5f2e63cc8d1c020f8cda6e6e'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>action</td>
|
<td>action</td>
|
||||||
<td>The URL which invoked the database write access</td>
|
<td>The URL which invoked the database write access</td>
|
||||||
<td>'POST /material/new'</td>
|
<td>'POST /material/new'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>collection_name</td>
|
<td>collection_name</td>
|
||||||
<td>Collection that was written to</td>
|
<td>Collection that was written to</td>
|
||||||
<td>'material_groups'</td>
|
<td>'material_groups'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>conditions</td>
|
<td>conditions</td>
|
||||||
<td>Condition arguments used when accessing the database</td>
|
<td>Condition arguments used when accessing the database</td>
|
||||||
<td>{{'{'}}id: '5f2e63118d1c020f8cda6a0a'{{'}'}}</td>
|
<td>{{'{'}}id: '5f2e63118d1c020f8cda6a0a'{{'}'}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>data</td>
|
<td>data</td>
|
||||||
<td>data which was written to the database</td>
|
<td>data which was written to the database</td>
|
||||||
<td>{{'{'}}name: 'PBT'{{'}'}}</td>
|
<td>{{'{'}}name: 'PBT'{{'}'}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>user_id</td>
|
<td>user_id</td>
|
||||||
<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><th>help</th><th></th><th></th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>_id</td>
|
<td>_id</td>
|
||||||
<td>Automatically generated unique id</td>
|
<td>Automatically generated unique id</td>
|
||||||
<td>'5f2e63d28d1c020f8cda6f86'</td>
|
<td>'5f2e63d28d1c020f8cda6f86'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>key</td>
|
<td>key</td>
|
||||||
<td>The key used to find the required help text</td>
|
<td>The key used to find the required help text</td>
|
||||||
<td>'/documentation/database'</td>
|
<td>'/documentation/database'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>level</td>
|
<td>level</td>
|
||||||
<td>The minimum level required to read this help</td>
|
<td>The minimum level required to read this help</td>
|
||||||
<td>'write'</td>
|
<td>'write'</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>text</td>
|
<td>text</td>
|
||||||
<td>The actual help text</td>
|
<td>The actual help text</td>
|
||||||
<td>'This page documents the database.'</td>
|
<td>'This page documents the database.'</td>
|
||||||
</tr>
|
</tr>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
.field-reference td:nth-child(3) {
|
.field-reference td:nth-child(3) {
|
||||||
font-family: boschmono, monospace;
|
font-family: boschmono, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.name {
|
span.name {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
color: #212529;
|
color: #212529;
|
||||||
background: #e6e6e6;
|
background: #e6e6e6;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-documentation-database',
|
selector: 'app-documentation-database',
|
||||||
templateUrl: './documentation-database.component.html',
|
templateUrl: './documentation-database.component.html',
|
||||||
styleUrls: ['./documentation-database.component.scss']
|
styleUrls: ['./documentation-database.component.scss']
|
||||||
})
|
})
|
||||||
export class DocumentationDatabaseComponent implements OnInit {
|
export class DocumentationDatabaseComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
<h4>Model file upload</h4>
|
<h4>Model file upload</h4>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Model files have to be saved as a .pkl file in 80_Modelle_BIC. <br>
|
Model files have to be saved as a .pkl file in 80_Modelle_BIC. <br>
|
||||||
For upload the upload script has to be opened (can be in every environment). The name of the model file has to be
|
For upload the upload script has to be opened (can be in every environment). The name of the model file has to be
|
||||||
entered wih the .pkl ending in the first command and without .pkl in the second.
|
entered wih the .pkl ending in the first command and without .pkl in the second.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4 class="space-above">Adding new model scripts</h4>
|
<h4 class="space-above">Adding new model scripts</h4>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Open Spyder in the base environment and open the model.py within.
|
Open Spyder in the base environment and open the model.py within.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Enter model file name without .pkl in fetchData</li>
|
<li>Enter model file name without .pkl in fetchData</li>
|
||||||
<li>Add new prediction function below others</li>
|
<li>Add new prediction function below others</li>
|
||||||
<li>duplicate another @app.route and rename according to desired route name and prediction function name</li>
|
<li>duplicate another @app.route and rename according to desired route name and prediction function name</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4 class="space-above">Testing locally</h4>
|
<h4 class="space-above">Testing locally</h4>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
To test the model Python script locally, you need to execute it in the base environment, in which all necessary
|
To test the model Python script locally, you need to execute it in the base environment, in which all necessary
|
||||||
packages have to be installed. Note that Spyder also has to be opened in the base environment.
|
packages have to be installed. Note that Spyder also has to be opened in the base environment.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Open a separate Anaconda Prompt and cd into the definma-UI folder</li>
|
<li>Open a separate Anaconda Prompt and cd into the definma-UI folder</li>
|
||||||
<li>Adapt the model-mock.json at definma-UI/assets/ and enter the new model URL and name</li>
|
<li>Adapt the model-mock.json at definma-UI/assets/ and enter the new model URL and name</li>
|
||||||
<li>Execute "python -m http.server" in the Anaconda Prompt</li>
|
<li>Execute "python -m http.server" in the Anaconda Prompt</li>
|
||||||
<li>Execute the model.py script in Spyder</li>
|
<li>Execute the model.py script in Spyder</li>
|
||||||
<li>Navigate to <a href="http://localhost:8000">http://localhost:8000</a> in the browser</li>
|
<li>Navigate to <a href="http://localhost:8000">http://localhost:8000</a> in the browser</li>
|
||||||
<li>If there are problems with cached data, open the Browser developer console (Ctrl+Shift+I) and tick
|
<li>If there are problems with cached data, open the Browser developer console (Ctrl+Shift+I) and tick
|
||||||
"Disable cache" in the Network tab. The console then has to stay open.</li>
|
"Disable cache" in the Network tab. The console then has to stay open.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4 class="space-above">Model script upload</h4>
|
<h4 class="space-above">Model script upload</h4>
|
||||||
@ -40,17 +40,17 @@
|
|||||||
In the Windows Powershell:
|
In the Windows Powershell:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>cf login (only at the first time)</li>
|
<li>cf login (only at the first time)</li>
|
||||||
<li>API endpoint: https://api.sys.de1.bosch-iot-cloud.com</li>
|
<li>API endpoint: https://api.sys.de1.bosch-iot-cloud.com</li>
|
||||||
<li>Enter email with .de.bosch.com</li>
|
<li>Enter email with .de.bosch.com</li>
|
||||||
<li>Enter BIC password</li>
|
<li>Enter BIC password</li>
|
||||||
<li>Select space to upload to (currently 3 - development)</li>
|
<li>Select space to upload to (currently 3 - development)</li>
|
||||||
<li>cd into the folder containing the manifest.yaml (here is also model.py)</li>
|
<li>cd into the folder containing the manifest.yaml (here is also model.py)</li>
|
||||||
<li>cf push</li>
|
<li>cf push</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
After upload the new model details have to be entered in the UI.
|
After upload the new model details have to be entered in the UI.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-documentation-models',
|
selector: 'app-documentation-models',
|
||||||
templateUrl: './documentation-models.component.html',
|
templateUrl: './documentation-models.component.html',
|
||||||
styleUrls: ['./documentation-models.component.scss']
|
styleUrls: ['./documentation-models.component.scss']
|
||||||
})
|
})
|
||||||
export class DocumentationModelsComponent implements OnInit {
|
export class DocumentationModelsComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,78 +1,78 @@
|
|||||||
<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
|
||||||
</a><br>
|
</a><br>
|
||||||
View the <a href="/assets/docs/Veit-Lukas_T3000.pdf">T3000 report</a> and
|
View the <a href="/assets/docs/Veit-Lukas_T3000.pdf">T3000 report</a> and
|
||||||
<a href="/assets/docs/Veit-Lukas_Bachelor-Thesis.pdf">Bachelor Thesis</a> for an in-depth application description.
|
<a href="/assets/docs/Veit-Lukas_Bachelor-Thesis.pdf">Bachelor Thesis</a> for an in-depth application description.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>User levels</h4>
|
<h4>User levels</h4>
|
||||||
|
|
||||||
<rb-table>
|
<rb-table>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>prediction</th>
|
<th>prediction</th>
|
||||||
<th>read</th>
|
<th>read</th>
|
||||||
<th>write</th>
|
<th>write</th>
|
||||||
<th>dev</th>
|
<th>dev</th>
|
||||||
<th>admin</th>
|
<th>admin</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>use prediction models</th>
|
<th>use prediction models</th>
|
||||||
<td>specified ones</td>
|
<td>specified ones</td>
|
||||||
<td>specified ones</td>
|
<td>specified ones</td>
|
||||||
<td>specified ones</td>
|
<td>specified ones</td>
|
||||||
<td>all</td>
|
<td>all</td>
|
||||||
<td>all</td>
|
<td>all</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>read sample data</th>
|
<th>read sample data</th>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>add samples/edit own</th>
|
<th>add samples/edit own</th>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>read spectral data</th>
|
<th>read spectral data</th>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>edit other's data</th>
|
<th>edit other's data</th>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>maintain templates</th>
|
<th>maintain templates</th>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>edit users</th>
|
<th>edit users</th>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
<td><span class="rb-ic rb-ic-abort-frame"></span></td>
|
||||||
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
<td><span class="rb-ic rb-ic-checkmark-frame"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
|
@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
img#db-structure {
|
img#db-structure {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rb-ic-checkmark-frame {
|
.rb-ic-checkmark-frame {
|
||||||
color: $brand-success;
|
color: $brand-success;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rb-ic-abort-frame {
|
.rb-ic-abort-frame {
|
||||||
color: $brand-danger;
|
color: $brand-danger;
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,17 @@ import {ApiService} from '../services/api.service';
|
|||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-documentation',
|
selector: 'app-documentation',
|
||||||
templateUrl: './documentation.component.html',
|
templateUrl: './documentation.component.html',
|
||||||
styleUrls: ['./documentation.component.scss']
|
styleUrls: ['./documentation.component.scss']
|
||||||
})
|
})
|
||||||
export class DocumentationComponent implements OnInit {
|
export class DocumentationComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public api: ApiService
|
public api: ApiService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<rb-alert alertTitle="Error" type="error" okBtnLabel="Discard">
|
<rb-alert alertTitle="Error" type="error" okBtnLabel="Discard">
|
||||||
<div class="space-below">
|
<div class="space-below">
|
||||||
{{message}}
|
{{message}}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="details.length">
|
<div *ngIf="details.length">
|
||||||
<a href="javascript:" class="rb-details-toggle" rbDetailsToggle #triggerDetails="rbDetailsToggle">Details</a>
|
<a href="javascript:" class="rb-details-toggle" rbDetailsToggle #triggerDetails="rbDetailsToggle">Details</a>
|
||||||
<div *ngIf="triggerDetails.open">
|
<div *ngIf="triggerDetails.open">
|
||||||
<p *ngFor="let detail of details">{{detail}}</p>
|
<p *ngFor="let detail of details">{{detail}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</rb-alert>
|
</rb-alert>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-error',
|
selector: 'app-error',
|
||||||
templateUrl: './error.component.html',
|
templateUrl: './error.component.html',
|
||||||
styleUrls: ['./error.component.scss']
|
styleUrls: ['./error.component.scss']
|
||||||
})
|
})
|
||||||
export class ErrorComponent implements OnInit {
|
export class ErrorComponent implements OnInit {
|
||||||
|
|
||||||
message = ''; // Main error message
|
message = ''; // Main error message
|
||||||
details: string[] = []; // Array of error detail paragraphs
|
details: string[] = []; // Array of error detail paragraphs
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ import { ExistsPipe } from './exists.pipe';
|
|||||||
|
|
||||||
|
|
||||||
describe('ExistsPipe', () => {
|
describe('ExistsPipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
const pipe = new ExistsPipe();
|
const pipe = new ExistsPipe();
|
||||||
expect(pipe).toBeTruthy();
|
expect(pipe).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'exists',
|
name: 'exists',
|
||||||
pure: true
|
pure: true
|
||||||
})
|
})
|
||||||
export class ExistsPipe implements PipeTransform {
|
export class ExistsPipe implements PipeTransform {
|
||||||
|
|
||||||
transform(value: unknown, key?): unknown {
|
transform(value: unknown, key?): unknown {
|
||||||
return value || value === 0 ? (key ? value[key] : value) : '';
|
return value || value === 0 ? (key ? value[key] : value) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
<h3>Help
|
<h3>Help
|
||||||
<span *ngIf="login.isLevel.dev" class="rb-ic rb-ic-edit clickable space-left" (click)="edit = true"></span>
|
<span *ngIf="login.isLevel.dev" class="rb-ic rb-ic-edit clickable space-left" (click)="edit = true"></span>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div *ngIf="edit; else normalView">
|
<div *ngIf="edit; else normalView">
|
||||||
<rb-form-select label="level" [(ngModel)]="content.level">
|
<rb-form-select label="level" [(ngModel)]="content.level">
|
||||||
<option value="none">none</option>
|
<option value="none">none</option>
|
||||||
<option *ngFor="let level of login.levels" [value]="level">{{level}}</option>
|
<option *ngFor="let level of login.levels" [value]="level">{{level}}</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
<rb-form-textarea label="text" [(ngModel)]="content.text"></rb-form-textarea>
|
<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="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>
|
<rb-icon-button icon="delete" mode="danger" (click)="deleteHelp()" class="delete-btn">Delete</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #normalView>
|
<ng-template #normalView>
|
||||||
<p *ngIf="content.text; else defaultContent" class="content-text">
|
<p *ngIf="content.text; else defaultContent" class="content-text">
|
||||||
{{content.text}}
|
{{content.text}}
|
||||||
</p>
|
</p>
|
||||||
<ng-template #defaultContent>
|
<ng-template #defaultContent>
|
||||||
<ng-container *ngIf="content.text === ''">
|
<ng-container *ngIf="content.text === ''">
|
||||||
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.
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.delete-btn {
|
.delete-btn {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-text {
|
.content-text {
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
@ -6,48 +6,48 @@ import {HelpModel} from '../models/help.model';
|
|||||||
import {LoginService} from '../services/login.service';
|
import {LoginService} from '../services/login.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-help',
|
selector: 'app-help',
|
||||||
templateUrl: './help.component.html',
|
templateUrl: './help.component.html',
|
||||||
styleUrls: ['./help.component.scss']
|
styleUrls: ['./help.component.scss']
|
||||||
})
|
})
|
||||||
export class HelpComponent implements OnInit {
|
export class HelpComponent implements OnInit {
|
||||||
|
|
||||||
content: HelpModel = new HelpModel().deserialize({text: null, level: 'none'}); // Help content
|
content: HelpModel = new HelpModel().deserialize({text: null, level: 'none'}); // Help content
|
||||||
edit = false; // Set true to change to edit mode
|
edit = false; // Set true to change to edit mode
|
||||||
private route = ''; // URIComponent encoded route which serves as a key to fetch the help document
|
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,
|
private api: ApiService,
|
||||||
public login: LoginService
|
public login: LoginService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Remove ids from path
|
// Remove ids from path
|
||||||
this.route = encodeURIComponent(this.router.url.replace(/\/[0-9a-f]{24}/, ''));
|
this.route = encodeURIComponent(this.router.url.replace(/\/[0-9a-f]{24}/, ''));
|
||||||
this.api.get<HelpModel>('/help/' + this.route, (data, err) => {
|
this.api.get<HelpModel>('/help/' + this.route, (data, err) => {
|
||||||
if (!err) { // Content was found
|
if (!err) { // Content was found
|
||||||
this.content = new HelpModel().deserialize(data);
|
this.content = new HelpModel().deserialize(data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.content.text = '';
|
this.content.text = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
saveHelp() {
|
saveHelp() {
|
||||||
this.api.post('/help/' + this.route, this.content.sendFormat(), () => {
|
this.api.post('/help/' + this.route, this.content.sendFormat(), () => {
|
||||||
this.edit = false;
|
this.edit = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteHelp() {
|
deleteHelp() {
|
||||||
this.api.delete('/help/' + this.route, (ignore, err) => {
|
this.api.delete('/help/' + this.route, (ignore, err) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
this.content = new HelpModel().deserialize({text: null, level: 'none'});
|
this.content = new HelpModel().deserialize({text: null, level: 'none'});
|
||||||
this.edit = false;
|
this.edit = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<div *ngIf="!login.isLoggedIn">
|
<div *ngIf="!login.isLoggedIn">
|
||||||
<app-login></app-login>
|
<app-login></app-login>
|
||||||
<img src="/assets/imgs/key-visual.png" alt="" class="key-visual">
|
<img src="/assets/imgs/key-visual.png" alt="" class="key-visual">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div *ngIf="login.isLoggedIn">
|
<div *ngIf="login.isLoggedIn">
|
||||||
<rb-form-multi-select name="groupSelect" idField="id" [items]="keys" [(ngModel)]="isActiveKey" label="Groups" class="selection">
|
<rb-form-multi-select name="groupSelect" idField="id" [items]="keys" [(ngModel)]="isActiveKey" label="Groups" class="selection">
|
||||||
<span *rbFormMultiSelectOption="let key">{{key.id}}</span>
|
<span *rbFormMultiSelectOption="let key">{{key.id}}</span>
|
||||||
|
|
||||||
</rb-form-multi-select>
|
</rb-form-multi-select>
|
||||||
<rb-icon-button icon="forward-right" mode="primary" (click)="updateGroups(isActiveKey)">
|
<rb-icon-button icon="forward-right" mode="primary" (click)="updateGroups(isActiveKey)">
|
||||||
Apply groups
|
Apply groups
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
|
|
||||||
<div id="divChart">
|
<div id="divChart">
|
||||||
<canvas id="myChart">
|
<canvas id="myChart">
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
app-login {
|
app-login {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-visual {
|
.key-visual {
|
||||||
width: 70%;
|
width: 70%;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selection{
|
.selection{
|
||||||
float: left;
|
float: left;
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-form-multi-select {
|
rb-form-multi-select {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
|
@ -5,138 +5,138 @@ import { Chart } from 'chart.js';
|
|||||||
|
|
||||||
|
|
||||||
interface KeyInterface {
|
interface KeyInterface {
|
||||||
id: string;
|
id: string;
|
||||||
count: number;
|
count: number;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrls: ['./home.component.scss']
|
styleUrls: ['./home.component.scss']
|
||||||
})
|
})
|
||||||
export class HomeComponent implements OnInit {
|
export class HomeComponent implements OnInit {
|
||||||
|
|
||||||
keys: KeyInterface[] = [];
|
keys: KeyInterface[] = [];
|
||||||
isActiveKey: { [key: string]: boolean } = {}; // Object to check if key is currently active
|
isActiveKey: { [key: string]: boolean } = {}; // Object to check if key is currently active
|
||||||
myChart: Chart;
|
myChart: Chart;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public login: LoginService,
|
public login: LoginService,
|
||||||
public api: ApiService
|
public api: ApiService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
// Fetch all available groups
|
// Fetch all available groups
|
||||||
this.fetchData('/material/groups', data => this.createGroup(data));
|
this.fetchData('/material/groups', data => this.createGroup(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Api access with callback
|
// Api access with callback
|
||||||
async fetchData(URL: string, processor: any) {
|
async fetchData(URL: string, processor: any) {
|
||||||
this.api.get(URL, (sData, err, headers) => {
|
this.api.get(URL, (sData, err, headers) => {
|
||||||
processor(sData);
|
processor(sData);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill interface with data
|
// Fill interface with data
|
||||||
createGroup(data: any) {
|
createGroup(data: any) {
|
||||||
let temp: KeyInterface[] = [];
|
let temp: KeyInterface[] = [];
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
temp.push({ id: data[i], count: 0, active: false });
|
temp.push({ id: data[i], count: 0, active: false });
|
||||||
}
|
}
|
||||||
this.keys = temp; // Invoke update in rb-multiselect
|
this.keys = temp; // Invoke update in rb-multiselect
|
||||||
this.initChart();
|
this.initChart();
|
||||||
|
|
||||||
// Only neccesary if keys get preselected
|
// Only neccesary if keys get preselected
|
||||||
//this.calcFieldSelectKeys();
|
//this.calcFieldSelectKeys();
|
||||||
|
|
||||||
// Fetch all samples populated with according group
|
// Fetch all samples populated with according group
|
||||||
this.getSamples();
|
this.getSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through active keys to assemble an api request for the required data
|
// Iterate through active keys to assemble an api request for the required data
|
||||||
getSamples() {
|
getSamples() {
|
||||||
let query = '/samples?status%5B%5D=validated&status=new&filters%5B%5D=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22material.group%22%2C%22values%22%3A%5B';
|
let query = '/samples?status%5B%5D=validated&status=new&filters%5B%5D=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22material.group%22%2C%22values%22%3A%5B';
|
||||||
let temp = '';
|
let temp = '';
|
||||||
this.keys.forEach(key => {
|
this.keys.forEach(key => {
|
||||||
temp += key.active ? '%22' + key.id.split("%").join("%25") + '%22%2C' : ""; // Replace split().join() with replaceAll()
|
temp += key.active ? '%22' + key.id.split("%").join("%25") + '%22%2C' : ""; // Replace split().join() with replaceAll()
|
||||||
});
|
});
|
||||||
if (temp === '') {
|
if (temp === '') {
|
||||||
this.countSamples('');
|
this.countSamples('');
|
||||||
} else {
|
} else {
|
||||||
query = query + temp.substr(0, temp.length - 3) + '%5D%7D&fields%5B%5D=material.group';
|
query = query + temp.substr(0, temp.length - 3) + '%5D%7D&fields%5B%5D=material.group';
|
||||||
this.fetchData(query, data => this.countSamples(data));
|
this.fetchData(query, data => this.countSamples(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through samples and count
|
// Loop through samples and count
|
||||||
countSamples(data: any) {
|
countSamples(data: any) {
|
||||||
this.keys.map(key => key.count = 0);
|
this.keys.map(key => key.count = 0);
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
this.keys.forEach(key => {
|
this.keys.forEach(key => {
|
||||||
if (key.id === data[i].material.group) {
|
if (key.id === data[i].material.group) {
|
||||||
key.count += 1;
|
key.count += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.updateGraph();
|
this.updateGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preset select
|
// Preset select
|
||||||
calcFieldSelectKeys() {
|
calcFieldSelectKeys() {
|
||||||
this.keys.forEach(key => {
|
this.keys.forEach(key => {
|
||||||
this.isActiveKey[key.id] = key.active;
|
this.isActiveKey[key.id] = key.active;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update keys based on select
|
// Update keys based on select
|
||||||
updateGroups(activeKeys: any) {
|
updateGroups(activeKeys: any) {
|
||||||
this.keys.forEach(key => {
|
this.keys.forEach(key => {
|
||||||
if (activeKeys.hasOwnProperty(key.id)) {
|
if (activeKeys.hasOwnProperty(key.id)) {
|
||||||
key.active = activeKeys[key.id];
|
key.active = activeKeys[key.id];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.getSamples();
|
this.getSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get data for graph based on active keys
|
// Get data for graph based on active keys
|
||||||
updateGraph() {
|
updateGraph() {
|
||||||
let nameList: string[] = [];
|
let nameList: string[] = [];
|
||||||
let dataList: number[] = [];
|
let dataList: number[] = [];
|
||||||
|
|
||||||
this.keys.forEach(key => {
|
this.keys.forEach(key => {
|
||||||
if (key.active) {
|
if (key.active) {
|
||||||
nameList.push(key.id);
|
nameList.push(key.id);
|
||||||
dataList.push(key.count);
|
dataList.push(key.count);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.myChart.data.labels = nameList;
|
this.myChart.data.labels = nameList;
|
||||||
this.myChart.data.datasets[0].data = dataList;
|
this.myChart.data.datasets[0].data = dataList;
|
||||||
this.myChart.update();
|
this.myChart.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize graph
|
// Initialize graph
|
||||||
async initChart() {
|
async initChart() {
|
||||||
this.myChart = new Chart("myChart", {
|
this.myChart = new Chart("myChart", {
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {
|
data: {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Number of samples per group',
|
label: 'Number of samples per group',
|
||||||
data: [],
|
data: [],
|
||||||
backgroundColor: 'rgba(0, 86, 145, 1)'
|
backgroundColor: 'rgba(0, 86, 145, 1)'
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
scales: {
|
scales: {
|
||||||
yAxes: [{
|
yAxes: [{
|
||||||
ticks: {
|
ticks: {
|
||||||
beginAtZero: true
|
beginAtZero: true
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<div class="img-container">
|
<div class="img-container">
|
||||||
<div class="magnifier"
|
<div class="magnifier"
|
||||||
[ngStyle]="{
|
[ngStyle]="{
|
||||||
'background-size': backgroundSize,
|
'background-size': backgroundSize,
|
||||||
'background-image': 'url(\'' + src + '\')',
|
'background-image': 'url(\'' + src + '\')',
|
||||||
'background-position': '-' + magnifierPos.x * zoom + 'px -' + magnifierPos.y * zoom + 'px',
|
'background-position': '-' + magnifierPos.x * zoom + 'px -' + magnifierPos.y * zoom + 'px',
|
||||||
left: magnifierPos.x + 'px',
|
left: magnifierPos.x + 'px',
|
||||||
top: magnifierPos.y + 'px',
|
top: magnifierPos.y + 'px',
|
||||||
width: magnifierSize.width + 'px',
|
width: magnifierSize.width + 'px',
|
||||||
height: magnifierSize.height + 'px'
|
height: magnifierSize.height + 'px'
|
||||||
}"
|
}"
|
||||||
(mousemove)="calcPos($event)"
|
(mousemove)="calcPos($event)"
|
||||||
(mouseleave)="showMagnifier = false"
|
(mouseleave)="showMagnifier = false"
|
||||||
*ngIf="showMagnifier">
|
*ngIf="showMagnifier">
|
||||||
</div>
|
</div>
|
||||||
<img [src]="src" alt="" (mousemove)="calcPos($event)" (mouseenter)="showMagnifier = true" #mainImg>
|
<img [src]="src" alt="" (mousemove)="calcPos($event)" (mouseenter)="showMagnifier = true" #mainImg>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
|
@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
|
||||||
|
|
||||||
.img-container {
|
.img-container {
|
||||||
position:relative;
|
position:relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
& > img {
|
& > img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.magnifier {
|
.magnifier {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: #FFF no-repeat -500px -500px;
|
background: #FFF no-repeat -500px -500px;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
border: 1px solid #FFF;
|
border: 1px solid #FFF;
|
||||||
box-shadow: 10px 10px 25px $color-bosch-light-gray-b25;
|
box-shadow: 10px 10px 25px $color-bosch-light-gray-b25;
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,48 @@
|
|||||||
import {AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
|
import {AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-img-magnifier',
|
selector: 'app-img-magnifier',
|
||||||
templateUrl: './img-magnifier.component.html',
|
templateUrl: './img-magnifier.component.html',
|
||||||
styleUrls: ['./img-magnifier.component.scss']
|
styleUrls: ['./img-magnifier.component.scss']
|
||||||
})
|
})
|
||||||
export class ImgMagnifierComponent implements OnInit, AfterViewInit {
|
export class ImgMagnifierComponent implements OnInit, AfterViewInit {
|
||||||
|
|
||||||
@Input() src: string; // Image source
|
@Input() src: string; // Image source
|
||||||
@Input() zoom: number; // Zoom level
|
@Input() zoom: number; // Zoom level
|
||||||
@Input() magnifierSize: {width: number, height: number}; // Size of the magnifier
|
@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}; // Position of the magnifier
|
magnifierPos = {x: 0, y: 0}; // Position of the magnifier
|
||||||
showMagnifier = false;
|
showMagnifier = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private window: Window
|
private window: Window
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.calcBackgroundSize();
|
this.calcBackgroundSize();
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
calcPos(event) { // Calculate the current magnifier position
|
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,
|
||||||
Math.max(0, event.pageX - img.left - this.window.pageXOffset - this.magnifierSize.width / 2)
|
Math.max(0, event.pageX - img.left - this.window.pageXOffset - this.magnifierSize.width / 2)
|
||||||
);
|
);
|
||||||
this.magnifierPos.y = Math.min(
|
this.magnifierPos.y = Math.min(
|
||||||
img.height - this.magnifierSize.height + 7,
|
img.height - this.magnifierSize.height + 7,
|
||||||
Math.max(0, event.pageY - img.top - this.window.pageYOffset - this.magnifierSize.height / 2)
|
Math.max(0, event.pageY - img.top - this.window.pageYOffset - this.magnifierSize.height / 2)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
calcBackgroundSize() {
|
calcBackgroundSize() {
|
||||||
this.backgroundSize = this.mainImg ? (this.mainImg.nativeElement.width * this.zoom - this.magnifierSize.width) +
|
this.backgroundSize = this.mainImg ? (this.mainImg.nativeElement.width * this.zoom - this.magnifierSize.width) +
|
||||||
'px ' + (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,25 +1,25 @@
|
|||||||
<div class="login-wrapper">
|
<div class="login-wrapper">
|
||||||
<h2>Please log in</h2>
|
<h2>Please log in</h2>
|
||||||
|
|
||||||
|
|
||||||
<form #loginForm="ngForm">
|
<form #loginForm="ngForm">
|
||||||
<rb-form-input name="username" label="username" appValidate="username" required [(ngModel)]="username"
|
<rb-form-input name="username" label="username" appValidate="username" required [(ngModel)]="username"
|
||||||
#usernameInput="ngModel">
|
#usernameInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{usernameInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{usernameInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input *ngIf="!passreset" type="password" name="password" label="password" appValidate="password" required
|
<rb-form-input *ngIf="!passreset" type="password" name="password" label="password" appValidate="password" required
|
||||||
[(ngModel)]="password" #passwordInput="ngModel">
|
[(ngModel)]="password" #passwordInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{passwordInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{passwordInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input *ngIf="passreset" type="email" name="email" label="email" email required [(ngModel)]="email"
|
<rb-form-input *ngIf="passreset" type="email" name="email" label="email" email required [(ngModel)]="email"
|
||||||
#emailInput="ngModel">
|
#emailInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{emailInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{emailInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<a href="#" class="forgot-pass" (click)="passreset = !passreset">Forgot password</a>
|
<a href="#" class="forgot-pass" (click)="passreset = !passreset">Forgot password</a>
|
||||||
<button class="rb-btn rb-primary login-button" (click)="userLogin()" type="submit"
|
<button class="rb-btn rb-primary login-button" (click)="userLogin()" type="submit"
|
||||||
[disabled]="!loginForm.form.valid">
|
[disabled]="!loginForm.form.valid">
|
||||||
{{passreset ? 'Send' : 'Login'}}
|
{{passreset ? 'Send' : 'Login'}}
|
||||||
</button>
|
</button>
|
||||||
<div class="message">{{message}}</div>
|
<div class="message">{{message}}</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
.login-wrapper {
|
.login-wrapper {
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-button {
|
.login-button {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.forgot-pass {
|
.forgot-pass {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
@ -6,57 +6,57 @@ import {ApiService} from '../services/api.service';
|
|||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login',
|
selector: 'app-login',
|
||||||
templateUrl: './login.component.html',
|
templateUrl: './login.component.html',
|
||||||
styleUrls: ['./login.component.scss']
|
styleUrls: ['./login.component.scss']
|
||||||
})
|
})
|
||||||
export class LoginComponent implements OnInit {
|
export class LoginComponent implements OnInit {
|
||||||
|
|
||||||
username = ''; // Credentials
|
username = ''; // Credentials
|
||||||
password = '';
|
password = '';
|
||||||
email = '';
|
email = '';
|
||||||
message = ''; // Message below login fields
|
message = ''; // Message below login fields
|
||||||
passreset = false; // To toggle between normal login and password reset form
|
passreset = false; // To toggle between normal login and password reset form
|
||||||
|
|
||||||
@ViewChild('loginForm') loginForm;
|
@ViewChild('loginForm') loginForm;
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private validate: ValidationService,
|
private validate: ValidationService,
|
||||||
private login: LoginService,
|
private login: LoginService,
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
private router: Router
|
private router: Router
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
userLogin() {
|
userLogin() {
|
||||||
if (this.passreset) { // Reset password
|
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';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.message = 'Password reset, check your inbox';
|
this.message = 'Password reset, check your inbox';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.login.login(this.username, this.password).then(ok => {
|
this.login.login(this.username, this.password).then(ok => {
|
||||||
if (ok) {
|
if (ok) {
|
||||||
this.message = 'Login successful';
|
this.message = 'Login successful';
|
||||||
if (this.login.isLevel.read) {
|
if (this.login.isLevel.read) {
|
||||||
this.router.navigate(['/samples']);
|
this.router.navigate(['/samples']);
|
||||||
}
|
}
|
||||||
else { // Navigate prediction users to prediction as they cannot access samples
|
else { // Navigate prediction users to prediction as they cannot access samples
|
||||||
this.router.navigate(['/prediction']);
|
this.router.navigate(['/prediction']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.message = 'Wrong credentials!';
|
this.message = 'Wrong credentials!';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,65 +1,65 @@
|
|||||||
<h2>{{material.name | exists}}</h2>
|
<h2>{{material.name | exists}}</h2>
|
||||||
|
|
||||||
<form #materialForm="ngForm" *ngIf="!loading">
|
<form #materialForm="ngForm" *ngIf="!loading">
|
||||||
<rb-form-input name="materialname" label="product name" appValidate="stringNin" [appValidateArgs]="[materialNames]"
|
<rb-form-input name="materialname" label="product name" appValidate="stringNin" [appValidateArgs]="[materialNames]"
|
||||||
required [(ngModel)]="material.name" #materialnameInput="ngModel">
|
required [(ngModel)]="material.name" #materialnameInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{materialnameInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{materialnameInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="supplier" label="supplier"
|
<rb-form-input name="supplier" label="supplier"
|
||||||
[rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialSuppliers)"
|
[rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialSuppliers)"
|
||||||
[rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
|
[rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
|
||||||
[(ngModel)]="material.supplier" #supplierInput="ngModel"
|
[(ngModel)]="material.supplier" #supplierInput="ngModel"
|
||||||
(focusout)="checkTypo($event, 'materialSuppliers', 'supplier', modalWarning)"
|
(focusout)="checkTypo($event, 'materialSuppliers', 'supplier', modalWarning)"
|
||||||
title="material supplier, eg. BASF">
|
title="material supplier, eg. BASF">
|
||||||
<ng-template rbFormValidationMessage="failure">{{supplierInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{supplierInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="group" label="group"
|
<rb-form-input name="group" label="group"
|
||||||
[rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialGroups)"
|
[rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialGroups)"
|
||||||
[rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
|
[rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
|
||||||
[(ngModel)]="material.group" #groupInput="ngModel"
|
[(ngModel)]="material.group" #groupInput="ngModel"
|
||||||
(focusout)="checkTypo($event, 'materialGroups', 'group', modalWarning)"
|
(focusout)="checkTypo($event, 'materialGroups', 'group', modalWarning)"
|
||||||
title="chemical material type, eg. PA66">
|
title="chemical material type, eg. PA66">
|
||||||
<ng-template rbFormValidationMessage="failure">{{groupInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{groupInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
|
|
||||||
<ng-template #modalWarning>
|
<ng-template #modalWarning>
|
||||||
<rb-alert alertTitle="Warning" type="warning" okBtnLabel="Use suggestion" cancelBtnLabel="Keep value">
|
<rb-alert alertTitle="Warning" type="warning" okBtnLabel="Use suggestion" cancelBtnLabel="Keep value">
|
||||||
The specified {{modalText.list}} could not be found in the list. <br>
|
The specified {{modalText.list}} could not be found in the list. <br>
|
||||||
Did you mean {{modalText.suggestion}}?
|
Did you mean {{modalText.suggestion}}?
|
||||||
</rb-alert>
|
</rb-alert>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<rb-array-input [(ngModel)]="material.numbers" name="materialNumbers" [pushTemplate]="''">
|
<rb-array-input [(ngModel)]="material.numbers" name="materialNumbers" [pushTemplate]="''">
|
||||||
<rb-form-input *rbArrayInputItem="let item" [rbArrayInputListener]="'materialNumber'" [index]="item.i"
|
<rb-form-input *rbArrayInputItem="let item" [rbArrayInputListener]="'materialNumber'" [index]="item.i"
|
||||||
label="material number" appValidate="string" [name]="'materialNumber-' + item.i"
|
label="material number" appValidate="string" [name]="'materialNumber-' + item.i"
|
||||||
[ngModel]="item.value" title="Bosch material part number, eg. 5515753021"></rb-form-input>
|
[ngModel]="item.value" title="Bosch material part number, eg. 5515753021"></rb-form-input>
|
||||||
</rb-array-input>
|
</rb-array-input>
|
||||||
<rb-form-select name="propertiesSelect" label="Type" title="=overall material group specific properties"
|
<rb-form-select name="propertiesSelect" label="Type" title="=overall material group specific properties"
|
||||||
[(ngModel)]="material.properties.material_template">
|
[(ngModel)]="material.properties.material_template">
|
||||||
<option *ngFor="let m of d.latest.materialTemplates" [value]="m._id">{{m.name}}</option>
|
<option *ngFor="let m of d.latest.materialTemplates" [value]="m._id">{{m.name}}</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
<rb-form-input *ngFor="let parameter of
|
<rb-form-input *ngFor="let parameter of
|
||||||
d.id.materialTemplates[material.properties.material_template].parameters;
|
d.id.materialTemplates[material.properties.material_template].parameters;
|
||||||
index as i" [name]="'materialParameter' + i"
|
index as i" [name]="'materialParameter' + i"
|
||||||
[label]="parameter.name" appValidate="string" required
|
[label]="parameter.name" appValidate="string" required
|
||||||
[(ngModel)]="material.properties[parameter.name]" #parameterInput="ngModel">
|
[(ngModel)]="material.properties[parameter.name]" #parameterInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
|
|
||||||
<rb-icon-button icon="save" mode="primary" type="submit" (click)="materialSave()"
|
<rb-icon-button icon="save" mode="primary" type="submit" (click)="materialSave()"
|
||||||
[disabled]="materialForm.form.invalid">
|
[disabled]="materialForm.form.invalid">
|
||||||
Save material
|
Save material
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
<rb-icon-button class="delete-material" icon="delete" mode="danger" (click)="deleteConfirm(modalDeleteConfirm)">
|
<rb-icon-button class="delete-material" icon="delete" mode="danger" (click)="deleteConfirm(modalDeleteConfirm)">
|
||||||
Delete sample
|
Delete sample
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
<ng-template #modalDeleteConfirm>
|
<ng-template #modalDeleteConfirm>
|
||||||
<rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete material'"
|
<rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete material'"
|
||||||
cancelBtnLabel="Cancel">
|
cancelBtnLabel="Cancel">
|
||||||
Do you really want to delete {{material.name}}?
|
Do you really want to delete {{material.name}}?
|
||||||
</rb-alert>
|
</rb-alert>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
.delete-material {
|
.delete-material {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
@ -11,132 +11,132 @@ import {ValidationService} from '../services/validation.service';
|
|||||||
import {ErrorComponent} from '../error/error.component';
|
import {ErrorComponent} from '../error/error.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-material',
|
selector: 'app-material',
|
||||||
templateUrl: './material.component.html',
|
templateUrl: './material.component.html',
|
||||||
styleUrls: ['./material.component.scss']
|
styleUrls: ['./material.component.scss']
|
||||||
})
|
})
|
||||||
export class MaterialComponent implements OnInit, AfterContentChecked {
|
export class MaterialComponent implements OnInit, AfterContentChecked {
|
||||||
|
|
||||||
@ViewChild('materialForm') materialForm: NgForm;
|
@ViewChild('materialForm') materialForm: NgForm;
|
||||||
|
|
||||||
material: MaterialModel; // Material to edit
|
material: MaterialModel; // Material to edit
|
||||||
materialNames: string[] = []; // All other material names for unique validation
|
materialNames: string[] = []; // All other material names for unique validation
|
||||||
|
|
||||||
modalText = {list: '', suggestion: ''}; // Modal for group and supplier correction
|
modalText = {list: '', suggestion: ''}; // Modal for group and supplier correction
|
||||||
loading = 0; // Number of loading instances
|
loading = 0; // Number of loading instances
|
||||||
checkFormAfterInit = true; // Revalidate all fields on the next AfterContentChecked
|
checkFormAfterInit = true; // Revalidate all fields on the next AfterContentChecked
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
public d: DataService,
|
public d: DataService,
|
||||||
private modal: ModalService,
|
private modal: ModalService,
|
||||||
public autocomplete: AutocompleteService,
|
public autocomplete: AutocompleteService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private validation: ValidationService
|
private validation: ValidationService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loading = 5;
|
this.loading = 5;
|
||||||
this.api.get<MaterialModel>('/material/' + this.route.snapshot.paramMap.get('id'), data => {
|
this.api.get<MaterialModel>('/material/' + this.route.snapshot.paramMap.get('id'), data => {
|
||||||
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
|
// 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--;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.d.load('materialSuppliers', () => {
|
this.d.load('materialSuppliers', () => {
|
||||||
this.loading--;
|
this.loading--;
|
||||||
});
|
});
|
||||||
this.d.load('materialGroups', () => {
|
this.d.load('materialGroups', () => {
|
||||||
this.loading--;
|
this.loading--;
|
||||||
});
|
});
|
||||||
this.d.load('materialTemplates', () => {
|
this.d.load('materialTemplates', () => {
|
||||||
this.loading--;
|
this.loading--;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
ngAfterContentChecked() {
|
||||||
// Attach validators
|
// 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
|
// 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 => {
|
||||||
this.materialForm.form.get(field).updateValueAndValidity();
|
this.materialForm.form.get(field).updateValueAndValidity();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach validators specified in range to input with name
|
// Attach validators specified in range to input with name
|
||||||
attachValidator(form, name: string, range: {[prop: string]: any}) {
|
attachValidator(form, name: string, range: {[prop: string]: any}) {
|
||||||
if (form && form.form.get(name)) {
|
if (form && form.form.get(name)) {
|
||||||
const validators = [];
|
const validators = [];
|
||||||
if (range.hasOwnProperty('required')) {
|
if (range.hasOwnProperty('required')) {
|
||||||
validators.push(Validators.required);
|
validators.push(Validators.required);
|
||||||
}
|
}
|
||||||
if (range.hasOwnProperty('values')) {
|
if (range.hasOwnProperty('values')) {
|
||||||
validators.push(this.validation.generate('stringOf', [range.values]));
|
validators.push(this.validation.generate('stringOf', [range.values]));
|
||||||
}
|
}
|
||||||
else if (range.hasOwnProperty('min') && range.hasOwnProperty('max')) {
|
else if (range.hasOwnProperty('min') && range.hasOwnProperty('max')) {
|
||||||
validators.push(this.validation.generate('minMax', [range.min, range.max]));
|
validators.push(this.validation.generate('minMax', [range.min, range.max]));
|
||||||
}
|
}
|
||||||
else if (range.hasOwnProperty('min')) {
|
else if (range.hasOwnProperty('min')) {
|
||||||
validators.push(this.validation.generate('min', [range.min]));
|
validators.push(this.validation.generate('min', [range.min]));
|
||||||
}
|
}
|
||||||
else if (range.hasOwnProperty('max')) {
|
else if (range.hasOwnProperty('max')) {
|
||||||
validators.push(this.validation.generate('max', [range.max]));
|
validators.push(this.validation.generate('max', [range.max]));
|
||||||
}
|
}
|
||||||
form.form.get(name).setValidators(validators);
|
form.form.get(name).setValidators(validators);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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; // Reload materials
|
delete this.d.arr.materials; // Reload materials
|
||||||
this.d.load('materials');
|
this.d.load('materials');
|
||||||
this.router.navigate(['/materials']);
|
this.router.navigate(['/materials']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteConfirm(modal) {
|
deleteConfirm(modal) {
|
||||||
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) { // Material cannot be deleted as it is still referenced by active samples
|
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!';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.router.navigate(['/materials']);
|
this.router.navigate(['/materials']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTypo(event, list, mKey, modal: TemplateRef<any>) {
|
checkTypo(event, list, mKey, modal: TemplateRef<any>) {
|
||||||
// User did not click on suggestion and entry is not in list
|
// User did not click on suggestion and entry is not in list
|
||||||
if (!(event.relatedTarget && (event.relatedTarget.className.indexOf('rb-dropdown-item') >= 0 ||
|
if (!(event.relatedTarget && (event.relatedTarget.className.indexOf('rb-dropdown-item') >= 0 ||
|
||||||
event.relatedTarget.className.indexOf('close-btn rb-btn rb-passive-link') >= 0)) &&
|
event.relatedTarget.className.indexOf('close-btn rb-btn rb-passive-link') >= 0)) &&
|
||||||
this.d.arr[list].indexOf(this.material[mKey]) < 0) {
|
this.d.arr[list].indexOf(this.material[mKey]) < 0) {
|
||||||
this.modalText.list = mKey;
|
this.modalText.list = mKey;
|
||||||
this.modalText.suggestion = this.d.arr[list] // Find possible entry from list
|
this.modalText.suggestion = this.d.arr[list] // Find possible entry from list
|
||||||
.map(e => ({v: e, s: strCompare.sorensenDice(e, this.material[mKey])}))
|
.map(e => ({v: e, s: strCompare.sorensenDice(e, this.material[mKey])}))
|
||||||
.sort((a, b) => b.s - a.s)[0].v;
|
.sort((a, b) => b.s - a.s)[0].v;
|
||||||
this.modal.open(modal).then(result => {
|
this.modal.open(modal).then(result => {
|
||||||
if (result) { // Use suggestion
|
if (result) { // Use suggestion
|
||||||
this.material[mKey] = this.modalText.suggestion;
|
this.material[mKey] = this.modalText.suggestion;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,91 +1,91 @@
|
|||||||
<div class="header-addnew">
|
<div class="header-addnew">
|
||||||
<rb-icon-button *ngIf="sampleSelect" mode="secondary" icon="close" (click)="sampleSelect = false"
|
<rb-icon-button *ngIf="sampleSelect" mode="secondary" icon="close" (click)="sampleSelect = false"
|
||||||
class="validation-close" iconOnly></rb-icon-button>
|
class="validation-close" iconOnly></rb-icon-button>
|
||||||
<rb-icon-button [icon]="sampleSelect ? 'checkmark' : 'clear-all'"
|
<rb-icon-button [icon]="sampleSelect ? 'checkmark' : 'clear-all'"
|
||||||
mode="secondary" (click)="validate()">
|
mode="secondary" (click)="validate()">
|
||||||
{{sampleSelect ? 'Validate' : 'Validation'}}
|
{{sampleSelect ? 'Validate' : 'Validation'}}
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="status-selection">
|
<div class="status-selection">
|
||||||
<label class="label">Status</label>
|
<label class="label">Status</label>
|
||||||
<rb-form-checkbox name="status-validated" [(ngModel)]="materialStatus.validated"
|
<rb-form-checkbox name="status-validated" [(ngModel)]="materialStatus.validated"
|
||||||
[disabled]="!materialStatus.new && !materialStatus.deleted"
|
[disabled]="!materialStatus.new && !materialStatus.deleted"
|
||||||
(ngModelChange)="loadMaterials()">
|
(ngModelChange)="loadMaterials()">
|
||||||
validated
|
validated
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
<rb-form-checkbox name="status-new" [(ngModel)]="materialStatus.new"
|
<rb-form-checkbox name="status-new" [(ngModel)]="materialStatus.new"
|
||||||
[disabled]="!materialStatus.validated && !materialStatus.deleted"
|
[disabled]="!materialStatus.validated && !materialStatus.deleted"
|
||||||
(ngModelChange)="loadMaterials()">
|
(ngModelChange)="loadMaterials()">
|
||||||
new
|
new
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
<rb-form-checkbox name="status-deleted" [(ngModel)]="materialStatus.deleted"
|
<rb-form-checkbox name="status-deleted" [(ngModel)]="materialStatus.deleted"
|
||||||
[disabled]="!materialStatus.validated && !materialStatus.new"
|
[disabled]="!materialStatus.validated && !materialStatus.new"
|
||||||
(ngModelChange)="loadMaterials()">
|
(ngModelChange)="loadMaterials()">
|
||||||
deleted
|
deleted
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="material-search space-right">
|
<div class="material-search space-right">
|
||||||
<rb-form-input label="search" [(ngModel)]="materialSearch" icon="close"></rb-form-input>
|
<rb-form-input label="search" [(ngModel)]="materialSearch" icon="close"></rb-form-input>
|
||||||
<span class="rb-ic rb-ic-close clickable" (click)="materialSearch = ''"></span>
|
<span class="rb-ic rb-ic-close clickable" (click)="materialSearch = ''"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngTemplateOutlet="paging"></ng-container>
|
<ng-container *ngTemplateOutlet="paging"></ng-container>
|
||||||
|
|
||||||
<rb-table ellipsis scrollTop>
|
<rb-table ellipsis scrollTop>
|
||||||
<tr>
|
<tr>
|
||||||
<th *ngIf="sampleSelect">
|
<th *ngIf="sampleSelect">
|
||||||
<rb-form-checkbox name="select-all" (change)="selectAll($event)">all</rb-form-checkbox>
|
<rb-form-checkbox name="select-all" (change)="selectAll($event)">all</rb-form-checkbox>
|
||||||
</th>
|
</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Supplier</th>
|
<th>Supplier</th>
|
||||||
<th>Group</th>
|
<th>Group</th>
|
||||||
<th *ngFor="let key of templateKeys">{{key.label}}</th>
|
<th *ngFor="let key of templateKeys">{{key.label}}</th>
|
||||||
<th>Numbers</th>
|
<th>Numbers</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let material of (materials || []).filter(materialFilter(materialSearch))
|
<tr *ngFor="let material of (materials || []).filter(materialFilter(materialSearch))
|
||||||
.slice((page - 1) * pageSize, page * pageSize); index as i">
|
.slice((page - 1) * pageSize, page * pageSize); index as i">
|
||||||
<td *ngIf="sampleSelect">
|
<td *ngIf="sampleSelect">
|
||||||
<rb-form-checkbox *ngIf="material.status !== 'deleted'" [name]="'validate-' + i"
|
<rb-form-checkbox *ngIf="material.status !== 'deleted'" [name]="'validate-' + i"
|
||||||
[(ngModel)]="material.selected">
|
[(ngModel)]="material.selected">
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
</td>
|
</td>
|
||||||
<td>{{material.name}}</td>
|
<td>{{material.name}}</td>
|
||||||
<td>{{material.supplier}}</td>
|
<td>{{material.supplier}}</td>
|
||||||
<td>{{material.group}}</td>
|
<td>{{material.group}}</td>
|
||||||
<td *ngFor="let key of templateKeys">{{material.properties[key.key] | exists}}</td>
|
<td *ngFor="let key of templateKeys">{{material.properties[key.key] | exists}}</td>
|
||||||
<td>{{material.numbers}}</td>
|
<td>{{material.numbers}}</td>
|
||||||
<td>
|
<td>
|
||||||
<a [routerLink]="'/materials/edit/' + material._id" *ngIf="material.status !== 'deleted'">
|
<a [routerLink]="'/materials/edit/' + material._id" *ngIf="material.status !== 'deleted'">
|
||||||
<span class="rb-ic rb-ic-edit clickable"></span>
|
<span class="rb-ic rb-ic-edit clickable"></span>
|
||||||
</a>
|
</a>
|
||||||
<span class="rb-ic rb-ic-undo clickable" *ngIf="material.status === 'deleted'"
|
<span class="rb-ic rb-ic-undo clickable" *ngIf="material.status === 'deleted'"
|
||||||
(click)="restoreMaterial(material._id, restoreConfirm)"></span>
|
(click)="restoreMaterial(material._id, restoreConfirm)"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
|
|
||||||
<ng-container *ngTemplateOutlet="paging"></ng-container>
|
<ng-container *ngTemplateOutlet="paging"></ng-container>
|
||||||
|
|
||||||
<ng-template #paging>
|
<ng-template #paging>
|
||||||
<div class="paging">
|
<div class="paging">
|
||||||
<button class="rb-btn rb-link" type="button" (click)="page = page - 1" [disabled]="page === 1">
|
<button class="rb-btn rb-link" type="button" (click)="page = page - 1" [disabled]="page === 1">
|
||||||
<span class="rb-ic rb-ic-back-left"></span>
|
<span class="rb-ic rb-ic-back-left"></span>
|
||||||
</button>
|
</button>
|
||||||
<rb-form-input label="page" [(ngModel)]="page"></rb-form-input>
|
<rb-form-input label="page" [(ngModel)]="page"></rb-form-input>
|
||||||
<span>
|
<span>
|
||||||
of {{pages}}
|
of {{pages}}
|
||||||
</span>
|
</span>
|
||||||
<button class="rb-btn rb-link" type="button" (click)="page = page + 1" [disabled]="page >= pages">
|
<button class="rb-btn rb-link" type="button" (click)="page = page + 1" [disabled]="page >= pages">
|
||||||
<span class="rb-ic rb-ic-forward-right"></span>
|
<span class="rb-ic rb-ic-forward-right"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #restoreConfirm>
|
<ng-template #restoreConfirm>
|
||||||
<rb-dialog dialogTitle="Restore sample">
|
<rb-dialog dialogTitle="Restore sample">
|
||||||
Do you really want to restore this sample?
|
Do you really want to restore this sample?
|
||||||
</rb-dialog>
|
</rb-dialog>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -1,63 +1,63 @@
|
|||||||
.paging {
|
.paging {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
float: left;
|
float: left;
|
||||||
|
|
||||||
rb-form-input {
|
rb-form-input {
|
||||||
max-width: 65px;
|
max-width: 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
> button {
|
> button {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-selection {
|
.status-selection {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-form-checkbox {
|
rb-form-checkbox {
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-addnew {
|
.header-addnew {
|
||||||
& > * {
|
& > * {
|
||||||
display: inline;
|
display: inline;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-icon-button {
|
rb-icon-button {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.material-search {
|
.material-search {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
float: left;
|
float: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
top: 24px;
|
top: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,92 +6,92 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-materials',
|
selector: 'app-materials',
|
||||||
templateUrl: './materials.component.html',
|
templateUrl: './materials.component.html',
|
||||||
styleUrls: ['./materials.component.scss']
|
styleUrls: ['./materials.component.scss']
|
||||||
})
|
})
|
||||||
export class MaterialsComponent implements OnInit {
|
export class MaterialsComponent implements OnInit {
|
||||||
|
|
||||||
materials: MaterialModel[] = []; // All materials
|
materials: MaterialModel[] = []; // All materials
|
||||||
templateKeys: {key: string, label: string}[] = []; // Material template keys
|
templateKeys: {key: string, label: string}[] = []; // Material template keys
|
||||||
materialStatus = {validated: true, new: true, deleted: false}; // Material statuses to show
|
materialStatus = {validated: true, new: true, deleted: false}; // Material statuses to show
|
||||||
materialSearch = ''; // Material name search string
|
materialSearch = ''; // Material name search string
|
||||||
sampleSelect = false; // Set to true to show checkboxes for validation
|
sampleSelect = false; // Set to true to show checkboxes for validation
|
||||||
|
|
||||||
page = 1; // Page settings
|
page = 1; // Page settings
|
||||||
pages = 0;
|
pages = 0;
|
||||||
pageSize = 25;
|
pageSize = 25;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
public d: DataService,
|
public d: DataService,
|
||||||
private modal: ModalService
|
private modal: ModalService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadMaterials();
|
this.loadMaterials();
|
||||||
this.d.load('materialTemplates', () => {
|
this.d.load('materialTemplates', () => {
|
||||||
this.d.arr.materialTemplates.forEach(template => {
|
this.d.arr.materialTemplates.forEach(template => {
|
||||||
template.parameters.forEach(parameter => {
|
template.parameters.forEach(parameter => {
|
||||||
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
|
// 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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMaterials() {
|
loadMaterials() {
|
||||||
this.api.get<MaterialModel[]>('/materials?' +
|
this.api.get<MaterialModel[]>('/materials?' +
|
||||||
Object.entries(this.materialStatus).filter(e => e[1]).map(e => 'status[]=' + e[0]).join('&'), data => {
|
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.materials = data.map(e => new MaterialModel().deserialize(e));
|
||||||
this.pages = Math.ceil(this.materials.length / this.pageSize);
|
this.pages = Math.ceil(this.materials.length / this.pageSize);
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
validate() {
|
validate() {
|
||||||
if (this.sampleSelect) { // Selection was done do actual validation
|
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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.loadMaterials();
|
this.loadMaterials();
|
||||||
this.sampleSelect = false;
|
this.sampleSelect = false;
|
||||||
}
|
}
|
||||||
else { // Activate validation mode
|
else { // Activate validation mode
|
||||||
this.sampleSelect = true;
|
this.sampleSelect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAll(event) { // Toggle selection for all items except deleted ones
|
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;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
material.selected = false;
|
material.selected = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreMaterial(id, modal) {
|
restoreMaterial(id, modal) {
|
||||||
this.modal.open(modal).then(res => {
|
this.modal.open(modal).then(res => {
|
||||||
if (res) {
|
if (res) {
|
||||||
this.api.put('/sample/restore/' + id, {}, ignore => {
|
this.api.put('/sample/restore/' + id, {}, ignore => {
|
||||||
this.materials.find(e => e._id === id).status = 'new';
|
this.materials.find(e => e._id === id).status = 'new';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ucFirst(string) { // Convert first character of string to uppercase
|
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) { // Filter function for material names
|
materialFilter(ms) { // Filter function for material names
|
||||||
return e => e.name.indexOf(ms) >= 0;
|
return e => e.name.indexOf(ms) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,73 @@
|
|||||||
<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>
|
||||||
|
|
||||||
<form *ngIf="newModel" #modelForm="ngForm">
|
<form *ngIf="newModel" #modelForm="ngForm">
|
||||||
<rb-form-input name="group" label="group" appValidate="string" required [(ngModel)]="modelGroup" #groupInput="ngModel"
|
<rb-form-input name="group" label="group" appValidate="string" required [(ngModel)]="modelGroup" #groupInput="ngModel"
|
||||||
[rbFormInputAutocomplete]="autocomplete.bind(this, groups)"
|
[rbFormInputAutocomplete]="autocomplete.bind(this, groups)"
|
||||||
[rbDebounceTime]="0" [rbInitialOpen]="true">
|
[rbDebounceTime]="0" [rbInitialOpen]="true">
|
||||||
<ng-template rbFormValidationMessage="failure">{{groupInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{groupInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="name" label="name" appValidate="string" required [(ngModel)]="model.name" #nameInput="ngModel">
|
<rb-form-input name="name" label="name" appValidate="string" required [(ngModel)]="model.name" #nameInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="url" label="URL" appValidate="url" required [(ngModel)]="model.url" #urlInput="ngModel">
|
<rb-form-input name="url" label="URL" appValidate="url" required [(ngModel)]="model.url" #urlInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{urlInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{urlInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
|
|
||||||
<rb-icon-button icon="save" mode="primary" type="submit" [disabled]="!modelForm.form.valid" (click)="saveModel()">
|
<rb-icon-button icon="save" mode="primary" type="submit" [disabled]="!modelForm.form.valid" (click)="saveModel()">
|
||||||
Save model
|
Save model
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<rb-table class="space-above space-below">
|
<rb-table class="space-above space-below">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>URL</th>
|
<th>URL</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<ng-container *ngFor="let group of d.arr.modelGroups">
|
<ng-container *ngFor="let group of d.arr.modelGroups">
|
||||||
<tr><th>{{group.group}}</th><th></th><th></th><th></th><th></th></tr>
|
<tr><th>{{group.group}}</th><th></th><th></th><th></th><th></th></tr>
|
||||||
<tr *ngFor="let modelItem of group.models">
|
<tr *ngFor="let modelItem of group.models">
|
||||||
<td>{{modelItem.name}}</td>
|
<td>{{modelItem.name}}</td>
|
||||||
<td>{{modelItem.url}}</td>
|
<td>{{modelItem.url}}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="rb-ic rb-ic-edit clickable"
|
<span class="rb-ic rb-ic-edit clickable"
|
||||||
(click)="modelGroup = group.group;
|
(click)="modelGroup = group.group;
|
||||||
oldModelGroup = group.group;
|
oldModelGroup = group.group;
|
||||||
oldModelName = modelItem.name;
|
oldModelName = modelItem.name;
|
||||||
model = modelItem;
|
model = modelItem;
|
||||||
newModel = true;">
|
newModel = true;">
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="rb-ic rb-ic-delete clickable"
|
<span class="rb-ic rb-ic-delete clickable"
|
||||||
(click)="delete(modalDeleteConfirm, modelItem.name, group.group)"></span>
|
(click)="delete(modalDeleteConfirm, modelItem.name, group.group)"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
|
|
||||||
<rb-table>
|
<rb-table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Model files</th>
|
<th>Model files</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let file of d.arr.modelFiles">
|
<tr *ngFor="let file of d.arr.modelFiles">
|
||||||
<td>{{file.name}}</td>
|
<td>{{file.name}}</td>
|
||||||
<td>{{file.size | size:'M'}}</td>
|
<td>{{file.size | size:'M'}}</td>
|
||||||
<td><span class="rb-ic rb-ic-delete clickable" (click)="delete(modalDeleteConfirm, file.name)"></span></td>
|
<td><span class="rb-ic rb-ic-delete clickable" (click)="delete(modalDeleteConfirm, file.name)"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
|
|
||||||
<ng-template #modalDeleteConfirm>
|
<ng-template #modalDeleteConfirm>
|
||||||
<rb-alert alertTitle="Are you sure?" type="danger" okBtnLabel="Delete model" cancelBtnLabel="Cancel">
|
<rb-alert alertTitle="Are you sure?" type="danger" okBtnLabel="Delete model" cancelBtnLabel="Cancel">
|
||||||
Do you really want to delete?
|
Do you really want to delete?
|
||||||
</rb-alert>
|
</rb-alert>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -7,77 +7,77 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-model-templates',
|
selector: 'app-model-templates',
|
||||||
templateUrl: './model-templates.component.html',
|
templateUrl: './model-templates.component.html',
|
||||||
styleUrls: ['./model-templates.component.scss']
|
styleUrls: ['./model-templates.component.scss']
|
||||||
})
|
})
|
||||||
export class ModelTemplatesComponent implements OnInit {
|
export class ModelTemplatesComponent implements OnInit {
|
||||||
|
|
||||||
newModel = false; // Display new model dialog
|
newModel = false; // Display new model dialog
|
||||||
modelGroup = ''; // Group of the edited model
|
modelGroup = ''; // Group of the edited model
|
||||||
oldModelGroup = ''; // Group of the edited model before editing started
|
oldModelGroup = ''; // Group of the edited model before editing started
|
||||||
oldModelName = ''; // Name of the edited model before editing started
|
oldModelName = ''; // Name of the edited model before editing started
|
||||||
model = new ModelItemModel().models[0]; // Edited model
|
model = new ModelItemModel().models[0]; // Edited model
|
||||||
groups = []; // All model group names
|
groups = []; // All model group names
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
public autocomplete: AutocompleteService,
|
public autocomplete: AutocompleteService,
|
||||||
public d: DataService,
|
public d: DataService,
|
||||||
private modal: ModalService
|
private modal: ModalService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadGroups();
|
this.loadGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadGroups() {
|
loadGroups() {
|
||||||
delete this.d.arr.modelGroups;
|
delete this.d.arr.modelGroups;
|
||||||
this.d.load('modelGroups', () => {
|
this.d.load('modelGroups', () => {
|
||||||
this.groups = this.d.arr.modelGroups.map(e => e.group);
|
this.groups = this.d.arr.modelGroups.map(e => e.group);
|
||||||
});
|
});
|
||||||
this.d.load('modelFiles');
|
this.d.load('modelFiles');
|
||||||
}
|
}
|
||||||
|
|
||||||
saveModel() {
|
saveModel() {
|
||||||
// Group was changed, delete model in old group
|
// Group was changed, delete model in old group
|
||||||
if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) {
|
if (this.oldModelGroup !== '' && this.modelGroup !== this.oldModelGroup) {
|
||||||
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; // Reset model edit parameters
|
this.newModel = false; // Reset model edit parameters
|
||||||
this.loadGroups();
|
this.loadGroups();
|
||||||
this.modelGroup = '';
|
this.modelGroup = '';
|
||||||
this.oldModelGroup = '';
|
this.oldModelGroup = '';
|
||||||
this.oldModelName = '';
|
this.oldModelName = '';
|
||||||
this.model = new ModelItemModel().models[0];
|
this.model = new ModelItemModel().models[0];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(modal, name, group = null) {
|
delete(modal, name, group = null) {
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
if (modal) { // If modal was given, wait for result
|
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
}
|
}
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (res) {
|
if (res) {
|
||||||
if (group) { // Delete model group if given
|
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 model 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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { BaseModel } from './base.model';
|
import { BaseModel } from './base.model';
|
||||||
|
|
||||||
describe('BaseModel', () => {
|
describe('BaseModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new BaseModel()).toBeTruthy();
|
expect(new BaseModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
export class BaseModel {
|
export class BaseModel {
|
||||||
deserialize(input: any): this {
|
deserialize(input: any): this {
|
||||||
Object.assign(this, input);
|
Object.assign(this, input);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendFormat(): this {
|
sendFormat(): this {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ChangelogModel } from './changelog.model';
|
import { ChangelogModel } from './changelog.model';
|
||||||
|
|
||||||
describe('ChangelogModel', () => {
|
describe('ChangelogModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new ChangelogModel()).toBeTruthy();
|
expect(new ChangelogModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,10 +2,10 @@ import {BaseModel} from './base.model';
|
|||||||
import {IdModel} from './id.model';
|
import {IdModel} from './id.model';
|
||||||
|
|
||||||
export class ChangelogModel extends BaseModel {
|
export class ChangelogModel extends BaseModel {
|
||||||
_id: IdModel = null;
|
_id: IdModel = null;
|
||||||
date: Date;
|
date: Date;
|
||||||
action: string;
|
action: string;
|
||||||
collection: string;
|
collection: string;
|
||||||
conditions: {[key: string]: any};
|
conditions: {[key: string]: any};
|
||||||
data: {[key: string]: any};
|
data: {[key: string]: any};
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { CustomFieldsModel } from './custom-fields.model';
|
import { CustomFieldsModel } from './custom-fields.model';
|
||||||
|
|
||||||
describe('CustomFieldsModel', () => {
|
describe('CustomFieldsModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new CustomFieldsModel()).toBeTruthy();
|
expect(new CustomFieldsModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,6 @@ import {BaseModel} from './base.model';
|
|||||||
|
|
||||||
|
|
||||||
export class CustomFieldsModel extends BaseModel {
|
export class CustomFieldsModel extends BaseModel {
|
||||||
name = '';
|
name = '';
|
||||||
qty = 0;
|
qty = 0;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { HelpModel } from './help.model';
|
import { HelpModel } from './help.model';
|
||||||
|
|
||||||
describe('Help.Model', () => {
|
describe('Help.Model', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new HelpModel()).toBeTruthy();
|
expect(new HelpModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {BaseModel} from './base.model';
|
import {BaseModel} from './base.model';
|
||||||
|
|
||||||
export class HelpModel extends BaseModel {
|
export class HelpModel extends BaseModel {
|
||||||
text = '';
|
text = '';
|
||||||
level = 'none';
|
level = 'none';
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { MaterialModel } from './material.model';
|
import { MaterialModel } from './material.model';
|
||||||
|
|
||||||
describe('MaterialModel', () => {
|
describe('MaterialModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new MaterialModel()).toBeTruthy();
|
expect(new MaterialModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,16 +3,16 @@ import {IdModel} from './id.model';
|
|||||||
import {BaseModel} from './base.model';
|
import {BaseModel} from './base.model';
|
||||||
|
|
||||||
export class MaterialModel extends BaseModel {
|
export class MaterialModel extends BaseModel {
|
||||||
_id: IdModel = null;
|
_id: IdModel = null;
|
||||||
name = '';
|
name = '';
|
||||||
supplier = '';
|
supplier = '';
|
||||||
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;
|
selected = false;
|
||||||
status = '';
|
status = '';
|
||||||
|
|
||||||
sendFormat() {
|
sendFormat() {
|
||||||
return pick(this, ['name', 'supplier', 'group', 'numbers', 'properties']);
|
return pick(this, ['name', 'supplier', 'group', 'numbers', 'properties']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { MeasurementModel } from './measurement.model';
|
import { MeasurementModel } from './measurement.model';
|
||||||
|
|
||||||
describe('MeasurementModel', () => {
|
describe('MeasurementModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new MeasurementModel()).toBeTruthy();
|
expect(new MeasurementModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,28 +4,28 @@ import {IdModel} from './id.model';
|
|||||||
import {BaseModel} from './base.model';
|
import {BaseModel} from './base.model';
|
||||||
|
|
||||||
export class MeasurementModel extends BaseModel {
|
export class MeasurementModel extends BaseModel {
|
||||||
_id: IdModel = null;
|
_id: IdModel = null;
|
||||||
sample_id: IdModel = null;
|
sample_id: IdModel = null;
|
||||||
measurement_template: IdModel;
|
measurement_template: IdModel;
|
||||||
values: {[prop: string]: any} = {};
|
values: {[prop: string]: any} = {};
|
||||||
status = '';
|
status = '';
|
||||||
|
|
||||||
constructor(measurementTemplate: IdModel = null) {
|
constructor(measurementTemplate: IdModel = null) {
|
||||||
super();
|
super();
|
||||||
this.measurement_template = measurementTemplate;
|
this.measurement_template = measurementTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(input: any): this {
|
deserialize(input: any): this {
|
||||||
Object.assign(this, input);
|
Object.assign(this, input);
|
||||||
Object.keys(this.values).forEach(key => {
|
Object.keys(this.values).forEach(key => {
|
||||||
if (this.values[key] === null) {
|
if (this.values[key] === null) {
|
||||||
this.values[key] = '';
|
this.values[key] = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendFormat(omitValues = []) {
|
sendFormat(omitValues = []) {
|
||||||
return omit(pick(this, ['sample_id', 'measurement_template', 'values']), omitValues);
|
return omit(pick(this, ['sample_id', 'measurement_template', 'values']), omitValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ModelFileModel } from './model-file.model';
|
import { ModelFileModel } from './model-file.model';
|
||||||
|
|
||||||
describe('ModelFile.Model', () => {
|
describe('ModelFile.Model', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new ModelFileModel()).toBeTruthy();
|
expect(new ModelFileModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {BaseModel} from './base.model';
|
import {BaseModel} from './base.model';
|
||||||
|
|
||||||
export class ModelFileModel extends BaseModel {
|
export class ModelFileModel extends BaseModel {
|
||||||
name = '';
|
name = '';
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ModelItemModel } from './model-item.model';
|
import { ModelItemModel } from './model-item.model';
|
||||||
|
|
||||||
describe('ModelItemModel', () => {
|
describe('ModelItemModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new ModelItemModel()).toBeTruthy();
|
expect(new ModelItemModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import {BaseModel} from './base.model';
|
import {BaseModel} from './base.model';
|
||||||
|
|
||||||
export class ModelItemModel extends BaseModel {
|
export class ModelItemModel extends BaseModel {
|
||||||
group = '';
|
group = '';
|
||||||
models = [{
|
models = [{
|
||||||
name: '',
|
name: '',
|
||||||
url: ''
|
url: ''
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { SampleModel } from './sample.model';
|
import { SampleModel } from './sample.model';
|
||||||
|
|
||||||
describe('SampleModel', () => {
|
describe('SampleModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new SampleModel()).toBeTruthy();
|
expect(new SampleModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,63 +6,63 @@ import {MeasurementModel} from './measurement.model';
|
|||||||
import {BaseModel} from './base.model';
|
import {BaseModel} from './base.model';
|
||||||
|
|
||||||
export class SampleModel extends BaseModel {
|
export class SampleModel extends BaseModel {
|
||||||
_id: IdModel = null;
|
_id: IdModel = null;
|
||||||
color = '';
|
color = '';
|
||||||
number = '';
|
number = '';
|
||||||
type = '';
|
type = '';
|
||||||
batch = '';
|
batch = '';
|
||||||
condition: {condition_template: string, [prop: string]: string} = {condition_template: null};
|
condition: {condition_template: string, [prop: string]: string} = {condition_template: null};
|
||||||
material_id: IdModel = null;
|
material_id: IdModel = null;
|
||||||
material: MaterialModel;
|
material: MaterialModel;
|
||||||
measurements: MeasurementModel[] = [];
|
measurements: MeasurementModel[] = [];
|
||||||
note_id: IdModel = null;
|
note_id: IdModel = null;
|
||||||
user_id: IdModel = null;
|
user_id: IdModel = null;
|
||||||
selected = false;
|
selected = false;
|
||||||
notes: {
|
notes: {
|
||||||
comment: string,
|
comment: string,
|
||||||
sample_references: {sample_id: IdModel, relation: string}[],
|
sample_references: {sample_id: IdModel, relation: string}[],
|
||||||
custom_fields: {[prop: string]: string}
|
custom_fields: {[prop: string]: string}
|
||||||
} = {comment: '', sample_references: [], custom_fields: {}};
|
} = {comment: '', sample_references: [], custom_fields: {}};
|
||||||
status = '';
|
status = '';
|
||||||
added: Date = null;
|
added: Date = null;
|
||||||
|
|
||||||
deserialize(input: any): this {
|
deserialize(input: any): this {
|
||||||
Object.assign(this, input);
|
Object.assign(this, input);
|
||||||
if (input.hasOwnProperty('material')) {
|
if (input.hasOwnProperty('material')) {
|
||||||
this.material = new MaterialModel().deserialize(input.material);
|
this.material = new MaterialModel().deserialize(input.material);
|
||||||
this.material_id = input.material._id;
|
this.material_id = input.material._id;
|
||||||
}
|
}
|
||||||
if (input.hasOwnProperty('measurements')) {
|
if (input.hasOwnProperty('measurements')) {
|
||||||
this.measurements = input.measurements.map(e => new MeasurementModel().deserialize(e));
|
this.measurements = input.measurements.map(e => new MeasurementModel().deserialize(e));
|
||||||
}
|
}
|
||||||
if (input.hasOwnProperty('added')) {
|
if (input.hasOwnProperty('added')) {
|
||||||
this.added = new Date(input.added);
|
this.added = new Date(input.added);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendFormat(pickCondition = true) {
|
sendFormat(pickCondition = true) {
|
||||||
const pickFields = ['color', 'type', 'batch', 'material_id', 'notes'];
|
const pickFields = ['color', 'type', 'batch', 'material_id', 'notes'];
|
||||||
if (pickCondition) {
|
if (pickCondition) {
|
||||||
pickFields.push('condition');
|
pickFields.push('condition');
|
||||||
}
|
}
|
||||||
const tmp = pick(this.conditionTemplateCheck(), pickFields);
|
const tmp = pick(this.conditionTemplateCheck(), pickFields);
|
||||||
Object.keys(tmp).forEach(key => {
|
Object.keys(tmp).forEach(key => {
|
||||||
if (tmp[key] === undefined) {
|
if (tmp[key] === undefined) {
|
||||||
delete tmp[key];
|
delete tmp[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (this.material && this.material.name === undefined) {
|
if (this.material && this.material.name === undefined) {
|
||||||
delete tmp.material_id;
|
delete tmp.material_id;
|
||||||
}
|
}
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private conditionTemplateCheck() {
|
private conditionTemplateCheck() {
|
||||||
const res = cloneDeep(this);
|
const res = cloneDeep(this);
|
||||||
if (res.condition.condition_template === null) {
|
if (res.condition.condition_template === null) {
|
||||||
res.condition = {};
|
res.condition = {};
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { TemplateModel } from './template.model';
|
import { TemplateModel } from './template.model';
|
||||||
|
|
||||||
describe('TemplateModel', () => {
|
describe('TemplateModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new TemplateModel()).toBeTruthy();
|
expect(new TemplateModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,9 +2,9 @@ import {IdModel} from './id.model';
|
|||||||
import {BaseModel} from './base.model';
|
import {BaseModel} from './base.model';
|
||||||
|
|
||||||
export class TemplateModel extends BaseModel {
|
export class TemplateModel extends BaseModel {
|
||||||
_id: IdModel = null;
|
_id: IdModel = null;
|
||||||
name = '';
|
name = '';
|
||||||
version = 0;
|
version = 0;
|
||||||
first_id: IdModel = null;
|
first_id: IdModel = null;
|
||||||
parameters: {name: string, range: {[prop: string]: any}, rangeString?: string}[] = [];
|
parameters: {name: string, range: {[prop: string]: any}, rangeString?: string}[] = [];
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { UserModel } from './user.model';
|
import { UserModel } from './user.model';
|
||||||
|
|
||||||
describe('UserModel', () => {
|
describe('UserModel', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new UserModel()).toBeTruthy();
|
expect(new UserModel()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,31 +3,31 @@ import {BaseModel} from './base.model';
|
|||||||
import {IdModel} from './id.model';
|
import {IdModel} from './id.model';
|
||||||
|
|
||||||
export class UserModel extends BaseModel{
|
export class UserModel extends BaseModel{
|
||||||
_id: IdModel = null;
|
_id: IdModel = null;
|
||||||
name = '';
|
name = '';
|
||||||
origName = '';
|
origName = '';
|
||||||
email = '';
|
email = '';
|
||||||
level = '';
|
level = '';
|
||||||
location = '';
|
location = '';
|
||||||
devices = [''];
|
devices = [''];
|
||||||
models = [''];
|
models = [''];
|
||||||
status = 'new';
|
status = 'new';
|
||||||
edit = false;
|
edit = false;
|
||||||
|
|
||||||
deserialize(input: any): this {
|
deserialize(input: any): this {
|
||||||
Object.assign(this, input);
|
Object.assign(this, input);
|
||||||
this.origName = this.name;
|
this.origName = this.name;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendFormat(mode = 'user') {
|
sendFormat(mode = 'user') {
|
||||||
const keys = ['name', 'email', 'location', 'devices'];
|
const keys = ['name', 'email', 'location', 'devices'];
|
||||||
if (mode === 'admin') {
|
if (mode === 'admin') {
|
||||||
keys.push('level');
|
keys.push('level');
|
||||||
keys.push('models');
|
keys.push('models');
|
||||||
this.devices = this.devices.filter(e => e);
|
this.devices = this.devices.filter(e => e);
|
||||||
this.models = this.models.filter(e => e);
|
this.models = this.models.filter(e => e);
|
||||||
}
|
}
|
||||||
return pick(this, keys);
|
return pick(this, keys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ import { ObjectPipe } from './object.pipe';
|
|||||||
|
|
||||||
|
|
||||||
describe('ObjectPipe', () => {
|
describe('ObjectPipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
const pipe = new ObjectPipe();
|
const pipe = new ObjectPipe();
|
||||||
expect(pipe).toBeTruthy();
|
expect(pipe).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,14 +2,14 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'object',
|
name: 'object',
|
||||||
pure: true
|
pure: true
|
||||||
})
|
})
|
||||||
export class ObjectPipe implements PipeTransform {
|
export class ObjectPipe implements PipeTransform {
|
||||||
|
|
||||||
transform(value: object, omitValue: string[] = []): string {
|
transform(value: object, omitValue: string[] = []): string {
|
||||||
const res = omit(value, omitValue);
|
const res = omit(value, omitValue);
|
||||||
return res && Object.keys(res).length ? JSON.stringify(res) : '';
|
return res && Object.keys(res).length ? JSON.stringify(res) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ import { ParametersPipe } from './parameters.pipe';
|
|||||||
|
|
||||||
|
|
||||||
describe('ParametersPipe', () => {
|
describe('ParametersPipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
const pipe = new ParametersPipe();
|
const pipe = new ParametersPipe();
|
||||||
expect(pipe).toBeTruthy();
|
expect(pipe).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'parameters'
|
name: 'parameters'
|
||||||
})
|
})
|
||||||
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')
|
return `{${value.map(e => `${e.name}: <${JSON.stringify(e.range).replace('{}', 'any')
|
||||||
.replace(/["{}]/g, '')}>`).join(', ')}}`;
|
.replace(/["{}]/g, '')}>`).join(', ')}}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,93 +1,93 @@
|
|||||||
<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>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</rb-tab-panel>
|
</rb-tab-panel>
|
||||||
|
|
||||||
<rb-form-select label="Model" (change)="result = undefined" [(ngModel)]="activeModelIndex">
|
<rb-form-select label="Model" (change)="result = undefined" [(ngModel)]="activeModelIndex">
|
||||||
<option *ngFor="let model of activeGroup.models; index as i" [value]="i">{{model.name}}</option>
|
<option *ngFor="let model of activeGroup.models; index as i" [value]="i">{{model.name}}</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
|
|
||||||
<div *ngIf="result" class="result" [@inOut]>
|
<div *ngIf="result" class="result" [@inOut]>
|
||||||
<ng-container *ngIf="multipleSamples; else singleSampleResult">
|
<ng-container *ngIf="multipleSamples; else singleSampleResult">
|
||||||
<h4 *ngFor="let prediction of result.predictions; index as i">
|
<h4 *ngFor="let prediction of result.predictions; index as i">
|
||||||
{{spectrumNames[i]}}:
|
{{spectrumNames[i]}}:
|
||||||
<span *ngFor="let predictionEntry of prediction">
|
<span *ngFor="let predictionEntry of prediction">
|
||||||
{{predictionEntry.category}} <span [ngStyle]="{color: predictionEntry.color}">{{predictionEntry.value}}</span> {{predictionEntry.label}}
|
{{predictionEntry.category}} <span [ngStyle]="{color: predictionEntry.color}">{{predictionEntry.value}}</span> {{predictionEntry.label}}
|
||||||
</span>
|
</span>
|
||||||
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
||||||
</h4>
|
</h4>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #singleSampleResult>
|
<ng-template #singleSampleResult>
|
||||||
<h4>
|
<h4>
|
||||||
Average result:
|
Average result:
|
||||||
<span *ngFor="let predictionEntry of result.mean">
|
<span *ngFor="let predictionEntry of result.mean">
|
||||||
{{predictionEntry.category}} <span [ngStyle]="{color: predictionEntry.color}">{{predictionEntry.value}}</span> {{predictionEntry.label}} {{( predictionEntry.std !== '' ? ' (standard deviation: '+ predictionEntry.std+')' : '') }}
|
{{predictionEntry.category}} <span [ngStyle]="{color: predictionEntry.color}">{{predictionEntry.value}}</span> {{predictionEntry.label}} {{( predictionEntry.std !== '' ? ' (standard deviation: '+ predictionEntry.std+')' : '') }}
|
||||||
</span>
|
</span>
|
||||||
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
||||||
</h4>
|
</h4>
|
||||||
<a 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]}}:
|
{{spectrumNames[i]}}:
|
||||||
<span *ngFor="let predictionEntry of prediction">
|
<span *ngFor="let predictionEntry of prediction">
|
||||||
{{predictionEntry.category}} <span [ngStyle]="{color: predictionEntry.color}">{{predictionEntry.value}}</span> {{predictionEntry.label}}
|
{{predictionEntry.category}} <span [ngStyle]="{color: predictionEntry.color}">{{predictionEntry.value}}</span> {{predictionEntry.label}}
|
||||||
</span>
|
</span>
|
||||||
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
<a [routerLink]='"."' fragment="disclaimer"><sup>#</sup></a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="file-input space-below">
|
<div class="file-input space-below">
|
||||||
<rb-form-file name="spectrum-upload" label="spectrum file" maxSize="10000000" class="space-below" multiple
|
<rb-form-file name="spectrum-upload" label="spectrum file" maxSize="10000000" class="space-below" multiple
|
||||||
(ngModelChange)="fileToArray($event)" placeholder="Select file or drag and drop" dragDrop ngModel>
|
(ngModelChange)="fileToArray($event)" placeholder="Select file or drag and drop" dragDrop ngModel>
|
||||||
</rb-form-file>
|
</rb-form-file>
|
||||||
|
|
||||||
<rb-loading-spinner *ngIf="loading; else predictButton"></rb-loading-spinner>
|
<rb-loading-spinner *ngIf="loading; else predictButton"></rb-loading-spinner>
|
||||||
<ng-template #predictButton>
|
<ng-template #predictButton>
|
||||||
<rb-icon-button icon="forward-right" mode="primary" *ngIf="spectrumNames.length; else placeholder"
|
<rb-icon-button icon="forward-right" mode="primary" *ngIf="spectrumNames.length; else placeholder"
|
||||||
(click)="loadPrediction()">
|
(click)="loadPrediction()">
|
||||||
Predict
|
Predict
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
<ng-template #placeholder><div></div></ng-template>
|
<ng-template #placeholder><div></div></ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
Prediction of:
|
Prediction of:
|
||||||
<rb-form-radio name="multiple-samples" label="Single sample" [(ngModel)]="multipleSamples" [value]="false">
|
<rb-form-radio name="multiple-samples" label="Single sample" [(ngModel)]="multipleSamples" [value]="false">
|
||||||
</rb-form-radio>
|
</rb-form-radio>
|
||||||
<rb-form-radio name="multiple-samples" label="Multiple samples" [(ngModel)]="multipleSamples" [value]="true">
|
<rb-form-radio name="multiple-samples" label="Multiple samples" [(ngModel)]="multipleSamples" [value]="true">
|
||||||
</rb-form-radio>
|
</rb-form-radio>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CSV export -->
|
<!-- CSV export -->
|
||||||
<rb-icon-button icon="forward-right" mode="secondary" (click)="exportCSV()" *ngIf="spectrumNames.length" style="margin-right: 0.5rem">
|
<rb-icon-button icon="forward-right" mode="secondary" (click)="exportCSV()" *ngIf="spectrumNames.length" style="margin-right: 0.5rem">
|
||||||
Export to CSV
|
Export to CSV
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
|
|
||||||
<!-- PDF exprot -->
|
<!-- PDF exprot -->
|
||||||
<rb-icon-button icon="forward-right" mode="secondary" (click)="exportPDF()" *ngIf="spectrumNames.length">
|
<rb-icon-button icon="forward-right" mode="secondary" (click)="exportPDF()" *ngIf="spectrumNames.length">
|
||||||
Export to PDF
|
Export to PDF
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
|
|
||||||
<div class="dpt-chart space-below">
|
<div class="dpt-chart space-below">
|
||||||
<canvas baseChart
|
<canvas baseChart
|
||||||
class="dpt-chart"
|
class="dpt-chart"
|
||||||
[datasets]="chart"
|
[datasets]="chart"
|
||||||
[labels]="[]"
|
[labels]="[]"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="false"
|
[legend]="false"
|
||||||
chartType="scatter">
|
chartType="scatter">
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="shaded-container" id="disclaimer">
|
<div class="shaded-container" id="disclaimer">
|
||||||
<h4><sup>#</sup>Disclaimer: This tool is still under development</h4>
|
<h4><sup>#</sup>Disclaimer: This tool is still under development</h4>
|
||||||
<p>
|
<p>
|
||||||
The prediction and classification of material parameters are validated only for certain conditions.
|
The prediction and classification of material parameters are validated only for certain conditions.
|
||||||
These results may therefore under no circumstances be used to evaluate quality-relevant issues. <br>
|
These results may therefore under no circumstances be used to evaluate quality-relevant issues. <br>
|
||||||
For more details please contact <a [href]="'mailto:' + d.contact.mail">{{d.contact.name}}</a>.
|
For more details please contact <a [href]="'mailto:' + d.contact.mail">{{d.contact.name}}</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
.dpt-chart {
|
.dpt-chart {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-input {
|
.file-input {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
grid-column-gap: 1rem;
|
grid-column-gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result {
|
.result {
|
||||||
margin: 30px 0;
|
margin: 30px 0;
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,267 +15,266 @@ import * as pdfFonts from 'pdfmake/build/vfs_fonts';
|
|||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-prediction',
|
selector: 'app-prediction',
|
||||||
templateUrl: './prediction.component.html',
|
templateUrl: './prediction.component.html',
|
||||||
styleUrls: ['./prediction.component.scss'],
|
styleUrls: ['./prediction.component.scss'],
|
||||||
animations: [
|
animations: [
|
||||||
trigger(
|
trigger(
|
||||||
'inOut', [
|
'inOut', [
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({height: 0, opacity: 0}),
|
style({height: 0, opacity: 0}),
|
||||||
animate('0.5s ease-out', style({height: '*', opacity: 1}))
|
animate('0.5s ease-out', style({height: '*', opacity: 1}))
|
||||||
]),
|
]),
|
||||||
transition(':leave', [
|
transition(':leave', [
|
||||||
style({height: '*', opacity: 1}),
|
style({height: '*', opacity: 1}),
|
||||||
animate('0.5s ease-in', style({height: 0, opacity: 0}))
|
animate('0.5s ease-in', style({height: 0, opacity: 0}))
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class PredictionComponent implements OnInit {
|
export class PredictionComponent implements OnInit {
|
||||||
|
|
||||||
result: { predictions: any[], mean: any[] }; // Prediction result from python container
|
result: { predictions: any[], mean: any[] }; // Prediction result from python container
|
||||||
loading = false;
|
loading = false;
|
||||||
activeGroup: ModelItemModel = new ModelItemModel();
|
activeGroup: ModelItemModel = new ModelItemModel();
|
||||||
activeModelIndex = 0;
|
activeModelIndex = 0;
|
||||||
// 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;
|
multipleSamples = false;
|
||||||
spectrumNames: string[] = [];
|
spectrumNames: string[] = [];
|
||||||
spectrum: string[][] = [[]];
|
spectrum: string[][] = [[]];
|
||||||
flattenedSpectra = [];
|
flattenedSpectra = [];
|
||||||
chart = [];
|
chart = [];
|
||||||
readonly chartInit = {
|
readonly chartInit = {
|
||||||
data: [],
|
data: [],
|
||||||
label: 'Spectrum',
|
label: 'Spectrum',
|
||||||
showLine: true,
|
showLine: true,
|
||||||
fill: false,
|
fill: false,
|
||||||
pointRadius: 0,
|
pointRadius: 0,
|
||||||
borderColor: '#00a8b0',
|
borderColor: '#00a8b0',
|
||||||
borderWidth: 2
|
borderWidth: 2
|
||||||
};
|
};
|
||||||
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: {}}]
|
yAxes: [{ticks: {}}]
|
||||||
},
|
},
|
||||||
responsive: true,
|
responsive: true,
|
||||||
tooltips: {enabled: false},
|
tooltips: {enabled: false},
|
||||||
hover: {mode: null},
|
hover: {mode: null},
|
||||||
maintainAspectRatio: true,
|
maintainAspectRatio: true,
|
||||||
plugins: {datalabels: {display: false}}
|
plugins: {datalabels: {display: false}}
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
public d: DataService,
|
public d: DataService,
|
||||||
public login: LoginService
|
public login: LoginService
|
||||||
) {
|
) {
|
||||||
this.chart[0] = cloneDeep(this.chartInit);
|
this.chart[0] = cloneDeep(this.chartInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.d.load('modelGroups', () => {
|
this.d.load('modelGroups', () => {
|
||||||
this.activeGroup = this.d.arr.modelGroups[0];
|
this.activeGroup = this.d.arr.modelGroups[0];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fileToArray(files) {
|
fileToArray(files) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.flattenedSpectra = [];
|
this.flattenedSpectra = [];
|
||||||
this.chart = [];
|
this.chart = [];
|
||||||
let load = files.length;
|
let load = files.length;
|
||||||
this.spectrumNames = files.map(e => e.name);
|
this.spectrumNames = files.map(e => e.name);
|
||||||
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();
|
||||||
fileReader.onload = () => {
|
fileReader.onload = () => {
|
||||||
// Parse to database spectrum representation
|
// 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
|
// 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
|
// 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) { // All loaded
|
if (load <= 0) { // All loaded
|
||||||
this.loadPrediction();
|
this.loadPrediction();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fileReader.readAsText(files[i]);
|
fileReader.readAsText(files[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPrediction() {
|
loadPrediction() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
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 => {
|
||||||
let tmp = Object.entries(omit(data, ['mean', 'std', 'label'])) // Form: [[label, [{value, color}]]]
|
let tmp = Object.entries(omit(data, ['mean', 'std', 'label'])) // Form: [[label, [{value, color}]]]
|
||||||
.map((entry: any) => entry[1].map(e => ({category: entry[0], label: data.label[entry[0]], value: e.value, color: e.color}))); // Form: [[{category, label, value, color}]]
|
.map((entry: any) => entry[1].map(e => ({category: entry[0], label: data.label[entry[0]], value: e.value, color: e.color}))); // Form: [[{category, label, value, color}]]
|
||||||
this.result = {
|
this.result = {
|
||||||
predictions: tmp[0].map((ignore, columnIndex) => tmp.map(row => row[columnIndex])), // Transpose tmp
|
predictions: tmp[0].map((ignore, columnIndex) => tmp.map(row => row[columnIndex])), // Transpose tmp
|
||||||
mean: Object.entries(data.mean)
|
mean: Object.entries(data.mean)
|
||||||
.map((entry:any) => ({category: entry[0], label: data.label[entry[0]], value: entry[1].value, color: entry[1].color, std: data.std[entry[0]]})) // Form: [{category, label, value, color}]
|
.map((entry:any) => ({category: entry[0], label: data.label[entry[0]], value: entry[1].value, color: entry[1].color, std: data.std[entry[0]]})) // Form: [{category, label, value, color}]
|
||||||
};
|
};
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
groupChange(index) { // Group was changed
|
groupChange(index) { // Group was changed
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregates spectrum names and prediction values into an associative array
|
// Aggregates spectrum names and prediction values into an associative array
|
||||||
prepareExport() {
|
prepareExport() {
|
||||||
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
||||||
const values = this.result.predictions
|
const values = this.result.predictions
|
||||||
.map(prediction => prediction
|
.map(prediction => prediction
|
||||||
.filter(field => field.category === 'Prediction')
|
.filter(field => field.category === 'Prediction')
|
||||||
.map(field => field.value));
|
.map(field => field.value));
|
||||||
return zip(this.spectrumNames, values);
|
return zip(this.spectrumNames, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a timestamp suitable for file naming
|
// Generates a timestamp suitable for file naming
|
||||||
generateTimestamp() {
|
generateTimestamp() {
|
||||||
let d = new Date();
|
let d = new Date();
|
||||||
return d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + '--' + d.getHours() + "-" + d.getMinutes();
|
return d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + '--' + d.getHours() + "-" + d.getMinutes();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts the prediction results to a CSV file
|
// Converts the prediction results to a CSV file
|
||||||
exportCSV() {
|
exportCSV() {
|
||||||
const predictions = this.prepareExport();
|
const predictions = this.prepareExport();
|
||||||
const csv = predictions.map(line => line.join(";")).join("\n");
|
const csv = predictions.map(line => line.join(";")).join("\n");
|
||||||
FileSaver.saveAs(new Blob([csv], { type: 'text/csv;charset=utf-8' }), 'predictions-' + this.generateTimestamp() + '.csv');
|
FileSaver.saveAs(new Blob([csv], { type: 'text/csv;charset=utf-8' }), 'predictions-' + this.generateTimestamp() + '.csv');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts the prediction results to a PDF file
|
// Converts the prediction results to a PDF file
|
||||||
exportPDF() {
|
exportPDF() {
|
||||||
const doc = {
|
const doc = {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
text: 'DeFinMa - Decoding the Fingerprint of Materials by AI',
|
text: 'DeFinMa - Decoding the Fingerprint of Materials by AI',
|
||||||
style: 'header'
|
style: 'header'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
table: {
|
table: {
|
||||||
widths: ['auto', '*', 'auto', 'auto', 'auto'],
|
widths: ['auto', '*', 'auto', 'auto', 'auto'],
|
||||||
body: [
|
body: [[
|
||||||
[
|
{text: new Date().toLocaleDateString(), style: 'tableHeader'},
|
||||||
{text: new Date().toLocaleDateString(), style: 'tableHeader'},
|
{text: 'Customer', style: 'tableHeader'},
|
||||||
{text: 'Customer', style: 'tableHeader'},
|
{text: 'Security class', style: 'tableHeader'},
|
||||||
{text: 'Security class', style: 'tableHeader'},
|
{text: 'Person in charge', style: 'tableHeader'},
|
||||||
{text: 'Person in charge', style: 'tableHeader'},
|
{text: 'Phone', style: 'tableHeader'}],
|
||||||
{text: 'Phone', style: 'tableHeader'}],
|
[
|
||||||
[
|
this.activeGroup.group,
|
||||||
this.activeGroup.group,
|
this.login.username,
|
||||||
this.login.username,
|
'Intern',
|
||||||
'Intern',
|
{
|
||||||
{
|
stack: [
|
||||||
stack: [
|
'CR/APS1-Lotter',
|
||||||
'CR/APS1-Lotter',
|
'CR/APS1-Lingenfelser'
|
||||||
'CR/APS1-Lingenfelser'
|
]
|
||||||
]
|
},
|
||||||
},
|
{
|
||||||
{
|
stack: [
|
||||||
stack: [
|
'0711/811-49017',
|
||||||
'0711/811-49017',
|
'0711/811-6897'
|
||||||
'0711/811-6897'
|
]
|
||||||
]
|
}
|
||||||
}
|
]
|
||||||
]
|
]
|
||||||
]
|
}
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
{
|
text: 'Prediction of ' + this.activeGroup.group + ' (' + this.activeGroup.models[this.activeModelIndex].name + ')*',
|
||||||
text: 'Prediction of ' + this.activeGroup.group + ' (' + this.activeGroup.models[this.activeModelIndex].name + ')*',
|
style: 'subheader'
|
||||||
style: 'subheader'
|
},
|
||||||
},
|
{
|
||||||
{
|
text: this.result.mean.map(e => e.category + ' ' + e.value + ' ' + e.label + ' ' + (e.std !== '' ? (' (standard deviation: ' + e.std + ')') : ''))
|
||||||
text: this.result.mean.map(e => e.category + ' ' + e.value + ' ' + e.label + ' ' + (e.std !== '' ? (' (standard deviation: ' + e.std + ')') : ''))
|
},
|
||||||
},
|
{
|
||||||
{
|
table: {
|
||||||
table: {
|
widths: ['*', 'auto'],
|
||||||
widths: ['*', 'auto'],
|
body: [
|
||||||
body: [
|
[{text: 'Input Data / Sample Name', style: 'tableHeader'}, {text: 'Prediction*', style: 'tableHeader'}]
|
||||||
[{text: 'Input Data / Sample Name', style: 'tableHeader'}, {text: 'Prediction*', style: 'tableHeader'}]
|
].concat(this.prepareExport())
|
||||||
].concat(this.prepareExport())
|
}
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
{
|
text: 'Reference Data',
|
||||||
text: 'Reference Data',
|
style: 'subheader'
|
||||||
style: 'subheader'
|
},
|
||||||
},
|
{
|
||||||
{
|
image: document.getElementsByTagName('canvas')[0].toDataURL('image/png'),
|
||||||
image: document.getElementsByTagName('canvas')[0].toDataURL('image/png'),
|
width: 500
|
||||||
width: 500
|
},
|
||||||
},
|
{
|
||||||
{
|
table: {
|
||||||
table: {
|
body: [[{
|
||||||
body: [[{
|
stack: [
|
||||||
stack: [
|
{
|
||||||
{
|
text: '*Disclaimer: This tool is still under development and Testing',
|
||||||
text: '*Disclaimer: This tool is still under development and Testing',
|
style: 'subsubheader'
|
||||||
style: 'subsubheader'
|
},
|
||||||
},
|
{
|
||||||
{
|
text: [
|
||||||
text: [
|
'The prediction and classification of material parameters are validated only for certain conditions.',
|
||||||
'The prediction and classification of material parameters are validated only for certain conditions.',
|
'These results may therefore under no circumstances be used to evaluate quality-relevant issues.',
|
||||||
'These results may therefore under no circumstances be used to evaluate quality-relevant issues.',
|
'For more details please contact ',
|
||||||
'For more details please contact ',
|
{
|
||||||
{
|
text: 'CR/APS1-Lingenfelser',
|
||||||
text: 'CR/APS1-Lingenfelser',
|
link: 'mailto:dominic.lingenfelser@bosch.com'
|
||||||
link: 'mailto:dominic.lingenfelser@bosch.com'
|
},
|
||||||
},
|
'.'
|
||||||
'.'
|
]
|
||||||
]
|
},
|
||||||
},
|
]
|
||||||
]
|
}]],
|
||||||
}]],
|
},
|
||||||
},
|
margin: [25, 20]
|
||||||
margin: [25, 20]
|
},
|
||||||
},
|
{
|
||||||
{
|
table: {
|
||||||
table: {
|
widths: ['*', '*', 'auto'],
|
||||||
widths: ['*', '*', 'auto'],
|
body: [
|
||||||
body: [
|
[{text: 'Pr\u00fcfung', style: 'tableHeader'}, {text: 'Freigabe', style: 'tableHeader'}, {text: 'Datum', style: 'tableHeader'}],
|
||||||
[{text: 'Pr\u00fcfung', style: 'tableHeader'}, {text: 'Freigabe', style: 'tableHeader'}, {text: 'Datum', style: 'tableHeader'}],
|
['CR/APS1-Lotter', 'CR/APS1-Lingenfelser', new Date().toLocaleDateString()]
|
||||||
['CR/APS1-Lotter', 'CR/APS1-Lingenfelser', new Date().toLocaleDateString()]
|
],
|
||||||
],
|
}
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
],
|
footer: {
|
||||||
footer: {
|
text: '\u00a9 Alle Rechte bei Robert Bosch GmbH, auch f\u00fcr den Fall von Schutzreichtsanmeldungen. Jede Verf\u00fcgungsbefugnis, wie Kopier- und Weitergaberecht, bei uns.',
|
||||||
text: '\u00a9 Alle Rechte bei Robert Bosch GmbH, auch f\u00fcr den Fall von Schutzreichtsanmeldungen. Jede Verf\u00fcgungsbefugnis, wie Kopier- und Weitergaberecht, bei uns.',
|
fontSize: 8,
|
||||||
fontSize: 8,
|
alignment: 'center'
|
||||||
alignment: 'center'
|
},
|
||||||
},
|
styles: {
|
||||||
styles: {
|
header: {
|
||||||
header: {
|
fontSize: 18,
|
||||||
fontSize: 18,
|
bold: true,
|
||||||
bold: true,
|
margin: [0, 10]
|
||||||
margin: [0, 10]
|
},
|
||||||
},
|
subheader: {
|
||||||
subheader: {
|
fontSize: 15,
|
||||||
fontSize: 15,
|
bold: true,
|
||||||
bold: true,
|
margin: [0, 8]
|
||||||
margin: [0, 8]
|
},
|
||||||
},
|
subsubheader: {
|
||||||
subsubheader: {
|
bold: true,
|
||||||
bold: true,
|
margin: [0, 5]
|
||||||
margin: [0, 5]
|
},
|
||||||
},
|
tableHeader: {
|
||||||
tableHeader: {
|
bold: true,
|
||||||
bold: true,
|
fontSize: 13,
|
||||||
fontSize: 13,
|
color: 'black'
|
||||||
color: 'black'
|
}
|
||||||
}
|
},
|
||||||
},
|
pageMargins: [50, 50, 50, 15]
|
||||||
pageMargins: [50, 50, 50, 15]
|
};
|
||||||
};
|
|
||||||
|
|
||||||
pdfMake.createPdf(doc).download('predictions-' + this.generateTimestamp() + '.pdf');
|
pdfMake.createPdf(doc).download('predictions-' + this.generateTimestamp() + '.pdf');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,25 +2,25 @@ import { Injectable } from '@angular/core';
|
|||||||
import {Observable, Subject} from 'rxjs';
|
import {Observable, Subject} from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ArrayInputHelperService {
|
export class ArrayInputHelperService {
|
||||||
|
|
||||||
com: Subject<{ id: string, index: number, value: any }> = new Subject();
|
com: Subject<{ id: string, index: number, value: any }> = new Subject();
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
values(id: string) { // Observable which returns new values as they come for subscribed id
|
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) {
|
||||||
observer.next({index: data.index, value: data.value});
|
observer.next({index: data.index, value: data.value});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
newValue(id: string, index: number, value: any) { // Set new value
|
newValue(id: string, index: number, value: any) { // Set new value
|
||||||
this.com.next({id, index, value});
|
this.com.next({id, index, value});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<ng-container *ngFor="let ignore of [].constructor(values.length); index as i">
|
<ng-container *ngFor="let ignore of [].constructor(values.length); index as i">
|
||||||
<ng-container *ngTemplateOutlet="item.templateRef; context: {$implicit: {i: i, value: values[i]}}"></ng-container>
|
<ng-container *ngTemplateOutlet="item.templateRef; context: {$implicit: {i: i, value: values[i]}}"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
Component,
|
Component,
|
||||||
ContentChild,
|
ContentChild,
|
||||||
Directive,
|
Directive,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
HostListener,
|
HostListener,
|
||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
TemplateRef
|
TemplateRef
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
@ -15,150 +15,150 @@ import {ArrayInputHelperService} from './array-input-helper.service';
|
|||||||
|
|
||||||
|
|
||||||
@Directive({ // Directive for template and input values
|
@Directive({ // Directive for template and input values
|
||||||
// tslint:disable-next-line:directive-selector
|
// tslint:disable-next-line:directive-selector
|
||||||
selector: '[rbArrayInputItem]'
|
selector: '[rbArrayInputItem]'
|
||||||
})
|
})
|
||||||
export class RbArrayInputItemDirective {
|
export class RbArrayInputItemDirective {
|
||||||
constructor(public templateRef: TemplateRef<any>) {
|
constructor(public templateRef: TemplateRef<any>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({ // Directive for change detection
|
@Directive({ // Directive for change detection
|
||||||
// tslint:disable-next-line:directive-selector
|
// tslint:disable-next-line:directive-selector
|
||||||
selector: '[rbArrayInputListener]'
|
selector: '[rbArrayInputListener]'
|
||||||
})
|
})
|
||||||
export class RbArrayInputListenerDirective {
|
export class RbArrayInputListenerDirective {
|
||||||
|
|
||||||
@Input() rbArrayInputListener: string;
|
@Input() rbArrayInputListener: string;
|
||||||
@Input() index;
|
@Input() index;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private helperService: ArrayInputHelperService
|
private helperService: ArrayInputHelperService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@HostListener('ngModelChange', ['$event'])
|
@HostListener('ngModelChange', ['$event'])
|
||||||
onChange(event) { // Emit new value
|
onChange(event) { // Emit new value
|
||||||
this.helperService.newValue(this.rbArrayInputListener, this.index, event);
|
this.helperService.newValue(this.rbArrayInputListener, this.index, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// tslint:disable-next-line:component-selector
|
// tslint:disable-next-line:component-selector
|
||||||
selector: 'rb-array-input',
|
selector: 'rb-array-input',
|
||||||
templateUrl: './rb-array-input.component.html',
|
templateUrl: './rb-array-input.component.html',
|
||||||
styleUrls: ['./rb-array-input.component.scss'],
|
styleUrls: ['./rb-array-input.component.scss'],
|
||||||
providers: [{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RbArrayInputComponent), multi: true}]
|
providers: [{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RbArrayInputComponent), multi: true}]
|
||||||
})
|
})
|
||||||
export class RbArrayInputComponent implements ControlValueAccessor, OnInit, AfterViewInit {
|
export class RbArrayInputComponent implements ControlValueAccessor, OnInit, AfterViewInit {
|
||||||
|
|
||||||
pushTemplate: any = ''; // Array element template
|
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) {
|
||||||
this.updateArray();
|
this.updateArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Input() pushPath: string = null;
|
@Input() pushPath: string = null;
|
||||||
|
|
||||||
@ContentChild(RbArrayInputItemDirective) item: RbArrayInputItemDirective;
|
@ContentChild(RbArrayInputItemDirective) item: RbArrayInputItemDirective;
|
||||||
@ContentChild(RbArrayInputListenerDirective) item2: RbArrayInputListenerDirective;
|
@ContentChild(RbArrayInputListenerDirective) item2: RbArrayInputListenerDirective;
|
||||||
|
|
||||||
values = []; // Main array to display
|
values = []; // Main array to display
|
||||||
|
|
||||||
onChange = (ignore?: any): void => {};
|
onChange = (ignore?: any): void => {};
|
||||||
onTouched = (ignore?: any): void => {};
|
onTouched = (ignore?: any): void => {};
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private helperService: ArrayInputHelperService
|
private helperService: ArrayInputHelperService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
setTimeout(() => { // Needed to find reference
|
setTimeout(() => { // Needed to find reference
|
||||||
this.helperService.values(this.item2.rbArrayInputListener).subscribe(data => { // Action on value change
|
this.helperService.values(this.item2.rbArrayInputListener).subscribe(data => { // Action on value change
|
||||||
// Assign value
|
// Assign value
|
||||||
if (this.pushPath) {
|
if (this.pushPath) {
|
||||||
this.values[data.index][this.pushPath] = data.value;
|
this.values[data.index][this.pushPath] = data.value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.values[data.index] = data.value;
|
this.values[data.index] = data.value;
|
||||||
}
|
}
|
||||||
this.updateArray();
|
this.updateArray();
|
||||||
});
|
});
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateArray() {
|
updateArray() {
|
||||||
let res;
|
let res;
|
||||||
// Adjust fields if pushTemplate is specified
|
// Adjust fields if pushTemplate is specified
|
||||||
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] === '' &&
|
if (this.values[this.values.length - 1][this.pushPath] === '' &&
|
||||||
this.values[this.values.length - 2][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
|
||||||
else if (this.values.filter(e => e[this.pushPath] !== '').length === this.values.length) {
|
else if (this.values.filter(e => e[this.pushPath] !== '').length === this.values.length) {
|
||||||
this.values.push(cloneDeep(this.pushTemplate));
|
this.values.push(cloneDeep(this.pushTemplate));
|
||||||
}
|
}
|
||||||
res = this.values.filter(e => e[this.pushPath] !== '');
|
res = this.values.filter(e => e[this.pushPath] !== '');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Remove last element if last two are empty
|
// Remove last element if last two are empty
|
||||||
if (this.values[this.values.length - 1] === '' && this.values[this.values.length - 2] === '') {
|
if (this.values[this.values.length - 1] === '' && this.values[this.values.length - 2] === '') {
|
||||||
this.values.pop();
|
this.values.pop();
|
||||||
}
|
}
|
||||||
else if (this.values.filter(e => e !== '').length === this.values.length) { // Add element if all are is filled
|
else if (this.values.filter(e => e !== '').length === this.values.length) { // Add element if all are is filled
|
||||||
this.values.push(cloneDeep(this.pushTemplate));
|
this.values.push(cloneDeep(this.pushTemplate));
|
||||||
}
|
}
|
||||||
res = this.values.filter(e => e !== '');
|
res = this.values.filter(e => e !== '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.values = [this.values[0]];
|
this.values = [this.values[0]];
|
||||||
res = this.values;
|
res = this.values;
|
||||||
}
|
}
|
||||||
if (!res.length) {
|
if (!res.length) {
|
||||||
res = [''];
|
res = [''];
|
||||||
}
|
}
|
||||||
this.onChange(res); // Trigger ngModel with filled elements
|
this.onChange(res); // Trigger ngModel with filled elements
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// 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)];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.values = [...obj.filter(e => e !== ''), cloneDeep(this.pushTemplate)];
|
this.values = [...obj.filter(e => e !== ''), cloneDeep(this.pushTemplate)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.values = obj;
|
this.values = obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (this.pushTemplate !== null) {
|
if (this.pushTemplate !== null) {
|
||||||
this.values = [cloneDeep(this.pushTemplate)];
|
this.values = [cloneDeep(this.pushTemplate)];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.values = [''];
|
this.values = [''];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(fn: any) {
|
registerOnChange(fn: any) {
|
||||||
this.onChange = fn;
|
this.onChange = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnTouched(fn: any) {
|
registerOnTouched(fn: any) {
|
||||||
this.onTouched = fn;
|
this.onTouched = fn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ 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
|
import {RbArrayInputComponent, RbArrayInputListenerDirective, RbArrayInputItemDirective} from
|
||||||
'./rb-array-input/rb-array-input.component';
|
'./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';
|
||||||
@ -10,24 +10,24 @@ import { RbIconButtonComponent } from './rb-icon-button/rb-icon-button.component
|
|||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
RbTableComponent,
|
RbTableComponent,
|
||||||
RbArrayInputComponent,
|
RbArrayInputComponent,
|
||||||
RbArrayInputListenerDirective,
|
RbArrayInputListenerDirective,
|
||||||
RbArrayInputItemDirective,
|
RbArrayInputItemDirective,
|
||||||
RbIconButtonComponent
|
RbIconButtonComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
RbUiComponentsModule
|
RbUiComponentsModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RbTableComponent,
|
RbTableComponent,
|
||||||
RbArrayInputComponent,
|
RbArrayInputComponent,
|
||||||
RbArrayInputListenerDirective,
|
RbArrayInputListenerDirective,
|
||||||
RbArrayInputItemDirective,
|
RbArrayInputItemDirective,
|
||||||
RbIconButtonComponent
|
RbIconButtonComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class RbCustomInputsModule { }
|
export class RbCustomInputsModule { }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<button class="rb-btn rb" [ngClass]="'rb-' + mode" [type]="type" [disabled]="disabled">
|
<button class="rb-btn rb" [ngClass]="'rb-' + mode" [type]="type" [disabled]="disabled">
|
||||||
<span class="rb-ic" [ngClass]="'rb-ic-' + icon" [class.icon-space]="iconOnly === undefined"></span>
|
<span class="rb-ic" [ngClass]="'rb-ic-' + icon" [class.icon-space]="iconOnly === undefined"></span>
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
.icon-space {
|
.icon-space {
|
||||||
margin-right: 0.1em !important;
|
margin-right: 0.1em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.rb-btn > span:not(.icon-space) {
|
button.rb-btn > span:not(.icon-space) {
|
||||||
margin-right: -10px;
|
margin-right: -10px;
|
||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
}
|
}
|
||||||
|
@ -2,22 +2,22 @@ import {Component, Input, OnInit} from '@angular/core';
|
|||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// tslint:disable-next-line:component-selector
|
// tslint:disable-next-line:component-selector
|
||||||
selector: 'rb-icon-button',
|
selector: 'rb-icon-button',
|
||||||
templateUrl: './rb-icon-button.component.html',
|
templateUrl: './rb-icon-button.component.html',
|
||||||
styleUrls: ['./rb-icon-button.component.scss']
|
styleUrls: ['./rb-icon-button.component.scss']
|
||||||
})
|
})
|
||||||
export class RbIconButtonComponent implements OnInit {
|
export class RbIconButtonComponent implements OnInit {
|
||||||
|
|
||||||
@Input() icon: string;
|
@Input() icon: string;
|
||||||
@Input() mode: string;
|
@Input() mode: string;
|
||||||
@Input() iconOnly;
|
@Input() iconOnly;
|
||||||
@Input() disabled;
|
@Input() disabled;
|
||||||
@Input() type = 'button';
|
@Input() type = 'button';
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="table-wrapper space-below" [class.scroll-top]="scrollTop !== undefined">
|
<div class="table-wrapper space-below" [class.scroll-top]="scrollTop !== undefined">
|
||||||
<table [class.ellipsis]="ellipsis !== undefined">
|
<table [class.ellipsis]="ellipsis !== undefined">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
|
@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
|
||||||
|
|
||||||
.table-wrapper.scroll-top {
|
.table-wrapper.scroll-top {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&, & > table { // scrollbar at the top
|
&, & > table { // scrollbar at the top
|
||||||
transform:rotateX(180deg);
|
transform:rotateX(180deg);
|
||||||
-ms-transform:rotateX(180deg); /* IE 9 */
|
-ms-transform:rotateX(180deg); /* IE 9 */
|
||||||
-webkit-transform:rotateX(180deg); /* Safari and Chrome */
|
-webkit-transform:rotateX(180deg); /* Safari and Chrome */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
|
||||||
::ng-deep tr {
|
::ng-deep tr {
|
||||||
border-bottom: 1px solid $color-gray-mercury;
|
border-bottom: 1px solid $color-gray-mercury;
|
||||||
|
|
||||||
::ng-deep td, ::ng-deep th {
|
::ng-deep td, ::ng-deep th {
|
||||||
padding: 8px 5px;
|
padding: 8px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep th {
|
::ng-deep th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.ellipsis {
|
table.ellipsis {
|
||||||
::ng-deep td, ::ng-deep th {
|
::ng-deep td, ::ng-deep th {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// tslint:disable-next-line:component-selector
|
// tslint:disable-next-line:component-selector
|
||||||
selector: 'rb-table',
|
selector: 'rb-table',
|
||||||
templateUrl: './rb-table.component.html',
|
templateUrl: './rb-table.component.html',
|
||||||
styleUrls: ['./rb-table.component.scss']
|
styleUrls: ['./rb-table.component.scss']
|
||||||
})
|
})
|
||||||
export class RbTableComponent implements OnInit {
|
export class RbTableComponent implements OnInit {
|
||||||
|
|
||||||
@Input() scrollTop;
|
@Input() scrollTop;
|
||||||
@Input() ellipsis;
|
@Input() ellipsis;
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,335 +4,335 @@
|
|||||||
|
|
||||||
<ng-template #content>
|
<ng-template #content>
|
||||||
|
|
||||||
<!--BASE-->
|
<!--BASE-->
|
||||||
|
|
||||||
<form #sampleForm="ngForm" *ngIf="view.base">
|
<form #sampleForm="ngForm" *ngIf="view.base">
|
||||||
<div class="sample">
|
<div class="sample">
|
||||||
<div>
|
<div>
|
||||||
<rb-form-input name="materialname" label="product name" [rbDebounceTime]="0" [rbInitialOpen]="true"
|
<rb-form-input name="materialname" label="product name" [rbDebounceTime]="0" [rbInitialOpen]="true"
|
||||||
[rbFormInputAutocomplete]="autocomplete.bind(this, materialNames)" appValidate="stringOf"
|
[rbFormInputAutocomplete]="autocomplete.bind(this, materialNames)" appValidate="stringOf"
|
||||||
(keydown)="preventDefault($event)" (ngModelChange)="findMaterial($event)" ngModel
|
(keydown)="preventDefault($event)" (ngModelChange)="findMaterial($event)" ngModel
|
||||||
[appValidateArgs]="[materialNames]" required [(ngModel)]="material.name" [autofocus]="true"
|
[appValidateArgs]="[materialNames]" required [(ngModel)]="material.name" [autofocus]="true"
|
||||||
*ngIf="mode === 'new' || (baseSample.material && baseSample.material.name !== undefined)"
|
*ngIf="mode === 'new' || (baseSample.material && baseSample.material.name !== undefined)"
|
||||||
title="trade name of the material, eg. Ultradur B4300 G6">
|
title="trade name of the material, eg. Ultradur B4300 G6">
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
<ng-template rbFormValidationMessage="failure">Unknown material, add properties for new material</ng-template>
|
<ng-template rbFormValidationMessage="failure">Unknown material, add properties for new material</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-icon-button class="set-new-material space-below" icon="add" mode="secondary"
|
<rb-icon-button class="set-new-material space-below" icon="add" mode="secondary"
|
||||||
(click)="setNewMaterial(!newMaterial)"
|
(click)="setNewMaterial(!newMaterial)"
|
||||||
*ngIf="mode === 'new' || (baseSample.material && baseSample.material.name !== undefined)">
|
*ngIf="mode === 'new' || (baseSample.material && baseSample.material.name !== undefined)">
|
||||||
New material
|
New material
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="material shaded-container" *ngIf="newMaterial" [@inOut]>
|
<div class="material shaded-container" *ngIf="newMaterial" [@inOut]>
|
||||||
<h4>Material properties</h4>
|
<h4>Material properties</h4>
|
||||||
<rb-form-input name="supplier" label="supplier"
|
<rb-form-input name="supplier" label="supplier"
|
||||||
[rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialSuppliers)"
|
[rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialSuppliers)"
|
||||||
[rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
|
[rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
|
||||||
[(ngModel)]="material.supplier" #supplierInput="ngModel"
|
[(ngModel)]="material.supplier" #supplierInput="ngModel"
|
||||||
(focusout)="checkTypo($event, 'materialSuppliers', 'supplier', modalWarning)"
|
(focusout)="checkTypo($event, 'materialSuppliers', 'supplier', modalWarning)"
|
||||||
title="material supplier, eg. BASF">
|
title="material supplier, eg. BASF">
|
||||||
<ng-template rbFormValidationMessage="failure">{{supplierInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{supplierInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="group" label="group"
|
<rb-form-input name="group" label="group"
|
||||||
[rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialGroups)"
|
[rbFormInputAutocomplete]="autocomplete.bind(this, d.arr.materialGroups)"
|
||||||
[rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
|
[rbDebounceTime]="0" [rbInitialOpen]="true" appValidate="string" required
|
||||||
[(ngModel)]="material.group" #groupInput="ngModel"
|
[(ngModel)]="material.group" #groupInput="ngModel"
|
||||||
(focusout)="checkTypo($event, 'materialGroups', 'group', modalWarning)"
|
(focusout)="checkTypo($event, 'materialGroups', 'group', modalWarning)"
|
||||||
title="chemical material type, eg. PA66">
|
title="chemical material type, eg. PA66">
|
||||||
<ng-template rbFormValidationMessage="failure">{{groupInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{groupInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<ng-template #modalWarning>
|
<ng-template #modalWarning>
|
||||||
<rb-alert alertTitle="Warning" type="warning" okBtnLabel="Use suggestion" cancelBtnLabel="Keep value">
|
<rb-alert alertTitle="Warning" type="warning" okBtnLabel="Use suggestion" cancelBtnLabel="Keep value">
|
||||||
The specified {{modalText.list}} could not be found in the list. <br>
|
The specified {{modalText.list}} could not be found in the list. <br>
|
||||||
Did you mean {{modalText.suggestion}}?
|
Did you mean {{modalText.suggestion}}?
|
||||||
</rb-alert>
|
</rb-alert>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<rb-array-input [(ngModel)]="material.numbers" name="materialNumbers" [pushTemplate]="''">
|
<rb-array-input [(ngModel)]="material.numbers" name="materialNumbers" [pushTemplate]="''">
|
||||||
<rb-form-input *rbArrayInputItem="let item" [rbArrayInputListener]="'materialNumber'" [index]="item.i"
|
<rb-form-input *rbArrayInputItem="let item" [rbArrayInputListener]="'materialNumber'" [index]="item.i"
|
||||||
label="material number" appValidate="string" [name]="'materialNumber-' + item.i"
|
label="material number" appValidate="string" [name]="'materialNumber-' + item.i"
|
||||||
[ngModel]="item.value" title="Bosch material part number, eg. 5515753021"></rb-form-input>
|
[ngModel]="item.value" title="Bosch material part number, eg. 5515753021"></rb-form-input>
|
||||||
</rb-array-input>
|
</rb-array-input>
|
||||||
<rb-form-select name="propertiesSelect" label="Type" title="=overall material group specific properties"
|
<rb-form-select name="propertiesSelect" label="Type" title="=overall material group specific properties"
|
||||||
[(ngModel)]="material.properties.material_template">
|
[(ngModel)]="material.properties.material_template">
|
||||||
<option *ngFor="let m of d.latest.materialTemplates" [value]="m._id">{{m.name}}</option>
|
<option *ngFor="let m of d.latest.materialTemplates" [value]="m._id">{{m.name}}</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
<rb-form-input *ngFor="let parameter of
|
<rb-form-input *ngFor="let parameter of
|
||||||
d.id.materialTemplates[material.properties.material_template].parameters;
|
d.id.materialTemplates[material.properties.material_template].parameters;
|
||||||
index as i" [name]="'materialParameter' + i"
|
index as i" [name]="'materialParameter' + i"
|
||||||
[label]="parameter.name" appValidate="string" required
|
[label]="parameter.name" appValidate="string" required
|
||||||
[(ngModel)]="material.properties[parameter.name]" #parameterInput="ngModel">
|
[(ngModel)]="material.properties[parameter.name]" #parameterInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<rb-form-select name="type" label="type" required [(ngModel)]="baseSample.type" ngModel
|
<rb-form-select name="type" label="type" required [(ngModel)]="baseSample.type" ngModel
|
||||||
*ngIf="baseSample.type !== undefined"
|
*ngIf="baseSample.type !== undefined"
|
||||||
title="material status of the sample">
|
title="material status of the sample">
|
||||||
<option value="as-delivered/raw">as-delivered/raw</option>
|
<option value="as-delivered/raw">as-delivered/raw</option>
|
||||||
<option value="processed">processed</option>
|
<option value="processed">processed</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="color" label="color" appValidate="string" [(ngModel)]="baseSample.color"
|
<rb-form-input name="color" label="color" appValidate="string" [(ngModel)]="baseSample.color"
|
||||||
*ngIf="baseSample.color !== undefined" #colorInput="ngModel" title="sample color, eg. black">
|
*ngIf="baseSample.color !== undefined" #colorInput="ngModel" title="sample color, eg. black">
|
||||||
<ng-template rbFormValidationMessage="failure">{{colorInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{colorInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="batch" label="batch" appValidate="string" [(ngModel)]="baseSample.batch"
|
<rb-form-input name="batch" label="batch" appValidate="string" [(ngModel)]="baseSample.batch"
|
||||||
#batchInput="ngModel" *ngIf="baseSample.batch !== undefined"
|
#batchInput="ngModel" *ngIf="baseSample.batch !== undefined"
|
||||||
title="batch number the sample was from, eg. 2264486614">
|
title="batch number the sample was from, eg. 2264486614">
|
||||||
<ng-template rbFormValidationMessage="failure">{{batchInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{batchInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="notes" *ngIf="baseSample.notes !== undefined">
|
<div class="notes" *ngIf="baseSample.notes !== undefined">
|
||||||
<rb-form-input name="comment" label="comment" appValidate="stringLength" [appValidateArgs]="[512]"
|
<rb-form-input name="comment" label="comment" appValidate="stringLength" [appValidateArgs]="[512]"
|
||||||
[(ngModel)]="baseSample.notes.comment" #commentInput="ngModel"
|
[(ngModel)]="baseSample.notes.comment" #commentInput="ngModel"
|
||||||
title="general remarks that cannot be expressed in additional properties, eg. stabilized">
|
title="general remarks that cannot be expressed in additional properties, eg. stabilized">
|
||||||
<ng-template rbFormValidationMessage="failure">{{commentInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{commentInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<h5>Sample references</h5>
|
<h5>Sample references</h5>
|
||||||
<div *ngFor="let reference of sampleReferences; index as i" class="two-col" [@inOut]>
|
<div *ngFor="let reference of sampleReferences; index as i" class="two-col" [@inOut]>
|
||||||
<div>
|
<div>
|
||||||
<rb-form-input [name]="'sr-id' + i" label="sample number"
|
<rb-form-input [name]="'sr-id' + i" label="sample number"
|
||||||
[rbFormInputAutocomplete]="sampleReferenceListBind()"
|
[rbFormInputAutocomplete]="sampleReferenceListBind()"
|
||||||
[rbDebounceTime]="300" appValidate="stringOf"
|
[rbDebounceTime]="300" appValidate="stringOf"
|
||||||
[appValidateArgs]="[sampleReferenceAutocomplete[i]]"
|
[appValidateArgs]="[sampleReferenceAutocomplete[i]]"
|
||||||
(ngModelChange)="checkSampleReference($event, i)" [ngModel]="reference[0]" ngModel
|
(ngModelChange)="checkSampleReference($event, i)" [ngModel]="reference[0]" ngModel
|
||||||
title="sample number of the referenced sample, eg. An31">
|
title="sample number of the referenced sample, eg. An31">
|
||||||
<ng-template rbFormValidationMessage="failure">Unknown sample number</ng-template>
|
<ng-template rbFormValidationMessage="failure">Unknown sample number</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</div>
|
</div>
|
||||||
<rb-form-input [name]="'sr-relation' + i" label="relation" appValidate="string" [required]="reference[0] !== ''"
|
<rb-form-input [name]="'sr-relation' + i" label="relation" appValidate="string" [required]="reference[0] !== ''"
|
||||||
[(ngModel)]="reference[1]" title="description how the samples are connected, eg. belongs to">
|
[(ngModel)]="reference[1]" title="description how the samples are connected, eg. belongs to">
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</div>
|
</div>
|
||||||
<h5>Additional properties</h5>
|
<h5>Additional properties</h5>
|
||||||
<rb-array-input [(ngModel)]="customFields" name="customFields" [pushTemplate]="['', '']" pushPath="0"
|
<rb-array-input [(ngModel)]="customFields" name="customFields" [pushTemplate]="['', '']" pushPath="0"
|
||||||
class="two-col" [@inOut]>
|
class="two-col" [@inOut]>
|
||||||
<ng-container *rbArrayInputItem="let item">
|
<ng-container *rbArrayInputItem="let item">
|
||||||
<div>
|
<div>
|
||||||
<rb-form-input [name]="'cf-key' + item.i" label="key" [rbArrayInputListener]="'cf-key'" [index]="item.i"
|
<rb-form-input [name]="'cf-key' + item.i" label="key" [rbArrayInputListener]="'cf-key'" [index]="item.i"
|
||||||
[rbFormInputAutocomplete]="autocomplete.bind(this, availableCustomFields)"
|
[rbFormInputAutocomplete]="autocomplete.bind(this, availableCustomFields)"
|
||||||
[rbDebounceTime]="0"
|
[rbDebounceTime]="0"
|
||||||
[rbInitialOpen]="true" appValidate="unique" [appValidateArgs]="[uniqueCfValues(item.i)]"
|
[rbInitialOpen]="true" appValidate="unique" [appValidateArgs]="[uniqueCfValues(item.i)]"
|
||||||
[(ngModel)]="item.value[0]" #keyInput="ngModel" title="name of additional property, eg. vwz">
|
[(ngModel)]="item.value[0]" #keyInput="ngModel" title="name of additional property, eg. vwz">
|
||||||
<ng-template rbFormValidationMessage="failure">{{keyInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{keyInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</div>
|
</div>
|
||||||
<rb-form-input [name]="'cf-value' + item.i" label="value" appValidate="string"
|
<rb-form-input [name]="'cf-value' + item.i" label="value" appValidate="string"
|
||||||
[required]="item.value[0] !== ''"
|
[required]="item.value[0] !== ''"
|
||||||
[(ngModel)]="item.value[1]" title="value of additional property, eg. 0 min">
|
[(ngModel)]="item.value[1]" title="value of additional property, eg. 0 min">
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</rb-array-input>
|
</rb-array-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="samples.length; else generateSamples" class="space-below">
|
<div *ngIf="samples.length; else generateSamples" class="space-below">
|
||||||
<rb-icon-button icon="save" mode="primary" type="submit" (click)="saveSample()"
|
<rb-icon-button icon="save" mode="primary" type="submit" (click)="saveSample()"
|
||||||
[disabled]="!sampleForm.form.valid">
|
[disabled]="!sampleForm.form.valid">
|
||||||
Save sample
|
Save sample
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
<rb-icon-button class="delete-sample" icon="delete" mode="danger" *ngIf="samples.length > 1"
|
<rb-icon-button class="delete-sample" icon="delete" mode="danger" *ngIf="samples.length > 1"
|
||||||
(click)="deleteConfirm(modalDeleteConfirm)">
|
(click)="deleteConfirm(modalDeleteConfirm)">
|
||||||
Delete samples
|
Delete samples
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
<ng-template #generateSamples>
|
<ng-template #generateSamples>
|
||||||
<rb-form-input type="number" name="sample-count" label="number of samples" pattern="^\d+?$" required
|
<rb-form-input type="number" name="sample-count" label="number of samples" pattern="^\d+?$" required
|
||||||
rbNumberConverter rbMin="1" [(ngModel)]="sampleCount" class="sample-count"
|
rbNumberConverter rbMin="1" [(ngModel)]="sampleCount" class="sample-count"
|
||||||
title="number of samples to create with this base information">
|
title="number of samples to create with this base information">
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
<ng-template rbFormValidationMessage="rbMin">Must be at least 1</ng-template>
|
<ng-template rbFormValidationMessage="rbMin">Must be at least 1</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<button class="rb-btn rb-primary space-below" type="submit" (click)="saveSample()"
|
<button class="rb-btn rb-primary space-below" type="submit" (click)="saveSample()"
|
||||||
[disabled]="!sampleForm.form.valid">
|
[disabled]="!sampleForm.form.valid">
|
||||||
Generate sample{{sampleCount > 1 ? 's' : ''}}
|
Generate sample{{sampleCount > 1 ? 's' : ''}}
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!--BASE SUMMARY-->
|
<!--BASE SUMMARY-->
|
||||||
|
|
||||||
<div *ngIf="view.baseSum">
|
<div *ngIf="view.baseSum">
|
||||||
<h3 *ngIf="mode === 'new'">Successfully added samples:</h3>
|
<h3 *ngIf="mode === 'new'">Successfully added samples:</h3>
|
||||||
<span class="rb-ic rb-ic-edit clickable" (click)="checkFormAfterInit = view.base = true; view.baseSum = false">
|
<span class="rb-ic rb-ic-edit clickable" (click)="checkFormAfterInit = view.base = true; view.baseSum = false">
|
||||||
</span>
|
</span>
|
||||||
<rb-table id="response-data">
|
<rb-table id="response-data">
|
||||||
<tr><td>Sample Number</td><td>{{baseSample.number}}</td></tr>
|
<tr><td>Sample Number</td><td>{{baseSample.number}}</td></tr>
|
||||||
<tr><td>Material</td><td>{{material.name}}</td></tr>
|
<tr><td>Material</td><td>{{material.name}}</td></tr>
|
||||||
<tr><td>Type</td><td>{{baseSample.type}}</td></tr>
|
<tr><td>Type</td><td>{{baseSample.type}}</td></tr>
|
||||||
<tr><td>Color</td><td>{{baseSample.color}}</td></tr>
|
<tr><td>Color</td><td>{{baseSample.color}}</td></tr>
|
||||||
<tr><td>Batch</td><td>{{baseSample.batch}}</td></tr>
|
<tr><td>Batch</td><td>{{baseSample.batch}}</td></tr>
|
||||||
<tr><td>Comment</td><td>{{baseSample.notes | exists:'comment'}}</td></tr>
|
<tr><td>Comment</td><td>{{baseSample.notes | exists:'comment'}}</td></tr>
|
||||||
<tr *ngFor="let reference of sampleReferences.slice(0, -1)">
|
<tr *ngFor="let reference of sampleReferences.slice(0, -1)">
|
||||||
<td>Sample reference</td><td>{{reference[0]}} - {{reference[1]}}</td>
|
<td>Sample reference</td><td>{{reference[0]}} - {{reference[1]}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let field of customFields"><td>{{field[0]}}</td><td>{{field[1]}}</td></tr>
|
<tr *ngFor="let field of customFields"><td>{{field[0]}}</td><td>{{field[1]}}</td></tr>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--CM-->
|
<!--CM-->
|
||||||
|
|
||||||
<div *ngIf="view.cm">
|
<div *ngIf="view.cm">
|
||||||
<form #cmForm="ngForm" [ngClass]="{'cm-form': samples.length > 1}">
|
<form #cmForm="ngForm" [ngClass]="{'cm-form': samples.length > 1}">
|
||||||
<rb-tab-panel (tabChanged)="cmSampleIndex = $event" class="space-below" *ngIf="samples.length > 1">
|
<rb-tab-panel (tabChanged)="cmSampleIndex = $event" class="space-below" *ngIf="samples.length > 1">
|
||||||
<ng-container *ngFor="let sample of samples; index as i">
|
<ng-container *ngFor="let sample of samples; index as i">
|
||||||
<div *rbTabPanelItem="sample.number; id: i"></div>
|
<div *rbTabPanelItem="sample.number; id: i"></div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</rb-tab-panel>
|
</rb-tab-panel>
|
||||||
<div *ngFor="let sample of samples; index as gIndex"
|
<div *ngFor="let sample of samples; index as gIndex"
|
||||||
[ngStyle]="{display: gIndex == cmSampleIndex || gIndex == cmSampleIndex + 1 ? 'initial' : 'none'}">
|
[ngStyle]="{display: gIndex == cmSampleIndex || gIndex == cmSampleIndex + 1 ? 'initial' : 'none'}">
|
||||||
<h4 *ngIf="samples.length > 1">{{sample.number}}</h4>
|
<h4 *ngIf="samples.length > 1">{{sample.number}}</h4>
|
||||||
<div class="conditions shaded-container space-below">
|
<div class="conditions shaded-container space-below">
|
||||||
<h5>
|
<h5>
|
||||||
Condition
|
Condition
|
||||||
<button class="rb-btn rb-secondary condition-set" type="button" (click)="toggleCondition(sample)">
|
<button class="rb-btn rb-secondary condition-set" type="button" (click)="toggleCondition(sample)">
|
||||||
{{sample.condition.condition_template ? 'Do not set condition' : 'Set condition'}}
|
{{sample.condition.condition_template ? 'Do not set condition' : 'Set condition'}}
|
||||||
</button>
|
</button>
|
||||||
</h5>
|
</h5>
|
||||||
<div *ngIf="sample.condition.condition_template" [@inOut]>
|
<div *ngIf="sample.condition.condition_template" [@inOut]>
|
||||||
<rb-form-select [name]="'conditionSelect-' + gIndex" label="Condition"
|
<rb-form-select [name]="'conditionSelect-' + gIndex" label="Condition"
|
||||||
[(ngModel)]="sample.condition.condition_template"
|
[(ngModel)]="sample.condition.condition_template"
|
||||||
(ngModelChange)="reValidate()">
|
(ngModelChange)="reValidate()">
|
||||||
<option [value]="sample.condition.condition_template">
|
<option [value]="sample.condition.condition_template">
|
||||||
{{d.id.conditionTemplates[sample.condition.condition_template].name}} - current
|
{{d.id.conditionTemplates[sample.condition.condition_template].name}} - current
|
||||||
</option>
|
</option>
|
||||||
<option *ngFor="let c of d.latest.conditionTemplates" [value]="c._id">{{c.name}}</option>
|
<option *ngFor="let c of d.latest.conditionTemplates" [value]="c._id">{{c.name}}</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
|
|
||||||
<ng-container *ngFor="let parameter of
|
<ng-container *ngFor="let parameter of
|
||||||
d.id.conditionTemplates[sample.condition.condition_template].parameters; index as i"
|
d.id.conditionTemplates[sample.condition.condition_template].parameters; index as i"
|
||||||
[ngSwitch]="(parameter.range.values ? 1 : 0)">
|
[ngSwitch]="(parameter.range.values ? 1 : 0)">
|
||||||
<rb-form-select *ngSwitchCase="1"
|
<rb-form-select *ngSwitchCase="1"
|
||||||
[name]="'conditionParameter-' + gIndex + '-' + i"
|
[name]="'conditionParameter-' + gIndex + '-' + i"
|
||||||
[label]="parameter.name" [(ngModel)]="sample.condition[parameter.name]" ngModel>
|
[label]="parameter.name" [(ngModel)]="sample.condition[parameter.name]" ngModel>
|
||||||
<option *ngFor="let value of parameter.range.values" [value]="value">{{value}}</option>
|
<option *ngFor="let value of parameter.range.values" [value]="value">{{value}}</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 *ngSwitchDefault (focus)="checkFormAfterInit = true"
|
<rb-form-input *ngSwitchDefault (focus)="checkFormAfterInit = true"
|
||||||
[name]="'conditionParameter-' + gIndex + '-' + i"
|
[name]="'conditionParameter-' + gIndex + '-' + i"
|
||||||
[label]="parameter.name" appValidate="string" required
|
[label]="parameter.name" appValidate="string" required
|
||||||
[(ngModel)]="sample.condition[parameter.name]" #parameterInput="ngModel">
|
[(ngModel)]="sample.condition[parameter.name]" #parameterInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="measurements shaded-container space-below">
|
<div class="measurements shaded-container space-below">
|
||||||
<h5>
|
<h5>
|
||||||
Measurements
|
Measurements
|
||||||
<rb-icon-button icon="undo" mode="secondary" *ngIf="measurementRestoreData.length"
|
<rb-icon-button icon="undo" mode="secondary" *ngIf="measurementRestoreData.length"
|
||||||
class="restore-measurements" (click)="restoreMeasurements()">
|
class="restore-measurements" (click)="restoreMeasurements()">
|
||||||
Restore measurements
|
Restore measurements
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</h5>
|
</h5>
|
||||||
<div *ngFor="let measurement of sample.measurements; index as mIndex" [@inOut] class="space-below">
|
<div *ngFor="let measurement of sample.measurements; index as mIndex" [@inOut] class="space-below">
|
||||||
<rb-form-select [name]="'measurementTemplateSelect-' + gIndex + '-' + mIndex" label="Template"
|
<rb-form-select [name]="'measurementTemplateSelect-' + gIndex + '-' + mIndex" label="Template"
|
||||||
[(ngModel)]="measurement.measurement_template"
|
[(ngModel)]="measurement.measurement_template"
|
||||||
(ngModelChange)="clearMeasurement(gIndex, mIndex)">
|
(ngModelChange)="clearMeasurement(gIndex, mIndex)">
|
||||||
<option [value]="sample.condition.condition_template">
|
<option [value]="sample.condition.condition_template">
|
||||||
{{d.id.measurementTemplates[measurement.measurement_template].name}} - old
|
{{d.id.measurementTemplates[measurement.measurement_template].name}} - old
|
||||||
</option>
|
</option>
|
||||||
<option *ngFor="let m of d.latest.measurementTemplates" [value]="m._id">{{m.name}}</option>
|
<option *ngFor="let m of d.latest.measurementTemplates" [value]="m._id">{{m.name}}</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
<div *ngFor="let parameter of d.id.measurementTemplates[measurement.measurement_template].parameters;
|
<div *ngFor="let parameter of d.id.measurementTemplates[measurement.measurement_template].parameters;
|
||||||
index as pIndex">
|
index as pIndex">
|
||||||
<ng-container [ngSwitch]="(parameter.range.type ? 1 : 0) + (parameter.range.values ? 2 : 0)">
|
<ng-container [ngSwitch]="(parameter.range.type ? 1 : 0) + (parameter.range.values ? 2 : 0)">
|
||||||
<rb-form-file *ngSwitchCase="1"
|
<rb-form-file *ngSwitchCase="1"
|
||||||
[name]="'measurementParameter-' + gIndex + '-' + mIndex + '-' + pIndex"
|
[name]="'measurementParameter-' + gIndex + '-' + mIndex + '-' + pIndex"
|
||||||
[label]="parameter.name" maxSize="10000000" multiple
|
[label]="parameter.name" maxSize="10000000" multiple
|
||||||
[required]="measurement.values[parameter.name] &&
|
[required]="measurement.values[parameter.name] &&
|
||||||
!measurement.values[parameter.name].length"
|
!measurement.values[parameter.name].length"
|
||||||
(ngModelChange)="fileToArray($event, gIndex, mIndex, parameter.name)"
|
(ngModelChange)="fileToArray($event, gIndex, mIndex, parameter.name)"
|
||||||
placeholder="Select file or drag and drop" dragDrop ngModel>
|
placeholder="Select file or drag and drop" dragDrop ngModel>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-file>
|
</rb-form-file>
|
||||||
<rb-form-select *ngSwitchCase="2"
|
<rb-form-select *ngSwitchCase="2"
|
||||||
[name]="'measurementParameter-' + gIndex + '-' + mIndex + '-' + pIndex"
|
[name]="'measurementParameter-' + gIndex + '-' + mIndex + '-' + pIndex"
|
||||||
[label]="parameter.name" [(ngModel)]="measurement.values[parameter.name]" ngModel>
|
[label]="parameter.name" [(ngModel)]="measurement.values[parameter.name]" ngModel>
|
||||||
<option *ngFor="let device of d.d.user.devices" [value]="device">{{device}}</option>
|
<option *ngFor="let device of d.d.user.devices" [value]="device">{{device}}</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 *ngSwitchDefault
|
<rb-form-input *ngSwitchDefault
|
||||||
[name]="'measurementParameter-' + gIndex + '-' + mIndex + '-' + pIndex"
|
[name]="'measurementParameter-' + gIndex + '-' + mIndex + '-' + pIndex"
|
||||||
[label]="parameter.name" appValidate="string"
|
[label]="parameter.name" appValidate="string"
|
||||||
[(ngModel)]="measurement.values[parameter.name]" #parameterInput="ngModel">
|
[(ngModel)]="measurement.values[parameter.name]" #parameterInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{parameterInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<canvas baseChart *ngIf="parameter.range.type && charts[gIndex][mIndex][0].data.length > 0"
|
<canvas baseChart *ngIf="parameter.range.type && charts[gIndex][mIndex][0].data.length > 0"
|
||||||
class="dpt-chart"
|
class="dpt-chart"
|
||||||
[@inOut]
|
[@inOut]
|
||||||
[datasets]="charts[gIndex][mIndex]"
|
[datasets]="charts[gIndex][mIndex]"
|
||||||
[labels]="[]"
|
[labels]="[]"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="false"
|
[legend]="false"
|
||||||
chartType="scatter">
|
chartType="scatter">
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
<rb-icon-button icon="delete" mode="danger" (click)="removeMeasurement(gIndex, mIndex)">
|
<rb-icon-button icon="delete" mode="danger" (click)="removeMeasurement(gIndex, mIndex)">
|
||||||
Delete measurement
|
Delete measurement
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<rb-icon-button icon="add" mode="secondary" (click)="addMeasurement(gIndex)">
|
<rb-icon-button icon="add" mode="secondary" (click)="addMeasurement(gIndex)">
|
||||||
New measurement
|
New measurement
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<rb-icon-button icon="summary" mode="primary" type="submit" (click)="view.cm = false; view.cmSum = true"
|
<rb-icon-button icon="summary" mode="primary" type="submit" (click)="view.cm = false; view.cmSum = true"
|
||||||
[disabled]="!cmForm.form.valid">
|
[disabled]="!cmForm.form.valid">
|
||||||
Summary
|
Summary
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
<rb-icon-button class="delete-sample" icon="delete" mode="danger" *ngIf="samples.length === 1"
|
<rb-icon-button class="delete-sample" icon="delete" mode="danger" *ngIf="samples.length === 1"
|
||||||
(click)="deleteConfirm(modalDeleteConfirm)">
|
(click)="deleteConfirm(modalDeleteConfirm)">
|
||||||
Delete sample
|
Delete sample
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--CM SUMMARY-->
|
<!--CM SUMMARY-->
|
||||||
|
|
||||||
<div *ngIf="view.cmSum">
|
<div *ngIf="view.cmSum">
|
||||||
<span class="rb-ic rb-ic-edit clickable" (click)="checkFormAfterInit = view.cm = true; view.cmSum = false">
|
<span class="rb-ic rb-ic-edit clickable" (click)="checkFormAfterInit = view.cm = true; view.cmSum = false">
|
||||||
</span>
|
</span>
|
||||||
<rb-table>
|
<rb-table>
|
||||||
<ng-container *ngFor="let sample of samples; index as gIndex">
|
<ng-container *ngFor="let sample of samples; index as gIndex">
|
||||||
<tr><th>{{sample.number}}</th><th></th></tr>
|
<tr><th>{{sample.number}}</th><th></th></tr>
|
||||||
<tr *ngFor="let parameter of (d.id.conditionTemplates[sample.condition.condition_template] || {parameters: []})
|
<tr *ngFor="let parameter of (d.id.conditionTemplates[sample.condition.condition_template] || {parameters: []})
|
||||||
.parameters">
|
.parameters">
|
||||||
<td>{{parameter.name}}</td><td>{{sample.condition[parameter.name]}}</td>
|
<td>{{parameter.name}}</td><td>{{sample.condition[parameter.name]}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<ng-container *ngFor="let measurement of sample.measurements">
|
<ng-container *ngFor="let measurement of sample.measurements">
|
||||||
<tr *ngFor="let parameter of d.id.measurementTemplates[measurement.measurement_template].parameters">
|
<tr *ngFor="let parameter of d.id.measurementTemplates[measurement.measurement_template].parameters">
|
||||||
<td>{{parameter.name}}</td><td>{{measurement.values[parameter.name]}}</td>
|
<td>{{parameter.name}}</td><td>{{measurement.values[parameter.name]}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
<rb-icon-button icon="save" mode="primary" type="submit" (click)="cmSave()">
|
<rb-icon-button icon="save" mode="primary" type="submit" (click)="cmSave()">
|
||||||
Save sample{{samples.length > 1 ? 's' : ''}}
|
Save sample{{samples.length > 1 ? 's' : ''}}
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #modalDeleteConfirm>
|
<ng-template #modalDeleteConfirm>
|
||||||
<rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete sample' + (samples.length > 1 ? 's' : '')"
|
<rb-alert alertTitle="Are you sure?" type="danger" [okBtnLabel]="'Delete sample' + (samples.length > 1 ? 's' : '')"
|
||||||
cancelBtnLabel="Cancel">
|
cancelBtnLabel="Cancel">
|
||||||
Do you really want to delete {{(samples.length > 1 ? 'samples ' : 'sample ') + sampleNames()}}?
|
Do you really want to delete {{(samples.length > 1 ? 'samples ' : 'sample ') + sampleNames()}}?
|
||||||
</rb-alert>
|
</rb-alert>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
::ng-deep rb-table#response-data > table {
|
::ng-deep rb-table#response-data > table {
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
td:first-child {
|
td:first-child {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.condition-set {
|
.condition-set {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.two-col {
|
.two-col {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
grid-column-gap: 10px;
|
grid-column-gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dpt-chart {
|
.dpt-chart {
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sample-count {
|
.sample-count {
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.set-new-material {
|
.set-new-material {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete-sample {
|
.delete-sample {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-form {
|
.cm-form {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
grid-column-gap: 1rem;
|
grid-column-gap: 1rem;
|
||||||
|
|
||||||
rb-tab-panel, div.buttons {
|
rb-tab-panel, div.buttons {
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.restore-measurements {
|
.restore-measurements {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,292 +1,292 @@
|
|||||||
<div class="header-addnew">
|
<div class="header-addnew">
|
||||||
<a routerLink="/samples/new" *ngIf="login.isLevel.write">
|
<a routerLink="/samples/new" *ngIf="login.isLevel.write">
|
||||||
<rb-icon-button icon="add" mode="primary" class="space-left">New sample</rb-icon-button>
|
<rb-icon-button icon="add" mode="primary" class="space-left">New sample</rb-icon-button>
|
||||||
</a>
|
</a>
|
||||||
<rb-icon-button *ngIf="sampleSelect === 2" mode="secondary" icon="close" (click)="sampleSelect = 0"
|
<rb-icon-button *ngIf="sampleSelect === 2" mode="secondary" icon="close" (click)="sampleSelect = 0"
|
||||||
class="validation-close" iconOnly></rb-icon-button>
|
class="validation-close" iconOnly></rb-icon-button>
|
||||||
<rb-icon-button *ngIf="login.isLevel.dev" [icon]="sampleSelect === 2 ? 'checkmark' : 'clear-all'" mode="secondary"
|
<rb-icon-button *ngIf="login.isLevel.dev" [icon]="sampleSelect === 2 ? 'checkmark' : 'clear-all'" mode="secondary"
|
||||||
(click)="validate()">
|
(click)="validate()">
|
||||||
{{sampleSelect === 2 ? 'Validate' : 'Validation'}}
|
{{sampleSelect === 2 ? 'Validate' : 'Validation'}}
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--FILTERS-->
|
<!--FILTERS-->
|
||||||
|
|
||||||
<rb-accordion>
|
<rb-accordion>
|
||||||
<rb-accordion-title [open]="false"><span class="rb-ic rb-ic-filter"></span> Filter</rb-accordion-title>
|
<rb-accordion-title [open]="false"><span class="rb-ic rb-ic-filter"></span> Filter</rb-accordion-title>
|
||||||
<rb-accordion-body>
|
<rb-accordion-body>
|
||||||
<form class="filters">
|
<form class="filters">
|
||||||
<div class="status-selection">
|
<div class="status-selection">
|
||||||
<label class="label">Status</label>
|
<label class="label">Status</label>
|
||||||
<rb-form-checkbox name="status-validated" [(ngModel)]="filters.status.validated"
|
<rb-form-checkbox name="status-validated" [(ngModel)]="filters.status.validated"
|
||||||
[disabled]="!filters.status.new && !filters.status.deleted" (ngModelChange)="loadSamples({firstPage: true})">
|
[disabled]="!filters.status.new && !filters.status.deleted" (ngModelChange)="loadSamples({firstPage: true})">
|
||||||
validated
|
validated
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
<rb-form-checkbox name="status-new" [(ngModel)]="filters.status.new"
|
<rb-form-checkbox name="status-new" [(ngModel)]="filters.status.new"
|
||||||
[disabled]="!filters.status.validated && !filters.status.deleted"
|
[disabled]="!filters.status.validated && !filters.status.deleted"
|
||||||
(ngModelChange)="loadSamples({firstPage: true})">
|
(ngModelChange)="loadSamples({firstPage: true})">
|
||||||
new
|
new
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
<rb-form-checkbox name="status-deleted" [(ngModel)]="filters.status.deleted"
|
<rb-form-checkbox name="status-deleted" [(ngModel)]="filters.status.deleted"
|
||||||
[disabled]="!filters.status.validated && !filters.status.new" (ngModelChange)="loadSamples({firstPage: true})"
|
[disabled]="!filters.status.validated && !filters.status.new" (ngModelChange)="loadSamples({firstPage: true})"
|
||||||
*ngIf="login.isLevel.dev">
|
*ngIf="login.isLevel.dev">
|
||||||
deleted
|
deleted
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<rb-form-select name="pageSizeSelection" label="page size" [(ngModel)]="filters.pageSize" class="selection"
|
<rb-form-select name="pageSizeSelection" label="page size" [(ngModel)]="filters.pageSize" class="selection"
|
||||||
(ngModelChange)="loadSamples({firstPage: true})" #pageSizeSelection>
|
(ngModelChange)="loadSamples({firstPage: true})" #pageSizeSelection>
|
||||||
<option value="3">3</option>
|
<option value="3">3</option>
|
||||||
<option value="10">10</option>
|
<option value="10">10</option>
|
||||||
<option value="25">25</option>
|
<option value="25">25</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
<option value="100">100</option>
|
<option value="100">100</option>
|
||||||
<option value="250">250</option>
|
<option value="250">250</option>
|
||||||
<option value="500">500</option>
|
<option value="500">500</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
|
|
||||||
<div id="multi">
|
<div id="multi">
|
||||||
<rb-form-multi-select *ngFor="let item of categories | keyvalue: originalOrder" name="fieldSelect" idField="id"
|
<rb-form-multi-select *ngFor="let item of categories | keyvalue: originalOrder" name="fieldSelect" idField="id"
|
||||||
[items]="item.value" [(ngModel)]="isActiveKey" [label]="item.key" class="selection"
|
[items]="item.value" [(ngModel)]="isActiveKey" [label]="item.key" class="selection"
|
||||||
(ngModelChange)="loadSamples({}, $event, item.key)">
|
(ngModelChange)="loadSamples({}, $event, item.key)">
|
||||||
<span *rbFormMultiSelectOption="let item" class="load-first-page">{{item.label}}</span>
|
<span *rbFormMultiSelectOption="let item" class="load-first-page">{{item.label}}</span>
|
||||||
</rb-form-multi-select>
|
</rb-form-multi-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<rb-icon-button icon="reset" mode="secondary" (click)="resetPreferences()" class="reset-preferences">
|
<rb-icon-button icon="reset" mode="secondary" (click)="resetPreferences()" class="reset-preferences">
|
||||||
Reset to default
|
Reset to default
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
|
|
||||||
<div class="fieldfilters">
|
<div class="fieldfilters">
|
||||||
<div *ngFor="let filter of filters.filters">
|
<div *ngFor="let filter of filters.filters">
|
||||||
<ng-container *ngIf="isActiveKey[filter.field]">
|
<ng-container *ngIf="isActiveKey[filter.field]">
|
||||||
<rb-form-checkbox [name]="'filteractive-' + filter.field" [(ngModel)]="filter.active"></rb-form-checkbox>
|
<rb-form-checkbox [name]="'filteractive-' + filter.field" [(ngModel)]="filter.active"></rb-form-checkbox>
|
||||||
<rb-form-select [name]="'filtermode-' + filter.field" class="filtermode" [(ngModel)]="filter.mode"
|
<rb-form-select [name]="'filtermode-' + filter.field" class="filtermode" [(ngModel)]="filter.mode"
|
||||||
(ngModelChange)="updateFilterFields(filter.field)">
|
(ngModelChange)="updateFilterFields(filter.field)">
|
||||||
<option value="eq" title="field is equal to value">=</option>
|
<option value="eq" title="field is equal to value">=</option>
|
||||||
<option value="ne" title="field is not equal to value">≠</option>
|
<option value="ne" title="field is not equal to value">≠</option>
|
||||||
<option value="lt" title="field is lower than value"><</option>
|
<option value="lt" title="field is lower than value"><</option>
|
||||||
<option value="lte" title="field is lower than or equal to value">≤</option>
|
<option value="lte" title="field is lower than or equal to value">≤</option>
|
||||||
<option value="gt" title="field is greater than value">></option>
|
<option value="gt" title="field is greater than value">></option>
|
||||||
<option value="gte" title="field is greater than or equal to value">≥</option>
|
<option value="gte" title="field is greater than or equal to value">≥</option>
|
||||||
<option value="stringin" title="field contains value">⊇</option>
|
<option value="stringin" title="field contains value">⊇</option>
|
||||||
<option value="in" title="field is one of the values">∈</option>
|
<option value="in" title="field is one of the values">∈</option>
|
||||||
<option value="nin" title="field is not one of the values">∉</option>
|
<option value="nin" title="field is not one of the values">∉</option>
|
||||||
<option value="null" title="field is null">∅</option>
|
<option value="null" title="field is null">∅</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
<div class="filter-inputs">
|
<div class="filter-inputs">
|
||||||
<rb-array-input [(ngModel)]="filter.values" [name]="'filter-' + filter.field"
|
<rb-array-input [(ngModel)]="filter.values" [name]="'filter-' + filter.field"
|
||||||
[pushTemplate]="!(filter.mode === 'in' || filter.mode === 'nin') ? null :''"
|
[pushTemplate]="!(filter.mode === 'in' || filter.mode === 'nin') ? null :''"
|
||||||
(ngModelChange)="updateFilterFields(filter.field)">
|
(ngModelChange)="updateFilterFields(filter.field)">
|
||||||
<ng-container *rbArrayInputItem="let item" [ngSwitch]="(filter.autocomplete.length ? 'autocomplete' : '') +
|
<ng-container *rbArrayInputItem="let item" [ngSwitch]="(filter.autocomplete.length ? 'autocomplete' : '') +
|
||||||
(filter.field == 'added' ? 'date' : (filter.field == 'type' ? 'type' : ''))">
|
(filter.field == 'added' ? 'date' : (filter.field == 'type' ? 'type' : ''))">
|
||||||
<rb-form-date-input *ngSwitchCase="'date'" [rbArrayInputListener]="'filter-' + filter.field"
|
<rb-form-date-input *ngSwitchCase="'date'" [rbArrayInputListener]="'filter-' + filter.field"
|
||||||
[name]="'filter-' + filter.field + item.i" [index]="item.i" [label]="filter.label"
|
[name]="'filter-' + filter.field + item.i" [index]="item.i" [label]="filter.label"
|
||||||
[(ngModel)]="item.value" [disabled]="filter.mode === 'null'"></rb-form-date-input>
|
[(ngModel)]="item.value" [disabled]="filter.mode === 'null'"></rb-form-date-input>
|
||||||
<rb-form-input *ngSwitchCase="''" [rbArrayInputListener]="'filter-' + filter.field"
|
<rb-form-input *ngSwitchCase="''" [rbArrayInputListener]="'filter-' + filter.field"
|
||||||
[name]="'filter-' + filter.field + item.i" [index]="item.i" [label]="filter.label"
|
[name]="'filter-' + filter.field + item.i" [index]="item.i" [label]="filter.label"
|
||||||
[(ngModel)]="item.value" [disabled]="filter.mode === 'null'">
|
[(ngModel)]="item.value" [disabled]="filter.mode === 'null'">
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input *ngSwitchCase="'autocomplete'" [rbArrayInputListener]="'filter-' + filter.field"
|
<rb-form-input *ngSwitchCase="'autocomplete'" [rbArrayInputListener]="'filter-' + filter.field"
|
||||||
[name]="'filter-' + filter.field + item.i" [index]="item.i" [label]="filter.label"
|
[name]="'filter-' + filter.field + item.i" [index]="item.i" [label]="filter.label"
|
||||||
[(ngModel)]="item.value" [rbDebounceTime]="0" (keydown)="preventDefault($event, 'Enter')"
|
[(ngModel)]="item.value" [rbDebounceTime]="0" (keydown)="preventDefault($event, 'Enter')"
|
||||||
[rbFormInputAutocomplete]="autocomplete.bind(this, filter.autocomplete)"
|
[rbFormInputAutocomplete]="autocomplete.bind(this, filter.autocomplete)"
|
||||||
[disabled]="filter.mode === 'null'" ngModel></rb-form-input>
|
[disabled]="filter.mode === 'null'" ngModel></rb-form-input>
|
||||||
<rb-form-select *ngSwitchCase="'type'" [rbArrayInputListener]="'filter-' + filter.field"
|
<rb-form-select *ngSwitchCase="'type'" [rbArrayInputListener]="'filter-' + filter.field"
|
||||||
[name]="'filter-' + filter.field + item.i" [index]="item.i" [label]="filter.label"
|
[name]="'filter-' + filter.field + item.i" [index]="item.i" [label]="filter.label"
|
||||||
[(ngModel)]="item.value">
|
[(ngModel)]="item.value">
|
||||||
<option value="as-delivered/raw">as-delivered/raw</option>
|
<option value="as-delivered/raw">as-delivered/raw</option>
|
||||||
<option value="processed">processed</option>
|
<option value="processed">processed</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</rb-array-input>
|
</rb-array-input>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<rb-form-checkbox name="no-condition" [(ngModel)]="filters.no.condition" class="space-right">
|
<rb-form-checkbox name="no-condition" [(ngModel)]="filters.no.condition" class="space-right">
|
||||||
has no condition
|
has no condition
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
<rb-form-checkbox name="no-measurements" [(ngModel)]="filters.no.measurements" class="space-right">
|
<rb-form-checkbox name="no-measurements" [(ngModel)]="filters.no.measurements" class="space-right">
|
||||||
has no measurements
|
has no measurements
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<rb-icon-button icon="forward-right" mode="primary" (click)="loadSamples({firstPage: true})">
|
<rb-icon-button icon="forward-right" mode="primary" (click)="loadSamples({firstPage: true})">
|
||||||
Apply filters
|
Apply filters
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</rb-accordion-body>
|
</rb-accordion-body>
|
||||||
</rb-accordion>
|
</rb-accordion>
|
||||||
|
|
||||||
<ng-container *ngTemplateOutlet="paging"></ng-container>
|
<ng-container *ngTemplateOutlet="paging"></ng-container>
|
||||||
|
|
||||||
<div class="samples-loading" *ngIf="loading">
|
<div class="samples-loading" *ngIf="loading">
|
||||||
<rb-loading-spinner></rb-loading-spinner>
|
<rb-loading-spinner></rb-loading-spinner>
|
||||||
<div>Loading...</div>
|
<div>Loading...</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--DOWNLOAD BUTTONS-->
|
<!--DOWNLOAD BUTTONS-->
|
||||||
|
|
||||||
<div class="download space-below" *ngIf="login.isLevel.dev">
|
<div class="download space-below" *ngIf="login.isLevel.dev">
|
||||||
<rb-icon-button class="space-right" icon="download" mode="secondary" [rbModal]="linkModal">
|
<rb-icon-button class="space-right" icon="download" mode="secondary" [rbModal]="linkModal">
|
||||||
JSON download link
|
JSON download link
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
<ng-template #linkModal>
|
<ng-template #linkModal>
|
||||||
<div class="link-dialog">
|
<div class="link-dialog">
|
||||||
<label for="jsonUrl">URL for JSON download</label>
|
<label for="jsonUrl">URL for JSON download</label>
|
||||||
<textarea class="linkmodal" id="jsonUrl" #linkarea [value]="sampleUrl({export: true, host: true})"
|
<textarea class="linkmodal" id="jsonUrl" #linkarea [value]="sampleUrl({export: true, host: true})"
|
||||||
(keydown)="preventDefault($event)"></textarea>
|
(keydown)="preventDefault($event)"></textarea>
|
||||||
<rb-form-checkbox class="space-right" name="download-spectra" [(ngModel)]="downloadSpectra">
|
<rb-form-checkbox class="space-right" name="download-spectra" [(ngModel)]="downloadSpectra">
|
||||||
add spectra
|
add spectra
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
<rb-form-checkbox class="space-right" name="download-conditions" [(ngModel)]="downloadCondition">
|
<rb-form-checkbox class="space-right" name="download-conditions" [(ngModel)]="downloadCondition">
|
||||||
add conditions
|
add conditions
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
<rb-form-checkbox class="space-right" name="download-flatten" [(ngModel)]="downloadFlatten">
|
<rb-form-checkbox class="space-right" name="download-flatten" [(ngModel)]="downloadFlatten">
|
||||||
flatten object
|
flatten object
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
<rb-icon-button icon="clipboard" mode="secondary" (click)="clipboard()">Copy to clipboard</rb-icon-button>
|
<rb-icon-button icon="clipboard" mode="secondary" (click)="clipboard()">Copy to clipboard</rb-icon-button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<a [href]="csvUrl" download="samples.csv">
|
<a [href]="csvUrl" download="samples.csv">
|
||||||
<rb-icon-button icon="download" mode="secondary" (mousedown)="csvUrl = sampleUrl({csv: true, export: true})">
|
<rb-icon-button icon="download" mode="secondary" (mousedown)="csvUrl = sampleUrl({csv: true, export: true})">
|
||||||
Download result as CSV
|
Download result as CSV
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--TABLE-->
|
<!--TABLE-->
|
||||||
|
|
||||||
<rb-table class="samples-table" scrollTop ellipsis>
|
<rb-table class="samples-table" scrollTop ellipsis>
|
||||||
<tr>
|
<tr>
|
||||||
<th *ngIf="login.isLevel.write">
|
<th *ngIf="login.isLevel.write">
|
||||||
<span class="rb-ic rb-ic-edit clickable" *ngIf="login.isLevel.dev" (click)="batchEdit()"></span>
|
<span class="rb-ic rb-ic-edit clickable" *ngIf="login.isLevel.dev" (click)="batchEdit()"></span>
|
||||||
<span class="rb-ic rb-ic-close clickable" *ngIf="sampleSelect === 1" (click)="sampleSelect = 0"></span>
|
<span class="rb-ic rb-ic-close clickable" *ngIf="sampleSelect === 1" (click)="sampleSelect = 0"></span>
|
||||||
</th>
|
</th>
|
||||||
<th *ngIf="sampleSelect">
|
<th *ngIf="sampleSelect">
|
||||||
<rb-form-checkbox name="select-all" (change)="selectAll($event)">all</rb-form-checkbox>
|
<rb-form-checkbox name="select-all" (change)="selectAll($event)">all</rb-form-checkbox>
|
||||||
</th>
|
</th>
|
||||||
<th *ngFor="let key of activeKeys" [title]="key.label">
|
<th *ngFor="let key of activeKeys" [title]="key.label">
|
||||||
<div class="sort-header">
|
<div class="sort-header">
|
||||||
<span>{{key.label}}</span>
|
<span>{{key.label}}</span>
|
||||||
<div *ngIf="key.sortable">
|
<div *ngIf="key.sortable">
|
||||||
<span class="rb-ic rb-ic-up sort-arr-up" (click)="setSort(key.id + '-' + 'desc')">
|
<span class="rb-ic rb-ic-up sort-arr-up" (click)="setSort(key.id + '-' + 'desc')">
|
||||||
<span *ngIf="filters.sort === key.id + '-' + 'desc'"></span>
|
<span *ngIf="filters.sort === key.id + '-' + 'desc'"></span>
|
||||||
</span>
|
</span>
|
||||||
<span class="rb-ic rb-ic-down sort-arr-down" (click)="setSort(key.id + '-' + 'asc')">
|
<span class="rb-ic rb-ic-down sort-arr-down" (click)="setSort(key.id + '-' + 'asc')">
|
||||||
<span *ngIf="filters.sort === key.id + '-' + 'asc'"></span>
|
<span *ngIf="filters.sort === key.id + '-' + 'asc'"></span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr *ngFor="let sample of samples; index as i" class="clickable" (click)="sampleDetails(sample._id, sampleModal)">
|
<tr *ngFor="let sample of samples; index as i" class="clickable" (click)="sampleDetails(sample._id, sampleModal)">
|
||||||
<td *ngIf="login.isLevel.write">
|
<td *ngIf="login.isLevel.write">
|
||||||
<a [routerLink]="'/samples/edit/' + sample._id" *ngIf="sample.status !== 'deleted' &&
|
<a [routerLink]="'/samples/edit/' + sample._id" *ngIf="sample.status !== 'deleted' &&
|
||||||
(login.isLevel.dev || (login.isLevel.write && sample.user_id === login.userId))">
|
(login.isLevel.dev || (login.isLevel.write && sample.user_id === login.userId))">
|
||||||
<span class="rb-ic rb-ic-edit clickable"></span>
|
<span class="rb-ic rb-ic-edit clickable"></span>
|
||||||
</a>
|
</a>
|
||||||
<span class="rb-ic rb-ic-undo clickable" *ngIf="sample.status === 'deleted' && login.isLevel.dev"
|
<span class="rb-ic rb-ic-undo clickable" *ngIf="sample.status === 'deleted' && login.isLevel.dev"
|
||||||
(click)="restoreSample(sample._id, restoreConfirm, $event)"></span>
|
(click)="restoreSample(sample._id, restoreConfirm, $event)"></span>
|
||||||
</td>
|
</td>
|
||||||
<td *ngIf="sampleSelect">
|
<td *ngIf="sampleSelect">
|
||||||
<rb-form-checkbox *ngIf="sample.status !== 'deleted'" [name]="'validate-' + i" (click)="stopPropagation($event)"
|
<rb-form-checkbox *ngIf="sample.status !== 'deleted'" [name]="'validate-' + i" (click)="stopPropagation($event)"
|
||||||
[(ngModel)]="sample.selected">
|
[(ngModel)]="sample.selected">
|
||||||
</rb-form-checkbox>
|
</rb-form-checkbox>
|
||||||
</td>
|
</td>
|
||||||
<td *ngFor="let item of activeKeys; index as j">
|
<td *ngFor="let item of activeKeys; index as j">
|
||||||
{{data[i].data[j]}}
|
{{data[i].data[j]}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
|
|
||||||
<ng-container *ngTemplateOutlet="paging"></ng-container>
|
<ng-container *ngTemplateOutlet="paging"></ng-container>
|
||||||
|
|
||||||
<ng-template #paging>
|
<ng-template #paging>
|
||||||
<div class="paging">
|
<div class="paging">
|
||||||
<button class="rb-btn rb-link" type="button" (click)="loadPage(-1)" [disabled]="page === 1">
|
<button class="rb-btn rb-link" type="button" (click)="loadPage(-1)" [disabled]="page === 1">
|
||||||
<span class="rb-ic rb-ic-back-left"></span>
|
<span class="rb-ic rb-ic-back-left"></span>
|
||||||
</button>
|
</button>
|
||||||
<rb-form-input label="page" (ngModelChange)="loadPage($event - page)" [ngModel]="page">
|
<rb-form-input label="page" (ngModelChange)="loadPage($event - page)" [ngModel]="page">
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<span>
|
<span>
|
||||||
of {{pages}} ({{totalSamples}} samples)
|
of {{pages}} ({{totalSamples}} samples)
|
||||||
</span>
|
</span>
|
||||||
<button class="rb-btn rb-link" type="button" (click)="loadPage(1)" [disabled]="page >= pages">
|
<button class="rb-btn rb-link" type="button" (click)="loadPage(1)" [disabled]="page >= pages">
|
||||||
<span class="rb-ic rb-ic-forward-right"></span>
|
<span class="rb-ic rb-ic-forward-right"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<!--SAMPLE DETAILS-->
|
<!--SAMPLE DETAILS-->
|
||||||
|
|
||||||
<ng-template #sampleModal>
|
<ng-template #sampleModal>
|
||||||
<rb-loading-spinner *ngIf="sampleDetailsSample === null; else sampleDetailsTemplate"></rb-loading-spinner>
|
<rb-loading-spinner *ngIf="sampleDetailsSample === null; else sampleDetailsTemplate"></rb-loading-spinner>
|
||||||
<ng-template #sampleDetailsTemplate>
|
<ng-template #sampleDetailsTemplate>
|
||||||
<h3>{{sampleDetailsSample.number}}</h3>
|
<h3>{{sampleDetailsSample.number}}</h3>
|
||||||
<rb-table class="sample-details-table">
|
<rb-table class="sample-details-table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Material</th>
|
<th>Material</th>
|
||||||
<td>{{sampleDetailsSample.material.name}}</td>
|
<td>{{sampleDetailsSample.material.name}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Supplier</th>
|
<th>Supplier</th>
|
||||||
<td>{{sampleDetailsSample.material.supplier}}</td>
|
<td>{{sampleDetailsSample.material.supplier}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Group</th>
|
<th>Group</th>
|
||||||
<td>{{sampleDetailsSample.material.group}}</td>
|
<td>{{sampleDetailsSample.material.group}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<td>{{sampleDetailsSample.type}}</td>
|
<td>{{sampleDetailsSample.type}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>color</th>
|
<th>color</th>
|
||||||
<td>{{sampleDetailsSample.color}}</td>
|
<td>{{sampleDetailsSample.color}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Batch</th>
|
<th>Batch</th>
|
||||||
<td>{{sampleDetailsSample.batch}}</td>
|
<td>{{sampleDetailsSample.batch}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Comment</th>
|
<th>Comment</th>
|
||||||
<td>{{sampleDetailsSample.notes.comment | exists}}</td>
|
<td>{{sampleDetailsSample.notes.comment | exists}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let customField of sampleDetailsSample.notes.custom_fields_entries">
|
<tr *ngFor="let customField of sampleDetailsSample.notes.custom_fields_entries">
|
||||||
<th>{{customField[0]}}</th>
|
<th>{{customField[0]}}</th>
|
||||||
<td>{{customField[1]}}</td>
|
<td>{{customField[1]}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let reference of sampleDetailsSample.notes.sample_references">
|
<tr *ngFor="let reference of sampleDetailsSample.notes.sample_references">
|
||||||
<th>{{reference.relation}}</th>
|
<th>{{reference.relation}}</th>
|
||||||
<td>
|
<td>
|
||||||
<button class="rb-btn rb-link" (click)="sampleDetails(reference.sample_id, sampleModal)">
|
<button class="rb-btn rb-link" (click)="sampleDetails(reference.sample_id, sampleModal)">
|
||||||
{{reference.number}}
|
{{reference.number}}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let conditionField of sampleDetailsSample.condition_entries">
|
<tr *ngFor="let conditionField of sampleDetailsSample.condition_entries">
|
||||||
<th>{{conditionField[0]}}</th>
|
<th>{{conditionField[0]}}</th>
|
||||||
<td>{{conditionField[1]}}</td>
|
<td>{{conditionField[1]}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let measurement of sampleDetailsSample.measurement_entries">
|
<tr *ngFor="let measurement of sampleDetailsSample.measurement_entries">
|
||||||
<th>{{measurement.name}}</th>
|
<th>{{measurement.name}}</th>
|
||||||
<td>{{measurement.value}}</td>
|
<td>{{measurement.value}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>User</th>
|
<th>User</th>
|
||||||
<td>{{sampleDetailsSample.user}}</td>
|
<td>{{sampleDetailsSample.user}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<td>{{sampleDetailsSample.status}}</td>
|
<td>{{sampleDetailsSample.status}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</rb-table>
|
</rb-table>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #restoreConfirm>
|
<ng-template #restoreConfirm>
|
||||||
<rb-dialog dialogTitle="Restore sample">
|
<rb-dialog dialogTitle="Restore sample">
|
||||||
Do you really want to restore this sample?
|
Do you really want to restore this sample?
|
||||||
</rb-dialog>
|
</rb-dialog>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -1,249 +1,249 @@
|
|||||||
@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
|
@import "~@inst-iot/bosch-angular-ui-components/styles/variables/colors";
|
||||||
|
|
||||||
.header-addnew {
|
.header-addnew {
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
display: inline;
|
display: inline;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-icon-button {
|
rb-icon-button {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-table {
|
rb-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
td .clickable.rb-ic {
|
td .clickable.rb-ic {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
color: $color-gray-silver-sand;
|
color: $color-gray-silver-sand;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-form-multi-select{
|
rb-form-multi-select{
|
||||||
min-width: 9rem;
|
min-width: 9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-selection {
|
.status-selection {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-form-checkbox {
|
rb-form-checkbox {
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.selection {
|
.selection {
|
||||||
max-width: 230px;
|
max-width: 230px;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.paging {
|
.paging {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
float: left;
|
float: left;
|
||||||
|
|
||||||
rb-form-input {
|
rb-form-input {
|
||||||
max-width: 65px;
|
max-width: 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
> button {
|
> button {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-header {
|
.sort-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& > span:first-child {
|
& > span:first-child {
|
||||||
max-width: 180px;
|
max-width: 180px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: block;
|
display: block;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
background: #FFF;
|
background: #FFF;
|
||||||
|
|
||||||
:nth-child(1) {
|
:nth-child(1) {
|
||||||
margin-bottom: -3px;
|
margin-bottom: -3px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
:nth-child(2) {
|
:nth-child(2) {
|
||||||
margin-top: -3px;
|
margin-top: -3px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.filters:after {
|
.filters:after {
|
||||||
content:"";
|
content:"";
|
||||||
clear:both;
|
clear:both;
|
||||||
display:block;
|
display:block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download {
|
.download {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
||||||
& > rb-form-checkbox {
|
& > rb-form-checkbox {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-arr-up {
|
.sort-arr-up {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& > span {
|
& > span {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-left: 6.3px solid transparent;
|
border-left: 6.3px solid transparent;
|
||||||
border-right: 6.3px solid transparent;
|
border-right: 6.3px solid transparent;
|
||||||
border-bottom: 6.3px solid #000;
|
border-bottom: 6.3px solid #000;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
display: block;
|
display: block;
|
||||||
left: 2px;
|
left: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-arr-down {
|
.sort-arr-down {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& > span {
|
& > span {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-left: 6.3px solid transparent;
|
border-left: 6.3px solid transparent;
|
||||||
border-right: 6.3px solid transparent;
|
border-right: 6.3px solid transparent;
|
||||||
border-top: 6.3px solid #000;
|
border-top: 6.3px solid #000;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
display: block;
|
display: block;
|
||||||
left: 2px;
|
left: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fieldfilters {
|
.fieldfilters {
|
||||||
clear: both;
|
clear: both;
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto auto 1fr;
|
grid-template-columns: auto auto 1fr;
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 30px;
|
margin-right: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > rb-form-checkbox {
|
& > rb-form-checkbox {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.filtermode {
|
.filtermode {
|
||||||
max-width: 82px;
|
max-width: 82px;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea.linkmodal {
|
textarea.linkmodal {
|
||||||
display: block;
|
display: block;
|
||||||
min-width: 600px;
|
min-width: 600px;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-inputs > * {
|
.filter-inputs > * {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 220px;
|
width: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sample-details-table {
|
.sample-details-table {
|
||||||
|
|
||||||
td {
|
td {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.validation-close {
|
.validation-close {
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.samples-table tr.clickable {
|
.samples-table tr.clickable {
|
||||||
background: none;
|
background: none;
|
||||||
transition: background-color 0.5s;
|
transition: background-color 0.5s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $color-gray-mercury;
|
background: $color-gray-mercury;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .samples-table rb-form-checkbox .input-wrapper {
|
::ng-deep .samples-table rb-form-checkbox .input-wrapper {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
margin-top: -4.5px;
|
margin-top: -4.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-dialog {
|
.link-dialog {
|
||||||
rb-form-checkbox {
|
rb-form-checkbox {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-icon-button {
|
rb-icon-button {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.samples-loading {
|
.samples-loading {
|
||||||
float: left;
|
float: left;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto auto;
|
grid-template-columns: auto auto;
|
||||||
grid-template-rows: 1fr auto 1fr;
|
grid-template-rows: 1fr auto 1fr;
|
||||||
|
|
||||||
rb-loading-spinner {
|
rb-loading-spinner {
|
||||||
grid-row: span 3;
|
grid-row: span 3;
|
||||||
transform: scale(0.5);
|
transform: scale(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
grid-column: 2 / 3;
|
grid-column: 2 / 3;
|
||||||
grid-row: 2 / 3;
|
grid-row: 2 / 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.reset-preferences {
|
.reset-preferences {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,93 +7,93 @@ import {ModalService} from '@inst-iot/bosch-angular-ui-components';
|
|||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ApiService {
|
export class ApiService {
|
||||||
|
|
||||||
private host = isDevMode() ? '/api' : 'https://definma-api.apps.de1.bosch-iot-cloud.com';
|
private host = isDevMode() ? '/api' : 'https://definma-api.apps.de1.bosch-iot-cloud.com';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private storage: LocalStorageService,
|
private storage: LocalStorageService,
|
||||||
private modalService: ModalService,
|
private modalService: ModalService,
|
||||||
private window: Window
|
private window: Window
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
get hostName() {
|
get hostName() {
|
||||||
return this.host;
|
return this.host;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main HTTP methods
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
post<T>(url, data = null, f: (data?: T, err?, headers?) => void = () => {}) {
|
post<T>(url, data = null, f: (data?: T, err?, headers?) => void = () => {}) {
|
||||||
this.requestErrorHandler<T>(this.http.post(this.url(url), data, this.options()), f);
|
this.requestErrorHandler<T>(this.http.post(this.url(url), data, this.options()), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
put<T>(url, data = null, f: (data?: T, err?, headers?) => void = () => {}) {
|
put<T>(url, data = null, f: (data?: T, err?, headers?) => void = () => {}) {
|
||||||
this.requestErrorHandler<T>(this.http.put(this.url(url), data, this.options()), f);
|
this.requestErrorHandler<T>(this.http.put(this.url(url), data, this.options()), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete<T>(url, f: (data?: T, err?, headers?) => void = () => {}) {
|
delete<T>(url, f: (data?: T, err?, headers?) => void = () => {}) {
|
||||||
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
|
// 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 => { // Successful request
|
observable.subscribe(data => { // Successful request
|
||||||
f(
|
f(
|
||||||
data.body,
|
data.body,
|
||||||
undefined,
|
undefined,
|
||||||
data.headers.keys().reduce((s, e) => {s[e.toLowerCase()] = data.headers.get(e); return s; }, {})
|
data.headers.keys().reduce((s, e) => {s[e.toLowerCase()] = data.headers.get(e); return s; }, {})
|
||||||
);
|
);
|
||||||
}, err => { // Error
|
}, err => { // Error
|
||||||
if (f.length > 1) { // Pass on error
|
if (f.length > 1) { // Pass on error
|
||||||
f(undefined, err, undefined);
|
f(undefined, err, undefined);
|
||||||
}
|
}
|
||||||
else { // Handle directly
|
else { // Handle directly
|
||||||
this.requestError(err);
|
this.requestError(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
requestError(err) { // Network error dialog
|
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];
|
||||||
if (err.error.details) {
|
if (err.error.details) {
|
||||||
details.push(err.error.details);
|
details.push(err.error.details);
|
||||||
}
|
}
|
||||||
modalRef.instance.details = details;
|
modalRef.instance.details = details;
|
||||||
modalRef.result.then(() => {
|
modalRef.result.then(() => {
|
||||||
this.window.location.reload();
|
this.window.location.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private url(url) { // Detect if host was given, otherwise use default host
|
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;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return this.host + url;
|
return this.host + url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate request options
|
// 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
|
// 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) {
|
||||||
return new HttpHeaders({Authorization: 'Basic ' + auth});
|
return new HttpHeaders({Authorization: 'Basic ' + auth});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new HttpHeaders();
|
return new HttpHeaders();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,18 @@ import {QuickScore} from 'quick-score';
|
|||||||
import {of} from 'rxjs';
|
import {of} from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AutocompleteService {
|
export class AutocompleteService {
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
bind(ref, list: string[]) {
|
bind(ref, list: string[]) {
|
||||||
return this.search.bind(ref, list);
|
return this.search.bind(ref, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
search(arr: string[], str: string) {
|
search(arr: string[], str: string) {
|
||||||
const qs = new QuickScore(arr);
|
const qs = new QuickScore(arr);
|
||||||
return of(str === '' ? [] : qs.search(str).map(e => e.item));
|
return of(str === '' ? [] : qs.search(str).map(e => e.item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,77 +8,77 @@ import {ModelItemModel} from '../models/model-item.model';
|
|||||||
import {ModelFileModel} from '../models/model-file.model';
|
import {ModelFileModel} from '../models/model-file.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class DataService {
|
export class DataService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService
|
private api: ApiService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
private collectionMap = { // List of available collections
|
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'},
|
||||||
materialTemplates: {path: '/template/materials', model: TemplateModel, type: 'template'},
|
materialTemplates: {path: '/template/materials', model: TemplateModel, type: 'template'},
|
||||||
measurementTemplates: {path: '/template/measurements', model: TemplateModel, type: 'template'},
|
measurementTemplates: {path: '/template/measurements', model: TemplateModel, type: 'template'},
|
||||||
conditionTemplates: {path: '/template/conditions', model: TemplateModel, type: 'template'},
|
conditionTemplates: {path: '/template/conditions', model: TemplateModel, type: 'template'},
|
||||||
sampleNotesFields: {path: '/sample/notes/fields', model: TemplateModel, type: 'idArray'},
|
sampleNotesFields: {path: '/sample/notes/fields', model: TemplateModel, type: 'idArray'},
|
||||||
users: {path: '/users', model: UserModel, type: 'idArray'},
|
users: {path: '/users', model: UserModel, type: 'idArray'},
|
||||||
modelGroups: {path: '/model/groups', model: ModelItemModel, type: 'array'},
|
modelGroups: {path: '/model/groups', model: ModelItemModel, type: 'array'},
|
||||||
modelFiles: {path: '/model/files', model: ModelFileModel, type: 'array'},
|
modelFiles: {path: '/model/files', model: ModelFileModel, type: 'array'},
|
||||||
user: {path: '/user', model: UserModel, type: 'string'},
|
user: {path: '/user', model: UserModel, type: 'string'},
|
||||||
userKey: {path: '/user/key', model: BaseModel, type: 'string'}
|
userKey: {path: '/user/key', model: BaseModel, type: 'string'}
|
||||||
};
|
};
|
||||||
|
|
||||||
arr: {[key: string]: any[]} = {}; // Array of data
|
arr: {[key: string]: any[]} = {}; // Array of data
|
||||||
latest: {[key: string]: any[]} = {}; // Array of latest template versions
|
latest: {[key: string]: any[]} = {}; // Array of latest template versions
|
||||||
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'}; // Global contact data
|
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
|
||||||
f();
|
f();
|
||||||
}
|
}
|
||||||
else { // Load data
|
else { // Load data
|
||||||
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(
|
.map(
|
||||||
e => this.collectionMap[collection].model ?
|
e => this.collectionMap[collection].model ?
|
||||||
new this.collectionMap[collection].model().deserialize(e) : e
|
new this.collectionMap[collection].model().deserialize(e) : e
|
||||||
);
|
);
|
||||||
// Load ids
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // Not array data
|
else { // Not array data
|
||||||
this.d[collection] = new this.collectionMap[collection].model().deserialize(data);
|
this.d[collection] = new this.collectionMap[collection].model().deserialize(data);
|
||||||
}
|
}
|
||||||
f();
|
f();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate id object
|
// 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') { // Generate array with latest templates
|
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
|
||||||
if (template.version > tmpTemplates[template.first_id].version) {
|
if (template.version > tmpTemplates[template.first_id].version) {
|
||||||
tmpTemplates[template.first_id] = template;
|
tmpTemplates[template.first_id] = template;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tmpTemplates[template.first_id] = template;
|
tmpTemplates[template.first_id] = template;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.latest[collection] = Object.values(tmpTemplates);
|
this.latest[collection] = Object.values(tmpTemplates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,130 +6,130 @@ import {Observable} from 'rxjs';
|
|||||||
import {DataService} from './data.service';
|
import {DataService} from './data.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class LoginService implements CanActivate {
|
export class LoginService implements CanActivate {
|
||||||
|
|
||||||
private pathPermissions = [ // Minimum level needed for the specified paths
|
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 = [ // All user levels in ascending permissions order
|
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
|
// 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 = '';
|
||||||
|
|
||||||
private loggedIn;
|
private loggedIn;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
private storage: LocalStorageService,
|
private storage: LocalStorageService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private d: DataService
|
private d: DataService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
login(username = '', password = '') {
|
login(username = '', password = '') {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (username !== '' || password !== '') { // Some credentials given
|
if (username !== '' || password !== '') { // Some credentials given
|
||||||
let credentials: string[];
|
let credentials: string[];
|
||||||
const credentialString: string = this.storage.get('basicAuth');
|
const credentialString: string = this.storage.get('basicAuth');
|
||||||
if (credentialString) { // Found stored credentials
|
if (credentialString) { // Found stored credentials
|
||||||
credentials = atob(credentialString).split(':');
|
credentials = atob(credentialString).split(':');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
credentials = ['', ''];
|
credentials = ['', ''];
|
||||||
}
|
}
|
||||||
if (username !== '' && password !== '') { // All credentials given
|
if (username !== '' && password !== '') { // All credentials given
|
||||||
this.storage.set('basicAuth', btoa(username + ':' + password));
|
this.storage.set('basicAuth', btoa(username + ':' + password));
|
||||||
}
|
}
|
||||||
else if (username !== '') { // Username given
|
else if (username !== '') { // Username given
|
||||||
this.storage.set('basicAuth', btoa(username + ':' + credentials[1]));
|
this.storage.set('basicAuth', btoa(username + ':' + credentials[1]));
|
||||||
}
|
}
|
||||||
else if (password !== '') { // Password given
|
else if (password !== '') { // Password given
|
||||||
this.storage.set('basicAuth', btoa(credentials[0] + ':' + password));
|
this.storage.set('basicAuth', btoa(credentials[0] + ':' + password));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.api.get('/authorized', (data: any, error) => {
|
this.api.get('/authorized', (data: any, error) => {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
if (data.status === 'Authorization successful') {
|
if (data.status === 'Authorization successful') {
|
||||||
this.loggedIn = true;
|
this.loggedIn = true;
|
||||||
this.levels.forEach(level => {
|
this.levels.forEach(level => {
|
||||||
this.isLevel[level] = this.levels.indexOf(data.level) >= this.levels.indexOf(level);
|
this.isLevel[level] = this.levels.indexOf(data.level) >= this.levels.indexOf(level);
|
||||||
if (this.isLevel.dev) { // Set hasPrediction
|
if (this.isLevel.dev) { // Set hasPrediction
|
||||||
this.hasPrediction = true;
|
this.hasPrediction = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.d.load('modelGroups', () => {
|
this.d.load('modelGroups', () => {
|
||||||
this.hasPrediction = this.d.arr.modelGroups.length > 0;
|
this.hasPrediction = this.d.arr.modelGroups.length > 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.userId = data.user_id;
|
this.userId = data.user_id;
|
||||||
resolve(true);
|
resolve(true);
|
||||||
} else {
|
} else {
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
this.storage.remove('basicAuth');
|
this.storage.remove('basicAuth');
|
||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
this.storage.remove('basicAuth');
|
this.storage.remove('basicAuth');
|
||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
this.storage.remove('basicAuth');
|
this.storage.remove('basicAuth');
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
this.levels.forEach(level => {
|
this.levels.forEach(level => {
|
||||||
this.isLevel[level] = false;
|
this.isLevel[level] = false;
|
||||||
});
|
});
|
||||||
this.hasPrediction = false;
|
this.hasPrediction = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanActivate for Angular routing
|
// 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 => {
|
||||||
if (this.loggedIn === undefined) {
|
if (this.loggedIn === undefined) {
|
||||||
this.login().then(res => {
|
this.login().then(res => {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolve(this.loggedIn);
|
resolve(this.loggedIn);
|
||||||
}
|
}
|
||||||
}).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);
|
||||||
// Check if level is permitted for path
|
// Check if level is permitted for path
|
||||||
const ok = res && (!pathPermission || this.isLevel[pathPermission.permission]);
|
const ok = res && (!pathPermission || this.isLevel[pathPermission.permission]);
|
||||||
observer.next(ok);
|
observer.next(ok);
|
||||||
observer.complete();
|
observer.complete();
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get isLoggedIn() {
|
get isLoggedIn() {
|
||||||
return this.loggedIn;
|
return this.loggedIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
get username() {
|
get username() {
|
||||||
return atob(this.storage.get('basicAuth')).split(':')[0];
|
return atob(this.storage.get('basicAuth')).split(':')[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,183 +3,183 @@ import Joi from '@hapi/joi';
|
|||||||
import {AbstractControl} from '@angular/forms';
|
import {AbstractControl} from '@angular/forms';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ValidationService {
|
export class ValidationService {
|
||||||
|
|
||||||
private vUsername = Joi.string()
|
private vUsername = Joi.string()
|
||||||
.lowercase()
|
.lowercase()
|
||||||
.pattern(new RegExp('^[a-z0-9-_.]+$'))
|
.pattern(new RegExp('^[a-z0-9-_.]+$'))
|
||||||
.min(1)
|
.min(1)
|
||||||
.max(128);
|
.max(128);
|
||||||
|
|
||||||
private vPassword = Joi.string()
|
private vPassword = Joi.string()
|
||||||
.min(8)
|
.min(8)
|
||||||
.max(128);
|
.max(128);
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
generate(method, args) { // Generate a Validator function
|
generate(method, args) { // Generate a Validator function
|
||||||
return (control: AbstractControl): {[key: string]: any} | null => {
|
return (control: AbstractControl): {[key: string]: any} | null => {
|
||||||
let ok;
|
let ok;
|
||||||
let error;
|
let error;
|
||||||
if (args) {
|
if (args) {
|
||||||
({ok, error} = this[method](control.value, ...args));
|
({ok, error} = this[method](control.value, ...args));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
({ok, error} = this[method](control.value));
|
({ok, error} = this[method](control.value));
|
||||||
}
|
}
|
||||||
return ok ? null : { failure: error };
|
return ok ? null : { failure: error };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
username(data) {
|
username(data) {
|
||||||
const {ignore, error} = this.vUsername.validate(data);
|
const {ignore, error} = this.vUsername.validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: 'username must only contain a-z0-9-_.'};
|
return {ok: false, error: 'username must only contain a-z0-9-_.'};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
password(data) {
|
password(data) {
|
||||||
const {ignore, error} = this.vPassword.validate(data);
|
const {ignore, error} = this.vPassword.validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: 'password must have at least 8 characters'};
|
return {ok: false, error: 'password must have at least 8 characters'};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
string(data, option = null) {
|
string(data, option = null) {
|
||||||
let validator = Joi.string().max(128).allow('', true, false);
|
let validator = Joi.string().max(128).allow('', true, false);
|
||||||
let errorMsg = 'must contain max 128 characters';
|
let errorMsg = 'must contain max 128 characters';
|
||||||
if (option === 'alphanum') {
|
if (option === 'alphanum') {
|
||||||
validator = validator.alphanum();
|
validator = validator.alphanum();
|
||||||
errorMsg = 'must contain max 128 alphanumerical characters';
|
errorMsg = 'must contain max 128 alphanumerical characters';
|
||||||
}
|
}
|
||||||
const {ignore, error} = validator.validate(data);
|
const {ignore, error} = validator.validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: errorMsg};
|
return {ok: false, error: errorMsg};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
stringOf(data, list) { // String must be in 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(', ')};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
stringNin(data, list) { // String must not be in 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'};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
stringLength(data, length) { // String with maximum 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'};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
minMax(data, min, max) { // Number between min and 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}`};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
min(data, min) { // Number above 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}`};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
max(data, max) { // Number below 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}`};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
url(data) {
|
url(data) {
|
||||||
const {ignore, error} = Joi.string().uri().validate(data);
|
const {ignore, error} = Joi.string().uri().validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: `must be a valid URL`};
|
return {ok: false, error: `must be a valid URL`};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
unique(data, list) {
|
unique(data, list) {
|
||||||
const {ignore, error} = Joi.string().allow('').invalid(...list.map(e => e.toString())).validate(data);
|
const {ignore, error} = Joi.string().allow('').invalid(...list.map(e => e.toString())).validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: `values must be unique`};
|
return {ok: false, error: `values must be unique`};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
equal(data, compare) {
|
equal(data, compare) {
|
||||||
if (data !== compare) {
|
if (data !== compare) {
|
||||||
return {ok: false, error: `must be equal`};
|
return {ok: false, error: `must be equal`};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
parameterName(data) {
|
parameterName(data) {
|
||||||
const {ignore, error} = Joi.string()
|
const {ignore, error} = Joi.string()
|
||||||
.max(128)
|
.max(128)
|
||||||
.invalid('condition_template', 'material_template')
|
.invalid('condition_template', 'material_template')
|
||||||
.allow('')
|
.allow('')
|
||||||
.pattern(/^[^.]+$/)
|
.pattern(/^[^.]+$/)
|
||||||
.required()
|
.required()
|
||||||
.messages({'string.pattern.base': 'name must not contain a dot'})
|
.messages({'string.pattern.base': 'name must not contain a dot'})
|
||||||
.validate(data);
|
.validate(data);
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: error.details[0].message};
|
return {ok: false, error: error.details[0].message};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
|
|
||||||
parameterRange(data) {
|
parameterRange(data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
try {
|
try {
|
||||||
const {ignore, error} = Joi.object({
|
const {ignore, error} = Joi.object({
|
||||||
values: Joi.array()
|
values: Joi.array()
|
||||||
.min(1),
|
.min(1),
|
||||||
|
|
||||||
min: Joi.number(),
|
min: Joi.number(),
|
||||||
|
|
||||||
max: Joi.number(),
|
max: Joi.number(),
|
||||||
|
|
||||||
type: Joi.string()
|
type: Joi.string()
|
||||||
.valid('string', 'number', 'boolean', 'array'),
|
.valid('string', 'number', 'boolean', 'array'),
|
||||||
|
|
||||||
required: Joi.boolean()
|
required: Joi.boolean()
|
||||||
})
|
})
|
||||||
.oxor('values', 'min')
|
.oxor('values', 'min')
|
||||||
.oxor('values', 'max')
|
.oxor('values', 'max')
|
||||||
.required()
|
.required()
|
||||||
.validate(JSON.parse(data));
|
.validate(JSON.parse(data));
|
||||||
if (error) {
|
if (error) {
|
||||||
return {ok: false, error: error.details[0].message};
|
return {ok: false, error: error.details[0].message};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return {ok: false, error: `no valid JSON`};
|
return {ok: false, error: `no valid JSON`};
|
||||||
}
|
}
|
||||||
return {ok: true, error: ''};
|
return {ok: true, error: ''};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return {ok: false, error: `no valid value`};
|
return {ok: false, error: `no valid value`};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,46 @@
|
|||||||
<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">
|
||||||
<ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="email" label="email" email required [(ngModel)]="user.email" ngModel>
|
<rb-form-input name="email" label="email" email required [(ngModel)]="user.email" ngModel>
|
||||||
<ng-template rbFormValidationMessage="email">Invalid email</ng-template>
|
<ng-template rbFormValidationMessage="email">Invalid email</ng-template>
|
||||||
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
<ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="location" label="location" appValidate="string" required [appValidateArgs]="['alphanum']"
|
<rb-form-input name="location" label="location" appValidate="string" required [appValidateArgs]="['alphanum']"
|
||||||
[(ngModel)]="user.location" #locationInput="ngModel">
|
[(ngModel)]="user.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>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<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"
|
label="device" appValidate="string" [name]="'device-' + item.i" [ngModel]="item.value"
|
||||||
#deviceInput="ngModel">
|
#deviceInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{deviceInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{deviceInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
</rb-array-input>
|
</rb-array-input>
|
||||||
<rb-icon-button icon="save" mode="primary" type="submit" [disabled]="!userForm.form.valid" (click)="saveUser()">
|
<rb-icon-button icon="save" mode="primary" type="submit" [disabled]="!userForm.form.valid" (click)="saveUser()">
|
||||||
Save change
|
Save change
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
<span class="message">{{messageUser}}</span>
|
<span class="message">{{messageUser}}</span>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
<h4 class="pass-heading">Change password</h4>
|
<h4 class="pass-heading">Change password</h4>
|
||||||
|
|
||||||
<form #passForm="ngForm">
|
<form #passForm="ngForm">
|
||||||
<rb-form-input name="passA" type="password" label="new password" appValidate="password" required
|
<rb-form-input name="passA" type="password" label="new password" appValidate="password" required
|
||||||
[(ngModel)]="password" #passAInput="ngModel">
|
[(ngModel)]="password" #passAInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{passAInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{passAInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-input name="passB" type="password" label="confirm password" appValidate="equal"
|
<rb-form-input name="passB" type="password" label="confirm password" appValidate="equal"
|
||||||
[appValidateArgs]="[password]" required #passBInput="ngModel" ngModel>
|
[appValidateArgs]="[password]" required #passBInput="ngModel" ngModel>
|
||||||
<ng-template rbFormValidationMessage="failure">{{passBInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{passBInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<button class="rb-btn rb-primary" type="submit" [disabled]="!passForm.form.valid" (click)="savePass()">
|
<button class="rb-btn rb-primary" type="submit" [disabled]="!passForm.form.valid" (click)="savePass()">
|
||||||
Change password
|
Change password
|
||||||
</button>
|
</button>
|
||||||
<span class="message">{{messagePass}}</span>
|
<span class="message">{{messagePass}}</span>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.pass-heading {
|
.pass-heading {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
@ -6,63 +6,63 @@ import {LoginService} from '../services/login.service';
|
|||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-settings',
|
selector: 'app-settings',
|
||||||
templateUrl: './settings.component.html',
|
templateUrl: './settings.component.html',
|
||||||
styleUrls: ['./settings.component.scss']
|
styleUrls: ['./settings.component.scss']
|
||||||
})
|
})
|
||||||
export class SettingsComponent implements OnInit {
|
export class SettingsComponent implements OnInit {
|
||||||
|
|
||||||
user: UserModel = new UserModel(); // User to edit
|
user: UserModel = new UserModel(); // User to edit
|
||||||
password = ''; // New password
|
password = ''; // New password
|
||||||
messageUser = ''; // Messages for user and pass part
|
messageUser = ''; // Messages for user and pass part
|
||||||
messagePass = '';
|
messagePass = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
private login: LoginService,
|
private login: LoginService,
|
||||||
private router: Router
|
private router: Router
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.api.get<UserModel>('/user', data => {
|
this.api.get<UserModel>('/user', data => {
|
||||||
this.user.deserialize(data);
|
this.user.deserialize(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
saveUser() {
|
saveUser() {
|
||||||
this.api.put<UserModel>('/user', this.user.sendFormat(), (data, err) => {
|
this.api.put<UserModel>('/user', this.user.sendFormat(), (data, err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.messageUser = err.error.status;
|
this.messageUser = err.error.status;
|
||||||
}
|
}
|
||||||
else { // Login with new credentials
|
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']);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.messageUser = 'request not successful, try again';
|
this.messageUser = 'request not successful, try again';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
savePass() {
|
savePass() {
|
||||||
this.api.put<UserModel>('/user', {pass: this.password}, (ignore, err) => {
|
this.api.put<UserModel>('/user', {pass: this.password}, (ignore, err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.messagePass = err.error.status;
|
this.messagePass = err.error.status;
|
||||||
}
|
}
|
||||||
else { // Login with new credentials
|
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']);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.messagePass = 'request not successful, try again';
|
this.messagePass = 'request not successful, try again';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { SizePipe } from './size.pipe';
|
import { SizePipe } from './size.pipe';
|
||||||
|
|
||||||
describe('SizePipe', () => {
|
describe('SizePipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
const pipe = new SizePipe();
|
const pipe = new SizePipe();
|
||||||
expect(pipe).toBeTruthy();
|
expect(pipe).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'size'
|
name: 'size'
|
||||||
})
|
})
|
||||||
export class SizePipe implements PipeTransform {
|
export class SizePipe implements PipeTransform {
|
||||||
|
|
||||||
transform(value: number, exp: string): string {
|
transform(value: number, exp: string): string {
|
||||||
const divide = ['', 'k', 'M', 'G', 'T'].indexOf(exp);
|
const divide = ['', 'k', 'M', 'G', 'T'].indexOf(exp);
|
||||||
for (let i = 0; i < divide; i ++) {
|
for (let i = 0; i < divide; i ++) {
|
||||||
value = value / 1024;
|
value = value / 1024;
|
||||||
}
|
}
|
||||||
return `${value.toFixed(2)} ${exp}B`;
|
return `${value.toFixed(2)} ${exp}B`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,66 +1,66 @@
|
|||||||
<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>
|
||||||
<option value="measurement">Measurements</option>
|
<option value="measurement">Measurements</option>
|
||||||
<option value="condition">Conditions</option>
|
<option value="condition">Conditions</option>
|
||||||
</rb-form-select>
|
</rb-form-select>
|
||||||
|
|
||||||
|
|
||||||
<rb-icon-button icon="add" mode="primary" (click)="newTemplate()">New template</rb-icon-button>
|
<rb-icon-button icon="add" mode="primary" (click)="newTemplate()">New template</rb-icon-button>
|
||||||
|
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="header">Name</div>
|
<div class="header">Name</div>
|
||||||
<div class="header">Version</div>
|
<div class="header">Version</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngFor="let group of groupsView">
|
<ng-container *ngFor="let group of groupsView">
|
||||||
<div class="row clickable">
|
<div class="row clickable">
|
||||||
<div (click)="group.expanded = !group.expanded">{{group.name}}</div>
|
<div (click)="group.expanded = !group.expanded">{{group.name}}</div>
|
||||||
<div (click)="group.expanded = !group.expanded">{{group.version}}</div>
|
<div (click)="group.expanded = !group.expanded">{{group.version}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" *ngIf="group.expanded" [@inOut]>
|
<div class="row" *ngIf="group.expanded" [@inOut]>
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<ng-container *ngFor="let template of group.entries">
|
<ng-container *ngFor="let template of group.entries">
|
||||||
<div>{{template.name}}</div>
|
<div>{{template.name}}</div>
|
||||||
<div>{{template.version}}</div>
|
<div>{{template.version}}</div>
|
||||||
<div>{{template.parameters | parameters}}</div>
|
<div>{{template.parameters | parameters}}</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div class="template-actions">
|
<div class="template-actions">
|
||||||
<form #templateForm="ngForm">
|
<form #templateForm="ngForm">
|
||||||
<div *ngIf="group.edit">
|
<div *ngIf="group.edit">
|
||||||
<rb-form-input [name]="'name-' + group.name" label="name" appValidate="string" required
|
<rb-form-input [name]="'name-' + group.name" label="name" appValidate="string" required
|
||||||
[(ngModel)]="templateEdit[group.first_id].name" #supplierInput="ngModel">
|
[(ngModel)]="templateEdit[group.first_id].name" #supplierInput="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{supplierInput.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{supplierInput.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-array-input [(ngModel)]="templateEdit[group.first_id].parameters" [name]="'parameters-' + group.name"
|
<rb-array-input [(ngModel)]="templateEdit[group.first_id].parameters" [name]="'parameters-' + group.name"
|
||||||
[pushTemplate]="{name: '', range: {}, rangeString: '{}'}" pushPath="name"
|
[pushTemplate]="{name: '', range: {}, rangeString: '{}'}" pushPath="name"
|
||||||
class="parameters">
|
class="parameters">
|
||||||
<ng-container *rbArrayInputItem="let item">
|
<ng-container *rbArrayInputItem="let item">
|
||||||
<rb-form-input [rbArrayInputListener]="'parameter-name-' + group.name" appValidate="parameterName"
|
<rb-form-input [rbArrayInputListener]="'parameter-name-' + group.name" appValidate="parameterName"
|
||||||
[index]="item.i" [name]="'parameter-name-' + group.name + item.i"
|
[index]="item.i" [name]="'parameter-name-' + group.name + item.i"
|
||||||
label="parameter name" [ngModel]="item.value.name" #parameterName="ngModel">
|
label="parameter name" [ngModel]="item.value.name" #parameterName="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{parameterName.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{parameterName.errors.failure}}</ng-template>
|
||||||
</rb-form-input>
|
</rb-form-input>
|
||||||
<rb-form-textarea [name]="'parameter-range-' + group.name + item.i" label="range"
|
<rb-form-textarea [name]="'parameter-range-' + group.name + item.i" label="range"
|
||||||
appValidate="parameterRange" [(ngModel)]="item.value.rangeString"
|
appValidate="parameterRange" [(ngModel)]="item.value.rangeString"
|
||||||
#parameterRange="ngModel">
|
#parameterRange="ngModel">
|
||||||
<ng-template rbFormValidationMessage="failure">{{parameterRange.errors.failure}}</ng-template>
|
<ng-template rbFormValidationMessage="failure">{{parameterRange.errors.failure}}</ng-template>
|
||||||
</rb-form-textarea>
|
</rb-form-textarea>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</rb-array-input>
|
</rb-array-input>
|
||||||
</div>
|
</div>
|
||||||
<rb-icon-button icon="edit" mode="secondary" (click)="group.edit = !group.edit">
|
<rb-icon-button icon="edit" mode="secondary" (click)="group.edit = !group.edit">
|
||||||
Edit template
|
Edit template
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
<rb-icon-button icon="save" mode="primary" (click)="saveTemplate(group.first_id)" *ngIf="group.edit"
|
<rb-icon-button icon="save" mode="primary" (click)="saveTemplate(group.first_id)" *ngIf="group.edit"
|
||||||
[disabled]="templateForm.invalid">
|
[disabled]="templateForm.invalid">
|
||||||
Save template
|
Save template
|
||||||
</rb-icon-button>
|
</rb-icon-button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -2,39 +2,39 @@
|
|||||||
|
|
||||||
.list {
|
.list {
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 4fr;
|
grid-template-columns: 1fr 4fr;
|
||||||
border-bottom: 1px solid $color-gray-mercury;
|
border-bottom: 1px solid $color-gray-mercury;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
padding: 8px 5px;
|
padding: 8px 5px;
|
||||||
|
|
||||||
&.header {
|
&.header {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.details {
|
&.details {
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 3fr;
|
grid-template-columns: 1fr 1fr 3fr;
|
||||||
background: $color-gray-alabaster;
|
background: $color-gray-alabaster;
|
||||||
|
|
||||||
.template-actions {
|
.template-actions {
|
||||||
grid-column: span 3;
|
grid-column: span 3;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|
||||||
.parameters {
|
.parameters {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 2fr;
|
grid-template-columns: 1fr 2fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb-icon-button[icon="save"] {
|
rb-icon-button[icon="save"] {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,132 +8,132 @@ import omit from 'lodash/omit';
|
|||||||
import {DataService} from '../services/data.service';
|
import {DataService} from '../services/data.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-templates',
|
selector: 'app-templates',
|
||||||
templateUrl: './templates.component.html',
|
templateUrl: './templates.component.html',
|
||||||
styleUrls: ['./templates.component.scss'],
|
styleUrls: ['./templates.component.scss'],
|
||||||
animations: [
|
animations: [
|
||||||
trigger(
|
trigger(
|
||||||
'inOut', [
|
'inOut', [
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({height: 0, opacity: 0}),
|
style({height: 0, opacity: 0}),
|
||||||
animate('0.5s ease-out', style({height: '*', opacity: 1}))
|
animate('0.5s ease-out', style({height: '*', opacity: 1}))
|
||||||
]),
|
]),
|
||||||
transition(':leave', [
|
transition(':leave', [
|
||||||
style({height: '*', opacity: 1}),
|
style({height: '*', opacity: 1}),
|
||||||
animate('0.5s ease-in', style({height: 0, opacity: 0}))
|
animate('0.5s ease-in', style({height: 0, opacity: 0}))
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class TemplatesComponent implements OnInit {
|
export class TemplatesComponent implements OnInit {
|
||||||
|
|
||||||
collection = 'measurement'; // Collection to view
|
collection = 'measurement'; // Collection to view
|
||||||
templates: TemplateModel[] = []; // All templates of current collection
|
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: { // Grouped templates
|
groupsView: { // Grouped templates
|
||||||
first_id: string,
|
first_id: string,
|
||||||
name: string,
|
name: string,
|
||||||
version: number,
|
version: number,
|
||||||
expanded: boolean,
|
expanded: boolean,
|
||||||
edit: boolean,
|
edit: boolean,
|
||||||
entries: TemplateModel[]
|
entries: TemplateModel[]
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
private validate: ValidationService,
|
private validate: ValidationService,
|
||||||
public d: DataService
|
public d: DataService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadTemplates();
|
this.loadTemplates();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTemplates() {
|
loadTemplates() {
|
||||||
this.d.load(this.collection + 'Templates', () => {
|
this.d.load(this.collection + 'Templates', () => {
|
||||||
this.templates = this.d.arr[this.collection + 'Templates'];
|
this.templates = this.d.arr[this.collection + 'Templates'];
|
||||||
this.templateFormat();
|
this.templateFormat();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
templateFormat() {
|
templateFormat() {
|
||||||
this.templateGroups = {};
|
this.templateGroups = {};
|
||||||
this.templateEdit = {};
|
this.templateEdit = {};
|
||||||
this.templates.forEach(template => { // Group templates
|
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);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.templateGroups[template.first_id] = [template];
|
this.templateGroups[template.first_id] = [template];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Object.keys(this.templateGroups).forEach(id => {
|
Object.keys(this.templateGroups).forEach(id => {
|
||||||
this.templateGroups[id] = this.templateGroups[id].sort((a, b) => a.version - b.version);
|
this.templateGroups[id] = this.templateGroups[id].sort((a, b) => a.version - b.version);
|
||||||
this.templateEdit[id] = cloneDeep(this.templateGroups[id][this.templateGroups[id].length - 1]);
|
this.templateEdit[id] = cloneDeep(this.templateGroups[id][this.templateGroups[id].length - 1]);
|
||||||
this.templateEdit[id].parameters = this.templateEdit[id].parameters
|
this.templateEdit[id].parameters = this.templateEdit[id].parameters
|
||||||
.map(e => {e.rangeString = JSON.stringify(e.range, null, 2); return e; });
|
.map(e => {e.rangeString = JSON.stringify(e.range, null, 2); return e; });
|
||||||
});
|
});
|
||||||
this.groupsView = Object.values(this.templateGroups)
|
this.groupsView = Object.values(this.templateGroups)
|
||||||
.map(e => ({
|
.map(e => ({
|
||||||
first_id: e[e.length - 1].first_id,
|
first_id: e[e.length - 1].first_id,
|
||||||
name: e[e.length - 1].name,
|
name: e[e.length - 1].name,
|
||||||
version: e[e.length - 1].version,
|
version: e[e.length - 1].version,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
edit: false,
|
edit: false,
|
||||||
entries: e
|
entries: e
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
saveTemplate(first_id) {
|
saveTemplate(first_id) {
|
||||||
const template = cloneDeep(this.templateEdit[first_id]);
|
const template = cloneDeep(this.templateEdit[first_id]);
|
||||||
template.parameters = template.parameters.filter(e => e.name !== '');
|
template.parameters = template.parameters.filter(e => e.name !== '');
|
||||||
let valid = true;
|
let valid = true;
|
||||||
valid = valid && this.validate.string(template.name).ok;
|
valid = valid && this.validate.string(template.name).ok;
|
||||||
template.parameters.forEach(parameter => {
|
template.parameters.forEach(parameter => {
|
||||||
valid = valid && this.validate.parameterName(parameter.name).ok;
|
valid = valid && this.validate.parameterName(parameter.name).ok;
|
||||||
valid = valid && this.validate.parameterRange(parameter.rangeString).ok;
|
valid = valid && this.validate.parameterRange(parameter.rangeString).ok;
|
||||||
if (valid) {
|
if (valid) {
|
||||||
parameter.range = JSON.parse(parameter.rangeString);
|
parameter.range = JSON.parse(parameter.rangeString);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
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') { // New template
|
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', () => {
|
||||||
this.templates = this.d.arr[this.collection + 'Templates'];
|
this.templates = this.d.arr[this.collection + 'Templates'];
|
||||||
this.templateFormat();
|
this.templateFormat();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.api.put<TemplateModel>(`/template/${this.collection}/${template.first_id}`, sendData, () => {
|
this.api.put<TemplateModel>(`/template/${this.collection}/${template.first_id}`, 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', () => {
|
||||||
this.templates = this.d.arr[this.collection + 'Templates'];
|
this.templates = this.d.arr[this.collection + 'Templates'];
|
||||||
this.templateFormat();
|
this.templateFormat();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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({
|
this.groupsView.push({
|
||||||
first_id: 'null',
|
first_id: 'null',
|
||||||
name: 'new template',
|
name: 'new template',
|
||||||
version: 0,
|
version: 0,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
edit: true,
|
edit: true,
|
||||||
entries: [template]
|
entries: [template]
|
||||||
});
|
});
|
||||||
this.templateEdit.null = new TemplateModel();
|
this.templateEdit.null = new TemplateModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user