settings and users dialog
This commit is contained in:
		@@ -1 +1,9 @@
 | 
			
		||||
add_header Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; connect-src https://definma-api.apps.de1.bosch-iot-cloud.com; form-action 'none'; frame-ancestors 'none'; base-uri 'self'";
 | 
			
		||||
add_header X-Frame-Options DENY
 | 
			
		||||
add_header X-DNS-Prefetch-Control off
 | 
			
		||||
add_header Strict-Transport-Security max-age=15552000
 | 
			
		||||
add_header X-Download-Options noopen
 | 
			
		||||
add_header X-Content-Type-Options nosniff
 | 
			
		||||
add_header X-Permitted-Cross-Domain-Policies none
 | 
			
		||||
add_header Referrer-Policy no-referrer
 | 
			
		||||
add_header X-XSS-Protection "1; mode=block"
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@ import {SampleComponent} from './sample/sample.component';
 | 
			
		||||
import {SamplesComponent} from './samples/samples.component';
 | 
			
		||||
import {DocumentationComponent} from './documentation/documentation.component';
 | 
			
		||||
import {TemplatesComponent} from './templates/templates.component';
 | 
			
		||||
import {SettingsComponent} from './settings/settings.component';
 | 
			
		||||
import {UsersComponent} from './users/users.component';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
@@ -14,8 +16,10 @@ const routes: Routes = [
 | 
			
		||||
  {path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'samples/edit/:id', component: SampleComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'templates', component: TemplatesComponent},  // TODO: change after development
 | 
			
		||||
  // {path: 'templates', component: TemplatesComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'templates', component: TemplatesComponent, canActivate: [LoginService]},
 | 
			
		||||
  // {path: 'users', component: UsersComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'users', component: UsersComponent},  // TODO: change
 | 
			
		||||
  {path: 'settings', component: SettingsComponent, canActivate: [LoginService]},
 | 
			
		||||
  {path: 'documentation', component: DocumentationComponent},
 | 
			
		||||
 | 
			
		||||
  // if not authenticated
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,8 @@
 | 
			
		||||
  <nav *rbMainNavItems>
 | 
			
		||||
    <a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a>
 | 
			
		||||
    <a routerLink="/samples" routerLinkActive="active" rbLoadingLink *ngIf="loginService.isLoggedIn">Samples</a>
 | 
			
		||||
    <a routerLink="/templates" routerLinkActive="active" rbLoadingLink *ngIf="loginService.isMaintain">Templates</a>
 | 
			
		||||
    <a routerLink="/templates" routerLinkActive="active" rbLoadingLink *ngIf="loginService.is('maintain')">Templates</a>
 | 
			
		||||
    <a routerLink="/users" routerLinkActive="active" rbLoadingLink *ngIf="loginService.is('admin')">Users</a>
 | 
			
		||||
    <a routerLink="/documentation" routerLinkActive="active" rbLoadingLink>Documentation</a>
 | 
			
		||||
  </nav>
 | 
			
		||||
 | 
			
		||||
@@ -11,12 +12,9 @@
 | 
			
		||||
      <a href="javascript:"  [rbPopover]="userPopover" [anchor]="popoverAnchor">
 | 
			
		||||
        {{loginService.username}} <span class="rb-ic rb-ic-my-brand-frame" #popoverAnchor></span></a>
 | 
			
		||||
    </nav>
 | 
			
		||||
    <ng-template #userPopover>
 | 
			
		||||
    <ng-template #userPopover let-close="close">
 | 
			
		||||
      <div class="spacing">
 | 
			
		||||
        <p>
 | 
			
		||||
<!--          Some user specific information-->
 | 
			
		||||
        </p>
 | 
			
		||||
 | 
			
		||||
        <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>
 | 
			
		||||
      </div>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,3 +3,9 @@
 | 
			
		||||
  font-size: 32px;
 | 
			
		||||
  margin-right: 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.spacing {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: 1fr;
 | 
			
		||||
  grid-row-gap: 10px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,8 @@ import { ImgMagnifierComponent } from './img-magnifier/img-magnifier.component';
 | 
			
		||||
import { ExistsPipe } from './exists.pipe';
 | 
			
		||||
import { TemplatesComponent } from './templates/templates.component';
 | 
			
		||||
import { ParametersPipe } from './parameters.pipe';
 | 
			
		||||
import { SettingsComponent } from './settings/settings.component';
 | 
			
		||||
import { UsersComponent } from './users/users.component';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@@ -38,7 +40,9 @@ import { ParametersPipe } from './parameters.pipe';
 | 
			
		||||
    ImgMagnifierComponent,
 | 
			
		||||
    ExistsPipe,
 | 
			
		||||
    TemplatesComponent,
 | 
			
		||||
    ParametersPipe
 | 
			
		||||
    ParametersPipe,
 | 
			
		||||
    SettingsComponent,
 | 
			
		||||
    UsersComponent
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    LocalStorageModule.forRoot({
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,14 @@
 | 
			
		||||
    <rb-form-input name="username" label="username" appValidate="username" required [(ngModel)]="username" #usernameInput="ngModel">
 | 
			
		||||
      <ng-template rbFormValidationMessage="failure">{{usernameInput.errors.failure}}</ng-template>
 | 
			
		||||
    </rb-form-input>
 | 
			
		||||
    <rb-form-input type="password" name="password" label="password" appValidate="password" required [(ngModel)]="password" #passwordInput="ngModel">
 | 
			
		||||
    <rb-form-input *ngIf="!passreset" type="password" name="password" label="password" appValidate="password" required [(ngModel)]="password" #passwordInput="ngModel">
 | 
			
		||||
      <ng-template rbFormValidationMessage="failure">{{passwordInput.errors.failure}}</ng-template>
 | 
			
		||||
    </rb-form-input>
 | 
			
		||||
    <button class="rb-btn rb-primary login-button" (click)="login()" type="submit" [disabled]="!loginForm.form.valid">Login</button>
 | 
			
		||||
    <span class="message">{{message}}</span>
 | 
			
		||||
    <rb-form-input *ngIf="passreset" type="email" name="email" label="email" email required [(ngModel)]="email" #emailInput="ngModel">
 | 
			
		||||
      <ng-template rbFormValidationMessage="failure">{{emailInput.errors.failure}}</ng-template>
 | 
			
		||||
    </rb-form-input>
 | 
			
		||||
    <a href="#" class="forgot-pass" (click)="passreset = !passreset">Forgot password</a>
 | 
			
		||||
    <button class="rb-btn rb-primary login-button" (click)="login()" type="submit" [disabled]="!loginForm.form.valid">{{passreset ? 'Send' : 'Login'}}</button>
 | 
			
		||||
    <div class="message">{{message}}</div>
 | 
			
		||||
  </form>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,14 @@
 | 
			
		||||
 | 
			
		||||
.message {
 | 
			
		||||
  font-size: 13px;
 | 
			
		||||
  white-space: pre-line;
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-button {
 | 
			
		||||
  margin-right: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.forgot-pass {
 | 
			
		||||
  display: block;
 | 
			
		||||
  margin-bottom: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import {Component, OnInit, ViewChild} from '@angular/core';
 | 
			
		||||
import {ValidationService} from '../services/validation.service';
 | 
			
		||||
import {LoginService} from '../services/login.service';
 | 
			
		||||
import {Router} from '@angular/router';
 | 
			
		||||
import {ApiService} from '../services/api.service';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@@ -13,13 +14,17 @@ export class LoginComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  username = '';  // credentials
 | 
			
		||||
  password = '';
 | 
			
		||||
  email = '';
 | 
			
		||||
  message = '';  // message below login fields
 | 
			
		||||
  passreset = false;
 | 
			
		||||
 | 
			
		||||
  @ViewChild('loginForm') loginForm;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private validate: ValidationService,
 | 
			
		||||
    private loginService: LoginService,
 | 
			
		||||
    private api: ApiService,
 | 
			
		||||
    private router: Router
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
@@ -27,6 +32,17 @@ export class LoginComponent implements OnInit {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  login() {
 | 
			
		||||
    if (this.passreset) {
 | 
			
		||||
      this.api.post('/user/passreset', {name: this.username, email: this.email}, (data, err) => {
 | 
			
		||||
        if (err) {
 | 
			
		||||
          this.message = 'Could not find a valid user';
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          this.message = 'Password reset, check your inbox';
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      this.loginService.login(this.username, this.password).then(ok => {
 | 
			
		||||
        if (ok) {
 | 
			
		||||
          this.message = 'Login successful';
 | 
			
		||||
@@ -38,3 +54,4 @@ export class LoginComponent implements OnInit {
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								src/app/models/user.model.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/app/models/user.model.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
import { UserModel } from './user.model';
 | 
			
		||||
 | 
			
		||||
describe('User.Model', () => {
 | 
			
		||||
  it('should create an instance', () => {
 | 
			
		||||
    expect(new UserModel()).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										28
									
								
								src/app/models/user.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/app/models/user.model.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import {BaseModel} from './base.model';
 | 
			
		||||
import {IdModel} from './id.model';
 | 
			
		||||
 | 
			
		||||
export class UserModel extends BaseModel{
 | 
			
		||||
  _id: IdModel = null;
 | 
			
		||||
  name = '';
 | 
			
		||||
  origName = '';
 | 
			
		||||
  email = '';
 | 
			
		||||
  level = '';
 | 
			
		||||
  location = '';
 | 
			
		||||
  device_name = '';
 | 
			
		||||
  edit = false;
 | 
			
		||||
 | 
			
		||||
  deserialize(input: any): this {
 | 
			
		||||
    Object.assign(this, input);
 | 
			
		||||
    this.origName = this.name;
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sendFormat(mode = 'user') {
 | 
			
		||||
    const keys = ['name', 'email', 'location', 'device_name'];
 | 
			
		||||
    if (mode === 'admin') {
 | 
			
		||||
      keys.push('level');
 | 
			
		||||
    }
 | 
			
		||||
    return _.pick(this, keys);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
<button class="rb-btn rb" [ngClass]="'rb-' + mode" type="button" [disabled]="disabled">
 | 
			
		||||
<button class="rb-btn rb" [ngClass]="'rb-' + mode" [type]="type" [disabled]="disabled">
 | 
			
		||||
  <span class="rb-ic" [ngClass]="'rb-ic-' + icon"></span>  
 | 
			
		||||
  <ng-content></ng-content>
 | 
			
		||||
</button>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ export class RbIconButtonComponent implements OnInit {
 | 
			
		||||
  @Input() icon: string;
 | 
			
		||||
  @Input() mode: string;
 | 
			
		||||
  @Input() disabled;
 | 
			
		||||
  @Input() type = 'button';
 | 
			
		||||
 | 
			
		||||
  constructor() { }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
<h2>{{new ? 'Add new sample' : 'Edit sample ' + sample.number}}</h2>
 | 
			
		||||
<script src="../models/template.model.ts"></script><h2>{{new ? 'Add new sample' : 'Edit sample ' + sample.number}}</h2>
 | 
			
		||||
 | 
			
		||||
<rb-loading-spinner *ngIf="loading"></rb-loading-spinner>
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
<!--<form #sampleForm="ngForm">-->
 | 
			
		||||
  <div class="sample">
 | 
			
		||||
    <div>
 | 
			
		||||
      <rb-form-input name="materialname" label="material name" [rbFormInputAutocomplete]="autocomplete.bind(this, materialNames)" [rbDebounceTime]="0" [rbInitialOpen]="true" (keydown)="preventDefault($event)" (ngModelChange)="findMaterial($event)" appValidate="stringOf" [appValidateArgs]="[materialNames]" required [(ngModel)]="material.name" [autofocus]="true" #materialNameInput="ngModel">
 | 
			
		||||
      <rb-form-input name="materialname" label="material name" [rbFormInputAutocomplete]="autocomplete.bind(this, materialNames)" [rbDebounceTime]="0" [rbInitialOpen]="true" (keydown)="preventDefault($event)" (ngModelChange)="findMaterial($event)" appValidate="stringOf" [appValidateArgs]="[materialNames]" required [(ngModel)]="material.name" [autofocus]="true" ngModel>
 | 
			
		||||
        <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
        <ng-template rbFormValidationMessage="failure">Unknown material, add properties for new material</ng-template>
 | 
			
		||||
      </rb-form-input>
 | 
			
		||||
@@ -57,7 +57,7 @@
 | 
			
		||||
    <h5>Sample references</h5>
 | 
			
		||||
    <div *ngFor="let reference of sampleReferences; index as i" class="two-col" [@inOut]>
 | 
			
		||||
      <div>
 | 
			
		||||
        <rb-form-input [name]="'sr-id' + i" label="sample number" [rbFormInputAutocomplete]="sampleReferenceListBind()" [rbDebounceTime]="300" appValidate="stringOf" [appValidateArgs]="[sampleReferenceAutocomplete[i]]" (ngModelChange)="checkSampleReference($event, i)" [ngModel]="reference[0]" #idInput="ngModel">
 | 
			
		||||
        <rb-form-input [name]="'sr-id' + i" label="sample number" [rbFormInputAutocomplete]="sampleReferenceListBind()" [rbDebounceTime]="300" appValidate="stringOf" [appValidateArgs]="[sampleReferenceAutocomplete[i]]" (ngModelChange)="checkSampleReference($event, i)" [ngModel]="reference[0]" ngModel>
 | 
			
		||||
          <ng-template rbFormValidationMessage="failure">Unknown sample number</ng-template>
 | 
			
		||||
        </rb-form-input>
 | 
			
		||||
      </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ import {Observable} from 'rxjs';
 | 
			
		||||
 | 
			
		||||
// TODO: material properties, color (in material and sample (not required))
 | 
			
		||||
 | 
			
		||||
// TODO: API $in Regex
 | 
			
		||||
// TODO: device autocomplete
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-sample',
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,17 @@ import {Observable} from 'rxjs';
 | 
			
		||||
})
 | 
			
		||||
export class LoginService implements CanActivate {
 | 
			
		||||
 | 
			
		||||
  private maintainPaths = ['templates'];
 | 
			
		||||
  private pathPermissions = [
 | 
			
		||||
    {path: 'templates', permission: 'maintain'},
 | 
			
		||||
    {path: 'users', permission: 'admin'}
 | 
			
		||||
  ];
 | 
			
		||||
  readonly levels = [
 | 
			
		||||
    'read',
 | 
			
		||||
    'write',
 | 
			
		||||
    'maintain',
 | 
			
		||||
    'dev',
 | 
			
		||||
    'admin'
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  private loggedIn;
 | 
			
		||||
  private level;
 | 
			
		||||
@@ -23,9 +33,28 @@ export class LoginService implements CanActivate {
 | 
			
		||||
 | 
			
		||||
  login(username = '', password = '') {
 | 
			
		||||
    return new Promise(resolve => {
 | 
			
		||||
      if (username !== '') {
 | 
			
		||||
      console.log(username);
 | 
			
		||||
      console.log(password);
 | 
			
		||||
      if (username !== '' || password !== '') {  // some credentials given
 | 
			
		||||
        let credentials: string[];
 | 
			
		||||
        const credentialString: string = this.storage.get('basicAuth');
 | 
			
		||||
        if (credentialString) {  // found stored credentials
 | 
			
		||||
          credentials = atob(credentialString).split(':');
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          credentials = ['', ''];
 | 
			
		||||
        }
 | 
			
		||||
        if (username !== '' && password !== '') {  // all credentials given
 | 
			
		||||
          this.storage.set('basicAuth', btoa(username + ':' + password));
 | 
			
		||||
        }
 | 
			
		||||
        else if (username !== '') {  // username given
 | 
			
		||||
          this.storage.set('basicAuth', btoa(username + ':' + credentials[1]));
 | 
			
		||||
        }
 | 
			
		||||
        else if (password !== '') {  // password given
 | 
			
		||||
          this.storage.set('basicAuth', btoa(credentials[0] + ':' + password));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      console.log(this.storage.get('basicAuth'));
 | 
			
		||||
      this.api.get('/authorized', (data: any, error) => {
 | 
			
		||||
        if (!error) {
 | 
			
		||||
          if (data.status === 'Authorization successful') {
 | 
			
		||||
@@ -53,8 +82,8 @@ export class LoginService implements CanActivate {
 | 
			
		||||
 | 
			
		||||
  canActivate(route: ActivatedRouteSnapshot = null, state: RouterStateSnapshot = null): Observable<boolean> {
 | 
			
		||||
    return new Observable<boolean>(observer => {
 | 
			
		||||
      const isMaintainPath = this.maintainPaths.indexOf(route.url[0].path) >= 0;
 | 
			
		||||
      if (!isMaintainPath || (isMaintainPath && this.isMaintain)) {
 | 
			
		||||
      const pathPermission = this.pathPermissions.find(e => e.path.indexOf(route.url[0].path) >= 0);
 | 
			
		||||
      if (!pathPermission || this.is(pathPermission.permission)) {  // check if level is permitted for path
 | 
			
		||||
        if (this.loggedIn === undefined) {
 | 
			
		||||
          this.login().then(res => {
 | 
			
		||||
            observer.next(res as any);
 | 
			
		||||
@@ -77,8 +106,8 @@ export class LoginService implements CanActivate {
 | 
			
		||||
    return this.loggedIn;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get isMaintain() {
 | 
			
		||||
    return this.level === 'maintain' || this.level === 'admin';
 | 
			
		||||
  is(level) {
 | 
			
		||||
    return this.levels.indexOf(this.level) >= this.levels.indexOf(level);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get username() {
 | 
			
		||||
 
 | 
			
		||||
@@ -66,10 +66,16 @@ export class ValidationService {
 | 
			
		||||
    return {ok: true, error: ''};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  string(data) {
 | 
			
		||||
    const {ignore, error} = Joi.string().max(128).allow('').validate(data);
 | 
			
		||||
  string(data, option = null) {
 | 
			
		||||
    let validator = Joi.string().max(128).allow('');
 | 
			
		||||
    let errorMsg = 'must contain max 128 characters';
 | 
			
		||||
    if (option === 'alphanum') {
 | 
			
		||||
      validator = validator.alphanum();
 | 
			
		||||
      errorMsg = 'must contain max 128 alphanumerical characters';
 | 
			
		||||
    }
 | 
			
		||||
    const {ignore, error} = validator.validate(data);
 | 
			
		||||
    if (error) {
 | 
			
		||||
      return {ok: false, error: 'must contain max 128 characters'};
 | 
			
		||||
      return {ok: false, error: errorMsg};
 | 
			
		||||
    }
 | 
			
		||||
    return {ok: true, error: ''};
 | 
			
		||||
  }
 | 
			
		||||
@@ -122,6 +128,13 @@ export class ValidationService {
 | 
			
		||||
    return {ok: true, error: ''};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  equal(data, compare) {
 | 
			
		||||
    if (data !== compare) {
 | 
			
		||||
      return {ok: false, error: `must be equal`};
 | 
			
		||||
    }
 | 
			
		||||
    return {ok: true, error: ''};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  parameterName(data) {
 | 
			
		||||
    const {ignore, error} = Joi.string()
 | 
			
		||||
      .max(128)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								src/app/settings/settings.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/app/settings/settings.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
<h2>Settings</h2>
 | 
			
		||||
 | 
			
		||||
<form #userForm="ngForm">
 | 
			
		||||
  <rb-form-input name="name" label="user name" appValidate="username" required [(ngModel)]="user.name"
 | 
			
		||||
                 #nameInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="email" label="email" email required [(ngModel)]="user.email" ngModel>
 | 
			
		||||
    <ng-template rbFormValidationMessage="email">Invalid email</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="location" label="location" appValidate="string" required [appValidateArgs]="['alphanum']"
 | 
			
		||||
                 [(ngModel)]="user.location" #locationInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{locationInput.errors.failure}}</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="device" label="device" appValidate="string" [(ngModel)]="user.device_name"
 | 
			
		||||
                 #deviceInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{deviceInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-icon-button icon="save" mode="primary" type="submit" [disabled]="!userForm.form.valid" (click)="saveUser()">
 | 
			
		||||
    Save change
 | 
			
		||||
  </rb-icon-button>
 | 
			
		||||
  <span class="message">{{messageUser}}</span>
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<h4 class="pass-heading">Change password</h4>
 | 
			
		||||
 | 
			
		||||
<form #passForm="ngForm">
 | 
			
		||||
  <rb-form-input name="passA" type="password" label="new password" appValidate="password" required
 | 
			
		||||
                 [(ngModel)]="password" #passAInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{passAInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="passB" type="password" label="confirm password" appValidate="equal"
 | 
			
		||||
                 [appValidateArgs]="[password]" required #passBInput="ngModel" ngModel>
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{passBInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <button class="rb-btn rb-primary" type="submit" [disabled]="!passForm.form.valid" (click)="savePass()">
 | 
			
		||||
    Change password
 | 
			
		||||
  </button>
 | 
			
		||||
  <span class="message">{{messagePass}}</span>
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								src/app/settings/settings.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/app/settings/settings.component.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
.pass-heading {
 | 
			
		||||
  margin-top: 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.message {
 | 
			
		||||
  margin-left: 20px;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/app/settings/settings.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/settings/settings.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { SettingsComponent } from './settings.component';
 | 
			
		||||
 | 
			
		||||
describe('SettingsComponent', () => {
 | 
			
		||||
  let component: SettingsComponent;
 | 
			
		||||
  let fixture: ComponentFixture<SettingsComponent>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      declarations: [ SettingsComponent ]
 | 
			
		||||
    })
 | 
			
		||||
    .compileComponents();
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    fixture = TestBed.createComponent(SettingsComponent);
 | 
			
		||||
    component = fixture.componentInstance;
 | 
			
		||||
    fixture.detectChanges();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(component).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										68
									
								
								src/app/settings/settings.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/app/settings/settings.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import {ApiService} from '../services/api.service';
 | 
			
		||||
import {UserModel} from '../models/user.model';
 | 
			
		||||
import {Router} from '@angular/router';
 | 
			
		||||
import {LoginService} from '../services/login.service';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-settings',
 | 
			
		||||
  templateUrl: './settings.component.html',
 | 
			
		||||
  styleUrls: ['./settings.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class SettingsComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  user: UserModel = new UserModel();
 | 
			
		||||
  password = '';
 | 
			
		||||
  messageUser = '';
 | 
			
		||||
  messagePass = '';
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private api: ApiService,
 | 
			
		||||
    private login: LoginService,
 | 
			
		||||
    private router: Router
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.api.get<UserModel>('/user', data => {
 | 
			
		||||
      this.user.deserialize(data);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  saveUser() {
 | 
			
		||||
    this.api.put<UserModel>('/user', this.user.sendFormat(), (data, err) => {
 | 
			
		||||
      if (err) {
 | 
			
		||||
        this.messageUser = err.error.status;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        this.login.login(data.name).then(res => {
 | 
			
		||||
          if (res) {
 | 
			
		||||
            this.router.navigate(['/samples']);
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            this.messageUser = 'request not successful, try again';
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  savePass() {
 | 
			
		||||
    this.api.put<UserModel>('/user', {pass: this.password}, (ignore, err) => {
 | 
			
		||||
      if (err) {
 | 
			
		||||
        this.messagePass = err.error.status;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        this.login.login('', this.password).then(res => {
 | 
			
		||||
          if (res) {
 | 
			
		||||
            this.router.navigate(['/samples']);
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            this.messagePass = 'request not successful, try again';
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -38,7 +38,3 @@
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.clickable {
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								src/app/users/users.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/app/users/users.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
<h2>Users</h2>
 | 
			
		||||
 | 
			
		||||
<rb-icon-button icon="add" mode="primary" (click)="addNewUser()">New user</rb-icon-button>
 | 
			
		||||
 | 
			
		||||
<form *ngIf="newUser" #userForm="ngForm">
 | 
			
		||||
  <rb-form-input name="name" label="user name" appValidate="username" required [(ngModel)]="newUser.name"
 | 
			
		||||
                 #nameInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="email" label="email" email required [(ngModel)]="newUser.email" ngModel>
 | 
			
		||||
    <ng-template rbFormValidationMessage="email">Invalid email</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-select name="level" label="level" required [(ngModel)]="newUser.level">
 | 
			
		||||
    <option *ngFor="let level of login.levels" [value]="level">{{level}}</option>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </rb-form-select>
 | 
			
		||||
  <rb-form-input name="location" label="location" appValidate="string" required [appValidateArgs]="['alphanum']"
 | 
			
		||||
                 [(ngModel)]="newUser.location" #locationInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{locationInput.errors.failure}}</ng-template>
 | 
			
		||||
    <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="device" label="device" appValidate="string" [(ngModel)]="newUser.device_name"
 | 
			
		||||
                 #deviceInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{deviceInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="passA" type="password" label="new password" appValidate="password" required
 | 
			
		||||
                 [(ngModel)]="newUserPass" #passAInput="ngModel">
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{passAInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input name="passB" type="password" label="confirm password" appValidate="equal"
 | 
			
		||||
                 [appValidateArgs]="[newUserPass]" required #passBInput="ngModel" ngModel>
 | 
			
		||||
    <ng-template rbFormValidationMessage="failure">{{passBInput.errors.failure}}</ng-template>
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-icon-button icon="save" mode="primary" type="submit" [disabled]="!userForm.form.valid" (click)="saveNewUser()">
 | 
			
		||||
    Save user
 | 
			
		||||
  </rb-icon-button>
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
<rb-table>
 | 
			
		||||
  <tr>
 | 
			
		||||
    <th>Name</th>
 | 
			
		||||
    <th>Email</th>
 | 
			
		||||
    <th>Level</th>
 | 
			
		||||
    <th>Location</th>
 | 
			
		||||
    <th>Device</th>
 | 
			
		||||
    <th></th>
 | 
			
		||||
  </tr>
 | 
			
		||||
 | 
			
		||||
  <tr *ngFor="let user of users">
 | 
			
		||||
    <ng-container *ngIf="!user.edit; else editUser">
 | 
			
		||||
      <td>{{user.name}}</td>
 | 
			
		||||
      <td>{{user.email}}</td>
 | 
			
		||||
      <td>{{user.level}}</td>
 | 
			
		||||
      <td>{{user.location}}</td>
 | 
			
		||||
      <td>{{user.device_name}}</td>
 | 
			
		||||
      <td><span class="rb-ic rb-ic-edit clickable" (click)="user.edit = true"></span></td>
 | 
			
		||||
    </ng-container>
 | 
			
		||||
    <ng-template #editUser>
 | 
			
		||||
      <td>
 | 
			
		||||
        <rb-form-input [name]="'name-' + user.name" appValidate="username" required [(ngModel)]="user.name" #nameInput="ngModel">
 | 
			
		||||
          <ng-template rbFormValidationMessage="failure">{{nameInput.errors.failure}}</ng-template>
 | 
			
		||||
          <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
        </rb-form-input>
 | 
			
		||||
      </td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <rb-form-input [name]="'email-' + user.name" email required [(ngModel)]="user.email"  #emailInput="ngModel">
 | 
			
		||||
          <ng-template rbFormValidationMessage="email">Invalid email</ng-template>
 | 
			
		||||
          <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
        </rb-form-input>
 | 
			
		||||
      </td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <rb-form-select [name]="'level-' + user.name" [(ngModel)]="user.level">
 | 
			
		||||
          <option *ngFor="let level of login.levels" [value]="level">{{level}}</option>
 | 
			
		||||
        </rb-form-select>
 | 
			
		||||
      </td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <rb-form-input [name]="'location-' + user.name" appValidate="string" required [appValidateArgs]="['alphanum']"
 | 
			
		||||
                       [(ngModel)]="user.location" #locationInput="ngModel">
 | 
			
		||||
          <ng-template rbFormValidationMessage="failure">{{locationInput.errors.failure}}</ng-template>
 | 
			
		||||
          <ng-template rbFormValidationMessage="required">Cannot be empty</ng-template>
 | 
			
		||||
        </rb-form-input>
 | 
			
		||||
      </td>
 | 
			
		||||
      <td>
 | 
			
		||||
        <rb-form-input [name]="'device-' + user.name" appValidate="string" [(ngModel)]="user.device_name"
 | 
			
		||||
                       #deviceInput="ngModel">
 | 
			
		||||
          <ng-template rbFormValidationMessage="failure">{{deviceInput.errors.failure}}</ng-template>
 | 
			
		||||
        </rb-form-input>
 | 
			
		||||
      </td>
 | 
			
		||||
      <td><rb-icon-button icon="save" mode="primary" (click)="saveUser(user)" [disabled]="nameInput.invalid || emailInput.invalid || locationInput.invalid || deviceInput.invalid">Save</rb-icon-button></td>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
  </tr>
 | 
			
		||||
</rb-table>
 | 
			
		||||
							
								
								
									
										3
									
								
								src/app/users/users.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/app/users/users.component.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
::ng-deep td .error-messages {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/app/users/users.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/users/users.component.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { UsersComponent } from './users.component';
 | 
			
		||||
 | 
			
		||||
describe('UsersComponent', () => {
 | 
			
		||||
  let component: UsersComponent;
 | 
			
		||||
  let fixture: ComponentFixture<UsersComponent>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      declarations: [ UsersComponent ]
 | 
			
		||||
    })
 | 
			
		||||
    .compileComponents();
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    fixture = TestBed.createComponent(UsersComponent);
 | 
			
		||||
    component = fixture.componentInstance;
 | 
			
		||||
    fixture.detectChanges();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(component).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										47
									
								
								src/app/users/users.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/app/users/users.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import {ApiService} from '../services/api.service';
 | 
			
		||||
import {UserModel} from '../models/user.model';
 | 
			
		||||
import {LoginService} from '../services/login.service';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-users',
 | 
			
		||||
  templateUrl: './users.component.html',
 | 
			
		||||
  styleUrls: ['./users.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class UsersComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  users: UserModel[] = [];
 | 
			
		||||
  newUser: UserModel | null = null;
 | 
			
		||||
  newUserPass = '';
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private api: ApiService,
 | 
			
		||||
    public login: LoginService
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.api.get<UserModel[]>('/users', data => {
 | 
			
		||||
      this.users = data.map(e => new UserModel().deserialize(e));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  saveUser(user: UserModel) {
 | 
			
		||||
    this.api.put<UserModel>('/user/' + user.origName, user.sendFormat('admin'), data => {
 | 
			
		||||
      user.deserialize(data);
 | 
			
		||||
      user.edit = false;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  saveNewUser() {
 | 
			
		||||
    this.api.post('/user/new', {...this.newUser.sendFormat('admin'), pass: this.newUserPass}, data => {
 | 
			
		||||
      this.newUser = null;
 | 
			
		||||
      this.users.push(new UserModel().deserialize(data));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addNewUser() {
 | 
			
		||||
    this.newUser = this.newUser ? null : new UserModel();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -19,3 +19,7 @@ button::-moz-focus-inner {
 | 
			
		||||
.supergraphic {
 | 
			
		||||
  background-image: url("assets/imgs/supergraphic.svg");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.clickable {
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user