diff --git a/front/src/app/app-routing.module.ts b/front/src/app/app-routing.module.ts index 1a6c609..02ad7fc 100644 --- a/front/src/app/app-routing.module.ts +++ b/front/src/app/app-routing.module.ts @@ -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, diff --git a/front/src/app/app.component.ts b/front/src/app/app.component.ts index e55bb0d..25cf9ab 100644 --- a/front/src/app/app.component.ts +++ b/front/src/app/app.component.ts @@ -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, }, ], }, diff --git a/front/src/app/app.module.ts b/front/src/app/app.module.ts index e63eeac..78f9f73 100644 --- a/front/src/app/app.module.ts +++ b/front/src/app/app.module.ts @@ -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, diff --git a/front/src/app/scene/index.ts b/front/src/app/scene/index.ts index f8cf3cf..784a946 100644 --- a/front/src/app/scene/index.ts +++ b/front/src/app/scene/index.ts @@ -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, }; diff --git a/front/src/app/scene/manage-accounts/manage-accounts.html b/front/src/app/scene/manage-accounts/manage-accounts.html new file mode 100644 index 0000000..90e2613 --- /dev/null +++ b/front/src/app/scene/manage-accounts/manage-accounts.html @@ -0,0 +1,100 @@ +
+
Manage User Accounts
+
+
+
+
Current users
+
List of all users
+
+
+ + + + + + + + + + + + + + + + + + + +
idlogine-mailadminblockedavatarlastConnection
{{user.id}}{{user.login}}{{user.email}}{{user.admin}}{{user.blocked}}{{user.avatar}}{{user.lastConnection}}
+
+
+ +
+
+
+

+
+
+
Create new user
+
Add a new user on the server
+
+
+ + + + + + + + + + + + + +
Login: + +
{{loginHelp}}
+
email: + +
{{emailHelp}}
+
Password: + +
{{passwordState}}
+
+ +
+
+ +
+
+
+
diff --git a/front/src/app/scene/manage-accounts/manage-accounts.less b/front/src/app/scene/manage-accounts/manage-accounts.less new file mode 100644 index 0000000..49e539a --- /dev/null +++ b/front/src/app/scene/manage-accounts/manage-accounts.less @@ -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; + } +} diff --git a/front/src/app/scene/manage-accounts/manage-accounts.ts b/front/src/app/scene/manage-accounts/manage-accounts.ts new file mode 100644 index 0000000..ab4e9c6 --- /dev/null +++ b/front/src/app/scene/manage-accounts/manage-accounts.ts @@ -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(); + } + +} diff --git a/front/src/app/service/admin-user.ts b/front/src/app/service/admin-user.ts index bc02d0e..e380ae7 100644 --- a/front/src/app/service/admin-user.ts +++ b/front/src/app/service/admin-user.ts @@ -118,6 +118,24 @@ export class AdminUserService { }); }); } + getUsers(): Promise { + 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));