[DEV] add basic interfaces to see users

This commit is contained in:
Edouard DUPIN 2023-01-06 00:26:47 +01:00
parent 0c5f524906
commit 80a9806bf4
8 changed files with 572 additions and 1 deletions

View File

@ -16,6 +16,7 @@ import {
SignOutScene,
SignUpScene,
HomeUnregisteredScene,
ManageAccountsScene,
} from './scene';
import { OnlyAdminGuard, OnlyUnregisteredGuardHome, OnlyUsersGuard, OnlyUsersGuardHome } from 'common/service/session';
import { ForbiddenScene, NotFound404Scene } from 'common/scene';
@ -66,6 +67,11 @@ const routes: Routes = [
component: SettingsScene,
canActivate: [OnlyAdminGuard],
},
{
path: 'manage_accounts',
component: ManageAccountsScene,
canActivate: [OnlyAdminGuard],
},
{
path: '**',
component: NotFound404Scene,

View File

@ -102,9 +102,16 @@ export class AppComponent implements OnInit {
position: MenuPosition.LEFT,
hover: 'Admin configuration karso management',
icon: 'settings',
title: 'Settings',
title: 'Admin Settings',
navigateTo: 'settings',
enable: this.sessionService.userAdmin === true,
}, { // TODO move this in the setting environment system ?
position: MenuPosition.LEFT,
hover: 'Admin of users',
icon: 'manage_accounts',
title: 'Manage Accounts',
navigateTo: 'manage_accounts',
enable: this.sessionService.userAdmin === true,
},
],
},

View File

