From 3e53ef874c74b60bbedeaad869a77ee09d9dc5fa Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Tue, 25 Aug 2020 09:01:45 +0200 Subject: [PATCH 1/2] fixed to the top button --- src/app/app.component.html | 13 +++++-------- src/app/app.component.scss | 14 +++----------- src/app/app.component.ts | 2 +- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index d98d743..3d78944 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -53,16 +53,13 @@ -
-
- -
- -
- -
+
+
+ + + CR/APS1 and CR/ANA1 2020 diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 2b6f0c7..c482ab2 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -14,21 +14,13 @@ width: 800px; } -.to-the-top-container { +.container { position: relative; overflow: hidden; } .to-the-top { - position: absolute; - top: 100vh; - bottom: 10px; + position: fixed; right: 1rem; - pointer-events: none; - - rb-icon-button { - position: sticky; - pointer-events: all; - top: calc(100vh - 3rem); - } + bottom: 20px; } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index f8836e5..c40f010 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -22,7 +22,7 @@ export class AppComponent implements OnInit{ constructor( public login: LoginService, public router: Router, - private window: Window, + public window: Window, private modal: ModalService, public d: DataService ) { From 6f95ff41489216acdb6368ad098f16684d9d8310 Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Wed, 26 Aug 2020 19:25:31 +0200 Subject: [PATCH 2/2] added user status and prediction user --- src/app/app-routing.module.ts | 10 ++- src/app/app.component.html | 4 +- .../documentation.component.html | 85 +++++++++++-------- src/app/help/help.component.html | 6 +- src/app/login/login.component.html | 3 +- src/app/login/login.component.ts | 13 ++- .../model-templates.component.ts | 1 - src/app/models/user.model.ts | 3 + src/app/prediction/prediction.component.html | 18 +++- src/app/prediction/prediction.component.scss | 3 +- src/app/prediction/prediction.component.ts | 4 +- src/app/sample/sample.component.html | 5 +- src/app/sample/sample.component.ts | 20 +++-- src/app/services/data.service.ts | 2 +- src/app/services/login.service.ts | 15 +++- src/app/users/users.component.html | 35 +++++++- src/app/users/users.component.ts | 25 ++++-- 17 files changed, 178 insertions(+), 74 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index dfa38f6..9883522 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,5 +1,5 @@ import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import {Routes, RouterModule, ExtraOptions} from '@angular/router'; import {HomeComponent} from './home/home.component'; import {LoginService} from './services/login.service'; import {SampleComponent} from './sample/sample.component'; @@ -33,8 +33,14 @@ const routes: Routes = [ { path: '**', redirectTo: '' } ]; +const routerOptions: ExtraOptions = { + scrollPositionRestoration: 'enabled', + anchorScrolling: 'enabled', + scrollOffset: [0, 64], +}; + @NgModule({ - imports: [RouterModule.forRoot(routes)], + imports: [RouterModule.forRoot(routes, routerOptions)], exports: [RouterModule] }) export class AppRoutingModule { } diff --git a/src/app/app.component.html b/src/app/app.component.html index 3d78944..dd81ca9 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -3,7 +3,7 @@ Home Prediction Models - Samples + Samples Templates @@ -65,6 +65,6 @@ CR/APS1 and CR/ANA1 2020 diff --git a/src/app/documentation/documentation.component.html b/src/app/documentation/documentation.component.html index 99bf6a2..3ae7a18 100644 --- a/src/app/documentation/documentation.component.html +++ b/src/app/documentation/documentation.component.html @@ -32,51 +32,66 @@ + prediction + read + write + dev + admin + + + use prediction models + specified ones + specified ones + specified ones + all + all + + read sample data + + + + + + + add samples/edit own + + + + + + + read spectral data + + + + + + + edit other's data + + + + + + + maintain templates + + + + + + + edit users - - - - read - - - - - - write - - - - - - - - - - dev - - - - - - - - - - admin - - - - - diff --git a/src/app/help/help.component.html b/src/app/help/help.component.html index 80ac352..2b615dc 100644 --- a/src/app/help/help.component.html +++ b/src/app/help/help.component.html @@ -2,13 +2,13 @@

Please log in for further access. If you do not have an account yet, please contact - {{d.contact}}. + {{d.contact.name}}.

Please log in for further access. If you do not have an account yet, please contact - {{d.contact}}. + {{d.contact.name}}.

Sadly, currently there is no help available for this page. Please contact - {{d.contact}} for further questions. + {{d.contact.name}} for further questions.

diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html index 4c41623..e3dfca1 100644 --- a/src/app/login/login.component.html +++ b/src/app/login/login.component.html @@ -16,7 +16,8 @@ {{emailInput.errors.failure}} Forgot password -
{{message}}
diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index c1bc69f..46cd572 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -23,7 +23,7 @@ export class LoginComponent implements OnInit { constructor( private validate: ValidationService, - private loginService: LoginService, + private login: LoginService, private api: ApiService, private router: Router ) { } @@ -31,7 +31,7 @@ export class LoginComponent implements OnInit { ngOnInit() { } - login() { + userLogin() { if (this.passreset) { this.api.post('/user/passreset', {name: this.username, email: this.email}, (data, err) => { if (err) { @@ -43,10 +43,15 @@ export class LoginComponent implements OnInit { }); } else { - this.loginService.login(this.username, this.password).then(ok => { + this.login.login(this.username, this.password).then(ok => { if (ok) { this.message = 'Login successful'; - this.router.navigate(['/samples']); + if (this.login.isLevel.read) { + this.router.navigate(['/samples']); + } + else { + this.router.navigate(['/prediction']); + } } else { this.message = 'Wrong credentials!'; diff --git a/src/app/model-templates/model-templates.component.ts b/src/app/model-templates/model-templates.component.ts index 0dfd042..8af9457 100644 --- a/src/app/model-templates/model-templates.component.ts +++ b/src/app/model-templates/model-templates.component.ts @@ -50,7 +50,6 @@ export class ModelTemplatesComponent implements OnInit { this.oldModelGroup = ''; this.oldModelName = ''; this.model = new ModelItemModel().models[0]; - this.groups = this.d.arr.modelGroups.map(e => e.group); }); } diff --git a/src/app/models/user.model.ts b/src/app/models/user.model.ts index a3f7cd7..d98e40d 100644 --- a/src/app/models/user.model.ts +++ b/src/app/models/user.model.ts @@ -10,6 +10,8 @@ export class UserModel extends BaseModel{ level = ''; location = ''; devices = ['']; + models = ['']; + status = 'new'; edit = false; deserialize(input: any): this { @@ -22,6 +24,7 @@ export class UserModel extends BaseModel{ const keys = ['name', 'email', 'location', 'devices']; if (mode === 'admin') { keys.push('level'); + keys.push('models'); } return pick(this, keys); } diff --git a/src/app/prediction/prediction.component.html b/src/app/prediction/prediction.component.html index 6adb6f5..cce80cd 100644 --- a/src/app/prediction/prediction.component.html +++ b/src/app/prediction/prediction.component.html @@ -14,17 +14,20 @@

{{spectrumNames[i]}}: {{prediction}} {{activeGroup.models[activeModelIndex].label}} + #

- Average result: {{result.meanPrediction}} {{activeGroup.models[activeModelIndex].label}}, - standard deviation: {{result.std}} + Average result: {{result.meanPrediction}} {{activeGroup.models[activeModelIndex].label}} + #, + standard deviation: {{result.std}}#

Details

{{spectrumNames[i]}}: {{prediction}} {{activeGroup.models[activeModelIndex].label}} + #

@@ -53,7 +56,7 @@
-
+
+ +
+

#Disclaimer: This tool is still under development

+

+ 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.
+ For more details please contact {{d.contact.name}}. +

+
diff --git a/src/app/prediction/prediction.component.scss b/src/app/prediction/prediction.component.scss index 18b1f5b..72ec1e2 100644 --- a/src/app/prediction/prediction.component.scss +++ b/src/app/prediction/prediction.component.scss @@ -1,6 +1,7 @@ .dpt-chart { max-width: 800px; - margin: 0 auto; + margin-left: auto; + margin-right: auto; } .file-input { diff --git a/src/app/prediction/prediction.component.ts b/src/app/prediction/prediction.component.ts index 0d4c3df..075c150 100644 --- a/src/app/prediction/prediction.component.ts +++ b/src/app/prediction/prediction.component.ts @@ -5,6 +5,7 @@ import {animate, style, transition, trigger} from '@angular/animations'; import cloneDeep from 'lodash/cloneDeep'; import {DataService} from '../services/data.service'; import {ModelItemModel} from '../models/model-item.model'; +import {HttpClient} from '@angular/common/http'; interface PredictionResult { meanPrediction: string; @@ -86,7 +87,8 @@ export class PredictionComponent implements OnInit { if (files.hasOwnProperty(i)) { const fileReader = new FileReader(); fileReader.onload = () => { - this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el))) as any; + this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el))) + .filter(el => el.length === 2) as any; this.flattenedSpectra[i] = {labels: this.spectrum.map(e => e[0]), values: this.spectrum.map(e => e[1])}; this.chart[i] = cloneDeep(this.chartInit); this.chart[i].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])})); diff --git a/src/app/sample/sample.component.html b/src/app/sample/sample.component.html index cc94873..ad1f03c 100644 --- a/src/app/sample/sample.component.html +++ b/src/app/sample/sample.component.html @@ -169,7 +169,7 @@ Type{{baseSample.type}} color{{baseSample.color}} Batch{{baseSample.batch}} - Comment{{baseSample.notes.comment}} + Comment{{baseSample.notes | exists:'comment'}} Sample reference{{reference[0]}} - {{reference[1]}} @@ -323,7 +323,8 @@ - + Do you really want to delete {{samples.length > 1 ? 'these samples' : 'this sample'}}? diff --git a/src/app/sample/sample.component.ts b/src/app/sample/sample.component.ts index cdcfa03..dadcfc1 100644 --- a/src/app/sample/sample.component.ts +++ b/src/app/sample/sample.component.ts @@ -329,15 +329,17 @@ export class SampleComponent implements OnInit, AfterContentChecked { resolve(); } }).then(() => { // save sample - this.baseSample.notes.custom_fields = {}; - this.customFields.forEach(element => { - if (element[0] !== '') { - this.baseSample.notes.custom_fields[element[0]] = element[1]; - } - }); - this.baseSample.notes.sample_references = this.sampleReferences - .filter(e => e[0] && e[1] && e[2]) - .map(e => ({sample_id: e[2], relation: e[1]})); + if (this.baseSample.notes) { + this.baseSample.notes.custom_fields = {}; + this.customFields.forEach(element => { + if (element[0] !== '') { + this.baseSample.notes.custom_fields[element[0]] = element[1]; + } + }); + this.baseSample.notes.sample_references = this.sampleReferences + .filter(e => e[0] && e[1] && e[2]) + .map(e => ({sample_id: e[2], relation: e[1]})); + } if (this.samples.length === 0) { // only save new sample for the first time in mode new, otherwise save changes for (let i = 0; i < this.sampleCount; i ++) { this.api.post('/sample/new', this.baseSample.sendFormat(), data => { diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts index 4edba0f..3e76e0a 100644 --- a/src/app/services/data.service.ts +++ b/src/app/services/data.service.ts @@ -34,7 +34,7 @@ export class DataService { id: {[key: string]: {[id: string]: any}} = {}; // data in format _id: data d: {[key: string]: any} = {}; // data not in array format - contact = 'dominic.lingenfelser@bosch.com'; + contact = {name: 'CR/APS1-Lingenfelser', mail: 'dominic.lingenfelser@bosch.com'}; load(collection, f = () => {}) { // load data if (this.arr[collection]) { // data already loaded diff --git a/src/app/services/login.service.ts b/src/app/services/login.service.ts index e8dd0db..ace657b 100644 --- a/src/app/services/login.service.ts +++ b/src/app/services/login.service.ts @@ -3,6 +3,7 @@ import {ApiService} from './api.service'; import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router'; import {LocalStorageService} from 'angular-2-local-storage'; import {Observable} from 'rxjs'; +import {DataService} from './data.service'; @Injectable({ providedIn: 'root' @@ -15,12 +16,14 @@ export class LoginService implements CanActivate { {path: 'users', permission: 'admin'} ]; readonly levels = [ + 'predict', 'read', 'write', 'dev', 'admin' ]; isLevel: {[level: string]: boolean} = {}; + hasPrediction = false; // true if user has prediction models specified userId = ''; private loggedIn; @@ -28,7 +31,8 @@ export class LoginService implements CanActivate { constructor( private api: ApiService, private storage: LocalStorageService, - private router: Router + private router: Router, + private d: DataService ) { } @@ -60,6 +64,14 @@ export class LoginService implements CanActivate { this.loggedIn = true; this.levels.forEach(level => { this.isLevel[level] = this.levels.indexOf(data.level) >= this.levels.indexOf(level); + if (this.isLevel.dev) { // set hasPrediction + this.hasPrediction = true; + } + else { + this.d.load('modelGroups', () => { + this.hasPrediction = this.d.arr.modelGroups.length > 0; + }); + } }); this.userId = data.user_id; resolve(true); @@ -83,6 +95,7 @@ export class LoginService implements CanActivate { this.levels.forEach(level => { this.isLevel[level] = false; }); + this.hasPrediction = false; } canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable { diff --git a/src/app/users/users.component.html b/src/app/users/users.component.html index c4d8b83..2b7a457 100644 --- a/src/app/users/users.component.html +++ b/src/app/users/users.component.html @@ -28,6 +28,12 @@ {{deviceInput.errors.failure}} + + + + + {{passAInput.errors.failure}} @@ -41,13 +47,15 @@ - + Name Email Level Location Device + Models + @@ -58,7 +66,13 @@ {{user.level}} {{user.location}} {{user.devices}} - + + + {{(i > 0 ? ', ' : '') + modelIds[model]}} + + + @@ -93,9 +107,17 @@ + + + + + + + + (click)="deleteConfirm(modalDeleteConfirm, user)"> Delete @@ -111,3 +133,10 @@ + + + + + + + diff --git a/src/app/users/users.component.ts b/src/app/users/users.component.ts index e7857be..27c587f 100644 --- a/src/app/users/users.component.ts +++ b/src/app/users/users.component.ts @@ -3,8 +3,8 @@ import {ApiService} from '../services/api.service'; import {UserModel} from '../models/user.model'; import {LoginService} from '../services/login.service'; import {ModalService} from '@inst-iot/bosch-angular-ui-components'; +import {DataService} from '../services/data.service'; -// TODO: somehow mail change triggered @Component({ selector: 'app-users', @@ -16,17 +16,26 @@ export class UsersComponent implements OnInit { users: UserModel[] = []; newUser: UserModel | null = null; newUserPass = ''; + modelSelect: {id: string, name: string}[] = []; + modelIds: {[id: string]: string} = {}; constructor( private api: ApiService, public login: LoginService, - private modal: ModalService + private modal: ModalService, + public d: DataService ) { } ngOnInit(): void { this.api.get('/users', data => { this.users = data.map(e => new UserModel().deserialize(e)); }); + this.d.load('modelGroups', () => { + this.d.arr.modelGroups.forEach(group => { + this.modelSelect.push(...group.models.map(e => ({id: e._id, name: `${group.group} - ${e.name}`}))); + }); + this.modelIds = this.modelSelect.reduce((s, e) => {s[e.id] = e.name; return s; }, {}); + }); } saveUser(user: UserModel) { @@ -48,14 +57,20 @@ export class UsersComponent implements OnInit { this.newUser = this.newUser ? null : new UserModel(); } - deleteConfirm(modal, username) { + deleteConfirm(modal, user) { this.modal.open(modal).then(result => { if (result) { - this.api.delete('/user/' + username, () => { - this.users.splice(this.users.findIndex(e => e.name === username), 1); + this.api.delete('/user/' + user.name, () => { + user.status = 'deleted'; + user.edit = false; }); } }); } + restoreUser(user) { + this.api.put('/user/restore/' + user.name, {}, () => { + user.status = 'new'; + }); + } }