settings and users dialog

This commit is contained in:
VLE2FE 2020-07-29 13:14:29 +02:00
parent 55248e25ef
commit 4876ba3c0c
26 changed files with 479 additions and 41 deletions

View File

@ -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"

View File

@ -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

View File

@ -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>&nbsp;&nbsp;Settings</a>
<button type="button" class="rb-btn rb-primary" (click)="logout()">Logout</button>
</div>
</ng-template>

View File

@ -3,3 +3,9 @@
font-size: 32px;
margin-right: 40px;
}
.spacing {
display: grid;
grid-template-columns: 1fr;
grid-row-gap: 10px;
}

View File

@ -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({

View File

@ -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>

View File

@ -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;
}

View File

@ -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 {
});
}
}
}

View File

@ -0,0 +1,7 @@
import { UserModel } from './user.model';
describe('User.Model', () => {
it('should create an instance', () => {
expect(new UserModel()).toBeTruthy();
});
});

View 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);
}
}

View File

@ -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>&nbsp;&nbsp;
<ng-content></ng-content>
</button>

View File

@ -13,6 +13,7 @@ export class RbIconButtonComponent implements OnInit {
@Input() icon: string;
@Input() mode: string;
@Input() disabled;
@Input() type = 'button';
constructor() { }

View File

@ -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>

View File

@ -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',

View File

@ -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() {

View File

@ -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)

View 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>

View File

@ -0,0 +1,7 @@
.pass-heading {
margin-top: 40px;
}
.message {
margin-left: 20px;
}

View 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();
});
});

View 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';
}
});
}
});
}
}

View File

@ -38,7 +38,3 @@
}
}
}
.clickable {
cursor: pointer;
}

View 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>

View File

@ -0,0 +1,3 @@
::ng-deep td .error-messages {
position: absolute;
}

View 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();
});
});

View 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();
}
}

View File

@ -19,3 +19,7 @@ button::-moz-focus-inner {
.supergraphic {
background-image: url("assets/imgs/supergraphic.svg");
}
.clickable {
cursor: pointer;
}