@ -22,6 +22,7 @@ import {
ChangePasswordScene,
SettingsScene,
HomeUnregisteredScene,
ManageAccountsScene,
} from 'app/scene';
import {
BddService,
@ -69,6 +70,7 @@ import { PasswordEntryComponent } from './component';
ForbiddenScene,
ChangePasswordScene,
HomeUnregisteredScene,
ManageAccountsScene,
],
imports: [
BrowserModule,

View File

@ -4,6 +4,7 @@ import { ForgotPasswordScene } from './forgot-password/forgot-password';
import { HelpScene } from './help/help';
import { HomeUnregisteredScene } from './home-unregistered/home-unregistered';
import { HomeScene } from './home/home';
import { ManageAccountsScene } from './manage-accounts/manage-accounts';
import { SettingsScene } from './settings/settings';
import { SignInScene } from './sign-in/sign-in';
import { SignOutScene } from './sign-out/sign-out';
@ -22,4 +23,5 @@ export {
ChangePasswordScene,
SettingsScene,
HomeUnregisteredScene,
ManageAccountsScene,
};

View File

@ -0,0 +1,100 @@
<div class="generic-page">
<div class="title">Manage User Accounts</div>
<div class="fill-all">
<div>
<div class="settings-title">
<div class="group-title">Current users</div>
<div class="group-description">List of all users</div>
</div>
<div class="settings-elements">
<table style="width:100%;">
<tr>
<th>id</th>
<th>login</th>
<th>e-mail</th>
<th>admin</th>
<th>blocked</th>
<th>avatar</th>
<th>lastConnection</th>
</tr>
<tr *ngFor="let user of users">
<th>{{user.id}}</th>
<th>{{user.login}}</th>
<th>{{user.email}}</th>
<th>{{user.admin}}</th>
<th>{{user.blocked}}</th>
<th>{{user.avatar}}</th>
<th>{{user.lastConnection}}</th>
</tr>
</table>
</div>
<div class="settings-validation">
</div>
</div>
</div>
<div class="clear"><br/></div>
<div style="padding-top:15px;">
<div class="settings-title">
<div class="group-title">Create new user</div>
<div class="group-description">Add a new user on the server</div>
</div>
<div class="settings-elements">
<table style="width:100%;">
<tr>
<td width="25%"><b>Login:</b></td>
<td width="75%">
<input
id="username"
name="username"
class="{{loginIcon}}"
type="text"
required=""
placeholder="Pick a Username"
[value]="login"
(input)="checkLogin($event.target.value)" />
<div class="error color-shadow-black" *ngIf="loginHelp">{{loginHelp}}</div>
</td>
</tr>
<tr>
<td><b>email:</b></td>
<td>
<input
id="email"
name="e-mail"
class="{{emailIcon}}"
type="text"
required=""
placeholder="you@example.com"
[value]="email"
(input)="checkEmail($event.target.value)" />
<div class="error color-shadow-black" *ngIf="emailHelp">{{emailHelp}}</div>
</td>
</tr>
<tr>
<td><b>Password:</b></td>
<td>
<app-password-entry
[value]="password"
placeholder="Enter new password (verify)"
[hasError]="passwordState !== true"
(changeValue)="checkPassword($event)"></app-password-entry>
<div class="error color-shadow-black" *ngIf="passwordState !== true && passwordState !== false">{{passwordState}}</div>
</td>
<tr>
</table>
<button
class="button login color-button-validate color-shadow-black"
id="login-button"
[disabled]="validateButtonCreateUserDisabled"
(click)="onCreateUser()"
type="submit">
Create user
</button>
</div>
<div class="settings-validation">
</div>
</div>
<div class="clear"></div>
</div>

View File

@ -0,0 +1,151 @@
.settings-title {
width: 80%;
border: solid;
border-width: 1px;
margin: auto;
padding: 10px 20px;
border-color: black;
border-radius: 10px 10px 0px 0px;
background-color: gray;
.group-title {
font-size: 24px;
font-weight: bold;
line-height: 24px;
vertical-align: middle;
margin: 10px 0 10px 0;
text-align: Left;
}
.group-description {
font-size: 16px;
line-height: 16px;
vertical-align: middle;
margin: 10px 0 10px 0;
text-align: Left;
}
}
.settings-elements {
width: 80%;
border: solid;
border-width: 0px 1px;
margin: auto;
padding: 10px 20px;
border-color: black;
border-radius: 0px;
background-color: lightgray;
vertical-align: middle;
text-align: Left;
.elem-hr {
width: 100%;
border-color: black;
border: dashed;
border-width: 1px 0 0 0;
margin: 10px auto;
}
.elem-checkbox {
font-size: 18px;
font-weight: bold;
margin: 0 10px 0 10px;
}
.elem-title {
font-size: 18px;
font-weight: bold;
margin: 0 10px 0 36px;
}
.elem-description {
font-size: 14px;
margin: 0 20px 10px 50px;
font-style: italic;
}
.elem-input {
font-size: 14px;
margin: 0 20px 10px 36px;
input {
width: 100%;
}
}
}
.settings-validation {
width: 80%;
border: solid;
border-width: 1px;
margin: auto;
padding: 10px 20px;
border-color: black;
border-radius: 0px 0px 10px 10px;
background-color: gray;
}
.title {
//background-color: green;
font-size: 45px;
font-weight: bold;
line-height: 60px;
width: 100%;
text-align: center;
vertical-align: middle;
margin: 10px 0 10px 0;
text-shadow: 1px 1px 2px white, 0 0 1em white, 0 0 0.2em white;
text-transform: uppercase;
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
}
.item-home {
background-color: rgba(200, 200, 200, 0.5);
font-size: 20px;
height: 190px;
width: 200px;
margin: 5px;
padding: 0;
overflow: hidden;
// box-shadow: 0 1px 1.5px 0 rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24);
box-shadow: 0px 2px 4px 0 rgba(0, 0, 0, 0.6);
line-height: normal;
border: none;
font-family: 'Roboto', 'Helvetica', 'Arial', 'sans-serif';
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0;
will-change: box-shadow;
outline: none;
cursor: pointer;
text-decoration: none;
text-align: center;
transition-duration: 0.4s;
float: left;
display: block;
h1 {
font-size: 24px;
}
&:hover {
background-color: rgba(200, 200, 200, 1);
//box-shadow: 0px 2px 4px 0 rgba(255, 0, 0, 0.6);
}
.material-icons {
vertical-align: middle;
}
.material-icons {
position: absolute;
top: 50%;
left: 50%;
transform: ~'translate(-12px,-12px)';
line-height: 24px;
width: 24px;
}
}
@media all and (min-width: 1310px) {
.colomn_mutiple {
width: ~'calc(210px * 5)';
margin: auto;
}
}

View File

@ -0,0 +1,285 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AdminUserService, SettingsService } from 'app/service';
import {
checkEmailValidity,
checkLoginValidity,
createPasswordState,
isArrayOf,
isBoolean,
isInArray,
isNullOrUndefined,
isNumber,
isObject,
isOptionalArrayOf,
isOptionalOf,
isString,
isUndefined,
} from 'common/utils';
export enum SettingType {
TITLE = 'TITLE',
GROUP = 'GROUP',
LINE = 'LINE',
BOOLEAN = 'BOOLEAN',
STRING = 'STRING',
PASSWORD = 'PASSWORD',
}
export function isSettingType(data: any): data is SettingType {
return isInArray(data, ['TITLE', 'GROUP', 'LINE', 'BOOLEAN', 'STRING', 'PASSWORD']);
}
export interface SettingsItem {
// Type of the menu Node
type: SettingType;
// Displayed Title
title?: string;
// Description of the parameter
description?: string;
// Image to dsplay that describe the parameter
image?: string;
// If true the parameter is applied directly to the backend
directApply?: boolean;
// Parameter key to SET/GET
key?: string;
// Parameter key to SET/GET or the sub-menu
value?: SettingsItem[] | boolean | string | Number;
// whendata is change the value is set here:
newValue?: boolean | string | Number;
}
export function isSettingsItem(data: any): data is SettingsItem {
if (isNullOrUndefined(data)) {
return false;
}
if (!isObject(data)) {
return false;
}
if (!isSettingType(data.type)) {
return false;
}
if (!isOptionalOf(data.title, isString)) {
return false;
}
if (!isOptionalOf(data.description, isString)) {
return false;
}
if (!isOptionalOf(data.image, isString)) {
return false;
}
if (!isOptionalOf(data.directApply, isBoolean)) {
return false;
}
if (!isOptionalOf(data.key, isString)) {
return false;
}
if (
!isOptionalOf(data.value, isBoolean) &&
!isOptionalOf(data.value, isString) &&
!isOptionalOf(data.value, isNumber) &&
!isOptionalArrayOf(data.value, isSettingsItem)
) {
return false;
}
if (
!isOptionalOf(data.newValue, isBoolean) &&
!isOptionalOf(data.newValue, isString) &&
!isOptionalOf(data.newValue, isNumber)
) {
return false;
}
return true;
}
@Component({
selector: 'app-settings',
templateUrl: './manage-accounts.html',
styleUrls: ['./manage-accounts.less'],
})
export class ManageAccountsScene implements OnInit {
users: any = [];
constructor(private adminUserService: AdminUserService) {
}
ngOnInit() {
let self = this;
this.adminUserService
.getUsers()
.then((response: any) => {
console.log(`??? get full response: ${JSON.stringify(response, null, 4)}`);
self.users = response;
})
.catch((error: any) => {
console.log(`??? get ERROR response: ${JSON.stringify(error, null, 4)}`);
});
}
public passwordState: boolean|string = false;
public password: string = '';
public validateButtonCreateUserDisabled: boolean = true;
/**
* update the state of the validation button. if all is OK, the button will became clickable
*/
updateButtonVisibility(): void {
if (this.passwordState === true && this.loginOK === true && this.emailOK === true) {
this.validateButtonCreateUserDisabled = false;
} else {
this.validateButtonCreateUserDisabled = true;
}
}
/**
* Check if the current password is valid or not (update error message)
* @param newValue New password value.
*/
checkPassword(newValue: string): void {
this.password = newValue;
this.passwordState = createPasswordState(this.password);
this.checkPassword(this.password);
this.updateButtonVisibility();
}
/**
* Request the creation of a new user.
*/
onCreateUser(): void {
}
private signUpIconWrong: string = 'icon-right-not-validate';
private signUpIconWait: string = 'icon-right-load';
private signUpIconRight: string = 'icon-right-validate';
public login: string = '';
public loginOK: boolean = false;
public loginHelp: string = '';
public loginIcon: string = '';
public email: string = '';
public emailOK: boolean = false;
public emailHelp: string = '';
public emailIcon: string = '';
checkLogin(newValue: string): void {
// this.userService.loginSha("loooogin", "ekljkj", true);
this.login = newValue;
if (this.login === null || this.login.length === 0) {
this.loginOK = false;
this.loginIcon = '';
//this.loginIcon = this.signUpIconWrong;
this.loginHelp = '';
this.updateButtonVisibility();
return;
}
if (this.login.length < 6) {
this.loginOK = false;
this.loginHelp = 'Need 6 characters';
this.loginIcon = this.signUpIconWrong;
this.updateButtonVisibility();
return;
}
if (checkLoginValidity(this.login) === true) {
this.loginOK = false;
// this.loginHelp = "check in progress...";
this.loginIcon = this.signUpIconWait;
let self = this;
this.adminUserService
.checkLogin(this.login)
.then((isNotUsed: boolean) => {
// check if the answer is correct with the question
if (newValue !== self.login) {
return;
}
if (isNotUsed === false) {
// the login exist ... ==> it is found...
self.loginOK = false;
self.loginHelp = 'Login already used ...';
self.loginIcon = self.signUpIconWrong;
self.updateButtonVisibility();
} else {
self.loginOK = true;
self.loginHelp = '';
self.loginIcon = self.signUpIconRight;
self.updateButtonVisibility();
return;
}
})
.catch((error: number) => {
console.log(`Status ${error}`);
self.loginOK = false;
self.loginHelp = 'ErrorOccured in fetching data ...';
self.loginIcon = self.signUpIconWrong;
self.updateButtonVisibility();
});
} else {
this.loginOK = false;
this.loginHelp = 'Not valid: characters, numbers and "_-."';
}
this.updateButtonVisibility();
}
checkEmail(newValue: string): void {
this.email = newValue;
if (this.email === null || this.email.length === 0) {
this.emailOK = false;
this.updateButtonVisibility();
this.emailIcon = '';
this.emailHelp = '';
return;
}
if (this.email.length < 6) {
this.emailOK = false;
this.emailHelp = 'Need 6 characters';
this.updateButtonVisibility();
this.emailIcon = this.signUpIconWrong;
return;
}
if (checkEmailValidity(this.email) === true) {
this.emailOK = false;
this.emailHelp = '';
// this.loginHelp = "check in progress...";
this.emailIcon = this.signUpIconWait;
let self = this;
this.adminUserService.checkEMail(this.email).then(
(isNotUsed: boolean) => {
// check if the answer is correct with the question
if (newValue !== self.email) {
return;
}
if (isNotUsed === false) {
// the email exist ... ==> it is found...
self.emailOK = false;
self.emailHelp = 'email already used ...';
self.emailIcon = self.signUpIconWrong;
self.updateButtonVisibility();
} else {
self.emailOK = true;
self.emailHelp = '';
self.emailIcon = self.signUpIconRight;
self.updateButtonVisibility();
return;
}
},
(error: number) => {
console.log(`Status ${error}`);
self.emailOK = false;
self.emailHelp = 'ErrorOccured in fetching data ...';
self.emailIcon = self.signUpIconWrong;
self.updateButtonVisibility();
}
);
} else {
this.emailOK = false;
this.emailHelp = 'Not valid: characters, numbers, "_-." and email format: you@example.com';
}
this.updateButtonVisibility();
}
}

View File

@ -118,6 +118,24 @@ export class AdminUserService {
});
});
}
getUsers(): Promise<any> {
return new Promise((resolve, reject) => {
this.http
.requestJson({
server: 'karso',
endPoint: 'users',
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
contentType: HTTPMimeType.JSON
})
.then((response: ModelResponseHttp) => {
resolve(response.data);
})
.catch((error: any) => {
reject(`return ERROR ${JSON.stringify(error, null, 2)}`);
});
});
}
create(login: string, email: string, password: string) {
return this.createSha(login, email, sha512(password));