settings and users dialog
This commit is contained in:
parent
55248e25ef
commit
4876ba3c0c
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user